Reputation: 396
I am having a problem configuring a React client accessing a .NET 5 web API using Azure AD B2C. I reviewed the following documents;
I ended up using the examples shown in the last document and using the configuration settings for the node.js API server in my .NET 5 Web API;
React Configuration
import { LogLevel } from "@azure/msal-browser";
export const b2cPolicies = {
names: {
signIn: "b2c_1_signin",
signInStaff: "b2c_1_signupin_staff"
},
authorities: {
signIn: {
authority: "https://<b2cTenantName>.b2clogin.com/<b2cTenantName>.onmicrosoft.com/b2c_1_signin",
},
signInStaff: {
authority: "https://<b2cTenantName>.b2clogin.com/<b2cTenantName>.onmicrosoft.com/b2c_1_signupin_staff"
}
},
authorityDomain: "<b2cTenantName>.b2clogin.com"
}
export const msalConfig = {
auth: {
clientId: "ec0441a4-89ac-1111-1111-111111111111",
authority: b2cPolicies.authorities.signIn.authority,
knownAuthorities: [b2cPolicies.authorityDomain],
redirectUri: "http://localhost:3000",
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) return;
level = LogLevel.Verbose;
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
default:
console.log(message);
}
}
}
}
};
export const loginRequest = {
scopes: ["openid", "offline_access"]
};
export const loginRequestStaff = {
scopes: ["openid", "profile"]
};
export const protectedResources = {
portalApi: {
scopes: ["https://<b2cTenantName>.onmicrosoft.com/PortalClient/access_as_user"],
redirectUri: "http://localhost:3000/Dashboard",
},
portalApiStaff: {
scopes: ["https://<b2cTenantName>.onmicrosoft.com/PortalClient/access_as_staff"],
redirectUri: "http://localhost:3000/Dashboard",
}
}
.NET 5 AppSettings.json
{
...
"AzureAd": {
"Instance": "https://<b2cTenantName>.b2clogin.com",
"Domain": "<b2cTenantName>.onmicrosoft.com",
"ClientId": "ec0441a4-89ac-1111-1111-111111111111",
"SignUpSignInPolicyId": "B2C_1_SignIn"
},
...
}
Portal API Registration
Property | Value |
---|---|
Application (Client) ID | d1a138a9-2379-1111-1111-111111111111 |
Directory (Tenant) ID | 6c977334-b859-2222-2222-222222222222 |
Redirect URIs | http://localhost:3000 |
Certificates & Secrets | None |
API Permissions (all have Admin consent) | Microsoft Graph - offline_access, openid |
Application ID URI | https://.onmicrosoft.com/PortalApi |
Exposed APIs (scopes) | access_as_staff, access_as_user |
Portal Client Registration
Property | Value |
---|---|
Application (Client) ID | ec0441a4-89ac-1111-1111-111111111111 |
Directory (Tenant) ID | 6c977334-b859-2222-2222-222222222222 |
Redirect URIs | http://localhost:3000/Dashboard |
http://localhost:3000 | |
Certificates & Secrets | None |
API Permissions (all have Admin consent) | Microsoft Graph: - offline_access, openid |
Portal API - access_as_staff, access_as_user |
MSAL Login & Access Token Code
import { PublicClientApplication } from "@azure/msal-browser";
import { msalConfig, b2cPolicies, protectedResources, loginRequest, loginRequestStaff } from "./authConfig";
let msalInstance = new PublicClientApplication(msalConfig);
export const getMsalInstance = () => msalInstance;
export const login = () => {
msalInstance.config.auth.authority = b2cPolicies.authorities.signIn.authority;
msalInstance.loginRedirect(loginRequest);
}
export const loginAsStaff = () => {
msalInstance.config.auth.authority = b2cPolicies.authorities.signInStaff.authority;
msalInstance.loginRedirect(loginRequestStaff);
}
export const getAccessToken = async (accessToken, isStaffUser) => {
const accounts = msalInstance.getAllAccounts();
if (accounts.length === 0) return null;
let _accessToken = {...accessToken};
if (_accessToken.token == null || _accessToken.expires <= Date()) {
const response = await msalInstance.acquireTokenSilent({
account: accounts[0],
scopes: isStaffUser ? protectedResources.portalApiStaff.scopes : protectedResources.portalApi.scopes
});
const expiryDt = new Date(0).setUTCSeconds((response.idTokenClaims.exp));
_accessToken = { token: response.idToken, expires: expiryDt };
}
return _accessToken;
}
.NET 5 Web API Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
Configuration.Bind("AzureAd", options);
options.TokenValidationParameters.NameClaimType = "name";
}, options => { Configuration.Bind("AzureAd", options); });
services.AddAuthorization();
...
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
...
}
}
When acquiring the access token in the MSAL code I get the following response in response.idToken
;
{
"typ": "JWT",
"alg": "RS256",
"kid": "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk"
}.{
"exp": 1669793111,
"nbf": 1669789511,
"ver": "1.0",
"iss": "https://<b2cTenantName>.b2clogin.com/6c977334-b859-2222-2222-222222222222/v2.0/",
"sub": "3a35cfce-de55-4217-9c33-217f14639578",
"aud": "ec0441a4-89ac-1111-1111-111111111111",
"nonce": "ce9b5c4f-010d-43cc-9dce-861e6a087489",
"iat": 1669789511,
"auth_time": 1669789496,
"idp_access_token": "eyJraWQiOiItVWRTSVB...7bBGxGDzNQWqw",
"idp": "https://<idp>/oauth2/auskoi3basJBCcDOy1t7",
"given_name": "John",
"family_name": "Doe",
"name": "John Doe",
"oid": "3a35cfce-de55-4217-9c33-217f14639578",
"emails": [
"[email protected]"
],
"tfp": "B2C_1_SignUpIn_Staff"
}.[Signature]
and if I then decode the idp-access-token
I get;
{
"kid": "-UdSIPeUmX7e4pqpXRBb7IXu3bCLsQo0hU67PYDhdfM",
"alg": "RS256"
}.{
"ver": 1,
"jti": "AT.r_KbqlRqqur1gxf9u5S8VReP2awA58YjtIfKiBdLokQ",
"iss": "https://<my-company>/oauth2/auskoi3basJBCcDOy1t7",
"aud": "Company-CustomerPortal",
"iat": 1669789481,
"exp": 1669793081,
"cid": "0oakoicxjrL5IMsmd1t7",
"uid": "00ua37oxlxRdmbFgb1t7",
"scp": [
"groups",
"profile",
"openid"
],
"auth_time": 1669789477,
"sub": "[email protected]"
}.[Signature]
Neither of which have the access_as_staff
scope.
If I configure the client and API code to use the Portal Client clientId (ec0441a4-89ac-1111-1111-111111111111) and submit the access token to an [Authorize]
route on the web API I get the following error;
IDW10201: Neither scope or roles claim was found in the bearer token.
If however I configure the API to use the Portal API clientId (d1a138a9-2379-1111-1111-111111111111) and the client to use the Portal Client clientId (ec0441a4-89ac-1111-1111-111111111111) as shown in the example, I get the following error;
IDX10214: Audience validation failed. Audiences: 'ec0441a4-89ac-1111-1111-111111111111'. Did not match: validationParameters.ValidAudience: 'd1a138a9-2379-1111-1111-111111111111' or validationParameters.ValidAudiences: 'null'.
Upvotes: 0
Views: 524
Reputation: 10859
I tried to reproduce the same in my environment:
I tried to change the scope parameter but still I received .
In my case I am calling graph api:
So my scope must be https://graph.microsoft.com/.default
I got the same error when I tried to call an API from my web app.
Error:
SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'xxx'. Did not match: validationParameters.ValidAudience: xxxx or validationParameters.ValidAudiences: xxxx
In my case I have given wrong clientId in my clientApp registration in
appsettings.json
{
"AzureAd": {
"Instance": "....",
"Domain": "xxx",
"ClientId": "xxx",
"TenantId": "xxxf3a0cxxb0",
"ClientSecret": "xxxxxxxxx",
"ClientCertificates": [
],
….
},
Make sure to expose the scopes for your API in your API configuration . Give that (exposed)API permissions to the APP that is clientAPP .
Also check the issuer endpoint,
"iss": "https://<b2cTenantName>.b2clogin.com/xxxx/v2.0/",
If it has v2 endpoint make sure , the value is 2 in> "accessTokenAcceptedVersion": 2,
With all changes , I could call my Api successfully:
Also check this reference : Azure AD B2C: Call an ASP.NET Web API from an ASP.NET Web App - Code Samples | Microsoft Learn
Upvotes: 0