IdentityServer Create and Revoke API Tokens - c#

I have a asp.net core hosted blazor application using
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
// ...
}
.AddEntityFrameworkStores<ApplicationDbContext>()
and
builder.Services.AddIdentityServer()
.AddApiAuthorization<IdentityUser, ApplicationDbContext>()
I want to add a feature, that a user can create an api token with passing an (optional) expiration date, and name.
The created tokens should be revokable.
Ideally the tokens are limited to api endpoints.
Is ServerSideSessions the way to go?
Since IdentityServer is rotating keys, will it be capable of validating tokens that were issued +1 year ago?
The reason for this is, that we have monitoring systems, that monitor data coming from that api. these systems only can issue http calls, no oauth integration.
EDIT
I found https://github.com/DuendeSoftware/Samples/blob/main/IdentityServer/v6/PAT/src/IdentityServer/Pages/PAT/Index.cshtml.cs
It seems like I can create Tokens with a Lifetime with Injecable ITokenService.
Still i didn't find a default way to revoke tokens.

I think IdentityServer is not the right tool to handle API Tokens. What IdentityServer can handle is Oauth Clients and their secrets but that just allowed an oauth capable client to create a token that can be directly used on an API endpoint. Server side session just stores the user data on the server side not in a cookie so this is not the right option.
In my opinion the right way to handle that is to create a custom AuthenticationHandler maybe similar to this:
https://rmauro.dev/api-key-authentication-extending-the-native-implementation/
In there you can get access to a dbcontext for example where the API Tokens are stored and you can validate them.
You can then use AddPolicyScheme to switch between either the apiauthentication or your custom scheme.
https://code-maze.com/dotnet-multiple-authentication-schemes/
Probably something like this:
builder.Services.AddAuthentication( x=> x.DefaultScheme == "TOKEN_OR_JWT")
.AddIdentityServerJwt()
.AddScheme<YourCustomHandler>("YOUR-CUSTOM-SCHEME")
.AddPolicyScheme("TOKEN_OR_JWT", "TOKEN_OR_JWT", options =>
{
// runs on each request
options.ForwardDefaultSelector = context =>
{
// check if an API-TOKEN header is present
string? apiToken = context.Request.Headers["API-TOKEN"];
if (!string.IsNullOrEmpty(apiToken))
return "YOUR-CUSTOM-SCHEME";
// otherwise always use the jwt scheme for identityserver
return IdentityServerJwtConstants.IdentityServerJwtScheme;
};
});

Related

Can I request IdentityServer4 tokens from client if server was rebooted?

Its very difficult to ask questions on ID4. Also all relevant discussions I could google point to non-existing links for code samples.
What I have: an angular client uses ID4 auth made up of MVC pages provided by Microsoft, hosted in ASP.NET Core.
Startup.cs
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<AppDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, AppDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
. . .
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
authorize.service.ts
const settings: any = await response.json();
settings.automaticSilentRenew = true;
settings.includeIdTokenInSilentRenew = true;
this.userManager = new UserManager(settings);
Use case: a user logs in on the webpage, ticks Remeber Me checkbox and uses the auth'ed website parts. If the server restarts - the tokens lost and user interaction with the website becomes broken - e.g. there are no errors in the webbrowser console and nothing happens if user tries to access any server-side data. It looks like nothing is there. Not as if there is issue with the auth.
It can only be fixed by manually logout/login. Or by opening new tab and going to the app again.
Is there simple explanation on to how can I a) detect that client token is broken and b) request new token?
EDIT if a user continues to use website next working day or opens new browser tab (without server restart) the ID4 works as expected - no login required.
The fiddler:
The short answer is: nothing should break if the server process (either application backend or identityserver4-based IDP) restarts so chances are you're missing some persistence (for persisted grants) and/or shared config (e.g. token signing keys and ASP.Net data protection keys) and things are being regenerated on startup and in-memory data is being lost.
This article covers the things you need to consider to deploy a viable production service: https://docs.identityserver.io/en/latest/topics/deployment.html
With that all in place though tokens will still expire and there are a couple of ways to refresh them depending on the context and grant type in use.
To detect if a token is not valid anymore:
Use it and detect if you get a 401 response from the endpoint you're calling
Check the exp claim inside the token yourself
Use the expires_in value returned with the token and calculate the expiry time based on that
To renew it (and some libs will automate this):
Use the iframe-based silent renewal mechanism (authorize endpoint with prompt=none) - note that third part cookie restrictions come into play for this
Use a refresh token via the token endpoint (not recommended for client side apps due to the need to persist a refresh token in the client side)

.AddOpenIdConnect() Middleware Clarification

So, I'm trying to implement an OIDC client application using ASP.NET Core 3.1. I am trying to leverage the .AddOpenIdConnect() and .AddJswtBearer() middleware. However, I need some clarification on what this middleware is doing.
Here is what I currently have for the middleware configuration:
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["auth:oidc:authority"];
options.ClientId = Configuration["auth:oidc:clientid"];
options.ClientSecret = Configuration["auth:oidc:clientsecret"];
options.ResponseType = OpenIdConnectResponseType.Code;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
})
.AddJwtBearer(options =>
{
options.Authority = Configuration["auth:oidc:authority"];
options.Audience = Configuration["auth:oidc:clientid"];
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = Configuration["auth:oidc:authority"],
ValidAudience = Configuration["auth:oidc:clientid"],
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero
};
}
I notice that requests to the Authorization server's /.well-known/oidc-configuration and /.well-known/keys endpoints are requested when the application is first started, based on my Fiddler capture below
Where does it do that?
I'm trying to also validate that the JWT received from the authorization server is valid (that it hasn't been tampered with between the time the server sent it, and the time that the client has received it). I understood this to happen when I added the TokenValidationParameters object in the .AddJwtBearer() middleware. To test this, I tried changing Valid Audience in the TokenValidationParameters to something like asdkwewrj which I know is not the valid audience for my token. But, I never got an error from the client saying that the audience was invalid. The authentication still worked, and I was able to access my secure dashboard still.
Another thing I'm trying to implement is refresh_token grant_type with this OIDC client. I thought that the options.saveTokens in the .AddOpenIdConnect() middleware would allow me to save the tokens. It looks like they're save as cookies, but these cookies look nothing like my token values (my access token is a JWT, but out of the cookies I see, none of them begin with ey).
In a nutshell, I'm trying to understand the following:
Does this .AddJwtBearer() middleware validate the ID Token for me if I have the correct JwtBearerOptions defined (like I do above)? Or do I need to manually validate the ID token against the JWKs from the JWKs URI?
If I have to manually validate the ID token using the JWKs from the JWKs URI, how do I store these JWKs when the middleware makes the request to the /.well-known/keys endpoint?
How do I get the cookies that correspond to the access token and refresh token, and then send the refresh token to my authorization server?
I noticed that I can utilize options.Events in both of these middlewares. Would any of those solve any of the items I'm trying to accomplish?
Overall, what do these two middlewares handle for me, that I shouldn't need to manually do (i.e token validation and/or token renewal)?
Thank you! I am still fairly new to in-depth ASP.NET development like this, so I appreciate any responses.
First of all, the OIDC authentication scheme and the JWT bearer authentication scheme are independent of each other. OIDC is mostly used for server-side authentication and will pretty much never be used on its own but always with the cookie scheme. The reason for this is that the OIDC scheme will just be used for the authentication but is not able to persist the information on its own. I’ve went into more details in a different answer of mine that also explains how the authentication flow works with OIDC.
As for JWT bearer, this authentication scheme will run on every request since it is completely stateless and expects clients to authenticate themselves using the Authorization header all the time. This makes it primarily used for protecting APIs since browsers wouldn’t be able to provide a JWT for normal browser requests.
So you should first ask yourself whether you are protecting your server-side web application (e.g. using Razor views or Razor pages) in which case you want to use OIDC and the cookies authentication scheme, or if you are protecting your API. Of course, the answer could be “both” in which case you want all of those three schemes but ASP.NET Core will not support this without further configuration.
With that being clarified, let’s get into your questions:
The requests to /.well-known/oidc-configuration and /.well-known/keys are done by both the OIDC and the JWT bearer scheme in order to retrieve information from your identity provider. They will do that regularly to update their data, including information about the signining keys which they will use to validate the tokens. This happens within the scheme handler and is usually not visible to you.
Correctly set up, the JWT bearer authentication will validate the token for you. It will do that by verifying the signatures using the retrieved signing keys, and then it may check additional properties like the specified audience or its lifetime.
You shouldn’t ever need to validate tokens manually. That’s the job of the authentication scheme. You are using the authentication stack so you can just access the user principal within your app without doing anything.
Cookies are protected using data protection so that they are safe against forgery. In order to retrieve the tokens stored with SaveTokens = true, you can use the GetTokenAsync method on your HTTP context.
You can use the authentication events to add to the default behavior of the authentication schemes. For validating your tokens using the standard mechanisms, you shouldn’t need to though.
There is just one middleware: The authentication middleware. It uses the configured authentication schemes to perform the authentication of users so that—once set up correctly—the authenticated user is available throughout the application, e.g. in controllers, MVC filters, Razor views, etc.
Does this .AddJwtBearer() middleware validate the ID Token for me if I
have the correct JwtBearerOptions defined (like I do above)? Or do I
need to manually validate the ID token against the JWKs from the JWKs
URI?
AddJwtBearer is only used by APIs to validate the access token and create a user (ClaimsPrincipal) out of it. It's all it does. It does not deal with id token.
In general its easier to put the API on a separate service, to make it more clear who is doing what. when you mix both the client and API in the same service, it can be harder to reason about it.
If I have to manually validate the ID token using the JWKs from the
JWKs URI, how do I store these JWKs when the middleware makes the
request to the /.well-known/keys endpoint? How do I get the cookies
that correspond to the access token and refresh token, and then send
the refresh token to my authorization server?
The ID token is validated and handled for you by AddOpenIdConnect. You dont need to validate the ID-token by yourself. AddOpenIdConnect will create the cookie and optionally store the tokens as well in the cookie.
Overall, what do these two middlewares handle for me, that I shouldn't
need to manually do (i.e token validation and/or token renewal)?
To summarize:
Ues .AddOpenIdConnect() for the client, that allows the user to login.
Use .AddJswtBearer() for the backend APIs.
Token renewal is a different story that none of them handles out of the box. For that, you can consider to use IdentityModel.AspNetCore or do something by yourself.

Client Claims not being checked by Policy

We have a system that uses C# Core 2.1, IdentityServer4, and Identity to authenticate users. Various other projects use the system for authorization. I can create policies in my API's that check user claims; and use those policies to secure resources. I add code similar to this in the API Sartup.cs:
services.AddAuthorization(options =>
{
options.AddPolicy("example",
policy => policy.RequireClaim("claim", "data"));
});
And add the following code before my API controller or specific task:
[Authorize(policy: "example")]
We have used this system for a long time. Now we want to lock down an action so that only a specific client can do it (not their users). But claims obtained through the grant type client_credentials are either not being added to the access token, or not being seen by the Authorization service.
Is there a way I can see what claims are in a token when it does not have openid as a scope?
Assuming the claim is there, why isn't the Authorization service able to see it?
Is there another alternative? We want to lock down an action so that only the client apps themselves can do it.
First you can always capture the raw tokens using Fiddler to see what claims that are actually passed to the receiver of the tokens. Then check in the User (ClaimsPrincipal) created by the authentication handler what claims it contains.
Then you need to explicitly map/add the missing claims so that the expected claims get into the claimsPrincipal User object. Some claims are removed in that process by default.

How to get application profile data into access token?

I'm using IdentityServer4 with a mix of v4/v3 clients.
I have custom profile data that is store on the application side that I'd like to include in the access_token so that my downstream APIs can use this with bearer/jwt authenication.
I understand I can manipulate claims via IProfileService, but that is registered on the identity side, not the application.
How can I get my custom profile claims into the requested access token?
Additional Details
I've done a proof of concept using Extension Grants to specifically pass my application claims through the IdS so that it includes those in the token. It works...but feels pretty hacky.
Please do not do that. The JWT token is sent with every request.
if the downstream API needs something from the user, then either submit it with the call, or have an endpoing the downstream api can call. Embedding rarely used large inforamtion in someting transmitted every call (except in http 2.0) is a nonononono.
You can not change jwt token content after being created and signed by authorization server. But you can use ClaimsTransformation to manipulate claims on the api project.
Edit: Another option to use JwtBearer OnTokenValidated event.
Any claims issued from your implementation of IProfileService should end up in the token.
Note that your implementation of IProfileService should check if it is issuing claims related to IdentityResources or ApiResources. It would be a bit pointless adding api claims to an id_token.
When the client receives the token from you IDS, it will pass it in calls to your API.
If your client is using cookie authentication, the tokens themselves as well as some user profile claims will be stored in the authentication cookie. This obviously depends on the flow your are using Implicit, Hybrid etc.
If you want to inspect what you get back from the IDS at the client you could add a Cookie Authentication Event handler (eg OnValidatePrincipal) to see whats stored in the cookie, or add an OnUserInformationReceived event handler to your OIDC handler and inspect what you get back in there.

Obtaining bearer access token to access o365 resource, using Azure AD auth

I'm using the bog-standard, file->new Asp.Net Core Web Application (Razor Pages) project, and configuring it to use Azure AD authentication against an o365 instance, which works just fine.
I now want to use the app to access an o365 resource (e.g. my calendar) using the Graph API. In asp.net core 2.0, I used the method described here to obtain the access token, cache it, and retrieve it for any graph requests. It relies on an OpenIdConnect event (OnAuthorizationCodeReceived) to obtain the access code.
I don't see any similar event on the new AddAzureAd method available using asp.net core 2.1. Is there now a new method for obtaining the token for use in Graph calls?
This always has been a complex question and depending on your scenario (permissions required, workloads you're talking too) this answer might need to be adjusted.
First thing, here, you have the code grant and the id_token. The easiest way to achieve way to achieve what you want to achieve (may not be the best in terms of user experience) it to store the id token temporary. (let's say in a token cache)
services.AddAuthentication()
.AddOpenIdConnect(opts =>
{
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = ctx =>
{
return Task.CompletedTask;
}
};
});
You can see an example here you also need to make sure your application is configured with the proper permissions and you should be ready to go!

Categories

Resources