Authenticating with a third-party OAuth2 scheme

This recipe uses the Spring social project in order to use the OAuth2 protocol from a client perspective.

Getting ready

We won't create an OAuth2 Authentication Server (AS) here. We will establish connections to third-party Authentication servers (Yahoo!) to authenticate on our application. Our application will be acting as a Service Provider (SP).

We will use Spring social whose first role is to manage social connections transparently and to provide a facade to invoke the provider APIs (Yahoo! Finance) using Java objects.

How to do it...

  1. Two Maven dependencies have been added for Spring social:
        <!– Spring Social Core –>
        <dependency>
          <groupId>org.springframework.social</groupId>
          <artifactId>spring-social-core</artifactId>
          <version>1.1.0.RELEASE</version>
        </dependency>
        <!– Spring Social Web (login/signup controllers) –>
        <dependency>
          <groupId>org.springframework.social</groupId>
          <artifactId>spring-social-web</artifactId>
          <version>1.1.0.RELEASE</version>
        </dependency>
  2. If we want to handle an OAuth2 connection to Twitter or Facebook, we would have to add the following dependencies as well:
      <!– Spring Social Twitter –>
        <dependency>
          <groupId>org.springframework.social</groupId>
          <artifactId>spring-social-twitter</artifactId>
          <version>1.1.0.RELEASE</version>
        </dependency>
        <!– Spring Social Facebook –>
          <dependency>
          <groupId>org.springframework.social</groupId>
          <artifactId>spring-social-facebook</artifactId>
          <version>1.1.0.RELEASE</version>
        </dependency>
  3. After the BASIC authentication section, the Spring Security configuration file hasn't changed much. A few interceptors can be noticed in the http bean:
    <security:http create-session="stateless" entry-point-ref="authenticationEntryPoint" authentication-manager-ref="authenticationManager">
      <security:custom-filter ref="basicAuthenticationFilter" after="BASIC_AUTH_FILTER" />
      <security:csrf disabled="true"/>
      <security:intercept-url pattern="/signup" access="permitAll"/>
      ...
      <security:intercept-url pattern="/**" access="permitAll"/>
    </security:http>

    With the following SocialUserConnectionRepositoryImpl, we have created our own implementation of org.sfw.social.connect.ConnectionRepository, which is a Spring Social core interface with methods to manage the social-users connections. The code is as follows:

    @Transactional(propagation = Propagation.REQUIRED)
    @SuppressWarnings("unchecked")
    public class SocialUserConnectionRepositoryImpl implements ConnectionRepository {
    @Autowired
    private SocialUserRepository socialUserRepository;
    private final String userId;
    private final ConnectionFactoryLocator connectionFactoryLocator;
    private final TextEncryptor textEncryptor;
    public SocialUserConnectionRepositoryImpl(String userId, SocialUserRepository socialUserRepository, ConnectionFactoryLocator connectionFactoryLocator, TextEncryptor textEncryptor){
        this.socialUserRepository = socialUserRepository;
        this.userId = userId;
        this.connectionFactoryLocator = connectionFactoryLocator;
        this.textEncryptor = textEncryptor;
    }
     ...
    public void addConnection(Connection<?> connection) {
    try {
       ConnectionData data = connection.createData();
      int rank = socialUserRepository.getRank(userId, data.getProviderId()) ;
      socialUserRepository.create(userId, data.getProviderId(), data.getProviderUserId(), rank, data.getDisplayName(), data.getProfileUrl(), data.getImageUrl(), encrypt(data.getAccessToken()), encrypt(data.getSecret()), encrypt(data.getRefreshToken()), data.getExpireTime()    );
        } catch (DuplicateKeyException e) {
      throw new   DuplicateConnectionException(connection.getKey());
    }
    }
    ...
    public void removeConnections(String providerId) {
      socialUserRepository.delete(userId,providerId);
    }
      ...
    }

    Note

    In reality, this custom implementation extends and adapts the work from https://github.com/mschipperheyn/spring-social-jpa published under a GNU GPL license.

  4. As you can see, SocialUserConnectionRepositoryImpl makes use of a custom Spring Data JPA SocialUserRepository interface whose definition is as follows:
    public interface SocialUserRepository {
      List<SocialUser> findUsersConnectedTo(String providerId);
      ...
      List<String> findUserIdsByProviderIdAndProviderUserIds( String providerId, Set<String> providerUserIds);
    ...
      List<SocialUser> getPrimary(String userId, String providerId);
      ...
      SocialUser findFirstByUserIdAndProviderId(String userId, String providerId);
    }
  5. This Spring Data JPA repository supports a SocialUser entity (social connections) that we have created. This Entity is the direct model of the UserConnection SQL table that JdbcUsersConnectionRepository would expect to find if we would use this implementation rather than ours. The SocialUser definition is the following code:
    @Entity
    @Table(name="userconnection", uniqueConstraints = {@UniqueConstraint(columnNames = { ""userId", "providerId", "providerUserId" }), 
    @UniqueConstraint(columnNames = { "userId", "providerId", "rank" })})
    public class SocialUser {
      @Id
      @GeneratedValue
      private Integer id;
    
      @Column(name = "userId")
      private String userId;
    
      @Column(nullable = false)
      private String providerId;
      private String providerUserId;
    
      @Column(nullable = false)
      private int rank;
      private String displayName;
      private String profileUrl;
      private String imageUrl;
    
      @Lob
      @Column(nullable = false)
      private String accessToken;
      private String secret;
      private String refreshToken;
      private Long expireTime;
      private Date createDate = new Date();
      //+ getters / setters
      ...
    } 
  6. The SocialUserConnectionRepositoryImpl is instantiated in a higher-level service layer: SocialUserServiceImpl, which is an implementation of the Spring UsersConnectionRepository interface. This implementation is created as follows:
    @Transactional(readOnly = true)
    public class SocialUserServiceImpl implements SocialUserService {
       @Autowired
       private SocialUserRepository socialUserRepository;
      @Autowired
       private ConnectionFactoryLocator connectionFactoryLocator;
      @Autowired
      private UserRepository userRepository;
      private TextEncryptor textEncryptor = Encryptors.noOpText();
    public List<String> findUserIdsWithConnection(Connection<?> connection) {
       ConnectionKey key = connection.getKey();
      return socialUserRepository. findUserIdsByProviderIdAndProviderUserId(key.getProviderId(), key.getProviderUserId());
    }
    public Set<String> findUserIdsConnectedTo(String providerId, Set<String> providerUserIds) {
        return Sets.newHashSet(socialUserRepository.findUserIdsByProviderIdAndProviderUserIds(providerId, providerUserIds));
    }
    public ConnectionRepository createConnectionRepository(String userId) {
      if (userId == null) {
        throw new IllegalArgumentException"("userId cannot be null"");
      }
      return new SocialUserConnectionRepositoryImpl(
          userId,
          socialUserRepository,
          connectionFactoryLocator,
          textEncryptor);
    }
     	...
    }
  7. This higher level SocialUserServiceImpl is registered in the cloudstreetmarket-api Spring configuration file (dispatcher-context.xml) as a factory-bean that has the capability to produce SocialUserConnectionRepositoryImpl under a request-scope (for a specific social-user profile). The code is as follows:
    <bean id="usersConnectionRepository" class="edu.zc.csm.core.services.SocialUserServiceImpl"/>
    <bean id="connectionRepository" factory-method="createConnectionRepository" factory-bean="usersConnectionRepository" scope"="request">
      <constructor-arg value="#{request.userPrincipal.name}"/>
      <aop:scoped-proxy proxy-target-class"="false"" />
    </bean>
  8. Three other beans are defined in this dispatcher-context.xml file:
    <bean id="signInAdapter" class="edu.zc.csm.api.signin.SignInAdapterImpl"/>
    <bean id="connectionFactoryLocator" 
      class="org.sfw.social.connect.support. ConnectionFactoryRegistry">
      <property name="connectionFactories">
        <list>
        <bean class"="org.sfw.social.yahoo.connect.YahooOAuth2ConnectionFactory"">
          <constructor-arg value="${yahoo.client.token}"/>
          <constructor-arg value="${yahoo.client.secret}" />
          <constructor-arg value="${yahoo.signin.url}" />
        </bean>
        </list>
      </property>
    </bean>
    <bean class="org.sfw.social.connect.web.ProviderSignInController">
      <constructor-arg ref="connectionFactoryLocator"/>
      <constructor-arg ref="usersConnectionRepository"/>
      <constructor-arg ref="signInAdapter"/>
      <property name="signUpUrl" value="/signup"/>
      <property name="postSignInUrl" value="${frontend.home.page.url}"/>
    </bean>
  9. The The SignInAdapterImpl signs in a user in our application after the OAuth2 authentication. It performs what we want it to perform at this step from the application business point of view. The code is as follows:
    @Transactional(propagation = Propagation.REQUIRED)
    @PropertySource("classpath:application.properties")
    public class SignInAdapterImpl implements SignInAdapter{
      @Autowired
      private UserRepository userRepository;
      @Autowired
      private CommunityService communityService;
      @Autowired
      private SocialUserRepository socialUserRepository;
      @Value("${oauth.success.view}")
      private String successView;
      public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {
          User user = userRepository.findOne(userId);
          String view = null;
          if(user == null){
            //temporary user for Spring Security
            //won't be persisted
            user = new User(userId, communityService.generatePassword(), null, true, true, true, true, communityService.createAuthorities(newRole[]{Role.ROLE_BASIC, Role.ROLE_OAUTH2}));
        }
        else{
            //We have a successful previous oAuth 	//authentication
            //The user is already registered
            //Only the guid is sent back
            List<SocialUser> socialUsers = 
            socialUserRepository. findByProviderUserIdOrUserId(userId, userId);
            if(CollectionUtils.isNotEmpty(socialUsers)){
              //For now we only deal with Yahoo!
              view = successView.concat(
                "?spi=" + socialUsers.get(0) .getProviderUserId());
        }
        }
        communityService.signInUser(user);
        return view;
      }
    }
  10. The connectionFactoryLocator can also refer to more than one connection factories. In our case, we have only one: YahooOAuth2ConnectionFactory. These classes are the entry points of social providers APIs (written for Java). We can normally find them on the web (from official sources or not) for the OAuth protocol we target (OAuth1, OAuth1.0a, and OAuth2).

    Note

    There are few existing OAuth2 adaptors right now for Yahoo! We've had to do it ourselves. That's why these classes are available as sources and not as jar dependencies (in the Zipcloud project).

  11. When it comes to controllers' declarations, the dispatcher-context.xml configures a ProviderSignInController, which is completely abstracted in Spring Social Core. However, to register a OAuth2 user in our application (the first time the user visits the site), we have created a custom SignUpController:
    @Controller
    @RequestMapping"("/signup"")
    @PropertySource"("classpath:application.properties"")
    public class SignUpController extends CloudstreetApiWCI{
      @Autowired
      private CommunityService communityService;
      @Autowired
      private SignInAdapter signInAdapter;
      @Autowired
      private ConnectionRepository connectionRepository;
      @Value("${oauth.signup.success.view}")
      private String successView;
      @RequestMapping(method = RequestMethod.GET)
      public String getForm(NativeWebRequest request, @ModelAttribute User user) {
        String view = successView;
        // check if this is a new user signing in via //Spring Social
        Connection<?> connection = ProviderSignInUtils.getConnection(request);
          if (connection != null) {
            // populate new User from social connection //user profile
            UserProfile userProfile = connection.fetchUserProfile();
            user.setUsername(userProfile.getUsername());
            // finish social signup/login
            ProviderSignInUtils. handlePostSignUp(user.getUsername(), request);
            // sign the user in and send them to the user //home page
            signInAdapter.signIn(user.getUsername(), connection, request);
          view += ?spi=+ user.getUsername();
        }
        return view;
      }
    }
  12. It's time to try it now. To proceed, we suggest you to create a Yahoo! account. We are not actually sponsored by Yahoo! It is only for the strategy of our great Zipcloud company which is oriented on financial services. It is not only for Marissa Mayer's blue eyes! (https://login.yahoo.com).
  13. Start your Tomcat server and click on the login button (at the far right of the main menu). Then hit the Sign-in with Yahoo! button
  14. You should be redirected to the Yahoo! servers in order for you to authenticate on their side (if you are not logged-in already):
    How to do it...
  15. Once logged-in, agree that Cloudstreet Market will be able to access your profile and your contacts. We won't make use of contacts; however, we have the Java adaptors to access them. If it's too scary, just create an empty new Yahoo! account:
    How to do it...
  16. Click on the Agree button.
  17. Yahoo! should now redirect to the local cloudstreetmarket.com server and specifically to the /api/signin/yahoo handler with an authorization code as URL parameter.
  18. The application detects when in the Cloudstreet Market database there isn't any User registered for the SocialUser. This triggers the following popup and it should come back to the user until the account actually gets created:
    How to do it...
  19. Fill the form with the following data:
    username: <marcus>
    email: <[email protected]>
    password: <123456>
    preferred currency: <USD>

    Also, click on the user icon in order to upload a profile picture (if you wish). While doing so, make sure the property pictures.user.path in cloudstreetmarket-api/src/main/resources/application.properties is pointing to a created path on the filesystem.

  20. Once this step is done, the new public activity Marcus registers a new account should appear on the welcome page.
  21. Also, bound to each REST response from the API, the extra-headers Authenticated and WWW-Authenticate must be present. This is proof that we are authenticated with OAuth2 capability in the application.
    How to do it...

How it works...

We perform in this recipe a social integration within our application. An OAuth2 authentication involves a service provider (cloudstreetmarket.com) and an identity provider (Yahoo!).

This can only happen if a user owns (or is ready to own) an account on both parties. It is a very popular authentication protocol nowadays. As most of Internet users have at least one account in one of the main Social SaaS providers (Facebook, Twitter, LinkedIn, Yahoo!, and so on), this technique dramatically drops the registration time and login time spent on web service providers.

From the application point of view

When the sign in with Yahoo! button is clicked by the user, a HTTP POST request is made to one of our API handler /api/signin/yahoo. This handler corresponds to ProviderSignInController, which is abstracted by Spring Social.

  • This handler redirects the user to the Yahoo! servers where he can authenticate and give the right for the application to use his social identity and access some of his Yahoo! data.
  • Yahoo! sends an authorization-code to the application as a parameter of the redirection to the callback URL it performs.
  • The application processes the callback with the authorization code as parameter. This callback targets a different method-handler in the abstracted ProviderSignInController. This handler completes the connection recalling Yahoo! in order to exchange the authorization code against a refresh token and an access token. This operation is done transparently in the Spring Social background.
  • The same handler looks-up in database for an existing persisted social connection for that user:
    • If one connection is found, the user is authenticated with it in Spring Security and redirected to the home page of the portal with the Yahoo! user-ID as request parameter (parameter named spi).
    • If no connection is found, the user is redirected to the SignupController where his connection is created and persisted. He is then authenticated in Spring Security and redirected to the portal's home page with the Yahoo! user ID as request parameter (named spi).
  • When the portal home page is loaded, the Yahoo! user ID request parameter is detected and this identifier is stored in the HTML5 sessionStorage (we have done all this).
  • From now on, in every single AJAX request the user makes to the API, the spi identifier will be passed as a request header, until the user actually logs out or closes his browser.

From the Yahoo! point of view

The Yahoo! APIs provide two ways of authenticating with OAuth2. This induces two different flows: the Explicit OAuth2 flow, suited for a server-side (web) application and the Implicit OAuth2 flow that particularly benefits to frontend web clients. We will focus on the implemented explicit flow here.

OAuth2 explicit grant flow

Here's a summary picture of the communication protocol between our application and Yahoo!. This is more or less a standard OAuth2 conversation:

OAuth2 explicit grant flow

The parameters marked with the * symbol are optional in the communication. This flow is also detailed on the OAuth2 Yahoo! guide:

https://developer.yahoo.com/oauth2/guide/flows_authcode

Refresh-token and access-token

The difference between these two tokens must be understood. An access-token is used to identify the user (Yahoo! user) when performing operations on the Yahoo! API. As an example, below is a GET request that can be performed to retrieve the Yahoo! profile of a user identified by the Yahoo! ID abcdef123:

GET https://social.yahooapis.com/v1/user/abcdef123/profile
Authorization: Bearer aXJUKynsTUXLVY 

To provide identification to this call, the access-token must be passed in as the value of the Authorization request header with the Bearer keyword. In general, access-tokens have a very limited life (for Yahoo!, it is an hour).

A refresh-token is used to request new access-tokens. Refresh-tokens have much longer lives (for Yahoo!, they actually never expire, but they can be revoked).

Spring social – role and key features

The role of Spring social is to establish connections with Software-as-a-Service (SaaS) providers such as Facebook, Twitter, or Yahoo! Spring social is also responsible for invoking APIs on the application (Cloudstreet Market) server side on behalf of the users.

These two duties are both served in the spring-social-core dependency using the Connect Framework and the OAuth client support, respectively.

In short, Spring social is:

  • A Connect Framework handling the core authorization and connection flow with service providers
  • A Connect Controller that handles the OAuth exchange between a service provider, consumer, and user in a web application environment
  • A Sign-in Controller that allows users to authenticate in our application, signing in with their Saas provider account

Social connection persistence

The Spring social core provides classes able to persist social connections in database using JDBC (especially with JdbcUsersConnectionRepository). The module even embeds a SQL script for the schema definition:

create table UserConnection (userId varchar(255) not null,
    providerId varchar(255) not null,
    providerUserId varchar(255),
    rank int not null,
    displayName varchar(255),
    profileUrl varchar(512),
    imageUrl varchar(512),
    accessToken varchar(255) not null,
    secret varchar(255),
    refreshToken varchar(255),
    expireTime bigint,
    primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, rank);

When an application (like ours) uses JPA, an Entity can be created to represent this table in the persistence context. We have created the SocialUser Entity for this purpose in the sixth step of the recipe.

In this table Entity, you can see the following fields:

  • userId: This field matches the @Id (username) of the User when the user is registered. If the user is not yet registered, userId is the GUID (Yahoo! user ID, also called spi on the web side)
  • providerId: This field is the lowercase name of the provider: Yahoo, Facebook or Twitter.
  • providerUserId: This field is the GUID, the unique identifier in the provider's system (Yahoo! user ID or spi.).
  • accessToken, secret, refreshToken, and expireTime: These are the OAuth2 tokens (credentials) for the connection and their related information.

Two interfaces come with the framework:

  • ConnectionRepository: This manages the persistence of one user connection. Implementations are request-scoped for the identified user.
  • UsersConnectionRepository: This provides access to the global store of connections across all users.

If you remember, we created our own UsersConnectionRepository implementation (SocialUserServiceImpl). Registered in the dispatcher-servlet.xml file, this implementation acts as a factory to produce request-scope connectionRepository implementations (SocialUserConnectionRepositoryImpl):

<bean id="connectionRepository" factory-method="createConnectionRepository" factory-bean="usersConnectionRepository" scop="request">
  <constructor-arg value="#{request.userPrincipal.name}" />
  <aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="usersConnectionRepository" class="edu.zc.csm.core.services.SocialUserServiceImpl"/>

Those two custom implementations both use the Spring Data JPA SocialUserRepository that we have created for finding, updating, persisting, and removing connections.

In the SocialUserServiceImpl implementation of the UsersConnectionRepository interface, a ConnectionFactoryLocator property is autowired and a TextEncryptor property is initialized with a default NoOpTextEncryptor instance.

Note

The default TextEncryptor instance can be replaced with a proper encryption for the SocialUser data maintained in the database. Take a look at the spring-security-crypto module:

http://docs.spring.io/spring-security/site/docs/3.1.x/reference/crypto.html

Provider-specific configuration

The provider-specific configuration (Facebook, Twitter, Yahoo!) starts with definitions of the connectionFactoryLocator bean.

One entry-point – connectionFactoryLocator

The connectionFactoryLocator bean that we have defined in the dispatcher-servlet.xml plays a central role in Spring Social. Its registration is as follows:

<bean id="connectionFactoryLocator" class="org.sfw.social.connect.support.ConnectionFactoryRegistry">
  <property name="connectionFactories">
    <list>
    <bean class"="org.sfw.social.yahoo.connect. YahooOAuth2ConnectionFactory"">
        <constructor-arg value="${yahoo.client.token}" />
        <constructor-arg value="${yahoo.client.secret}" />
        <constructor-arg value="${yahoo.signin.url}" />
      </bean>
    </list>
  </property>
</bean>

With this bean, Spring social implements a ServiceLocator pattern that allows us to easily plug-in/plug-out new social connectors. Most importantly, it allows the system to resolve at runtime a provider-specific connector (a connectionFactory).

The specified Type for our connectionFactoryLocator is ConnectionFactoryRegistry, which is a provided implementation of the ConnectionFactoryLocator interface:

public interface ConnectionFactoryLocator {
    ConnectionFactory<?> getConnectionFactory(String providerId);
    <A> ConnectionFactory<A> getConnectionFactory(Class<A> apiType);
    Set<String> registeredProviderIds();
}

We have an example of the connectionFactory lookup in the ProviderSignInController.signin method:

ConnectionFactory<?> connectionFactory = connectionFactoryLocator.getConnectionFactory(providerId);

Here, the providerId argument is a simple String (yahoo in our case).

Provider-specific ConnectionFactories

The ConnectionFactory such as YahooOAuth2ConnectionFactory are registered in ConnectionFactoryRegistry with the OAuth2 consumer key and consumer secret, which identify (with authorization) our application on the provider's side.

We have developed the YahooOAuth2ConnectionFactory class, but you should be able to find your ProviderSpecificConnectionFactory either from official Spring Social subprojects (spring-social-facebook, spring-social-twitter, and so on) or from open sources projects.

Signing in with provider accounts

In order to perform the OAuth2 authentication steps, Spring social provides an abstracted Spring MVC Controller: ProviderSignInController.

This controller performs the OAuth flows and establishes connections with the provider. It tries to find a previously established connection and uses the connected account to authenticate the user in the application.

If no previous connection matches, the flow is sent to the created SignUpController matching the specific request mapping/signup. The user is not automatically registered as a CloudStreetMarket User at this point. We force the user to create his account manually via a Must-Register response header when an API call appears OAuth2 authenticated without a bound local user. This Must-Register response header triggers the create an account now popup on the client side (see in home_community_activity.js, the loadMore function).

It is during this registration that the connection (the SocialUser Entity) is synchronized with the created User Entity (see the CommunityController.createUser method).

The ProviderSignInController works closely with a SignInAdapter implementation (that we had to build as well) which actually authenticates the user into CloudStreetMarket with Spring Security. The authentication is triggered with the call to communityService.signInUser(user).

Here are the details of the method that creates the Authentication object and stores it into the SecurityContext:

@Override
public Authentication signInUser(User user) {
  Authentication authentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
  SecurityContextHolder.getContext().setAuthentication(authentication);
  return authentication;
}

We register and initialize a Spring bean for ProviderSigninController with the following configuration:

<bean class"="org.sfw.social.connect.web.ProviderSignInController"">
  <constructor-arg ref="connectionFactoryLocator"/>
  <constructor-arg ref="usersConnectionRepository"/>
  <constructor-arg ref="signInAdapter"/>
  <property name="signUpUrl"" value"="/signup"/>
  <property name="postSignInUrl" value="${frontend.home.page.url}"/>
</bean>

As you can see, we have specified the signUpUrl request mapping to redirect to our custom SignupController when no previous connection is found in database.

Alternatively, the specified postSignInUrl allows the user to be redirected to the home page of the portal when the ProviderSignInController resolves an existing connection to reuse.

There is more…

Let's have a look at other features of Spring social.

Performing authenticated API calls

In this recipe, we focused on presenting the OAuth2-client authentication process. In the next chapter, we will see how to use Spring social to perform requests to Yahoo! APIs on behalf of the users. We will see how can be used existing libraries in this purpose and how they work. In our case, we had to develop API connectors to Yahoo! financial APIs.

The Spring social ConnectController

Spring social web provides another abstracted controller which allows social users to directly interact with their social connections to connect, disconnect, and obtain their connection status. The ConnectController can also be used to build an interactive monitoring screen for managing connections to all the providers a site could possibly handle. Check out the Spring social reference for more details:

http://docs.spring.io/spring-social/docs/current/reference/htmlsingle/#connecting

See also

SocialAuthenticationFilter

This is a filter to be added to Spring Security so that a social authentication can be performed from the Spring Security filter-chain (and not externally as we did).

http://docs.spring.io/spring-social/docs/current/reference/htmlsingle/#enabling-provider-sign-in-with-code-socialauthenticationfilter-code

The list of Spring social connectors

You will find a list of implemented connectors to Saas-providers from the main page of the project: http://projects.spring.io/spring-social

Implementing an OAuth2 authentication server

You can use the Spring Security OAuth project:

http://projects.spring.io/spring-security-oauth

The harmonic development blog

The articles about Spring social have inspired this recipe. Feel free to visit this blog:

http://harmonicdevelopment.tumblr.com/post/13613051804/adding-spring-social-to-a-spring-mvc-and-spring

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset