Azure AD B2C error - IDX10501: Signature validation failed - c#

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"],
});

Related

Validate Bearer token (azure ad) with MSAL

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

Azure Function with AD auth results in 401 Unauthorized when using Bearer tokens

I have a very simple Azure function in C# for which I've setup Azure AD Auth. I've just used the Express settings to create an App registration in the Function configuration.
public static class IsAuthenticated
{
[FunctionName("IsAuthenticated")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "options", Route = null)]
HttpRequest req,
ILogger log)
{
return new OkObjectResult("You are " + req.HttpContext.User.Identity.Name);
}
}
When I access the function in my browser everything works as expected (if not logged in I have to login and get redirected to my API). But if I try to access my function anywhere a Bearer token is needed I get an 401 Unauthorized error. Even weirder I also can't execute the function in the Azure Portal.
But the token was aquired without a problem and added to the request:
I've tried a few different things to solve this problem. First I thought maybe it's a CORS problem (since I've also had a few of those) and just set CORS to accept *, but nothing changed.
Then I've added my API login endpoints to the redirect and tried setting the implicit grant to also accept Access tokens, it's still not working.
Is there anything I've overlooked? Shouldn't the App registration express config just work with azure functions?
EDIT:
Putting the URL to my function app in the redirects as suggested by #thomas-schreiter didn't change anything (I've tried the config in the screenshot and also just putting each of those values on it's own).
EDIT 2:
I've now also tried to aquire an Bearer token the manual way with Postman, but I still run into a 401 when calling my API.
UPDATE 2020-05-12: According to ambrose-leung's answer further below you can now add a custom issuer URL which should potentially enable you to use v2 tokens. I haven't tried this myself, but maybe this will provide useful for someone in the future. (If his answer helped you please give him an upvote and maybe leave a comment 😉)
This took forever to figure out, and there is very little information about this in the offical documentations.
But it turns out the problem was/is that Azure Functions don't support Bearer tokens generated by the oauth2/v2.0/ Azure API. Since the portal uses those (if your AD supports them) you are out of luck to be able to run the function in there.
This also explains why my postman requests didn't work, because I was also using the v2 api. After switching to v1 I could access my API (Postman doesn't allow you to add a resource_id when you use the integrated auth feature, so I had to switch to handling everything manually).
After that came the realisation that you can't use MSAL either if you are writing a JS client (Angular in my case). So one alternative is ADAL, where the Angular implementation looks kind of awkward. So I decided to use angular-oauth2-oidc which took another hour of tinkering to get it to play nicely with Azure AD.
But after all that I can finally access my API.
I really don't understand why you wouldn't allow users to access Azure Function Apps with Azure AD v2 tokens, but at least this should be so much better documented. But whatever, I can finally go to sleep.
EDIT: After I opend an issue for this, they added a note that v2 isn't supported by Azure Functions, hopefully making life easier for other people.
https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad
I managed to get it working through postman using following configuration.
Important lesson was setting in "Allowed token audiences" and "resource" name used in postman to acquire token should be same in this case. I used the same code provided here in question. in this case app registered in Azure AD is a client and resource as well. configuration and testing through postman as follows
Acquire token in postman
Calling azure function using Postman .. Authorization header with bearer token
You can now use v2.0 tokens!
Instead of choosing 'Express' when you configure AAD, you have to choose 'Advance' and add the /v2.0 part at the end of the URL.
This is the code that I use in my console app to present the user with a login prompt, then take the bearer token for use with the Azure Function.
string[] scopes = new string[] { "profile", "email", "openid" };
string ClientId = [clientId of Azure Function];
string Tenant = [tenantId];
string Instance = "https://login.microsoftonline.com/";
var _clientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority($"{Instance}{Tenant}")
.WithDefaultRedirectUri()
.Build();
var accounts = _clientApp.GetAccountsAsync().Result;
var authResult = _clientApp.AcquireTokenInteractive(scopes)
.WithAccount(accounts.FirstOrDefault())
.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync().Result;
var bearerTokenForAzureFunction = authResult.IdToken;
When setting up your Active Directory authentication on your Function App, set management mode to advanced and fill in the Client ID and Issuer URL as required (and the client secret if necessary).
Importantly, under the Allowed Token Audiences, enter the Application ID URI. This can be found in your registered App Registration (in your AD) under the Expose an API option.
This is what I was missing to get authentication working on my Function App. Before I added that token audience, I would always get a 401 with a valid access token.
This Azure active directory - Allow token audiences helped me get my answer but it took me a while to realise what it was referring to. Remember, it's the Application ID URI that can be found within your App Registration.
I hope it helps!
If you are banging your head against the wall like myself and the original poster, it may be that you are allowing users to sign in from "Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)."
Note that as of May 2021, v2.0 works perfectly. If you use https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token to get a token with Postman (as described above), you will get a valid token that you can use to auth your AZ Function with.
With that said, IF a user is signed in via a personal account or an account not within your AAD, the token call made by MSAL is requested with the default Microsoft tenant id, NOT your tenant id.
THIS is why I was unable to auth my function. If you are logged in with a user in your tenant's AAD, MSAL is amazing and easy to use and everything will work as described in the documentation.
In the AAD app itself, go to Settings -> Reply URLs and verify that the url of the Function App is in the list, which has the following format: https://mycoolapp.azurewebsites.net. If it isn't, then add it.
If you use slots, you have to add it for both slots.
The only thing i can think of right now is Allowed Audience.
Go to Your Active directory settings and click Advance. Under Allowed Token Audience
Add your exact function url. It might already be there with call back url but Simply replace it with only function base url without any call back as mentioned in the picture.
Make sure when you press ok , you also save your Authentication / Authorization setting to take effect and try again after 1min or so. I tested using PostMan and passing bearer token and it works !
I'm facing the exact same issue today. The issue turned out to be the resource id that I was passing when requesting the access token.
For example, initially I was requesting a token like this, using the function URL as the resource id:
AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync("https://myfunction.azurewebsites.net", "myClientAppIdGUID", new Uri("https://login.live.com/oauth20_desktop.srf"), new PlatformParameters(PromptBehavior.SelectAccount)).Result;
While this returned an access token, I was receiving a 401 unauthorized when using the access token to call my function api.
I changed my code to pass my function apps App Id as the resource:
AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync("myFunctionAppIdGUID", "myClientAppIdGUID", new Uri("https://login.live.com/oauth20_desktop.srf"), new PlatformParameters(PromptBehavior.SelectAccount)).Result;
Everything works fine now.
For me this was solved when I added the scope: [clientId]/.default
per this article

Azure AD Multitenant WebApi Bearer Authorization Configuration

I'm currently working on an ASP.net MVC and Web API project with AAD single sign on. The current problem I'm facing is that I don't know how to configure the authorization for the WebAPI. This is my current way I've tried but it doesn't work.
public void ConfigureAuth(IAppBuilder app)
{
...
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = "common",
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = audience,
ValidateIssuer = false
} });
...}
And I fetch the access token trough postman token predefined token request and that works.
postman token request
When I call the WebAPI (Header: Authorization -> Bearer )I got the following exception back:
{
"Message": "Authorization has been denied for this request."
}
Do I have to prepare the token request, the API header, or the configuration?
BG,
Tom
Here is a sample about Building a multi-tenant web API secured by Azure AD. You could download it and follow the steps it provided to configure it.
The application uses the Active Directory Authentication Library (ADAL) to obtain a JWT access token through the OAuth 2.0 protocol. The access token is sent to the web API to authenticate the user. The web API project demonstrates how to structure your services for being accessed by users coming from multiple Azure AD tenants.
As you want to login multiple tenants, you need to go to the app registered and click Manifest option set availableToOtherTenants to true.
For more details, you could refer to this article.

Azure AD callback is not going to AuthenticateExternalAsync method

I am using OpenID Connect to connect to Azure ID, I can successfully authenticate in Azure and get the request coming back to the redirect uri specified in OpenID Azure AD Configuration.
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationType = " TEST",
Caption = "Test Azure AD",
SignInAsAuthenticationType = signInAsType,
ClientId = "<client ID>",
Authority = "https://sts.windows.net/<tenantId>",
ResponseType = OpenIdConnectResponseTypes.CodeIdToken,
RedirectUri = "https://localhost:44392/External/Login", This is another webapi project, not identityserver host.
AuthenticationMode = AuthenticationMode.Passive,
});
After succesful authentication it is redirecting back to https://localhost:44392/External/Login with Code, IdToken.
Questions :
Does it not stop at AuthenticateExternalAsync method on redirection unlike google-signin ?
Do i have to decode IdToken JWT to get user claims?
In the redirection method, how do i generate Access Token from IdSrv3 to authorize other webapis ?
Can a user have both Local Login and Multiple External logins ( Azure AD, Google etc ). In this case how does SSO works with IDsrv3 ?
Is there any IdSrv3 sample with External logins implemented ? Preferably Azure AD ?
I've just struggled through this process, so I'll attempt to answer as best I can to help you/others. Forgive me if I misunderstand your question.
AuthenticateExternalAsync should be called, but you need to have AzureAd return to the IDS (Identity Server) rather than to your App. Your flow should look something like: app -> IDS -> AzureAd -> IDS (AuthenticateExternalAsync) -> App.
In AuthenticateExternalAsync you get the ExternalAuthenticationContext.ExternalIdentity, which contains the claims - no need to decode the JWT token.
IDS handles this once you return a successful AuthenticatedResult in AuthenticateExternalAsync, something like context.AuthenticateResult = new AuthenticateResult("UserId", name, claims);
Yes. You can force the method of logging in as described for SSO purposes, otherwise I imagine IDS would handle it post first-login.
I found this helpful (runs through setup of IDS and AzureAd), but it does use the old Azure Portal rather than the new one. They don't seem to have any samples in their gallery.
Hope that helps a bit :)

Azure AD Authentication With Auth0

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!

Categories

Resources