Get JSON Web Token payload data within controller action - asp.net-web-api2

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.

Related

Get "groups" claims from Okta using the OpenID Connect Authorization Code Flow

I'm trying to include "groups" claims in what is returned by Okta after a user authenticates. It returns them when the response_type is 'id_token' but not when response_type is 'code'. For the Authorization Code flow I would expect to get the groups claims from the userinfo endpoint but they're not there.
However I've read that the authorization code flow is more secure than the hybrid flow (id_token) so I'd like to ensure there is not a way to do this?
My webapp is built on ASPNET Core 3 and I've tried the Okta.AspNetCore Nuget package.
One thing that might trip you up is that Okta do return the tokens you ask for, but the OpenIDConnect handler in your client blocks them.
You need to explicitly map those extra claims in your client, using code like:
options.ClaimActions.MapUniqueJsonKey("website", "website");
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
options.ClaimActions.MapUniqueJsonKey("birthdate", "birthdate");
There is also this option you can set:
options.GetClaimsFromUserInfoEndpoint = true;
Do verify using tools like Fiddler the the claims actually is returned or not.
And yes, authorization code flow is what you should aim to use.
/userinfo response should contain all claims (for all flows including authorization code flow) including 'groups' as long as the groups scope is sent in the requests to mint the token.
Could you make sure the user is part of this group and the right scope is passed in the request ?
You can easily add 'groups' claim in access token as well. You can refer to the guide below:
https://developer.okta.com/docs/guides/customize-tokens-groups-claim/overview/

How to exchange an authorization token on the server side

I've got an ASP.Net Core 2.1 Web API, using OAuth authentication. In addition, we are using claims-based authentication, with the claims encoded in the auth token.
This has been working well for a while, but lately things have gotten out of hand when a new requirement came up that radically multiplied the number of claims that had to be stored in the token. To cut a long story short, we're now at the point where the auth token is so long that we had to reconfigure our server to allow headers over 128k.
This is obviously untenable; just from the perspective of network traffic it's absurd to have to send over 100k of headers along with the simplest GET request. So now I'm thinking along the following lines: instead of encoding the claims in the token, just use a Guid token and store the claims in the database along with the token, so when we validate the token we can pull out the claims at the same time and exchange those locally.
Theoretically this should solve our entire problem in one fell swoop, but I'm just stuck on the implementation details, specifically: since the claim-based authentication is expecting to find the claims encoded in the auth token, how do I swap out the Guid auth token for the one that includes all the claims?
Existing code:
public class MyAuthenticationEvents : OAuthValidationEvents
{
public override async Task ValidateToken(ValidateTokenContext context)
{
if (context.Properties.ExpiresUtc < DateTime.UtcNow)
{
context.Fail("Access Token has expired.");
return;
}
if (!await TokenIsValidAsync(context)) // code to validate the auth token against the database, could be modified to return claims
{
context.Fail("Access Token has not been properly set or has been invalidated.");
return;
}
// Here I would expect to do some skullduggery to switch the compact Guid token for a large token containing claims
context.Success();
}
}
What's the correct syntax? Or am I barking up the wrong tree, and there's an entirely better way of achieving my objective?
Turned out to be pretty simple. All I needed to do in the ValidateToken() method was to create a new ClaimsPrincipal using a ClaimsIdentity which I reconstructed based on the serialized claims that I stored on the database:
... // create claimsIdentity from serialized claims
context.Principal = new ClaimsPrincipal(claimsIdentity);
context.Success();
}
Just one thing to take note of: on the initial login, it is mandatory to return a Subject claim, so in the HandleTokenRequest() method we still need:
identity.AddClaim(new Claim(OpenIdConnectConstants.Claims.Subject, user.Username));
Your existing authentication middleware is populating claims on a ClaimsPrincipal based on the contents of the JWT, but by the time your authorization handlers inspect the claims they're not read directly from the token, so there's no need to modify the token itself.
You should be able to access and modify the current user (HttpContext.User) claims from middleware, so you can make custom middleware that executes after the authentication middleware and adds claims based on the results of the database lookup.
Edit: As Shaul points out in his answer, this can also be done in the Validate method of your authentication components. That's actually a good place to do it in this case. Separate middleware (as I described) would work if you wanted the 'translation' of claims to those from a database to be decoupled from the auth process. The key point is that ASP.NET Core stores user claims in ClaimsPrincipal objects and you just need to update that rather than worrying about the token itself.

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.

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

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.

Different Authentication Schemes for the Same ASP.NET Web API

The Situation
I have an upcoming project where the web pages will be making AJAX calls. External clients will also be provided a REST API. I will be implementing this project using ASP.NET MVC 4 with Web API.
I have seen various examples online where people use the [Authorize] attribute for security. I presume this is whenever Web API is called via AJAX on a web page.
I have also seen various examples where an API key was passed along with each request (via query string or header). I presume this is whenever Web API is called from an external system.
The Questions
Here are the questions that immediately come to mind:
Should I be creating a separate controller for internal and external clients?
or should I force the web pages to use the same external authentication model?
or is there a way that external clients can use the Authorize attribute?
or should I somehow support both forms or authentication at the same time?
A Side Note
A colleague pointed out that I might want to deploy the API to a totally different URL than where the web app is hosted. Along the same lines, he pointed out that the external API may need to be more coarse grain or evolve separately.
I don't want to reinvent the wheel, here. This makes me wonder whether I should be using Web API as an internal API for my AJAX calls in the first place or if I should stick to old-school MVC actions with [HttpPost] attributes.
[Authorize] attribute is not meant only for Ajax. When you apply the [Authorize] attribute to say an action method, what this does is it ensures the identity is authenticated before the action method runs,irrespective of the clients making the request and irrespective of the type of credentials submitted to your API. All it looks for is Thread.CurrentPrincipal. Here is the copy-paste of the code from the Authorize filter.
protected virtual bool IsAuthorized(HttpActionContext actionContext)
{
...
IPrincipal user = Thread.CurrentPrincipal;
if (user == null || !user.Identity.IsAuthenticated)
{
return false;
}
...
}
As you can see, all it does it gets the Thread.CurrentPrincipal and checks if the identity is authenticated. Of course, when you include the roles, there are additional checks.
So, what this means is that you will be able to use different means of authentication as long as Thread.CurrentPrincipal is set as a result of authentication. If you have two handlers (or HttpModules in case of web hosting) or authentication filters in case of Web API 2, you can establish the identity based on different factors. For example, you can have a BasicAuthnHandler and a ApiKeyHandler added to config.Handlers and hence run in the web API pipeline one after the other. What they can do is to look for the credentials and set Thread.CurrentPrincipal. If Authorize header comes in the basic scheme, BasicAuthnHandler will authenticate and set Thread.CurrentPrincipal and if the API key comes in, it does nothing and ApiKeyHandler sets Thread.CurrentPrincipal. Both handlers can create same type of prinicipal say GenericPrinicpal or even different one. It does not matter because all the principals must implement IPrincipal. So, by the time Authorize filter runs, Thread.CurrentPrincipal will be set and authorization will work regardless of how you authenticate. Note: If you web host, you will also need to set HttpContext.User as well, in addition to Thread.CurrentPrincipal.