2. Service Provider 'Connect' Framework

The spring-social-core module includes a Service Provider 'Connect' Framework for managing connections to Software-as-a-Service (SaaS) providers such as Facebook and Twitter. This framework allows your application to establish connections between local user accounts and accounts those users have with external service providers. Once a connection is established, it can be be used to obtain a strongly-typed Java binding to the ServiceProvider's API, giving your application the ability to invoke the API on behalf of a user.

To illustrate, consider Facebook as an example ServiceProvider. Suppose your application, AcmeApp, allows users to share content with their Facebook friends. To support this, a connection needs to be established between a user's AcmeApp account and her Facebook account. Once established, a FacebookApi instance can be obtained and used to post content to the user's wall. Spring Social's 'Connect' framework provides a clean API for managing service provider connections such as this.

2.1 Base API

The ServiceProvider<S> interface defines the central API for managing connections to an external service provider such as Facebook:

public interface ServiceProvider<S> {
    
    String getId();
    
    boolean isConnected(Serializable accountId);
    
    List<ServiceProviderConnection<S>> getConnections(Serializable accountId);

}
			

The <S> parameterized type represents the Java binding to the ServiceProvider's API. For example, the Facebook ServiceProvider implementation is parameterized as ServiceProvider<FacebookApi>, where FacebookApi is the Java interface that may be used to invoke Facebook's graph API on behalf of a user.

Each ServiceProvider is identified by an ID, as returned by the getId() method. This id is expected to be unique across all ServiceProviders registered with your application.

A single local user account can have one-to-many connections established with a ServiceProvider, where each connection represents a link between the local user's account and an external account the user has on the provider's system. isConnected() checks to see if any connections exist between a user account and the service provider. If there are connections, getConnections() returns them in rank order.

With a reference to a ServiceProviderConnection you can do the following:

public interface ServiceProviderConnection<S> {

    S getServiceApi();

    void disconnect();
	
}
			

getServiceApi() returns a Java binding to the ServiceProvider's API for the external user account associated with the connection. The API can be used to access and update user data on the provider's system.

disconnect() may be used to remove the connection with the ServiceProvider, if it is no longer desired.

To put this framework into action, consider Twitter as an example ServiceProvider. Suppose user 'kdonald' of AcmeApp has three Twitter accounts and has connected with each of them:

  1. ServiceProvider#getId() would return 'twitter'.

  2. ServiceProvider#isConnected("kdonald") would return 'true'.

  3. ServiceProvider#getConnections("kdonald") would return a 'connections' List with three elements, one for each Twitter account.

  4. connections.get(0) would return the 'connection' to the first Twitter account, and connection.getServiceApi() would return a TwitterApi that can access and update information about that Twitter account.

  5. connections.get(1) and connections.get(2) would allow AcmeApp to access and update information about the second and third Twitter accounts, respectively.

  6. connection.disconnect() can be called to remove a connection, at which the linked Twitter account is no longer accessible to the application.

2.2 Establishing Connections

So far we have discussed how existing connections are managed using the ServiceProvider framework, but we have not yet discussed how new connections are established. The manner in which connections between local user accounts and external provider accounts are established varies based on the authorization protocol used by the ServiceProvider. Some service providers use OAuth, others use Basic Auth, others may use something else. Spring Social currently provides native support for OAuth-based service providers, including support for OAuth 1 and OAuth 2. This covers the leading social networks, such as Facebook and Twitter, all of which use OAuth to secure their APIs. Support for other authorization protocols can be added by extending the framework.

Because each authorization protocol is different, protocol-specific details are kept out of the base ServiceProvider interface. Sub-interfaces have been defined for each protocol, reflecting a distinct ServiceProvider type. In the following sections, we will discuss each type of ServiceProvider supported by the framework. Each section will also describe the protocol-specific flow required to establish a new connection.

2.2.1 OAuth2 Service Providers

OAuth 2 is rapidly becoming a preferred authorization protocol, and is used by major service providers such as Facebook, Github, Gowalla, and 37signals. In Spring Social, the OAuth2ServiceProvider interface models a service provider based on the OAuth 2 protocol:

public interface OAuth2ServiceProvider<S> extends ServiceProvider<S> {

    OAuth2Operations getOAuthOperations();

    ServiceProviderConnection<S> connect(Serializable accountId, AccessGrant accessGrant);

}
			

getOAuthOperations() returns an API to use to conduct the authorization flow, or "OAuth Dance", with a service provider. The result of this flow is an AccessGrant that can be used to establish a connection with a local user account by calling connect. The OAuth2Operations interface is shown below:

public interface OAuth2Operations {

    String buildAuthorizeUrl(String redirectUri, String scope);

    AccessGrant exchangeForAccess(String authorizationGrant, String redirectUri);

}
			

Callers are first expected to call buildAuthorizeUrl(String, String) to construct the URL to redirect the user to for connection authorization. Upon user authorization, the authorizationGrant returned by the provider should be exchanged for an AccessGrant. The AccessGrant should then used to create a connection. This flow is illustrated below:

As you can see, there is a back-and-forth conversation that takes place between the application and the service provider to grant the application access to the provider account. This exchange, commonly known as the "OAuth Dance", follows these steps:

  1. The flow starts by the application redirecting the user to the provider's authorization URL. Here the provider displays a web page asking the user if he or she wishes to grant the application access to read and update their data.

  2. The user agrees to grant the application access.

  3. The service provider redirects the user back to the application (via the redirect URI), passing an authorization code as a parameter.

  4. The application exchanges the authorization grant for an access grant.

  5. The service provider issues the access grant to the application. The grant includes an access token and a refresh token. One receipt of these tokens, the "OAuth dance" is complete.

  6. The application uses the AccessGrant to establish a connection between the local user account and the external provider account. With the connection established, the application can now obtain a reference to the Service API and invoke the provider on behalf of the user.

2.2.2 OAuth1 Service Providers

OAuth 1 is the previous version of the OAuth protocol. It is more complex OAuth 2, and sufficiently different that it is supported separately. Twitter, Linked In, and TripIt are some of the well-known ServiceProviders that use OAuth 1. In Spring Social, the OAuth1ServiceProvider interface models a service provider based on the OAuth 1 protocol:
public interface OAuth1ServiceProvider<S> extends ServiceProvider<S> {

    OAuth1Operations getOAuthOperations();

    ServiceProviderConnection<S> connect(Serializable accountId, OAuthToken accessToken);

}
			

Like a OAuth2-based provider, getOAuthOperations() returns an API to use to conduct the authorization flow, or "OAuth Dance". The result of the OAuth 1 flow is an OAuthToken that can be used to establish a connection with a local user account by calling connect. The OAuth1Operations interface is shown below:

public interface OAuth1Operations {

    OAuthToken fetchNewRequestToken(String callbackUrl);

    String buildAuthorizeUrl(String requestToken);

    OAuthToken exchangeForAccessToken(AuthorizedRequestToken requestToken);

}
			

Callers are first expected to call fetchNewRequestToken(String) obtain a temporary token from the ServiceProvider to use during the authorization session. Next, callers should call buildAuthorizeUrl(String) to construct the URL to redirect the user to for connection authorization. Upon user authorization, the authorized request token returned by the provider should be exchanged for an access token. The access token should then used to create a connection. This flow is illustrated below:

  1. The flow starts with the application asking for a request token. The purpose of the request token is to obtain user approval and it can only be used to obtain an access token. In OAuth 1.0a, the consumer callback URL is passed to the provider when asking for a request token.

  2. The service provider issues a request token to the consumer.

  3. The application redirects the user to the provider's authorization page, passing the request token as a parameter. In OAuth 1.0, the callback URL is also passed as a parameter in this step.

  4. The service provider prompts the user to authorize the consumer application and the user agrees.

  5. The service provider redirects the user's browser back to the application (via the callback URL). In OAuth 1.0a, this redirect includes a verifier code as a parameter. At this point, the request token is authorized.

  6. The application exchanges the authorized request token (including the verifier in OAuth 1.0a) for an access token.

  7. The service provider issues an access token to the consumer. The "dance" is now complete.

  8. The application uses the access token to establish a connection between the local user account and the external provider account. With the connection established, the application can now obtain a reference to the Service API and invoke the provider on behalf of the user.