OpenID Connect server. Generate access token based on 3-rd party token (social login) - asp.net-core

I had implemented OpenID Connect server that generates access tokens for mobile client based on username/password using OpenIddict.
My next goal was to provide ability to generate Access Token using 3-rd party tokens (social login for example), and I started from integration with Google token, but stuck as cannot find any samples/informations about how to do this.
The only one idea that I currently have is to make request to "/connect/token" endpoint and send Google token in "code" parameter, for example in "google:" format, then override OpenIdConnectServerProvider.DeserializeAuthorizationCode method:
Called when receiving an authorization code. An application may use this context to deserialize the code using a custom format and to skip the default logic using
So I have created own CustomProvider class based on OpenIddictProvider, registered it
services.AddOpenIddict<ApplicationUser, ApplicationRole, ApplicationDbContext, int>()
.Configure(builder =>
{ builder.Provider = new CustomProvider(sp.GetRequiredService<SignInService>()); }
and overridden the DeserializeAuthorizationCode method:
public override async Task DeserializeAuthorizationCode(DeserializeAuthorizationCodeContext context)
{
string code = context.Request.Code;
if (code.StartsWith("google:"))
{
string token = code.Replace("google:", "");
var principal = new GoogleTokenValidator().ValidateToken(token, null).Result;
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), "Bearer");
ticket.SetPresenters(context.Request.ClientId);
context.Ticket = ticket;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
context.HandleResponse();
await _signInService.Login(principal);
return;
}
else
{
base.DeserializeAuthorizationCode(context);
}
}
where GoogleTokenValidator is a custom class for Google token handling (it makes call to Google User Information Endpoint and generate ClaimsPrincipal), based on "copy-pasted" code from GoogleHandler class in aspnet/Security repo.
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...

In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
You're not only reinventing the wheel, but you're also implementing something totally non-standard that is not supported (at all) by OpenIddict.
Here's the approach I recommend (which is the one we use in the MVC server sample):
The OAuth2/OpenID Connect client application redirects the user agent to your authorization controller (you can take a look at this controller for an example).
OpenIddict will validate the authorization request and allow your controller to be invoked if it's fully valid.
If the user is not already logged in, your authorization controller will redirect the user to the login endpoint, provided by AccountController. At this point, you're free to propose local authentication (e.g using a username/password couple) or Google authentication (you can use the Google authentication middleware for that). You can even offer 2-FA as part of this login process.
Once the user is logged in (e.g after a registration process and/or an external authentication association), his/her browser is redirected back to the authorization endpoint, where a consent form indicating he/she's about to allow your JS app to access his personal data on his/her behalf is displayed.
When the user allows your client application to access his data, the request is handled by your authorization controller, that calls SignInAsync to inform OpenIddict that an authorization code/access token should be returned to your application.

Related

IdentityServer login without web interaction

I'm currently writing a Xamarin application and I wish to login to my app, this one use IdentityServer 4 for auth.
I wish to avoid any "web" interface for the email/password login, but I'm not sure how to do this (If possible).
So, for now I have a controller with a method like this:
public void LoginAsync(string cliendId, string clientSecret, string email, string password)
{
// Check the clientId & secrets match
....
// Sign in the user
var result = await SignInManager.PasswordSignInAsync(email, password, false, true);
// How to return a token to the Xamarin client here?
}
So, I have 2 questions please:
first I directly check the clientId & secret, is it the way to go? Or should I get a token, then protect this API, and then access it with the token.
how can I generate a new token for the Xamarin client to access the other APIs?
Thanks
Using OpenID Connect you are supposed to open a new web interface and let the user login through that page. Creating your own login/password form is a bad for many reasons. One is that the user does not now where his credentials will end up. Your application should never touch or see the users username/password.
If there is no user involved, then you can look at using the client credentials flow that is supported by IdentityServer.

Should Name Claim remain omitted in JWT Access Token with IdentityServer4 (using Identity) and ASP.Net Core API?

While configuring my IdentityServer4 (using Identity) resource owner grant flow with an asp.net core API backend, I got to thinking that perhaps the "Name" claim should remain omitted in the JWT access token for user security? This claim is not available with out of the box behavior of IS4.
Previously, I had been adding in the "Name" claim for the access token in my IS4 Config.cs file as follows:
var claims = new List<string>
{
JwtClaimTypes.Name
};
return new List<ApiResource>
{
new ApiResource("api1", "Auth API", claims)
};
I was doing this because it allows a straightforward approach to get a logged in user's ClaimsPrincipal.Identity.Name for user look up inside a Controller action.
var name = User.Identity.Name;
var user = await _userManager.FindByNameAsync(name);
However, IS4 access tokens (when using Identity) include the user's GUID id in the "Sub" claim. With this, we can also look up a user using the following:
var userId = User.Claims.FirstOrDefault(u => u.Type == "sub").Value;
var user = await _userManager.FindByIdAsync(userId);
I know there is slightly more processing with the LINQ query (hardly anything tbh), but I was thinking it might be of worth to protect a user's username (email address in my situation) if an access token ever fell into the wrong hands. Especially since JWT's are so easy to decode with the likes of jwt.io.
Do you guys agree or disagree? Or am I looking at this the wrong way and missing something?
JWT usually contain the public data and it is self-contained. i.e. You don't need to communicate with a backend server to construct user's identity. You should prevent the token fell into wrong hand by using https. Also, you should balance your token validity window(usability vs security) and use a nonce for maximizing the security.
I don't think 'name' should be omitted from claim collection. A valid use-case for what you are doing is that you need to make sure that changes to your user store immediately reflect in your web API. In the case of a self-contained token, if you change the 'name' in the data store, the user will not see that change until he was issued a new token. In this case use of a 'reference token' might be a good option.
Also, It looks like you are directly accessing user store from the web API. While you might have valid reasoning behind this, Idea of using token based authentication is to delegate authentication to external party(Identity Server). So common pattern is to
Include every public data that you require in the web API in the
access token.
If token getting too big, include a subset of claims in the token and query user info endpoint when required.
Use reference tokens if you have valid reasons to do so. But this will affect the performance as it will require back channel communication with identity server.

Get JSON Web Token payload data within controller action

I'm implementing JWT based authorization for my ASP.NET Web API application with Angular2 SPA. All is well and clear regarding authorization flow except for one detail. I am wondering how to get JWT payload information within the Web API controller action?
Looking through the web I can't find any solution that I would go for, for example, setting Thread.Principal or something like that.
What are the recommended ways to accomplish that?
The normal process to handle a JWT token as authentication in ASP.NET is:
Get the token from the request and ensure is valid.
Create a principal based on the information contained within the token and associate it with the request.
This implies that the payload information within the token is available through the request principal usually in the form of claims. For example, if your JWT contains information about the user roles it would get mapped to a role claim available in the principal.
You don't mention if you're using OWIN or not so I'll assume OWIN for the example, but it shouldn't really matter or be much different at the controller level.
Despite the fact you're concerned only with the data consumption part, if curious, you can read through this set of tutorials about ASP.NET Web API (OWIN) for a more in-depth look on the whole process:
Introduction
Authentication (HS256)
Authorization
The last one would be the one with most interest , you'll note the following code snippet on that one:
[HttpGet]
[Authorize]
[Route("claims")]
public object Claims()
{
var claimsIdentity = User.Identity as ClaimsIdentity;
return claimsIdentity.Claims.Select(c =>
new
{
Type = c.Type,
Value = c.Value
});
}
This relies on the User.Identity available within the controller to list all the claims of the currently authenticated identity. Unless you have an authentication pipeline configured rather different then what it's the norm, these claims are mapped from the JWT payload your API receives.

Save information immediately after Google login in Azure Mobile Services (.NET Back-end)

What I basically want to be able to do is authenticate to azure mobile services (using google or some other provider), and immediately save some of the user information (i.e. email address) on the server.
I know I could call a custom method from the app after authentication, but I was hoping to have some hook to do this straight after the google login on the server side.
Is this possible? How do I do it?!
This is currently only possible in the .NET runtime. If using the Node runtime, you will not be able to do this.
For the .NET runtime, you would want to create a class which inherits from GoogleLoginProvider (I'll call mine CustomGoogleLoginProvider), and then you'll need to override the CreateCredentials method:
public override ProviderCredentials CreateCredentials(ClaimsIdentity claimsIdentity)
{
// grab any information from claimsIdentity which you would like to store
// If you need the access token for use with the graph APIs, you can use the following
string providerAccessToken = claimsIdentity.GetClaimValueOrNull(ServiceClaimTypes.ProviderAccessToken);
// use the access token with HttpClient to get graph information to store
return base.CreateCredentials(claimsIdentity);
}
Then in your WebApiConfig.cs, add the following to the Register() method, immediately after the options object is created:
options.LoginProviders.Remove(typeof(GoogleLoginProvider));
options.LoginProviders.Add(typeof(CustomGoogleLoginProvider));
The CreateCredentials() method gets called immediately before a Mobile Services token is created. At this point, the Google token has been validated, and the claimsIdentity has been populated with whatever Google sent back.
Some information will be available in the claimsIdentity by default, but you may also have information which requires you to call through to Google. You can only do this if you set the proper scopes configured.
If you did want to go the custom API route, you would just need to make a call from your controller:
ServiceUser user = (ServiceUser)this.User;
GoogleCredentials creds = (await user.GetIdentitiesAsync()).OfType<GoogleCredentials>().FirstOrDefault();
string accessToken = creds.AccessToken;
The Node version of getIdentities() is documented here.

Problems configuring user authentication by external API on Symfony2

I have a problem authenticating users for my new Symfony2 application.
This applications gets all the info through an API, so no database is used. When a user goes to login page, he introduce the user and password in the login form. Then, I have to authenticate him using an API call. This API call returns "false" if it's not a user, and return a token key and a token secret if its a correct user. With this token key and secret, during the user session, I can make all the API requests I need for rendering all the pages of the application. Once the user session is over and token key and secret are erased, the user has to login again.
I don't know really how ti implement that. I read this http://symfony.com/doc/current/cookbook/security/custom_provider.html and that http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html, and I'm still so lost... :(
Can any one help me?
Thank you so much :)
If you want to write custom authentication you have found the correct links. As an example you can see the implementation of the OAuth authorization HWIOAuthBundle. But keep in mind that this type of authentication creates a user on your system. If you do not use a database, you must make a request to the API every time user send a request.
First you need to understand that there is no magic. On every request symfony checks if url matches one of the specified firewalls (see secutity.yml). Listener that fired you can see in the firewall factory. If matches are found, the action switches to the corresponding AuthenticationListener. Listener attempts to authenticate the credewntials by creating Token, which is sended to AuthenticationProvider
$this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
in AuthenticationProvider
public function authenticate(TokenInterface $token) {
...
}
AuthenticationProvider try to get user via UserProvider. In case of success, Token stored in the session. On subsequent requests, ContextListener comes into play first, checks the session, extract token and send it to AuthenticationProvider similar.
In general terms, the scheme looks like that. More info you can find examining the source code of Symfony Security component.
Really good starting point is a UsernamePasswordFormAuthenticationListener. It just take login and password from request and make simplest UsernamePasswordToken.
protected function attemptAuthentication(Request $request)
{
...
}
Good luck!