Vyhněte se opětovnému načítání stránky při získávání a obnovování tokenů bezobslužně pomocí MSAL.js

Microsoft Authentication Library pro JavaScript (MSAL.js) používá skryté iframe prvky k získání a obnovení tokenů bezobslužně na pozadí. Microsoft Entra ID vrátí token zpět k registrovanému redirect_uri zadanému v požadavku tokenu (ve výchozím nastavení se jedná o kořenovou stránku aplikace). Protože má odpověď stavový kód 302, načte se do iframe HTML odpovídající redirect_uri. Obvykle je aplikace redirect_uri kořenovou stránkou a způsobí to její opětovné načtení.

V jiných případech může přechod na kořenovou stránku aplikace vyžadující autentizaci vést k vnořeným prvkům iframe nebo k chybě X-Frame-Options: deny.

Protože MSAL.js nemůže zavřít verzi 302 vystavenou Microsoft Entra ID a je nutná ke zpracování vráceného tokenu, nemůže zabránit redirect_uri načtení do objektu iframe.

Pokud se chcete vyhnout opětovnému načítání celé aplikace nebo jiným chybám způsobeným tímto způsobem, postupujte podle těchto alternativních řešení.

Zadání jiného kódu HTML pro prvek iframe

redirect_uri Nastavte vlastnost konfigurace na jednoduchou stránku, která nevyžaduje ověřování. Musíte se ujistit, že odpovídá položce zaregistrované v redirect_uri centru pro správu Microsoft Entra. To neovlivní přihlašovací prostředí uživatele, protože služba MSAL uloží úvodní stránku, když uživatel zahájí proces přihlášení a přesměruje se zpět na přesné místo po dokončení přihlášení.

Inicializace v hlavním souboru aplikace

Pokud je vaše aplikace strukturovaná tak, že existuje jeden centrální JavaScriptový soubor, který definuje inicializaci aplikace, směrování a další části, můžete podmíněně načítat moduly aplikace podle toho, zda se aplikace načítá uvnitř iframe, nebo ne. Příklad:

V AngularJS: app.js

// Check that the window is an iframe and not popup
if (window !== window.parent && !window.opener) {
angular.module('todoApp', ['ui.router', 'MsalAngular'])
    .config(['$httpProvider', 'msalAuthenticationServiceProvider','$locationProvider', function ($httpProvider, msalProvider,$locationProvider) {
        msalProvider.init(
            // msal configuration
        );

        $locationProvider.html5Mode(false).hashPrefix('');
    }]);
}
else {
    angular.module('todoApp', ['ui.router', 'MsalAngular'])
        .config(['$stateProvider', '$httpProvider', 'msalAuthenticationServiceProvider', '$locationProvider', function ($stateProvider, $httpProvider, msalProvider, $locationProvider) {
            $stateProvider.state("Home", {
                url: '/Home',
                controller: "homeCtrl",
                templateUrl: "/App/Views/Home.html",
            }).state("TodoList", {
                url: '/TodoList',
                controller: "todoListCtrl",
                templateUrl: "/App/Views/TodoList.html",
                requireLogin: true
            })

            $locationProvider.html5Mode(false).hashPrefix('');

            msalProvider.init(
                // msal configuration
            );
        }]);
}

V Angularu: app.module.ts

// Imports...
@NgModule({
  declarations: [
    AppComponent,
    MsalComponent,
    MainMenuComponent,
    AccountMenuComponent,
    OsNavComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
    MsalModule.forRoot(environment.MsalConfig),
    SuiModule,
    PagesModule
  ],
  providers: [
    HttpServiceHelper,
    {provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, multi: true},
    AuthService
  ],
  entryComponents: [
    AppComponent,
    MsalComponent
  ]
})
export class AppModule {
  constructor() {
    console.log('APP Module Constructor!');
  }

  ngDoBootstrap(ref: ApplicationRef) {
    if (window !== window.parent && !window.opener)
    {
      console.log("Bootstrap: MSAL");
      ref.bootstrap(MsalComponent);
    }
    else
    {
    //this.router.resetConfig(RouterModule);
      console.log("Bootstrap: App");
      ref.bootstrap(AppComponent);
    }
  }
}

MsalComponent:

import { Component} from '@angular/core';
import { MsalService } from '@azure/msal-angular';

// This component is used only to avoid Angular reload
// when doing acquireTokenSilent()

@Component({
  selector: 'app-root',
  template: '',
})
export class MsalComponent {
  constructor(private Msal: MsalService) {
  }
}

Další kroky

Přečtěte si další informace o vytváření jednostránkové aplikace (SPA) pomocí MSAL.js.