Menyiapkan halaman jembatan pengalihan di Browser MSAL

Panduan ini menyediakan instruksi khusus kerangka kerja untuk menyiapkan halaman jembatan pengalihan yang diperkenalkan di Browser MSAL v5. Untuk latar belakang mengapa jembatan pengalihan diperlukan, lihat panduan migrasi v4 ke v5.

Warning

Halaman jembatan pengalihan TIDAK boleh dilayani dengan Cross-Origin-Opener-Policy header. Halaman bridge adalah perantara yang menerima respons autentikasi setelah IdP menyelesaikan alur OAuth. Jika header COOP diatur pada halaman perantara, browser akan melakukan pertukaran kelompok konteks penjelajahan yang memutus saluran komunikasi ke aplikasi utama — sehingga memunculkan kembali masalah yang memang ingin diatasi oleh perantara ini.

Important

Setelah memperbarui redirectUri untuk mengarahkan ke halaman jembatan pengalihan baru, Anda juga HARUS memperbarui URI pengalihan di pendaftaran aplikasi Entra ID Anda. URI harus sama persis — termasuk jalur, protokol, dan port. Kegagalan untuk memperbarui pendaftaran aplikasi akan mengakibatkan redirect_uri_mismatch kesalahan.

Angular

  1. Buat komponen jembatan pengalihan (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);
        });
    }
}
  1. Tambahkan rute /redirect di konfigurasi perutean Anda. Rute pengalihan harus berada di luarMsalGuard, dan halaman pengalihan tidak boleh melakukan panggilan API yang akan memicu MsalInterceptor (atau memanggil API MSAL):
import { RedirectComponent } from "./redirect/redirect.component";

const routes: Routes = [
    { path: "redirect", component: RedirectComponent },
    // ... your other routes
];
  1. Pastikan hasil build menyertakan komponen tersebut. Tidak ada angular.json perubahan aset yang diperlukan saat menggunakan komponen rute Angular — Angular CLI menggabungkan komponen secara otomatis. Jika Anda lebih suka statis redirect.html alih-alih komponen yang dirutekan, tambahkan ke array aset:
// angular.json
{
    "projects": {
        "your-app": {
            "architect": {
                "build": {
                    "options": {
                        "assets": [
                            { "glob": "**/*", "input": "public" },
                            "src/redirect.html" // ← Add redirect bridge page
                        ]
                    }
                }
            }
        }
    }
}

Sampel: Lihat sampel angular-standalone dan angular-modules-sample.

Vite

Vite memerlukan konfigurasi multihalaman agar redirect.html disertakan sebagai titik entri terpisah dalam hasil kompilasi.

  1. Buat redirect.html di akar proyek Anda (di samping 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>
  1. Update vite.config.ts untuk menambahkan halaman pengalihan sebagai entri kedua:
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
            },
        },
    },
});

Selama pengembangan (vite dev), halaman pengalihan secara otomatis dilayani di /redirect.html. Dalam build produksi, Rollup menghasilkan index.html dan redirect.html di direktori output.

Sampel: Lihat react-router-sample, typescript-sample, dan b2c-sample.

Webpack

Webpack memerlukan titik masuk khusus dan HtmlWebpackPlugin instans untuk halaman pengalihan.

  1. Buat 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>
  1. Membuat src/redirect.js (titik masuk untuk Webpack):
import { broadcastResponseToMainFrame } from "@azure/msal-browser/redirect-bridge";

broadcastResponseToMainFrame().catch((error) => {
    console.error("Error broadcasting response:", error);
});
  1. Perbarui 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

Halaman Next.js secara otomatis menjadi rute, sehingga perantara pengalihan diwujudkan sebagai komponen halaman. Penyiapan berbeda antara Router Halaman dan Router Aplikasi.

Pages Router (pages/)

  1. Buat 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>;
}
  1. Mengecualikan halaman pengalihan dari MsalProvider dalam _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 Router (app/)

  1. Buat app/redirect/page.js — ini harus merupakan Komponen Klien ("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>;
}
  1. Mengecualikan rute pengalihan dari MsalProvider di tata letak akar Anda. Jika Anda app/layout.js membungkus anak-anak di MsalProvider, buat tata letak terpisah untuk rute pengalihan yang melewatinya:
// app/redirect/layout.js — no MsalProvider wrapper
export default function RedirectLayout({ children }) {
    return <>{children}</>;
}

Ini mencegah MSAL memproses hash respons autentikasi sebelum broadcastResponseToMainFrame() dijalankan.


Tidak diperlukan next.config.js perubahan apa pun untuk kedua router — Next.js menyajikan halaman secara otomatis.

Sampel: Lihat contoh Nextjs-sample untuk Pages Router.

Express.js / Node.js Backend

Saat menggunakan Express.js (atau backend Node.js apa pun yang melayani file statis), konfigurasikan server untuk melayani halaman pengalihan tanpa header 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")));

Sampel: Lihat HybridSample.

Sumber Daya Tambahan