Запросы автоматического маркера к идентификатору Microsoft Entra могут завершиться ошибкой по таким причинам, как изменение пароля или обновленные политики условного доступа. Чаще всего сбои связаны с тем, что истек 24-часовой срок существования маркера обновления и браузер блокирует сторонние файлы cookie, что предотвращает использование скрытых окон iframe для продолжения проверки подлинности пользователя. В этих случаях следует вызвать один из интерактивных методов получения токенов (из-за которого пользователь может получить запрос):
Выбор между методами всплывающего окна и перенаправления зависит от блок-схемы приложения:
Можно задать области API, которые должен включать маркер доступа, когда создает запрос маркера доступа. Маркер доступа может включать не все запрошенные области. Это зависит от согласия пользователя.
Следующий код объединяет ранее описанный шаблон с методами для всплывающего окна:
// 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);
});
Следующий код объединяет ранее описанный шаблон с методами для всплывающего окна:
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);
});
Программа-оболочка MSAL Angular предоставляет перехватчик HTTP, который автоматически получает маркеры доступа и вкладывает их в HTTP-запросы к API-интерфейсам.
Области для API-интерфейсов можно указать в параметре конфигурации protectedResourceMap
. MsalInterceptor
запрашивает указанные области при автоматическом получении маркеров.
// 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 {}
Для успешного или неудачного получения токена без вывода сообщений MSAL Angular предоставляет события, на которые вы можете подписаться. Важно также помнить об отмене подписки.
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();
}
}
Кроме того, можно явно получить маркеры с помощью методов получения маркера, как описано в основной библиотеке MSAL.js.
Программа-оболочка MSAL Angular предоставляет перехватчик HTTP, который автоматически получает маркеры доступа и вкладывает их в HTTP-запросы к API-интерфейсам.
Области для API-интерфейсов можно указать в параметре конфигурации protectedResourceMap
. MsalInterceptor
запрашивает указанные области при автоматическом получении маркеров.
// 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 {}
При успешном или неудачном автоматическом получении маркера MSAL Angular предоставляет обратные вызовы, на которые можно подписываться. Важно также помнить об отмене подписки.
// 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();
}
}
Кроме того, можно явно получить маркеры с помощью методов получения маркера, как описано в основной библиотеке MSAL.js.
Следующий код объединяет ранее описанный шаблон с методами для всплывающего окна:
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>
);
}
В качестве альтернативы, если вам необходимо получить маркер вне компонента React, вы можете вызвать acquireTokenSilent
, но не должны возвращаться к взаимодействию в случае сбоя. Все взаимодействие в дереве компонентов должно происходить ниже компонента MsalProvider
.
// 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);
});
Следующий шаблон подобен описанному ранее, но показан с помощью метода перенаправления для получения маркеров в интерактивном режиме. Вам нужно вызвать и ожидать handleRedirectPromise
нагрузку на страницу.
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);
}
});
}
Следующий шаблон подобен описанному ранее, но показан с помощью метода перенаправления для получения маркеров в интерактивном режиме. Необходимо зарегистрировать обратный вызов перенаправления, как упоминалось ранее.
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);
}
});
Запрос необязательных утверждений
Необязательные утверждения можно использовать в следующих целях:
- включить дополнительные утверждения в маркеры для приложения;
- Измените поведение определенных утверждений, возвращаемых идентификатором Microsoft Entra в токенах.
- добавлять пользовательские утверждения для приложения и обращаться к ним.
Чтобы запросить необязательные утверждения в IdToken
, можно отправить объект преобразованных утверждений в поле claimsRequest
класса AuthenticationParameters.ts
.
var claims = {
optionalClaims: {
idToken: [
{
name: "auth_time",
essential: true,
},
],
},
};
var request = {
scopes: ["user.read"],
claimsRequest: JSON.stringify(claims),
};
myMSALObj.acquireTokenPopup(request);
Дополнительные сведения см. в статье о необязательных утверждениях.
Этот код аналогичен описанному ранее, за исключением того, что мы рекомендуем выполнить начальную загрузку MsalRedirectComponent
для обработки перенаправлений. Конфигурации MsalInterceptor
также можно изменить для использования переадресации.
// 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 {}
Этот код аналогичен описанному ранее.
Если в acquireTokenSilent
происходит сбой, вернитесь к acquireTokenRedirect
. Этот метод инициирует перенаправление с полным кадром, а ответ будет обрабатываться при возвращении в приложение. Когда этот компонент отображается после возврата из перенаправления, acquireTokenSilent
теперь должен быть выполнен успешно, поскольку токены будут извлечены из кэша.
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>
);
}
В качестве альтернативы, если вам необходимо получить маркер вне компонента React, вы можете вызвать acquireTokenSilent
, но не должны возвращаться к взаимодействию в случае сбоя. Все взаимодействие в дереве компонентов должно происходить ниже компонента MsalProvider
.
// 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);
});