Context

The OAuth2 specification defines and describes a number of ways for client applications to obtain access tokens. This tokens essentially identifies a user’s permission to access protected resources. These methods of obtaining access tokens defined by the specification are called Grants.

Each of these grants has specific use-cases and are only suitable for use in the context in which they’ve been defined. The specification doesn’t exactly define a Social Network context but it does define one that’s close enough for our use-case —Resource owner credentials grant for trusted first-party clients. Going forward, the terms Resource-owner and User would be used interchangeably.

Traditionally, to authenticate users on your APIs for your first-party clients you’ll want to use the Resource-owner Credentials Grant. This allows your users to authenticate by providing their credentials (usually username and password) to obtain a token. Let’s take a look at the request format for obtaining a token using this grant.

The client sends a POST request with the following body parameters to the authorization server:

grant_type with value password

with value client_id with the client’s ID

with the client’s ID client_secret with the client’s secret

with the client’s secret username with the user’s username

with the user’s username password with the user’s password

with the user’s password scope with a list of scope separated by spaces.

You’ll notice that this grant accepts a client_secret but there’s a security concern about first-party clients like native apps and SPAs that can’t store this securely. The security implications of this is beyond the scope of this article but there are ways to mitigate this such as using a proxied backend to interface with the authorization server. However, for the purpose of demonstrating the idea behind this article, we’ll assume that the first-party client can keep a secret.

For social networks, using a username and a password as credentials would be counterintuitive and defeats the entire purpose of integrating social authentication in the first place. As mentioned earlier, the specification doesn’t define a Social Network Grant so we’ll be implementing one ourselves.

Social Network Grant

To create our social network grant, we can tailor the Resource-owner Credentials Grant to fit our use-case and build the grant off of its structure. Let’s define the typical authentication flow when working with social network authentication, this will inform us on how to tailor the authorization grant and identify elements that would be candidates for credentials.

The Flow

The client makes a request to the social network provider (Google for example)

The user authenticates on the provider and gets an ephemeral access code that would be used to obtain a token

The client exchanges the code for an access token and may now use the access token to access protected resources on the social network provider within the requested scopes.

However, the access token obtained in the last step is only valid for the social network provider, it can’t be used to access protected resources on our resource server. Since we effectively now act as an authorization server, we need a way to authenticate the user and issue them access tokens that are valid on our resource server. At the end of the authentication flow above, we ended up with an access token that essentially represents the user trying to access our API. This access token can now become part of the User’s credentials.

But the access token in itself is pretty much useless we know which social network provider it’s authorized for. The network provider and the access token obtained from the provider are two good candidates for a User’s credentials.

Now that we’ve identified what elements should make up credentials in a social network context, we can now go ahead and define the request structure for obtaining access tokens from our authorization server using our bespoke grant:

grant_type with value social

with value client_id with the client’s ID

with the client’s ID client_secret with the client’s secret

with the client’s secret provider with the name of the social provider

with the name of the social provider access_token with the access token from the social provider

with the access token from the social provider scope with a list of scope separated by spaces.

Below is a graphic illustration of the authentication process:

Implementation

We’ve now defined something of an acceptable structure for the authentication request to obtain an access token from our authorization server and we can go ahead and implement this grant. To get started, we’ll install the Laravel passport package. Passport will act as a bridge to help us manage our clients, generate access tokens and authorize requests into our API. (See installation for instructions) We’ll also be needing the Laravel Socialite package to help us validate the User on various social networks.

Once we’ve installed and configured Laravel passport and socialite, the next logical step is to create our Social Grant and enable it on the authorization server so that it can respond to access token requests.

Grants in the authorization server implement the respondToAccessTokenRequest method, this method handles requests for access tokens for the specified grant. For our purpose, the validateUser method is the most important.

Within this grant, we need a way to retrieve the User by their access token, this means validating their credentials. This grant depends on a SocialUserProvider that validates the given access token from the social network provider against the social network provider. The validateUser method returns an instance of UserEntityInterface or throws an OAuthServerException if the access token is invalid. The SocialUserProvider implements a SocialUserProviderInterface. The interface declares a single method getUserEntityByAccessToken that retrieves the social user from the provider:

The SocialUserProvider Implementation:

First, we attempt to retrieve the user from the social network provider by their access token using Laravel Socialite. If this operation is successful, we’ll convert the user from the social network provider into a resource owner on our resource server and return the entity back out to the authorization server. We throw an OAuthServerException exception if the operation isn’t successful.

Next, we define the UserRepository implementation on our resource server to retrieve or create a resource owner from a social user:

Finally, we will enable this grant on the authorization server from a service provider so that it can respond to access token requests:

The authorization server can now issue new access and refresh tokens to the resource owner and the authentication flow is complete.

Requesting All Scopes

When you authenticate users on your first-party clients, you may want to authorize the access token for all scopes that are supported by the application. This is particularly true for resource-owner credentials and client credentials grants. Since our social grant is something of a resource-owner credentials grant, we can enable this behavior on the authorization server so that it can grant all scopes to this grant type on request. To do this, we will extend the ScopeRepository implemented by Laravel passport on the authorization server, override the finalizeScopes method and inject the modified ScopeRepository on the grant. The finalizeScopes method on the ScopeRepository filters the scope against the ones supported by the application and also based on the grant type:

We essentially just let requests for all scopes fall through to Passport and let it handle checking the scopes. Passport will allow all scopes if the scope is an asterisk * . The can method on the token instance on the resource server will always return true for this scope. Since we don’t want to allow this behavior on all grant types on the authorization server but the Social Grant, we will only set it on that grant when we enable the grant in the service provider:

The code is available on Github if you’re interested.

Conclusion

We’ve talked about how to authenticate social network users on your OAuth server from first-party applications. This is achieved by creating and enabling a Social Grant type on the authorization server. In practice, this solution is not limited to OAuth, it can also be used outside the context of OAuth to authenticate social network users on your API. The key elements are the Social Network Provider and the Access Token from the provider which would be used to validate users on the provider before issuing access tokens (and refresh tokens) to them.

Thanks for reading and I hope that this article was helpful.