16

I'm implementing an OAuth 2 based authorization model for an application I'm developing. I'm offering end-users the ability to login with Facebook or by setting up an email/password account with my API. The email/password authentication is straightforward using a password grant. I'm looking for help with the Facebook login flow.

My application is a single-page application that consumes a JSON API (my "resource server"). I'm using the Facebook JavaScript SDK to authorize the web app to access the end-user's email address.

When a user attempts to login with Facebook, the entire process takes place between Facebook and the web application. As a result, my API can't trust the Facebook authorization token until it verifies the token with Facebook's OAuth server.

As of right now I'm passing the Facebook accessToken to my API, which then verifies the user's authorization with Facebook via a server-to-server call of the "me" graph API. Here's an illustration of my current setup:

My current OAuth flow

So, at this point, I have a Facebook access token and an email address. I need to persist my session between my API server and the web application. What is the standard method of persisting the session at this point?

From reading the OAuth documentation, it seems that this is the type of situation that calls for an "implicit grant" between my API server and the web application, but that grant type is not available in the OAuth package I'm using. Also the author of the package says implicit grants are "very insecure".

My other thought is that I can create a random client ID and client secret, then pass them back to the web app so it can request an access token via a credentials grant. This seems illogical to me. Why wouldn't I just create an access token and send it back to the client to use directly?

I should be maintaining authentication directly between my web app and API server after the initial authorization from Facebook, is that correct?

I realize I could just generate a random password and send the user an HTTP Basic token, but I'd prefer to use OAuth unless there are no benefits.

Ben Harold
  • 6,242
  • 6
  • 47
  • 71

2 Answers2

2

You can code your own custom grant class and authentication provider in your oauth implementation if you can.

Define a new client id for this kind of authentications.

  1. User authenticates via using Facebook.

  2. Facebook generates a token for client.

  3. Client sends a request to your backend application with this access token, email adress and authentication type/client id(Lets say 'facebook')

  4. Your custom grant decides which kind of token will be used. You can control client id/authentication type(above).
  5. If auth type is 'facebook', you will ask that access token to session store if it is not here you will ask to Facebook api and then if it is ok, save it in your session store(database, redis whatever else). We put in a session store to not ask fb everytime.(it is so basical concept)
  6. Then oauth will return token with your configuration.
  7. Your client will send the token for next requests with client id/authentication type. credentials will be facebook token and email will be username.

Custom grant decides token type(Spring security-oauth implementation)

    String clientId = tokenRequest.getClientId();
    ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
    Map<String, String> parameters = tokenRequest.getRequestParameters();
    String username = parameters.get("username"); //username is email
    String password = parameters.get("password"); //password is fb access token
    Authentication authentication = null;
    if ("facebook".equals(clientId)) {
        authentication = new FacebookAuthenticationToken(username, password);
    } else {
        authentication = new UserAuthenticationToken(username, password);
    }
    authentication = authenticationManager.authenticate(authentication);

Authentication Provider checks facebook token and validate it.

public class FacebookAuthenticationProvider implements AuthenticationProvider {

@Autowired
private FacebookApi facebookApi;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String username = authentication.getName();
    String password = (String) authentication.getCredentials();
    // you can check your facebook session store
    boolean valid = facebookApi.isValidToken(username, password);

    if (!valid) {
        throw new BadCredentialsException("Username not found.");
    }

    Collection<? extends GrantedAuthority> authorities = user.getAuthorities();

    return new FacebookAuthenticationToken(user, password, authorities);
}

@Override
public boolean supports(Class<?> authentication) {
    return FacebookAuthenticationToken.class.isAssignableFrom(authentication);
}
}

I put codes to be sample.

I usually force the users to set a password for my internal app after first facebook login (getting their email). Then everything is going much more simple. You can figure out what i mean.

1

We are doing exactly what you are talking about on DynamicApis.com.

Here is what we are doing:

1) Prompt a user to log in. In our case we author oAuth2 access tokens ourselves OR we can proxy you through to facebook, github, etc... But at the end of the day the oAuth2 token that drives the session for DynamicApis is owned by us. But the rest of the workflow would be the exact same if it were authored entirely by a 3rd party like GitHub.

2) User logs in with email/passowrd.

3) User is redirected to DynamicApis.com with the authorization code

4) DynamicApis.com exchanges authorization code for access token (server to server call). At this point I have just explained oAuth2 flow

5) At this point we (let's say encrypt) the access token that was returned and issued on behalf of that user and use that as the KEY to a session provider (let's say Sql Server based). This session provider is owned by DynamicApis.com.

6) Now your session provider works exactly as it normally would. You have a key that represents a logged in user (hashed/encrypted access token) and multiple key/values associated to that key.

  • Your session provider will persist the session state (key value pairs) of data that you want to store on behalf of the user in the database or whatever you use

  • The session provider should have an expiration date equal to the expiration date of the access token (or a date less than the access token).

  • When the user logs out the session is destroyed.

  • The session key should exist in encrypted form in a cookie, url, or hidden on the page (just as session keys are persisted today)

7) Poof. You have a session for a logged in user.

You can actually reuse much of the built in ASP.NET session provider logic if you want to. Or you can build your own home grown system like we have at DynamicApis.com.

Jerrod Horton
  • 1,605
  • 1
  • 15
  • 30
  • 10
    This answer looks more than an advertisement rather than a solution to the problem. – RandyTek Jun 01 '16 at 00:15
  • 2
    No it's not. This is how I went about doing it on a platform that is working today. It can be seen and used by the person asking the question as well as correctly answering the question. – Jerrod Horton Jun 01 '16 at 15:57