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:
- User press "Login with Facebook" in Mobile Application
- Mobile Application call the
/api/auth/login-facebook/
passing the access-token
from Facebook
- In the API Application, I will check the
access-token
with the Facebook
package
- 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
- If the user exists, I will generate the
access-token
to grant access to my API Application
- 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!
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);
}