The silent token requests to Microsoft Entra ID might fail for reasons like a password change or updated Conditional Access policies. More often, failures are due to the refresh token's 24-hour lifetime expiring and the browser blocking third party cookies, which prevents the use of hidden iframes to continue authenticating the user. In these cases, you should invoke one of the interactive methods (which may prompt the user) to acquire tokens:
The choice between a pop-up or redirect experience depends on your application flow:
You can set the API scopes that you want the access token to include when it's building the access token request. All requested scopes might not be granted in the access token. That depends on the user's consent.
The following code combines the previously described pattern with the methods for a pop-up experience:
// MSAL.js v2 exposes several account APIs, logic to determine which account to use is the responsibility of the developer
const account = publicClientApplication.getAllAccounts()[0];
const accessTokenRequest = {
scopes: ["user.read"],
account: account,
};
publicClientApplication
.acquireTokenSilent(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken);
})
.catch(function (error) {
//Acquire token silent failure, and send an interactive request
if (error instanceof InteractionRequiredAuthError) {
publicClientApplication
.acquireTokenPopup(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token interactive success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken);
})
.catch(function (error) {
// Acquire token interactive failure
console.log(error);
});
}
console.log(error);
});
The following code combines the previously described pattern with the methods for a pop-up experience:
const accessTokenRequest = {
scopes: ["user.read"],
};
userAgentApplication
.acquireTokenSilent(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token silent success
// Call API with token
let accessToken = accessTokenResponse.accessToken;
})
.catch(function (error) {
//Acquire token silent failure, and send an interactive request
if (error.errorMessage.indexOf("interaction_required") !== -1) {
userAgentApplication
.acquireTokenPopup(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token interactive success
})
.catch(function (error) {
// Acquire token interactive failure
console.log(error);
});
}
console.log(error);
});
The MSAL Angular wrapper provides the HTTP interceptor, which will automatically acquire access tokens silently and attach them to the HTTP requests to APIs.
You can specify the scopes for APIs in the protectedResourceMap
configuration option. MsalInterceptor
requests the specified scopes when automatically acquiring tokens.
// In app.module.ts
import { PublicClientApplication, InteractionType } from "@azure/msal-browser";
import { MsalInterceptor, MsalModule } from "@azure/msal-angular";
@NgModule({
declarations: [
// ...
],
imports: [
// ...
MsalModule.forRoot(
new PublicClientApplication({
auth: {
clientId: "Enter_the_Application_Id_Here",
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: isIE,
},
}),
{
interactionType: InteractionType.Popup,
authRequest: {
scopes: ["user.read"],
},
},
{
interactionType: InteractionType.Popup,
protectedResourceMap: new Map([
["https://graph.microsoft.com/v1.0/me", ["user.read"]],
]),
}
),
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
For success and failure of the silent token acquisition, MSAL Angular provides events that you can subscribe to. It's also important to remember to unsubscribe.
import { MsalBroadcastService } from '@azure/msal-angular';
import { EventMessage, EventType } from '@azure/msal-browser';
import { filter, Subject, takeUntil } from 'rxjs';
// In app.component.ts
export class AppComponent implements OnInit {
private readonly _destroying$ = new Subject<void>();
constructor(private broadcastService: MsalBroadcastService) { }
ngOnInit() {
this.broadcastService.msalSubject$
.pipe(
filter((msg: EventMessage) => msg.eventType === EventType.ACQUIRE_TOKEN_SUCCESS),
takeUntil(this._destroying$)
)
.subscribe((result: EventMessage) => {
// Do something with event payload here
});
}
ngOnDestroy(): void {
this._destroying$.next(undefined);
this._destroying$.complete();
}
}
Alternatively, you can explicitly acquire tokens by using the acquire-token methods as described in the core MSAL.js library.
The MSAL Angular wrapper provides the HTTP interceptor, which will automatically acquire access tokens silently and attach them to the HTTP requests to APIs.
You can specify the scopes for APIs in the protectedResourceMap
configuration option. MsalInterceptor
requests the specified scopes when automatically acquiring tokens.
// app.module.ts
@NgModule({
declarations: [
// ...
],
imports: [
// ...
MsalModule.forRoot(
{
auth: {
clientId: "Enter_the_Application_Id_Here",
},
},
{
popUp: !isIE,
consentScopes: ["user.read", "openid", "profile"],
protectedResourceMap: [
["https://graph.microsoft.com/v1.0/me", ["user.read"]],
],
}
),
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
For success and failure of the silent token acquisition, MSAL Angular provides callbacks that you can subscribe to. It's also important to remember to unsubscribe.
// In app.component.ts
ngOnInit() {
this.subscription = this.broadcastService.subscribe("msal:acquireTokenFailure", (payload) => {
});
}
ngOnDestroy() {
this.broadcastService.getMSALSubject().next(1);
if (this.subscription) {
this.subscription.unsubscribe();
}
}
Alternatively, you can explicitly acquire tokens by using the acquire-token methods as described in the core MSAL.js library.
The following code combines the previously described pattern with the methods for a pop-up experience:
import {
InteractionRequiredAuthError,
InteractionStatus,
} from "@azure/msal-browser";
import { AuthenticatedTemplate, useMsal } from "@azure/msal-react";
function ProtectedComponent() {
const { instance, inProgress, accounts } = useMsal();
const [apiData, setApiData] = useState(null);
useEffect(() => {
if (!apiData && inProgress === InteractionStatus.None) {
const accessTokenRequest = {
scopes: ["user.read"],
account: accounts[0],
};
instance
.acquireTokenSilent(accessTokenRequest)
.then((accessTokenResponse) => {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken).then((response) => {
setApiData(response);
});
})
.catch((error) => {
if (error instanceof InteractionRequiredAuthError) {
instance
.acquireTokenPopup(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token interactive success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken).then((response) => {
setApiData(response);
});
})
.catch(function (error) {
// Acquire token interactive failure
console.log(error);
});
}
console.log(error);
});
}
}, [instance, accounts, inProgress, apiData]);
return <p>Return your protected content here: {apiData}</p>;
}
function App() {
return (
<AuthenticatedTemplate>
<ProtectedComponent />
</AuthenticatedTemplate>
);
}
Alternatively, if you need to acquire a token outside of a React component you can call acquireTokenSilent
but shouldn't fall back to interaction if it fails. All interactions should take place underneath the MsalProvider
component in your component tree.
// MSAL.js v2 exposes several account APIs, logic to determine which account to use is the responsibility of the developer
const account = publicClientApplication.getAllAccounts()[0];
const accessTokenRequest = {
scopes: ["user.read"],
account: account,
};
// Use the same publicClientApplication instance provided to MsalProvider
publicClientApplication
.acquireTokenSilent(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken);
})
.catch(function (error) {
//Acquire token silent failure
console.log(error);
});
The following pattern is as described earlier but shown with a redirect method to acquire tokens interactively. You need to call and await handleRedirectPromise
on page load.
const redirectResponse = await publicClientApplication.handleRedirectPromise();
if (redirectResponse !== null) {
// Acquire token silent success
let accessToken = redirectResponse.accessToken;
// Call your API with token
callApi(accessToken);
} else {
// MSAL.js v2 exposes several account APIs, logic to determine which account to use is the responsibility of the developer
const account = publicClientApplication.getAllAccounts()[0];
const accessTokenRequest = {
scopes: ["user.read"],
account: account,
};
publicClientApplication
.acquireTokenSilent(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token silent success
// Call API with token
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken);
})
.catch(function (error) {
//Acquire token silent failure, and send an interactive request
console.log(error);
if (error instanceof InteractionRequiredAuthError) {
publicClientApplication.acquireTokenRedirect(accessTokenRequest);
}
});
}
The following pattern is as described earlier but shown with a redirect method to acquire tokens interactively. You need to register the redirect callback as mentioned earlier.
function authCallback(error, response) {
// Handle redirect response
}
userAgentApplication.handleRedirectCallback(authCallback);
const accessTokenRequest: AuthenticationParameters = {
scopes: ["user.read"],
};
userAgentApplication
.acquireTokenSilent(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token silent success
// Call API with token
let accessToken = accessTokenResponse.accessToken;
})
.catch(function (error) {
//Acquire token silent failure, and send an interactive request
console.log(error);
if (error.errorMessage.indexOf("interaction_required") !== -1) {
userAgentApplication.acquireTokenRedirect(accessTokenRequest);
}
});
Request optional claims
You can use optional claims for the following purposes:
- Include extra claims in tokens for your application.
- Change the behavior of certain claims that Microsoft Entra ID returns in tokens.
- Add and access custom claims for your application.
To request optional claims in IdToken
, you can send a stringified claims object to the claimsRequest
field of the AuthenticationParameters.ts
class.
var claims = {
optionalClaims: {
idToken: [
{
name: "auth_time",
essential: true,
},
],
},
};
var request = {
scopes: ["user.read"],
claimsRequest: JSON.stringify(claims),
};
myMSALObj.acquireTokenPopup(request);
To learn more, see Optional claims.
This code is the same as described earlier, except we recommend bootstrapping the MsalRedirectComponent
to handle redirects. MsalInterceptor
configurations can also be changed to use redirects.
// In app.module.ts
import { PublicClientApplication, InteractionType } from "@azure/msal-browser";
import {
MsalInterceptor,
MsalModule,
MsalRedirectComponent,
} from "@azure/msal-angular";
@NgModule({
declarations: [
// ...
],
imports: [
// ...
MsalModule.forRoot(
new PublicClientApplication({
auth: {
clientId: "Enter_the_Application_Id_Here",
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: isIE,
},
}),
{
interactionType: InteractionType.Redirect,
authRequest: {
scopes: ["user.read"],
},
},
{
interactionType: InteractionType.Redirect,
protectedResourceMap: new Map([
["https://graph.microsoft.com/v1.0/me", ["user.read"]],
]),
}
),
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
],
bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}
This code is the same as described earlier.
If acquireTokenSilent
fails, fallback to acquireTokenRedirect
. This method initiates a full-frame redirect and the response will be handled when returning to the application. When this component is rendered after returning from the redirect, acquireTokenSilent
should now succeed as the tokens will be pulled from the cache.
import {
InteractionRequiredAuthError,
InteractionStatus,
} from "@azure/msal-browser";
import { AuthenticatedTemplate, useMsal } from "@azure/msal-react";
function ProtectedComponent() {
const { instance, inProgress, accounts } = useMsal();
const [apiData, setApiData] = useState(null);
useEffect(() => {
const accessTokenRequest = {
scopes: ["user.read"],
account: accounts[0],
};
if (!apiData && inProgress === InteractionStatus.None) {
instance
.acquireTokenSilent(accessTokenRequest)
.then((accessTokenResponse) => {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken).then((response) => {
setApiData(response);
});
})
.catch((error) => {
if (error instanceof InteractionRequiredAuthError) {
instance.acquireTokenRedirect(accessTokenRequest);
}
console.log(error);
});
}
}, [instance, accounts, inProgress, apiData]);
return <p>Return your protected content here: {apiData}</p>;
}
function App() {
return (
<AuthenticatedTemplate>
<ProtectedComponent />
</AuthenticatedTemplate>
);
}
Alternatively, if you need to acquire a token outside of a React component you can call acquireTokenSilent
but shouldn't fall back to interaction if it fails. All interactions should take place underneath the MsalProvider
component in your component tree.
// MSAL.js v2 exposes several account APIs, logic to determine which account to use is the responsibility of the developer
const account = publicClientApplication.getAllAccounts()[0];
const accessTokenRequest = {
scopes: ["user.read"],
account: account,
};
// Use the same publicClientApplication instance provided to MsalProvider
publicClientApplication
.acquireTokenSilent(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token silent success
let accessToken = accessTokenResponse.accessToken;
// Call your API with token
callApi(accessToken);
})
.catch(function (error) {
//Acquire token silent failure
console.log(error);
});