Use OpenIdConnectServer and try to connect to Facebook through API service
Asked Answered
W

1

7

I'm trying to figure out a way of making my API being able to associate a user from Facebook to my Identity users.

The application context

I'm developing a mobile application (In Xamarin) that needs to make login with Username/Password and with Facebook. I have already set the app.UseOpenIdConnectServer configuration and created the custom Provider so my application is already working with Username/Password.

Now I'm trying to make this integration with Facebook and not finding a way I can achieve this.

I was thinking in creating a service in the API like /api/auth/login-facebook/ passing the access-token from Facebook but I need to return the access-token of my API application to the mobile application so the mobile app could call all the other services that needs authorization.

Any help about that?

A visual way of what I'm trying to get:

  1. User press "Login with Facebook" in Mobile Application
  2. Mobile Application call the /api/auth/login-facebook/ passing the access-token from Facebook
  3. In the API Application, I will check the access-token with the Facebook package
  4. If the user doesn't exists, I will create him with the data that Facebook returned me and after that I will generate the access-token to grant access to my API Application
  5. If the user exists, I will generate the access-token to grant access to my API Application
  6. Return the access-token to the Mobile Application so it can call the other services

If my knowledge is wrong and I should do this integration/login in another way, please fell free to tell me!

Watford answered 18/8, 2016 at 1:33 Comment(0)
I
7

The flow you describe is very similar to "assertion grants", a flow that was standardized last year.

To use this flow, you usually have to retrieve a standard token from the external provider (e.g a JWT or a SAML assertion) so your own authorization server can validate it and extract the claims it exposes. Unfortunately, this is not something you can do with Facebook or with most social providers in general.

A new OAuth2 draft will likely help change that in the future, but it will probably take a while before major services start implementing it.

The good news is that nothing prevents you from creating your own "Facebook access token" grant type in the meantime. Here's how you could implement the assertion grant with ASOS beta6:

public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
{
    // Reject the token request if it doesn't use grant_type=password, refresh_token
    // or urn:ietf:params:oauth:grant-type:facebook_access_token.
    if (!context.Request.IsPasswordGrantType() &&
        !context.Request.IsRefreshTokenGrantType() &&
         context.Request.GrantType != "urn:ietf:params:oauth:grant-type:facebook_access_token")
    {
        context.Reject(
            error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
            description: "The specified grant type is not supported by this server.");

        return Task.FromResult(0);
    }

    // Reject the token request if the assertion parameter is missing.
    if (context.Request.GrantType == "urn:ietf:params:oauth:grant-type:facebook_access_token" &&
        string.IsNullOrEmpty(context.Request.Assertion))
    {
        context.Reject(
            error: OpenIdConnectConstants.Errors.InvalidRequest,
            description: "The assertion is missing.");

        return Task.FromResult(0);
    }

    // Since there's only one application and since it's a public client
    // (i.e a client that cannot keep its credentials private), call Skip()
    // to inform the server the request should be accepted without 
    // enforcing client authentication.
    context.Skip();

    return Task.FromResult(0);
}

public override Task HandleTokenRequest(HandleTokenRequestContext context)
{
    // Only handle grant_type=password and urn:ietf:params:oauth:grant-type:facebook_access_token
    // requests and let the OpenID Connect server middleware handle the refresh token requests.
    if (context.Request.IsPasswordGrantType())
    {
        // Skipped for brevity.
    }

    else if (context.Request.GrantType == "urn:ietf:params:oauth:grant-type:facebook_access_token")
    {
        // The assertion corresponds to the Facebook access token.
        var assertion = context.Request.Assertion;

        // Create a new ClaimsIdentity containing the claims that
        // will be used to create an id_token and/or an access token.
        var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);

        // Validate the access token using Facebook's token validation
        // endpoint and add the user claims you retrieved here.
        identity.AddClaim(ClaimTypes.NameIdentifier, "FB user identifier");

        // Create a new authentication ticket holding the user identity.
        var ticket = new AuthenticationTicket(
            new ClaimsPrincipal(identity),
            new AuthenticationProperties(),
            OpenIdConnectServerDefaults.AuthenticationScheme);

        // Set the list of scopes granted to the client application.
        ticket.SetScopes(new[]
        {
            /* openid: */ OpenIdConnectConstants.Scopes.OpenId,
            /* email: */ OpenIdConnectConstants.Scopes.Email,
            /* profile: */ OpenIdConnectConstants.Scopes.Profile,
            /* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess
        }.Intersect(context.Request.GetScopes()));

        context.Validate(ticket);
    }

    return Task.FromResult(0);
}

enter image description here

Ineffable answered 18/8, 2016 at 12:18 Comment(4)
@Pinpoint Great answer, but how does refresh token occurs in this particular flow? The access_token issued by Google would expire, so is it possible to implement kinda "refresh token flow" using Assertion grant?Gabionade
@Ben while my screenshot doesn't show it, a refresh token is indeed returned if you request and grant the offline_access scope. In this case, you won't need the initial "external" access token when redeeming the refresh token for a new "local" access token.Plasm
@Pinpoint i am getting this error { "error": "invalid_client", "error_description": "The specified 'client_id' parameter is invalid." }Bounder
client_id is optional so i remove it and now its working fineBounder

© 2022 - 2024 — McMap. All rights reserved.