Get "groups" claims from Okta using the OpenID Connect Authorization Code Flow - asp.net-core

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/

Related

How do I restrict a user from accessing/updating another user's details?

I have an http endpoint /update-user-details that is authenticated by a JWT token.
There are two valid users in my system User1 and User2.
How do I restrict User1 from updating User2's details using the /update-user-details endpoint?
You have 3 options:
DIY: implement code yourself that will do it. That's what Chappie Johnson recommends in their response.
Externalize authorization logic: use an authorization framework to do the check for you. The way to externalize really depends on the framework you developed the API in. For instance, you could look into Flask Authorization for Python or Ruby CanCanCan or .NET claims.
Externalize authorization using a standard approach: Attribute-Based Access Control (ABAC) is actually what you are looking for. In ABAC you write policies that state what can and cannot happen. alfa and xacml are the two ways you can write policies. The good thing about this approach is that you can always change the policies without rewriting your API.
In your JWT you should have a claim in the body of the token that contains the user id of the requesting user. Before making an edit, you could check to see that the user_id value in your JWT matches the user_id value that user1 is attempting to edit. If the user_id's do not match, then reject the change.
String userId = getUserIdFromJwt();
if (!userId.equals("some user id")) {
throw new HttpUnauthorizedException("You do not have access to edit" +
"this resource.");
}
You have all the information about the current requesting user in the JWT so you are able to make assertions about the user.

Is there a way to know which aad to use for authenticating in advance?

I have a UWP app that needs to authenticate, and I would like to avoid asking the user to choose which national cloud to authenticate with. I could just try them all, but I hope there is a better way to tell which Azure Active Directory the user belongs to (.us or .com)
Native apps can discover the Azure AD endpoint for a national cloud by passing an instance_aware parameter in the authorization request to the global Azure AD endpoint. This is done in the acquireToken call, where you need to pass in instance_aware = true as an extra query parameter when initializing the Authentication context.
From the Authentication result, you can read and store the cloud_instance_host_name attribute to learn the correct Azure AD endpoint. You must pass this value as the authority to re-initialize the Authentication context for the subsequent acquireTokenSilent calls to succeed.
An example ADAL.Net code snippet is below:
var authenticationContext= new AuthenticationContext(Authority, false, new TokenCache());
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource,
clientId,
redirectUri,
platformParameters,
userIdentifier,
"instance_aware=true"
);
Also, here are example OAuth request and responses using the instance_aware parameter:
Request:
https://login.microsoftonline.com/common/oauth2/authorize?
response_type=code&
client_id=f5d01c1c-abe6-4207-ae2d-5bc9af251724&
instance_aware=true&
redirect_uri=http://localhost/appcheck&
resource=00000002-0000-0000-c000-000000000000
Response:
http://localhost/appcheck?
code=AQABAAIAAQDnLpu3ikefR73l_aNlxt5x0ulCIcjaTlOoWp412SJ2Oxlih65_h_Ju3OdOqpEy-mz0giFzZtU2_MbIgSG12e6RjwxpcaXaVPene_lMtmR2DPexUZZ3QhFRl8Vgl76SidX_nJ1CN-hJAejCi139FG_YZit4ePbiNySC3zR9GcP3B3St7HDsdEhMh1Vi1XHSSKfpgVqzLnOiBSO_jXrm1WJVqXSlt4_M_KO92Gdpbpy8H7zpsRg0O6blbuSw_83YUcj0w1gEfByHZP2Hk5AToDy_DWepPqJ0GWOJYeKcfIiEFleNYaeyEJDDuMyFhV16IOT28mq1oNOWL0dnhjwr-OV0JnyajQCT_LZzapxp7Y-8jSPDgW6SR878sgrq6CS2z3Zos8_T31n4DucQaPqv2Ae_jxlGHHSENBFy2RhHy397B7BBohXGqhDj_OdIroimDOJGVewn612gQOA6-9p0llv-PNd7vj9VZL-9Q8kEuYuhTqaBsH3yKm6y9FfgxMWovVkYtDt4YgxbqCV2Wb_lzImtyTHKxazn6YhH6R2pCvFdVSAA&
cloud_instance_name=microsoftonline.us&
cloud_instance_host_name=login.microsoftonline.us&
cloud_graph_host_name=graph.windows.net&
msgraph_host=graph.microsoft.com&
session_state=899b8a55-034f-4dcd-8b4b-888b7874b041
One way to "spot check" which cloud/AAD environment the user belongs to is by making a call to the Azure AD OpenID Connect discovery endpoint:
https://login.microsoftonline.com/place_tenantname_or_tenantid_here/.well-known/openid-configuration
For Azure Government, "USG" or "USGov" as the value in the tenant_region_scope field will indicate tenants that should be using login.microsoftonline.us.
I hope this helps. Let me know if not.
Bernie

Look up the user with bearer token with Openiddict Core

I am currently using Openiddict, Identity and Entity Framework to manage my users and assign Bearer tokens to users to protect my API.
My infrastructure is currently using ASP.NET Core Web API in the back end and a separate React application in the front end. The React application makes HTTP calls to my API to retrieve it's data. There is no server side HTML rendering at all in the back end.
Everything works as I need it to for the most part. I can register users and retrieve tokens from the API. These tokens are included in my HTTP call in the Authorization header. My AuthorizationController uses this: https://github.com/openiddict/openiddict-samples/blob/dev/samples/PasswordFlow/AuthorizationServer/Controllers/AuthorizationController.cs with a few minor tweaks. My Startup.cs also uses almost exactly this https://github.com/openiddict/openiddict-samples/blob/dev/samples/PasswordFlow/AuthorizationServer/Startup.cs
In some instances, I need to make API calls to the endpoints that are specific to the user. For instance, if I need to know if a user has voted on a comment or not. Instead of passing along the users ID in a query string to get the user details, I would like to use the Bearer token I received that they use to make the API call for that endpoint. I am not sure how to do this though.
In some research I have done it looks like some samples use ASP.NET Core MVC as opposed to the API to retrieve the user with the User variable as seen here https://github.com/openiddict/openiddict-samples/blob/dev/samples/PasswordFlow/AuthorizationServer/Controllers/ResourceController.cs#L20-L31 however this seems not to apply to my infrastructure.
My question is how do I look up a user based on the Bearer token passed to the API to look up a users details from my database? I am assuming that all of the tokens passed out by the API are assigned to that specific user, right? If that's the case it should be easy to look them up based on the Bearer token.
The question is: How with Openiddict can you look up a user based on the token that was assigned to them for API calls? I need to get the user details before anything else can be done with the application first. Is there something baked internally or do I have to write my own support for this?
When you create an AuthenticationTicket in your authorization controller (which is later used by OpenIddict to generate an access token), you have to add a sub claim that corresponds to the subject/entity represented by the access token.
Assuming you use the user identifier as the sub claim, you can easily extract it from your API endpoints using User.FindFirst(OpenIdConnectConstants.Claims.Subject)?.Value and use it to make your DB lookup.

Is it possible to use MSAL.js to get refresh token?

I want to integrate with Miscrosoft Outlook. I am able to login with MSAL.js and get an access token, but I am not able to get a refresh token. Is there a way to do it?
I'll assume that since you're using the MSAL.js (https://github.com/AzureAD/microsoft-authentication-library-for-js) that you're using implicit flow for authentication and authorization.
Implicit flow doesn't support refresh tokens, but you can request a new token silently. This is done similarly to how you request the token (id or access) in the first place. Unfortunately, I haven't found that MSAL.js does this transparently and I've needed to detect expired tokens and request the new tokens in my code. You can read more about refreshing tokens here.
Alternatively, if what you're implementing allows you to use one of the other MSAL libraries (for example, the .Net one) then you can use one of the other OAuth flows that explicitly support refresh tokens.
I couldn't find any answer in the MSAL.js documentation, however this source code comment suggests you can renew a token manually by passing only the clientId as your scope to acquireTokenSilent.
To renew idToken, please pass clientId as the only scope in the Authentication Parameters
I use msal v1.4.0
I remove 2 keys in storage (see picture) then call acquireTokenSilent again to get new access token.
Code to remove those 2 keys:
const keys = Object.keys(sessionStorage).filter(x => x.indexOf('authority') > 0)
keys.forEach(x => sessionStorage.removeItem(x))

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.