Azure AD multitenancy configuration with .NET Core - azure

I have two applications registered in Azure AD. One for front-end app and one for API(.NET Core). I need these applications to be multitenant. So I have set Accounts in any organizational directory (Any Azure AD directory - Multitenant) selection in AzureAD for both applications. I am using Azure AD V2.0 endpoints. I am able to get the id_token successfully from front-end app. But when it is passed to API, even though I have set TokenValidationParameters.ValidateIssuer to false, it tries to validate the issuer and returns 401 Unauthorized status. It seems TokenValidationParameters.ValidateIssuer flag is ignored.
I noticed a mismatch when I check the log
INFO Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.? [?] - MESSAGE: AzureADJwtBearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'https://sts.windows.net/9967b0b6-c5d3-*************/'. Did not match: validationParameters.ValidIssuer: 'null' or validationParameters.ValidIssuers: 'https://sts.windows.net/{tenantid}/'.
2020-01-13 14:34:51,884 INFO Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.? [?] - MESSAGE: Authorization failed
Please notice here the Issuer is https://sts.windows.net/9967b0b6-c5d3-*************/
But when I decode the id_token I can see the Issuer is set as "iss":"https://login.microsoftonline.com/9967b0b6-c5d3-*************/v2.0" which I think the correct end point for AzureAD V2.0. Could this be a reason for the above unauthorized return.
Below is the authentication related code in ConfigureServices method in Startup.cs
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
Below is the appSettings.json configuration
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "my_doamin.com",
"TenantId": "common",
"ClientId": "https://my_doamin.com/api-test"
}
The API end point with Authorize attribute
[Authorize(AuthenticationSchemes = "AzureADBearer")]
[Route("getPotalAdminUsers")]
[HttpGet]
public async Task<IActionResult> getPotalAdminUsers()
{
//Code
}
I went through some SO questions regarding this issue. But none helped me.
I appreciate any help on this. Thanks.

I found the solution and the reason behind the issue I faced.
I am posting the answer so that anyone who is facing the same issue can get the issue cleared out.
I was using OpenIdConnect middleware in Startup.cs which was the reason behind the issue. OpenIdConnect middleware is used when we need to sign-ing in the users from our app. But in our context we sign-ing in the users from the front-end app. API does not sign-ing in users! API just needs to validate the access_token.
To validate access_tokens we should use JwtBearer middleware instead of OpenIdConnect middleware.
So replace the
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
With
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
For more information refer the below link
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/How-ASP.NET-Core-uses-Microsoft.IdentityModel-extensions-for-.NET

Related

Azure AD Identity authentication error : IDX10214: Audience validation failed

I have an .net core 5 api that I want to protect using Azure AD, and then call it from a console app. So my case is only service-to-service authentication. I registered both the apps to Azure AD an then gave the permission to the console app on the api using App roles.
I succeeded to retrieve a token for the console app passing the right scope :
static async Task Main(string[] args)
{
var app = ConfidentialClientApplicationBuilder
.Create(<MY_CONSOLE_APP_ID>)
.WithClientSecret(<MY_CONSOLE_APP_CLIENT_SECRET>)
.WithAuthority(new Uri($"https://login.microsoftonline.com/<MY_TENANT_ID>"))
.Build();
var result = await app.AcquireTokenForClient(new String[]
{
"api://<MY_API_APP_ID>/.default"
})
.ExecuteAsync();
}
Then in the Startup class of the API, I added the following code :
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration, "AzureAd");
and in the appsettings.json I defined the required settings:
"AzureAd": {
"ClientId": "<MY_API_APP_ID>",
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<MY_TENANT_ID>"
}
Then I marked a controller as [Authorize] but when I call one of its endpoint I get the following error :
{
"code": 401,
"message": "IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'."
}
I also tried to prefix the clientId by api:// but got the same error. I decoded the token to ensure the audience was correct and it seems ok since I have "aud": "api://<MY_API_APP_ID>"
Do you have an idea of what I am missing ?
Whatever code provided looks good.
Make sure the Audience config matches the "aud" claim in the access token.
As that part of code is not provided here,
you may be missing an entry of audience in code configuration under services.AddAuthentication(…
Please check if you have given audience entry in any of these ways .
1
.AddJwtBearer(options =>
{
options.Authority = "";
//options.Audience = Configuration["Audience"];
};
(or)
2
.AddJwtBearer(options =>
{
options.Audience = clientId;
options.Authority = authority;
})
(or)
3)
.AddJwtBearer(options =>
{
options.Authority = "";
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
//here you give required valid audiences
ValidateIssuer = true,
ValidAudiences = new List<string>
{
"AUDIENCE1",
"AUDIENCE2"
}
or valid audiences can be like below in place of AUDIENCE1 or 2.
ValidAudiences = new List<string>
{
Configuration["Authentication:ClientId"]
}
The aud (audience) should match your API's id, and make sure required scopes are present .If these are fine , then check if the token you are trying to validate is ID token or Access token .It differs for API and graph api.

Bearer Token not working in AAD B2C .NETCorre

I have a Web API built using ASP.NET Core. I have a React App that will call this API. The identity is managed using AAD B2C. I am running into an issue where the bearer token generated by the app is not recognized by the API.
I am certain that this has to do with my settings because the token itself has all the claims I need (as decoded by JWT.io). However, when I pass it through the code in .NET Core to allow authorization, the ClaimsIdentity has nothing and contains no user information.
I am setting up the instance using the following lines of code:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
configuration.Bind("AzureAdClient", options);
options.TokenValidationParameters.NameClaimType = "name";
}, options => { configuration.Bind("AzureAdClient", options); });
I also have the following configuration:
"AzureAdClient": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "somename.onmicrosoft.com",
"ClientId": "guid here",
"TenantId": "guid here",
"Audience": "https://somename.onmicrosoft.com/tenants-api",
"SignUpSignInPolicyId": "B2C_1_RwSignIn"
}
Am I doing something wrong here?
I was able to get this figured out using a different strategy. The recommended configuration in most examples still does not work.
Instance: https://yourname.b2clogin.com
Domain: yourname.onmicrosoft.com
SignUpSignInPolicyId: the actual name of
your signup/sign in policy
ClientId: the client Id of the API Client.
var section = configuration.GetSection("AzureAdClient");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = $"{section["Instance"]}/{section["Domain"]}/{section["SignUpSignInPolicyId"]}/v2.0/";
options.Audience = $"{section["ClientId"]}";
});

msal.js 2.0 tokenResponse null after loginRedirect

I am developing an Angular 10 app that utilizes Azure B2C for policy and user management. I set up my app registration in Azure Active Directory as a singlepage app without the implicit option checked. I am using msal.js 2.0 #azure/msal-browser to log into B2C and retrieve id and access tokens using code flow. I set up my configuration, created the msal object, defined the redirect promise, then later call loginRedirect with the appropriate user scopes. The page redirects properly.
However, after I sign in the tokenResponse comes back as null. I have tried altering the authority and scopes, but it always comes back as null. How do I get the handleRedirectPromise to return a valid token response?
Here's my code:
private msalConfig: Msal.Configuration = {
auth: {
clientId: xxxx-xx-xx-xx-xxxxx,
authority: 'https://login.microsoftonline.com/common',
redirectUri: 'https://localhost:4200'
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false
},
};
private loginRequest: Msal.RedirectRequest = {
scopes: ['user.read'],
};
const msalInstance = new Msal.PublicClientApplication(this.msalConfig);
msalInstance
.handleRedirectPromise()
.then((tokenResponse: Msal.AuthenticationResult) => {
let accountObj = null;
if (tokenResponse !== null) {
accountObj = tokenResponse.account;
const id_token = tokenResponse.idToken;
const access_token = tokenResponse.accessToken;
console.log('id_token', id_token);
console.log('access_token', access_token);
}
})
.catch(error => {
authStore.loginError$.next(true);
console.error(error);
});
msalInstance.loginRedirect(this.loginRequest);
Edit:
I have also tried authority: `https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/<policy-name> and https://login.microsoftonline.com/tfp/{tenant}.onmicrosoft.com/B2C_1_SiupIn for the authority in the msalConfig object as well as scopes: ['openid'] in the loginRequest. When I use this I get the following error in the browser when I try to log in:
zone-evergreen.js:1068 GET https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https://<tenant>.b2clogin.com/<tenant>.onmicrosoft.com/b2c_1_defaultsigninsignup/oauth2/v2.0/authorize 400 (Bad Request)
core.js:4197 ERROR Error: Uncaught (in promise): ClientAuthError: endpoints_resolution_error: Error: could not resolve endpoints. Please check network and try again. Detail: ClientConfigurationError: untrusted_authority: The provided authority is not a trusted authority. Please include this authority in the knownAuthorities config parameter.
ClientAuthError: endpoints_resolution_error: Error: could not resolve endpoints. Please check network and try again. Detail: ClientConfigurationError: untrusted_authority: The provided authority is not a trusted authority. Please include this authority in the knownAuthorities config parameter.
The way you set up the redirect flow seems correct. You first have to call the handleRedirectPromise() (which registers it), and then call the loginRedirect(). At page load handleRedirectPromise() will return null, and after sign-in it should return the token.
There are issues with your configuration, however.
You need to designate your domain as a knownAuthority, like:
auth: {
clientId: 'xxxx-xx-xx-xx-xxxxx',
authority: 'https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/<policy-name>',
knownAuthorities: ['<your-tenant-name>.b2clogin.com']
redirectUri: 'https://localhost:4200'
},
User.Read is a MS Graph API scope. You cannot use it with B2C. Only the OIDC scopes are allowed i.e. use openid instead.
See this for more.
The problem was with my angular app. I had my app redirecting the base url to my /home route. So whenever you open the base route the app is redirected. From this route, the request is made. I added the redirect uri for the /home route to my AAD app registration, commented out the redirectUri in my b2c configuration and set navigateToLoginRequestUrl to true.

Can't get access token from HttpContext for AAD with OpenIDConnect

I have configured Open ID Connect with Azure AD. I wish to retrieve the access_token from AAD. Currently I am only able to retrieve the id_token. I have configured my Azure Active Directory App registration to include both the access_token and the id_token.
I have configured my Azure Active Directory App registration to include both the access_token and the id_token.
I have also tried retrieving the token from the header without any luck.
Startup.cs
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAD", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = true;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.SaveTokens = true;
});
MyController.cs
if(User.Identity.IsAuthenticated)
{
string accessToken = await HttpContext.GetTokenAsync("access_token");
string idToken = await HttpContext.GetTokenAsync("id_token");
}
appsettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mydomain",
"TenantId": "organizations",
"ClientId": "myclientid",
"ClientSecret": "myclientsecret",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath ": "/signout-callback-oidc"
}
You will need to use CodeIdTokenToken response type, according to the documentation
options.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;
I managed to fix this. To anyone that would encounter this issue, set the response type to Code to get both the id_token and the access_token. This will instruct Open ID Connect to use the authorization code flow.
options.ResponseType = OpenIdConnectResponseType.Code

Error IDX10500 after successfully resetting password via B2C policy

We've successfully implemented Azure AD B2C for authentication in our web application, and now I'm on to trying to use a built-in policy for Password Reset. The web app successfully captures the error code (AADB2C90118) thrown when the user clicks the Forgot Password link, redirecting to the Account/ResetPassword method which properly performs the Challenge, sending the user through the Password Reset experience.
The user gets validated and is able to change their password, but when the response is posted back to the web app, we end up in the AuthenticationFailed event with the following exception:
{"IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier\r\n (\r\n IsReadOnly = False,\r\n Count = 1,\r\n Clause[0] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause\r\n )\r\n', \ntoken: '{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk\"}.{\"exp\":1537473897,\"nbf\":1537470297,\"ver\":\"1.0\",\"iss\":\"https://login.microsoftonline.com/1f0535de-b375-48bf-8bc8-d9e0b6ff185a/v2.0/\",\"sub\":\"454bd9a9-7a7c-4e7f-9535-213e07408d14\",\"aud\":\"f3afd0e2-9f6e-435a-918c-e8c542f9f5ad\",\"nonce\":\"636730670637853311.OTk5YTlhMWYtOTQyYi00ZDBjLTg0ZjItZDExMjI5MTQ5NzU1ZTY5MzZjODgtMmE2Yi00NmQ0LTg0MTYtOTc5YTJjNWEzOGQ4\",\"iat\":1537470297,\"auth_time\":1537470297,\"emails\":[\"ls1grrrl#gmail.com\"],\"oid\":\"454bd9a9-7a7c-4e7f-9535-213e07408d14\",\"name\":\"hbecker\",\"tfp\":\"B2C_1_SSPR\"}'."}
I can't seem to find anyone with this issue in the Password Reset process in B2C, so any help is appreciated!
In Startup.cs (ConfigureServices method)
services.AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/", Portable.Constants.Constants.Tenant, Portable.Constants.Constants.PolicySignUpSignIn); options.Audience = Portable.Constants.Constants.ClientID; options.RequireHttpsMetadata = false; options.Events = new JwtBearerEvents { OnAuthenticationFailed = AuthenticationFailed }; });
In Configure method (before app.UseMvc();):
app.UseAuthentication();
Ensure that you also pass the correct metadata endpoint.
See this thread for the full workaround: https://github.com/Azure-Samples/active-directory-b2c-dotnetcore-webapi/issues/9
See also this very detailed thread discussing how to design the password reset policy to avoid this issue.

Resources