このガイドでは、MSAL Browser v5 で導入されたリダイレクト ブリッジ ページを設定するためのフレームワーク固有の手順について説明します。 リダイレクト ブリッジが必要な理由の背景については、 v4 から v5 への移行ガイドを参照してください。
Warning
リダイレクト ブリッジ ページは、ヘッダーと共に提供Cross-Origin-Opener-Policy。 ブリッジ ページは、IdP が OAuth フローを完了した後に認証応答を受信する中継局です。 ブリッジ ページに COOP ヘッダーが設定されている場合、ブラウザーは、通信チャネルをメイン アプリケーションに切り替える参照コンテキスト グループスワップを実行します。ブリッジが解決するように設計されている正確な問題を再導入します。
Important
新しいリダイレクト ブリッジ ページを指すようにredirectUriを更新した後、Entra ID アプリ登録のリダイレクト URI も更新する必要があります。
URI は、パス、プロトコル、ポートなど、 正確に 一致する必要があります。
アプリの登録を更新しないと、 redirect_uri_mismatch エラーが発生します。
Angular
-
リダイレクト ブリッジ コンポーネント (
src/app/redirect/redirect.component.ts) を作成します。
import { Component, OnInit } from "@angular/core";
import { broadcastResponseToMainFrame } from "@azure/msal-browser/redirect-bridge";
@Component({
selector: "app-redirect",
standalone: true,
template: "<p>Processing authentication...</p>",
})
export class RedirectComponent implements OnInit {
ngOnInit(): void {
broadcastResponseToMainFrame().catch((error: Error) => {
console.error("Error broadcasting response to main frame:", error);
});
}
}
- ルーティング構成に
/redirectルートを追加します。 リダイレクト ルートはMsalGuardにある必要があり、リダイレクト ページは、MsalInterceptorをトリガーする API 呼び出しを行わない (または MSAL API を呼び出す) ようにする必要があります。
import { RedirectComponent } from "./redirect/redirect.component";
const routes: Routes = [
{ path: "redirect", component: RedirectComponent },
// ... your other routes
];
- ビルドにコンポーネントが含まれていることを確認します。 Angular ルート コンポーネントを使用する場合、
angular.jsonアセットの変更は必要ありません。Angular CLI によってコンポーネントが自動的にバンドルされます。 ルーティング コンポーネントではなく静的redirect.htmlを使用する場合は、それを assets 配列に追加します。
// angular.json
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"assets": [
{ "glob": "**/*", "input": "public" },
"src/redirect.html" // ← Add redirect bridge page
]
}
}
}
}
}
}
サンプル:angular-standalone-sample と angular-modules-sample を参照してください。
Vite
Vite では、ビルド出力に別のエントリ ポイントとして redirect.html が含まれるように、マルチページ構成が必要です。
-
redirect.htmlを作成します(index.htmlの隣のプロジェクト ルート内):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Redirect</title>
</head>
<body>
<p>Processing authentication...</p>
<script type="module">
import { broadcastResponseToMainFrame } from "@azure/msal-browser/redirect-bridge";
broadcastResponseToMainFrame().catch((error) => {
console.error("Error broadcasting response:", error);
});
</script>
</body>
</html>
-
vite.config.tsを更新して、2番目のエントリとしてリダイレクトページを追加します:
import { defineConfig } from "vite";
import { resolve } from "path";
export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, "index.html"),
redirect: resolve(__dirname, "redirect.html"), // ← Redirect bridge entry
},
},
},
});
開発中 (vite dev)、リダイレクト ページは自動的に /redirect.htmlで提供されます。 実稼働ビルドでは、Rollup によって出力ディレクトリに index.html と redirect.html の両方が出力されます。
サンプル:react-router-sample、typescript-sample、および b2c-sample を参照してください。
Webpack
Webpack には、専用のエントリ ポイントと、リダイレクト ページの HtmlWebpackPlugin インスタンスが必要です。
-
作成
src/redirect.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Redirect</title>
</head>
<body>
<p>Processing authentication...</p>
<!-- The redirect script bundle will be injected by HtmlWebpackPlugin (see redirect.js entry). -->
</body>
</html>
-
作成
src/redirect.js(Webpack のエントリポイント):
import { broadcastResponseToMainFrame } from "@azure/msal-browser/redirect-bridge";
broadcastResponseToMainFrame().catch((error) => {
console.error("Error broadcasting response:", error);
});
-
更新
webpack.config.js:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
main: "./src/index.js",
redirect: "./src/redirect.js", // ← Redirect bridge entry
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/index.html",
chunks: ["main"],
}),
new HtmlWebpackPlugin({
filename: "redirect.html",
template: "./src/redirect.html",
chunks: ["redirect"], // ← Only include the redirect chunk
}),
],
};
Next.js
Next.js ページは自動的にルートになるため、リダイレクト ブリッジはページ コンポーネントです。 セットアップは、ページ ルーターとアプリ ルーターの間で異なります。
Pages Router (pages/)
-
作成
pages/redirect.js:
import { useEffect } from "react";
import { broadcastResponseToMainFrame } from "@azure/msal-browser/redirect-bridge";
export default function Redirect() {
useEffect(() => {
broadcastResponseToMainFrame().catch((error) => {
console.error("Error broadcasting response to main frame:", error);
});
}, []);
return <p>Processing authentication...</p>;
}
-
MsalProviderでリダイレクトページを除外する_app.js:
// pages/_app.js
import { useRouter } from "next/router";
import { MsalProvider } from "@azure/msal-react";
function MyApp({ Component, pageProps }) {
const router = useRouter();
// The redirect page must NOT be wrapped in MsalProvider
if (router.pathname === "/redirect") {
return <Component {...pageProps} />;
}
return (
<MsalProvider instance={msalInstance}>
<Component {...pageProps} />
</MsalProvider>
);
}
アプリ ルーター (app/)
-
app/redirect/page.jsの作成— クライアント コンポーネント ("use client") である必要があります。
"use client";
import { useEffect } from "react";
import { broadcastResponseToMainFrame } from "@azure/msal-browser/redirect-bridge";
export default function Redirect() {
useEffect(() => {
broadcastResponseToMainFrame().catch((error) => {
console.error("Error broadcasting response to main frame:", error);
});
}, []);
return <p>Processing authentication...</p>;
}
-
ルート レイアウトで
MsalProviderからリダイレクト ルートを除外します。app/layout.jsが子要素をMsalProviderで囲む場合は、そのラップ処理をスキップするリダイレクトルート用に別のレイアウトを作成してください。
// app/redirect/layout.js — no MsalProvider wrapper
export default function RedirectLayout({ children }) {
return <>{children}</>;
}
これにより、実行前に MSAL が認証応答ハッシュを処理できなくなります。
いずれのルーターにも next.config.js 変更は必要ありません。Next.js はページを自動的に処理します。
サンプル: ページ ルーターの例については、 nextjs-sample を参照してください。
Express.js/Node.js バックエンド
Express.js (または静的ファイルを提供する Node.js バックエンド) を使用する場合は、COOP ヘッダー なしで リダイレクト ページを提供するようにサーバーを構成します。
const express = require("express");
const path = require("path");
const app = express();
// Serve the redirect bridge page WITHOUT COOP headers
app.get("/redirect", (req, res) => {
res.sendFile(path.join(__dirname, "public", "redirect.html"));
});
// Set COOP headers for all other routes
app.use((req, res, next) => {
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
next();
});
app.use(express.static(path.join(__dirname, "public")));
サンプル:HybridSample を参照してください。