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!
Related
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;
};
});
Before posting this question I' ve done lots of research in the Internet, I' ve found some stuff, but I wasn't able to find something that fits my case.
So please provide me the right direction or code snippets to go on.
I'm developing an app in .NET 6 which consists of 2 projects: Blazor WASM project for the client-side and a WEB-API project for the API's of my app.
At the moment, I've successfully implemented authentication from a central Identity Server 4. I receive "id_token" and "access_token" and use them to
secure access to my web-APIs from unauthenticated users.
The problem is that now I want to implement "role-based authorization". The facts are:
I cannot modify the code of the common Identity Server I use.
I have the users, the roles and their connection inside my app's database.
I think that the right solution here is implementing a MIDDLEWARE which reads the roles from my database and adds them into the "Claims".
Where should I develop the middleware? Web-Api project, blazor project or both???
If I developed a middleware in the Web-Api project (where by default there is already a pipeline) which adds roles into the Claims of "access_token",
the blazor project wouldn't work bacause of the modified token, right???
Could you help me with code snippets or provide me the right directions?
Thank you for your time!!!
Authorization is solely an API responsibility:
The Blazor app sends up an access token to identify the user, via the subject claim
The API verifies the JWT on every request, then looks up other claims for that subject
The extra claims can be cached for subsequent requests with the same access token
My sample .NET API shows one way of doing this and building a custom ClaimsPrincipal. Once you've done that, .NET's standard authorization techniques such as the [Authorize] attribute will work based on the ClaimsPrincipal's contents.
Authorizer class
Blog post
The client sends the access_token in every request. The server validates the access_token and then creates a ClaimsPrincipal with the claims that were included in the token. In your server can add additional claims to the ClaimsPrincipal using the OnTokenValidated event. Example:
.AddJwtBearer("Bearer", options =>
{
options.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
var user = context.Principal;
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
// Get DbContext. If you don't use Entity Framework request your own data access service.
//var dbContext = context.HttpContext.RequestServices.GetRequiredService<AppDbContext>();
// Retrieve user roles from database and create a list of role claims.
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "admin")
};
var claimsIdentity = new ClaimsIdentity(claims);
user.AddIdentity(claimsIdentity);
await Task.CompletedTask;
}
};
});
Then in your controllers you can check for the role using [Authorize(roles = "admin")] or User.IsInRole("admin");.
I'm using the Firebase Admin SDK with ASP.net Core and because we are using workload identity federation instead of the service account key json file i'm creating my FirebaseApp instance this way:
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromAccessToken(serviceAccountToken),
ProjectId = projectNo
})
Where "serviceAccountToken" is a Google Access Token received in a previous step.
This works like intended, but the access token i'm using expires after 1 hour.
My problem: I couldn't find a way to update the existing FirebaseApp instance with a new access token. My best approach is to use FirebaseApp.DefaultInstance.Delete() to dispose the current instance and then create a new one, but that seems a bit heavy for just updating the access token.
Is there a better approach?
Update:
My next attempt was simply to assign a new token after the token expired via FirebaseApp.DefaultInstance.Options.Credentials = GoogleCredential.FromAccessToken (newToken), but the new credentials are not taken into account.
After reading through the 'google-api-dotnet-client' source code, I noticed that GoogleCredential.FromAccessToken uses AccessTokenCredentials internally (see github). So I tried to write my own implementation of AccessTokenCredential and implement a refresh function there, unfortunately the interfaces for it are all declared internal.
In the end, there is still no good solution.
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.
I am currently using OpenIdConnect to execute HTTP GET requests for my Azure B2C custom policies. For example here is the Unified SignInSignUp:
public static void Unified(HttpRequest Request, HttpResponse Response)
{
string nonce = "defaultNonce";
string clientID = ConfigurationManager.AppSettings["aad.clientid"];
string authUrl = ConfigurationManager.AppSettings["aad.authUrl"];
string redirectUri = ConfigurationManager.AppSettings["aad.redirecturi"];
string unifiedPolicy = ConfigurationManager.AppSettings["aad.unifiedPolicy"];
// build url for AAD auth and redirect to ourself
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}?", authUrl);
sb.AppendFormat("p={0}", unifiedPolicy);
sb.AppendFormat("&client_id={0}", clientID);
sb.AppendFormat("&redirect_uri={0}", redirectUri);
sb.AppendFormat("&nonce={0}", nonce);
sb.AppendFormat("&scope=openid");
sb.AppendFormat("&response_type=id_token");
sb.AppendFormat("&prompt=login");
sb.AppendFormat("&response_mode=form_post");
// redirect to auth via AAD (and then redirect back to ourself)
Response.Redirect(sb.ToString(), true);
}
I would like to use MSAL instead but am having trouble finding a C# .NET sample that executes an HTTP GET request like I am doing with OpenIdConnect. My current technique returns the id_token just fine, but I would like to take advantage of MSAL's capabilities like caching etc.
Is such a sample around?
I wanted to add this as a comment, but the system indicated it was too long. So I would like to answer my question, since I did find some sample code (mentioned in my comment to self above) that helped me get started ~using~ MSAL.net. However I am having a number of issues (e.g. when I call my edit profile custom policy the set of claims I get is are very incomplete, using OpenIdConnect I get all my claims) and questions and will probably post another questions that is more specific, laying out my requirements. I really just need the id_token (and the OpenId Connect code I posted above was great for that), not the access or refresh tokens, and all I really need to do is validate that id_token and implement a KMSI (keep me signed in) strategy. I have added the B2C KMSI in my custom sign-in policy, but can't see how to query it in my web (asp.net web forms) web project. Currently (without using Azure) I have a "remember me" checkbox on my website and I write a persistent cookie with a rolling 30-day expiry with encrypted data in it and everything works as required; I am trying to move to Azure B2C and keep that all working.Also for validating the token I am currently using some code I found in a post here that uses low-level code (System.Security.Cryptography namespace) and it validates well, but I don't see how to validate the few claims that are recommended in documentation I have seen. So I will create a new post that inquires about all that. Thanks.