I have a aspnet core 6 API and i need in my controller of API, validated the bearer token that with Microsoft Authentication Libraries (MSAL).
I want verify in token, for example, my tenant or clientId.
How i do this simple? I need add notation to verify, for example [AuthorizeMSAL]?
Or i need to call "https://graph.microsoft.com" to validate token?
Example my code:
I receive an exception
The provided value for scope offline_access openid profile is not valid. Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).
I tried to reproduce the same in my environment and got the same error as below:
The error usually occurs if the scope value is invalid while generating the token.
Note that, Client Credential Grant Type accepts scope with ./default or api://ClientId/.default only.
To resolve the error, use the parameters like below while generating the token:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
grant_type:client_credentials
scope:https://graph.microsoft.com/.default
Access token generated successfully like below:
To validate the Bearer token, use the below sample code in your _Startup.cs_ file as mentioned in this MsDoc:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration);
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingOnTokenValidatedHandler(context);
options.TokenValidationParameters.ValidIssuers = new[] { Issuers };
options.TokenValidationParameters.ValidAudiences = new[] { valid};
};
});
The Bearer token will be validated successfully like below:
What you are looking for in here is the Microsoft Identity Platform , which does the job of handling Tokens and authorization in the Bakcend part ( .net web APIs ) . In here I will suppose you have read and done the required configuration on the Azure portal , client app registration , Api registration , and exposing API (via scope or app roles ) if not please refer to the following docs for that.
Then you will need to install Microsft.Identity.Web nuget package to be able to handle the token .In order to make use of the token you need to register the following authentication service to the container inside program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
At this point you should already have and AzureAd section in in your appsetting.json with details from you Azure AD configuration like mentioned below :
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "Enter_the_Application_(client)_ID_here",
"TenantId": "common",
"Audience": "Enter_the_Application_ID_URI_here"
},
}
If the configuration is done right in the Azure portal side and you have the correct credentials at your appsettings.json , you should be able to allow only authenticated users into a specific controller by adding the [Authorize] annotation above the controller definition.
You find here a link to the documentation mentioning all the steps , along with a sample that you can follow on . and this link contains more information about MSAL issued bearer token configuration and validation
Related
I am likely expecting or doing something that is not correct. Need some help in getting back to the right path.
Simple Usecase - How to configure a client so it can request a token with all the right scopes? currently I am running it via postman but actual client is going to be a react app using msal.
Setup:
App Registration in Azure.
API Permissions:
Microsoft.Graph --> email & User.Read
Exposed an API:
Scope URI: api://someguid
One Scope is Added : api//someguid/testscope
Net Core 6 API
AppSettings.Json
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Audience":"api//someguid"
"ClientId": "my-client-id",
"TenantId": "my-tenant-id"
},
"Graph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "user.read,email"
}
}
Middleware
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration, Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(Configuration.GetSection("Graph"))
.AddInMemoryTokenCaches();
This is how I am calling graph in Controller.
[Authorize]
public class AbcController: Controller
{
private readonly GraphServiceClient _graphClient;
public AbcController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
[HttpGet("get-me")]
public async Task<ActionResult> GetSomeDetails()
{
var user = await _graphClient.Me.Request().GetAsync();
return null;
}
I run this via postman with Auth Code flow with PKCE, Here are the issues
When I set the Scope as : api//someguid/testscope
Call gets authenticated and the Token is acquired correctly in postman
The API get-me get authorized correctly
But the call _graphClient.Me.Request().GetAsync() throws throws a 500 error
Also, a direct call to https://graph.microsoft.com/v1.0/me in postman using the token
gives insufficient privilege error
When I set the scope as : api//someguid/testscope https://graph.microsoft.com/email
Call gets authenticated But the acquire token fails with incorrect scope
When I set the scope as : https://graph.microsoft.com/email https://graph.microsoft.com/user.read
Call gets authenticated and the acquire token is acquired
Direct call to https://graph.microsoft.com/v1.0/me works as expected
But now my API does not get Authorized and gives 401
Can someone suggest what am i missing in my setup or if i am doing something crazy wrong?
All i am looking to do is get my API authorized, and get the email address pulled from graph in the API, without explicitly re-acquiring the token or specifying my client secret in the API to build the graph client.
This was taken as an input to try and build my poc
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-web-api-call-api-call-api?tabs=aspnetcore
Note that, one token can only be issued to one audience. You cannot acquire access token for multiple audience (Ex: custom API and MS Graph) in single call.
In your scenario, you need to make two separate requests for acquiring access tokens i.e., one for your API and other for Microsoft Graph.
I tried to reproduce the same in my environment via Postman and got below results
I registered one Azure AD application and added same API permissions as below:
Now I exposed one API named testscope same as you like below:
Make sure to select Single-page application while adding Redirect URIs to your application like below:
I acquired token successfully using Auth code flow with PKCE from Postman like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id:<appID>
grant_type:authorization_code
scope: api://someguid/testscope
code:code
redirect_uri: https://jwt.ms
code_verifier:S256
The above token won't work for calling Microsoft Graph /me endpoint and works only to authorize API based on its audience.
To check the audience of above token, decode it in jwt.ms like below:
To call /me endpoint for mail, you need to acquire token again with Microsoft graph scope without configuring client secret like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id:<appID>
grant_type:authorization_code
scope: https://graph.microsoft.com/.default
code:code
redirect_uri: https://jwt.ms
code_verifier:S256
The above token won't work for authorizing API whereas you can call Microsoft Graph based on its audience.
To check the audience of above token, decode it in jwt.ms like below:
When I call /me endpoint using above token, I got the results successfully with mail like below:
References:
Azure AD Oauth2 implicit grant multiple scopes by juunas
reactjs - azure msal-brower with nextJs: passing only one scope to the API
I have 2 APIs and I wanted to use OAuth 2.0 On-Behalf-Of flow to generate the token in API1 and Authorize to API2 with the generated token.
I followed https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow#middle-tier-access-token-request and I was able to generate the token.
I use this following parameters:
grant_type:urn:ietf:params:oauth:grant-type:jwt-bearer
client_id:xxxxx
client_secret:xxxxxx
requested_token_use:on_behalf_of
scope:https://graph.microsoft.com/user.read
But I don't know how to authenticate to my API2 with that token and get the user and what are the next steps. My API to uses .netframework app.
I created OwinStartup and added UseWindowsAzureActiveDirectoryBearerAuthentication
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions()
{
Tenant = "xxxxxxxxxx",// Azure tenant Id
TokenValidationParameters = new TokenValidationParameters()
{
SaveSigninToken = true,
ValidAudience = "https://graph.microsoft.com"
}
});
and added Authorize attribute into my controller. but when I called the get endpoint to that controller, it gave me an error saying:
Authorization has been denied for this request.
You need to request two tokens, one is the access token of API A, and the other is the access token of API B obtained using the access token of API A as a parameter.
The document you refer to is based on the api A access token you have obtained, and there are instructions in the document.
First, you need to use the auth code flow or other login flow to obtain the access token of api A:
Then use the access token of API A as a parameter to obtain the access token of API B.
In API A I was using a wrong scope to generate token. https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow#middle-tier-access-token-request
I'm working with a .Net Core Web API and a .Net Core Web MVC Application. They both use Azure AD B2C to authenticate users. But in order to get a response from a HttpRequest from the Web API I need to provide the JWT Access Token from B2C for the signed in user on my web MVC application. Is there a way to obtain this access token inside a controller using the authenticated "User".
I have tried accessing the claims of the signed in user but no luck there, I have also used jwt.ms to review that the B2C workflow works well and that the JWT token is being generated and it works as well. The MVC application is authenticating the user and the web API is working fine with a hardcoded token. I just need to obtain the access token from a signed in user rather than doing it hardcoded.
I expect to be able to get the B2C JWT access token so that I can later on pass it to the Web Api and be able to secure my requests.
After getting some help from the MS AzureADB2C.UI GitHub crew we were able to solve the issue. The issue was that the tokens aren't saved by default on the library, so we needed to configure OIDC to specify that the tokens have to be saved for future use within the application. And so here is the example code of the "Startup" configuration and the example of how to query the "JWT access token" from the controller.
Startup.cs:
services.Configure(AzureADB2CDefaults.OpenIdScheme, options => {
options.SaveTokens = true;
});
Controller:
string idToken = await HttpContext.GetTokenAsync("id_token");
More information on how was the issue solved can be found on the following link:
https://github.com/aspnet/AspNetCore/issues/11424
You can refer to this sample application.
It uses the ASP.NET Core Azure AD B2C middleware to authenticate the end user and MSAL.NET to acquire, cache, and refresh the access token.
The access token is acquired in the AzureADB2COpenIdConnectOptionsConfigurator class.
A code example for a controller method referencing the access token is here.
Is it the actual token string you need? If so, you can access the headers using the HttpContext within the controller? The HttpContext will have a collection of headers that were passed in
I'm having hard times trying to use Azure AD B2C to authenticate My Web API.
I'll start with some background
I created mobile application which is using Azure AD B2C to authenticate users. I'm creating a WebView which display this url:
User is asked to login to azure ad, if the login data is successfull i'm receiving a response containing the access token - this part went smooth, everything works properly.
Now i want to create backend Web Api. I created ASP NET Core Web application which allows me to choose authentication method. I choose the Azure AD authentication so the template generated all required data for me. The relevant part in the code is here:
I updated all required config properties to match my azure settings. At this point i would expect to be able to call the API using access token i received on the mobile app. I run mobile app locally, signed in, received access token, copied it and tried to call my web api(hosted in IIS express) using postman ( with authorization header "Bearer ..." ). Unfortunately with no luck - i'm receiving 401 with following header:
Bearer error="invalid_token", error_description="The signature key was
not found"
I thought token is enough to Authorize the API - i understand this is a whole point of OAuth. Am i missing something ? Should i have some additional config ? I Noticed the config is missing the sign in policy ( which seems to be required by AD B2C name so i tried adding that:
var validationParameters = new TokenValidationParameters
{
AuthenticationType = "MY_POLICY",
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"],
Audience = Configuration["Authentication:AzureAd:Audience"],
TokenValidationParameters = validationParameters
});
But this didn't work too. Will appreciate any help.
EDIT
I found following error in Visual Studio logs:
Bearer was not authenticated. Failure message: IDX10501: Signature
validation failed. Unable to match 'kid': '...'
#juunas comment help me to find the issue. I inspected outgoing requests with fiddler and i found that with this piece of code:
Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"]
The request was being send to following address:
https://login.microsoftonline.com/MYTENANTID/.well-known/openid-configuration
There are two issues with above:
It's not using v2 endpoint. Proper link for B2C should always use v2 so it would look like:
https://login.microsoftonline.com/MYTENANTID/v2.0/.well-known/openid-configuration
It was not adding sign in policy to the link ( even if i set it in token options )
I managed to make it work with removing "Authority" parameter and changing the configure auth function to following:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
MetadataAddress = string.Format("https://login.microsoftonline.com/{0}/v2.0/.well-known/openid-configuration?p={1}",
Configuration["Authentication:AzureAd:TenantId"], "MYPOLICY"),
AuthenticationScheme = "MYPOLICY",
Audience = Configuration["Authentication:AzureAD:ClientId"],
});
I created an Azure API App and set the [Authorize] attribute on my controller and published it to Azure. Then I registered an Auth0 app, providing the data from my AD app, following Auth0's documentation. When I test the connection on their site it works just fine. I can also log in fine in my app and retrieve the access token with:
var auth0 = new Auth0Client("myUrl", "myTenant");
var user = await auth0.LoginAsync();
var accessToken = user.IdToken;
However, when I make a request to my API, passing the access token in the Authorization header, it just throws an error 401 (Unauthorized). From what the documentation says, I was under the impression this is all that should be needed, and I've not found any information to suggest otherwise. Is there an additional step to linking these?
The Solution is to configure ur API to accept tokens by that issuer, like for example by using owin middleware app.UseJwtBearerAuthentication(). Glad I could help!