Reputation: 1981
I'm developing a cloud application that use AD B2C as OpenID Connect Provider.
Below my configuration environment's:
AD B2C
Inside my AD B2C I've created two applications:
API Gateway
I created one API Management. After the API importations I've added one policy like following:
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/{myTenantAzureId}/.well-known/openid-configuration?p={myPolicies}" />
</validate-jwt>
MyClient
My client is an Angular 4 application. I'm using MSAL.js Microsoft official library.
This is my authorization service TypeScript class:
import { Injectable } from '@angular/core';
declare var bootbox: any;
declare var Msal: any;
@Injectable()
export class MsalService {
public access_token: string;
private logger = new Msal.Logger(this.loggerCallback, { level: Msal.LogLevel.Verbose });
tenantConfig = {
tenant: "{myTenant}.onmicrosoft.com",
clientID: '{MyClientClientId}',
signUpSignInPolicy: "{myPolicies}",
b2cScopes: ["openid"]
};
options = {
logger: this.logger,
postLogoutRedirectUri: window.location.protocol + "//" + window.location.host
}
//authority = null;
authority = "https://login.microsoftonline.com/tfp/" + this.tenantConfig.tenant + "/" + this.tenantConfig.signUpSignInPolicy;
clientApplication = new Msal.UserAgentApplication(
this.tenantConfig.clientID,
this.authority,
this.authCallback,
this.options
);
public login(callback: Function): void {
var _this = this;
this.clientApplication.loginPopup(this.tenantConfig.b2cScopes).then(function (idToken: any) {
_this.clientApplication.acquireTokenSilent(_this.tenantConfig.b2cScopes).then(
function (accessToken: any) {
_this.access_token = accessToken;
localStorage.setItem("access_token", accessToken);
if (callback) {
callback(accessToken);
}
}, function (error: any) {
_this.clientApplication.acquireTokenPopup(_this.tenantConfig.b2cScopes).then(
function (accessToken: any) {
_this.access_token = accessToken;
console.log(accessToken);
}, function (error: any) {
bootbox.alert("Error acquiring the popup:\n" + error);
});
})
}, function (error: any) {
console.log(error);
bootbox.alert("Error during login:\n" + error);
});
}
public logout(callback: Function): void {
this.clientApplication.logout();
if (callback) {
callback();
}
}
private loggerCallback(logLevel, message, piiLoggingEnabled) {
console.log(message);
}
private authCallback(errorDesc: any, token: any, error: any, tokenType: any) {
if (token) {
}
else {
console.error(error + ":" + errorDesc);
}
}
}
Problem
If I try call one API Management's API with Authorization header with access token I obtain this error:
{ "statusCode": 401, "message": "Unauthorized. Access token is missing or invalid." }
But If I try access directly by Developer Portal I can call the same API with success.
Why my API Manager not authorize my application?
Thank you a lot
Upvotes: 4
Views: 3394
Reputation: 14704
I believe the above error is occurring because the API gateway doesn't recognize the aud
(audience) claim for the access token that is being passed from the Angular client.
The above scenario is similar to "Azure AD B2C: Call a .NET web API from a .NET web app" where the Angular client is the web app and the API gateway is the web API.
I recommend you:
1) Create an Azure AD B2C app that represents the API gateway. Enter an App ID URI for this app to identify the API gateway.
2) Add one or more permission scopes to this gateway app and/or keep the default permission scope of user_impersonation
.
3) Create an Azure AD B2C app that represents the Angular client. You've already created this client app.
4) Grant access by the client app to the gateway app so that the client app can acquire an access token to act on behalf of the signed-in user to call the gateway app.
5) Update the validate-jwt
policy with the app ID for the gateway app.
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/tfp/{tenant}/{policy}/v2.0/.well-known/openid-configuration" />
<audiences>
<audience><!-- Paste the app ID for the gateway app --></audience>
</audiences>
</validate-jwt>
6) Include any permission scopes, which were added in step 2, to be issued to the client app to the tenantConfig.b2cScopes
array.
Upvotes: 2
Reputation: 16011
Based upon
{ "statusCode": 401, "message": "Unauthorized. Access token is missing or invalid." }
It would seem you're not sending the token from Angular app when making your http request. One way to do this in Angular is with an interceptor.
@Injectable()
export class AuthenticationHttpInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthenticationService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return Observable.fromPromise(this.authenticationService.getAuthenticationToken())
.switchMap(token => {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(req);
});
}
}
Source: authentication.httpInterceptor.ts
Upvotes: 0