Microsoft Entra ID へのサイレント トークン要求は、パスワードの変更や条件付きアクセス ポリシーの更新などの理由により、失敗する場合があります。 エラーは多くの場合、更新トークンの 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 ラッパーを使用すると、自動的にアクセス トークンをサイレントに取得して API への HTTP 要求にそれを添付する HTTP インターセプターが提供されます。
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 ラッパーを使用すると、自動的にアクセス トークンをサイレントに取得して API への HTTP 要求にそれを添付する HTTP インターセプターが提供されます。
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 ID からトークンで返される特定の要求の動作を変更する。
- アプリケーションのカスタムの要求を追加してアクセスする。
IdToken
で省略可能な要求を要請するには、AuthenticationParameters.ts
クラスの claimsRequest
フィールドに文字列化された要求オブジェクトを送信します。
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);
});