Tutorial: Inscreva-se nos utilizadores e ligue para a Microsoft Graph API a partir de uma aplicação angular de uma página única (SPA) usando fluxo de código auth

Neste tutorial, você constrói uma aplicação angular de uma página única (SPA) que assina nos utilizadores e chama a API do Microsoft Graph usando o fluxo de código de autorização com PKCE. O SPA que constrói utiliza a Microsoft Authentication Library (MSAL) para angular v2.

Neste tutorial:

  • Criar um projeto angular com npm
  • Registe a inscrição no portal Azure
  • Adicione código para suportar a inscrição do utilizador e a s inscrição
  • Adicione código para ligar para a Microsoft Graph API
  • Testar a aplicação

O MSAL Angular v2 melhora no MSAL Angular v1, suportando o fluxo de código de autorização no navegador em vez do fluxo de concessão implícito. MSAL Angular v2 NÃO suporta o fluxo implícito.

Pré-requisitos

Como funciona a aplicação da amostra

Diagrama mostrando o fluxo de código de autorização numa aplicação de uma página

A aplicação de amostra criada neste tutorial permite a um SPA angular consultar a API do Microsoft Graph ou uma API web que aceita fichas emitidas pela plataforma de identidade microsoft. Utiliza a Biblioteca de Autenticação microsoft (MSAL) para angular v2, um invólucro da biblioteca MSAL.js v2. O MSAL Angular permite que as aplicações Angular 9+ autentem a autenticação dos utilizadores empresariais utilizando o Azure Ative Directory (Azure AD), bem como utilizadores com contas da Microsoft e identidades sociais como facebook, Google e LinkedIn. A biblioteca também permite que as aplicações tenham acesso aos serviços de cloud da Microsoft e ao Microsoft Graph.

Neste cenário, após a indicação de um utilizador, é solicitado um token de acesso e adicionado aos pedidos HTTP através do cabeçalho de autorização. A aquisição e renovação da Token são tratadas pela MSAL.

Bibliotecas

Este tutorial utiliza as seguintes bibliotecas:

Biblioteca Description
MSAL Angular Biblioteca de autenticação da Microsoft para invólucro angular JavaScript
Navegador MSAL Biblioteca de autenticação da Microsoft para pacote de navegador JavaScript v2

Pode encontrar o código fonte de todas as bibliotecas MSAL.js no repositório AzureAD/microsoft-authentication-library-for-js no GitHub.

Criar o seu projeto

Uma vez instalado Node.js, abra uma janela do terminal e, em seguida, execute os seguintes comandos para gerar uma nova aplicação Angular:

npm install -g @angular/cli                         # Install the Angular CLI
ng new msal-angular-tutorial --routing=true --style=css --strict=false    # Generate a new Angular app
cd msal-angular-tutorial                            # Change to the app directory
npm install @angular/material @angular/cdk          # Install the Angular Material component library (optional, for UI)
npm install @azure/msal-browser @azure/msal-angular # Install MSAL Browser and MSAL Angular in your application
ng generate component home                          # To add a home page
ng generate component profile                       # To add a profile page

Registar a aplicação

Siga as instruções para registar uma aplicação de uma página no portal Azure.

Na página geral da aplicação do seu registo, note o valor de ID da Aplicação (cliente) para posterior utilização.

Registe o seu valor URI de redirecionamento como http://localhost:4200/ e escreva como 'SPA'.

Configurar a aplicação

  1. Na pasta src/app, edite app.module.ts e adicione MsalModule e MsalInterceptor imports adicione, bem como a isIE constante. O código deverá ser semelhante a:

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    
    import { MsalModule } from '@azure/msal-angular';
    import { PublicClientApplication } from '@azure/msal-browser';
    
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      ],
      imports: [
        BrowserModule,
        AppRoutingModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here', // This is your client ID
            authority: 'Enter_the_Cloud_Instance_Id_Here'/'Enter_the_Tenant_Info_Here', // This is your tenant ID
            redirectUri: 'Enter_the_Redirect_Uri_Here'// This is your redirect URI
          },
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
          }
        }), null, null)
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    Substitua estes valores:

    Nome do valor Sobre
    Enter_the_Application_Id_Here Na página geral do registo da sua candidatura, este é o seu valor de ID de candidatura (cliente).
    Enter_the_Cloud_Instance_Id_Here Este é o exemplo da nuvem Azure. Para a nuvem Azure principal ou global, https://login.microsoftonline.com entre. Para nuvens nacionais (por exemplo, China), ver nuvens nacionais.
    Enter_the_Tenant_Info_Here Definir uma das seguintes opções: Se a sua candidatura suporta contas neste diretório organizacional, substitua este valor pelo ID do diretório (inquilino) ou nome do inquilino (por exemplo, contoso.microsoft.com). Se a sua candidatura suporta contas em qualquer diretório organizacional, substitua este valor por organizações. Se a sua aplicação suportar contas em qualquer diretório organizacional e contas pessoais da Microsoft, substitua este valor por comum. Para restringir apenas o suporte às contas pessoais da Microsoft, substitua este valor pelos consumidores.
    Enter_the_Redirect_Uri_Here Substitua-a por http://localhost:4200 .

    Para obter mais informações sobre as opções disponíveis, consulte As aplicações do cliente Inicialize.

  2. Adicione rotas aos componentes domésticos e de perfil no src/app/app-encaminhamento.module.ts. O seu código deve ser o seguinte:

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    
    const routes: Routes = [
      {
        path: 'profile',
        component: ProfileComponent,
      },
      {
        path: '',
        component: HomeComponent
      },
    ];
    
    const isIframe = window !== window.parent && !window.opener;
    
    @NgModule({
      imports: [RouterModule.forRoot(routes, {
        initialNavigation: !isIframe ? 'enabled' : 'disabled' // Don't perform initial navigation in iframes
      })],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    

Substitua a UI base

  1. Substitua o código de espaço reservado no src/app/app.component.html com o seguinte:

    <mat-toolbar color="primary">
      <a class="title" href="/">{{ title }}</a>
    
      <div class="toolbar-spacer"></div>
    
      <a mat-button [routerLink]="['profile']">Profile</a>
    
      <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
    
    </mat-toolbar>
    <div class="container">
      <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe -->
      <router-outlet *ngIf="!isIframe"></router-outlet>
    </div>
    
  2. Adicione módulos de material ao src/app/app.module.ts. Devia AppModule ficar assim:

    import { BrowserModule } from '@angular/platform-browser';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { NgModule } from '@angular/core';
    
    import { MatButtonModule } from '@angular/material/button';
    import { MatToolbarModule } from '@angular/material/toolbar';
    import { MatListModule } from '@angular/material/list';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    
    import { MsalModule } from '@azure/msal-angular';
    import { PublicClientApplication } from '@azure/msal-browser';
    
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here',
            authority: 'Enter_the_Cloud_Instance_Id_Here'/'Enter_the_Tenant_Info_Here',
            redirectUri: 'Enter_the_Redirect_Uri_Here'
          },
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE, 
          }
        }), null, null)
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
  3. (OPCIONAL) Adicione CSS ao src/estilo.css:

    @import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
    
    html, body { height: 100%; }
    body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
    .container { margin: 1%; }
    
  4. (OPCIONAL) Adicione CSS ao src/app/app.component.css:

    .toolbar-spacer {
        flex: 1 1 auto;
      }
    
      a.title {
        color: white;
      }
    

Inscreva-se num utilizador

Adicione o código das seguintes secções para invocar o login utilizando uma janela pop-up ou um redirecionamento de quadro completo:

Inscreva-se usando popups

  1. Altere o código em src/app/app.component.ts para o seguinte para iniciar s-s num utilizador utilizando uma janela popup:

    import { MsalService } from '@azure/msal-angular';
    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
    
      constructor(private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
      }
    
      login() {
        this.authService.loginPopup()
          .subscribe({
            next: (result) => {
              console.log(result);
              this.setLoginDisplay();
            },
            error: (error) => console.log(error)
          });
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    }
    

Nota

O resto deste tutorial utiliza o método com o loginRedirect Microsoft Internet Explorer devido a um problema conhecido relacionado com o manuseamento de janelas pop-up pelo Internet Explorer.

Iniciar s-se-in usando redirecionamentos

  1. Atualizar src/app/app.module.ts para bootstrap o MsalRedirectComponent . Este é um componente de redirecionamento dedicado que irá lidar com redirecionamentos. O seu código deve agora ser assim:

    import { BrowserModule } from '@angular/platform-browser';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { NgModule } from '@angular/core';
    
    import { MatButtonModule } from '@angular/material/button';
    import { MatToolbarModule } from '@angular/material/toolbar';
    import { MatListModule } from '@angular/material/list';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    
    import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import
    import { PublicClientApplication } from '@azure/msal-browser';
    
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here',
            authority: 'Enter_the_Cloud_Instance_Id_Here'/'Enter_the_Tenant_Info_Here', 
            redirectUri: 'Enter_the_Redirect_Uri_Here'
          },
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE,
          }
        }), null, null)
      ],
      providers: [],
      bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
    })
    export class AppModule { }
    
  2. Adicione o <app-redirect> seletor ao src/index.html. Este seletor é utilizado pelo MsalRedirectComponent . O seu src/index.html deve ficar assim:

    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>msal-angular-tutorial</title>
      <base href="/">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">
    </head>
    <body>
      <app-root></app-root>
      <app-redirect></app-redirect>
    </body>
    </html>
    
  3. Substitua o código em src/app/app.component.ts pelo seguinte para iniciar s-as num utilizador utilizando um redirecionamento de quadro completo:

    import { MsalService } from '@azure/msal-angular';
    import { Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
    
      constructor(private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
      }
    
      login() {
        this.authService.loginRedirect();
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    }
    
  4. Substitua o código existente em src/app/home/home.component.ts para subscrever o LOGIN_SUCCESS evento. Isto permitir-lhe-á aceder ao resultado do login bem sucedido com redirecionamento. O código deverá ser semelhante a:

    import { Component, OnInit } from '@angular/core';
    import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
    import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
    import { filter } from 'rxjs/operators';
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    })
    export class HomeComponent implements OnInit {
      constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
    
      ngOnInit(): void {
        this.msalBroadcastService.msalSubject$
          .pipe(
            filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
          )
          .subscribe((result: EventMessage) => {
            console.log(result);
          });
      }
    }
    

Renderização condicional

Para tornar certos UI apenas para utilizadores autenticados, os componentes têm de subscrever MsalBroadcastService para ver se os utilizadores foram assinados e a interação foi concluída.

  1. Adicione o MsalBroadcastService src/app/app.component.ts e subscreva o inProgress$ observável para verificar se a interação está completa e uma conta é assinada antes de renderizar uI. O seu código deve agora ser assim:

    import { Component, OnInit } from '@angular/core';
    import { MsalService, MsalBroadcastService } from '@azure/msal-angular';
    import { InteractionStatus } from '@azure/msal-browser';
    import { Subject } from 'rxjs';
    import { filter, takeUntil } from 'rxjs/operators';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
    
      constructor(private broadcastService: MsalBroadcastService, private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
    
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        )
        .subscribe(() => {
          this.setLoginDisplay();
        })
      }
    
      login() {
        this.authService.loginRedirect();
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
      }
    }
    
  2. Atualize o código em src/app/home/home.component.ts para verificar se a interação deve ser completada antes de atualizar a UI. O seu código deve agora ser assim:

    import { Component, OnInit } from '@angular/core';
    import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
    import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
    import { filter } from 'rxjs/operators';
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    })
    export class HomeComponent implements OnInit {
      loginDisplay = false;
    
      constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
    
      ngOnInit(): void {
        this.msalBroadcastService.msalSubject$
          .pipe(
            filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
          )
          .subscribe((result: EventMessage) => {
            console.log(result);
          });
    
        this.msalBroadcastService.inProgress$
          .pipe(
            filter((status: InteractionStatus) => status === InteractionStatus.None)
          )
          .subscribe(() => {
            this.setLoginDisplay();
          })
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    }
    
  3. Substitua o código em src/app/home/home.component.html pelos seguintes visores condicional:

    <div *ngIf="!loginDisplay">
        <p>Please sign-in to see your profile information.</p>
    </div>
    
    <div *ngIf="loginDisplay">
        <p>Login successful!</p>
        <p>Request your profile information by clicking Profile above.</p>
    </div>
    

Rotas de guarda

Guarda Angular

A MSAL Angular MsalGuard fornece, uma classe que pode usar para proteger rotas e exigir autenticação antes de aceder à rota protegida. Os passos abaixo adicionam MsalGuard a Profile rota. Proteger a Profile rota significa que mesmo que um utilizador não faça o sônibus usando o Login botão, se tentar aceder à Profile rota ou clicar no Profile botão, MsalGuard o utilizador irá pedir ao utilizador para autenticar através de popup ou redirecionamento antes de mostrar a Profile página.

MsalGuard é uma classe de conveniência que pode usar para melhorar a experiência do utilizador, mas não deve ser confiada para a segurança. Os atacantes podem potencialmente contornar os guardas do lado do cliente, e deve garantir que o servidor não devolve quaisquer dados que o utilizador não deva aceder.

  1. Adicione a MsalGuard classe como provedor na sua aplicação em src/app/app.module.ts, e adicione as configurações para o MsalGuard . Os âmbitos necessários para a aquisição de fichas posteriores podem ser fornecidos no authRequest , e o tipo de interação para a Guarda pode ser definido para ou Redirect Popup . O seu código deve ser o seguinte:

    import { BrowserModule } from '@angular/platform-browser';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { NgModule } from '@angular/core';
    
    import { MatButtonModule } from '@angular/material/button';
    import { MatToolbarModule } from '@angular/material/toolbar';
    import { MatListModule } from '@angular/material/list';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    
    import { MsalModule, MsalRedirectComponent, MsalGuard } from '@azure/msal-angular'; // MsalGuard added to imports
    import { PublicClientApplication, InteractionType } from '@azure/msal-browser'; // InteractionType added to imports
    
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here',
            authority: 'Enter_the_Cloud_Instance_Id_Here'/'Enter_the_Tenant_Info_Here', 
            redirectUri: 'Enter_the_Redirect_Uri_Here'
          },
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE,
          }
        }), {
            interactionType: InteractionType.Redirect, // MSAL Guard Configuration
            authRequest: {
              scopes: ['user.read']
            }
        }, null)
      ],
      providers: [
        MsalGuard // MsalGuard added as provider here
      ],
      bootstrap: [AppComponent, MsalRedirectComponent]
    })
    export class AppModule { }
    
  2. Desaprova as MsalGuard rotas que pretende proteger em src/app/app-encaminhamento.módulo.ts:

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    import { MsalGuard } from '@azure/msal-angular';
    
    const routes: Routes = [
      {
        path: 'profile',
        component: ProfileComponent,
        canActivate: [MsalGuard]
      },
      {
        path: '',
        component: HomeComponent
      },
    ];
    
    const isIframe = window !== window.parent && !window.opener;
    
    @NgModule({
      imports: [RouterModule.forRoot(routes, {
        initialNavigation: !isIframe ? 'enabled' : 'disabled' // Don't perform initial navigation in iframes
      })],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    
  3. Ajuste as chamadas de login em src/app/app.component.ts para ter authRequest em conta o conjunto nas configurações da proteção. O seu código deve agora parecer o seguinte:

    import { Component, OnInit, Inject } from '@angular/core';
    import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
    import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
    import { Subject } from 'rxjs';
    import { filter, takeUntil } from 'rxjs/operators';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
    
      constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
    
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
    
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        )
        .subscribe(() => {
          this.setLoginDisplay();
        })
      }
    
      login() {
        if (this.msalGuardConfig.authRequest){
          this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
        } else {
          this.authService.loginRedirect();
        }
      }
    
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      }
    
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
      }
    }
    

Adquirir um token

Angular Interceptor

A MSAL Angular fornece uma Interceptor classe que adquire automaticamente fichas para pedidos de saída que usam o cliente Angular para recursos http protegidos conhecidos.

  1. Adicione a Interceptor classe como fornecedor à sua aplicação em src/app/app.module.ts, com as suas configurações. O seu código deve agora ser assim:

    import { BrowserModule } from '@angular/platform-browser';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { NgModule } from '@angular/core';
    import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; // Import 
    
    import { MatButtonModule } from '@angular/material/button';
    import { MatToolbarModule } from '@angular/material/toolbar';
    import { MatListModule } from '@angular/material/list';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    
    import { MsalModule, MsalRedirectComponent, MsalGuard, MsalInterceptor } from '@azure/msal-angular'; // Import MsalInterceptor
    import { InteractionType, PublicClientApplication } from '@azure/msal-browser';
    
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      ],
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        HttpClientModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_Here',
            authority: 'Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here',
            redirectUri: 'Enter_the_Redirect_Uri_Here',
          },
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE,
          }
        }), {
          interactionType: InteractionType.Redirect,
          authRequest: {
            scopes: ['user.read']
            }
        }, {
          interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
          protectedResourceMap: new Map([ 
              ['Enter_the_Graph_Endpoint_Here/v1.0/me', ['user.read']]
          ])
        })
      ],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: MsalInterceptor,
          multi: true
        },
        MsalGuard
      ],
      bootstrap: [AppComponent, MsalRedirectComponent]
    })
    export class AppModule { }
    
    

    Os recursos protegidos são fornecidos como um protectedResourceMap . Os URLs que fornece na protectedResourceMap coleção são sensíveis a casos. Para cada recurso, adicione âmbitos a serem solicitados para serem devolvidos no token de acesso.

    Por exemplo:

    • ["user.read"] para o Microsoft Graph
    • ["<Application ID URL>/scope"] para APIs web personalizado (isto é, api://<Application ID>/access_as_user )

    Modifique os valores nos protectedResourceMap conforme descrito aqui:

    Nome do valor Sobre
    Enter_the_Graph_Endpoint_Here A instância da Microsoft Graph API com a aplicação deve comunicar.com. Para o ponto final global da Microsoft Graph API, substitua ambas as instâncias desta cadeia por https://graph.microsoft.com . Para pontos finais em implementações nacionais em nuvem, consulte as implementações nacionais em nuvem na documentação do Microsoft Graph.
  2. Substitua o código em src/app/profile/profile.component.ts para recuperar o perfil de um utilizador com um pedido HTTP:

    import { Component, OnInit } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    const GRAPH_ENDPOINT = 'Enter_the_Graph_Endpoint_Here/v1.0/me';
    
    type ProfileType = {
      givenName?: string,
      surname?: string,
      userPrincipalName?: string,
      id?: string
    };
    
    @Component({
      selector: 'app-profile',
      templateUrl: './profile.component.html',
      styleUrls: ['./profile.component.css']
    })
    export class ProfileComponent implements OnInit {
      profile!: ProfileType;
    
      constructor(
        private http: HttpClient
      ) { }
    
      ngOnInit() {
        this.getProfile();
      }
    
      getProfile() {
        this.http.get(GRAPH_ENDPOINT)
          .subscribe(profile => {
            this.profile = profile;
          });
      }
    }
    
  3. Substitua a UI no src/app/profile/profile.component.html para apresentar informações de perfil:

    <div>
        <p><strong>First Name: </strong> {{profile?.givenName}}</p>
        <p><strong>Last Name: </strong> {{profile?.surname}}</p>
        <p><strong>Email: </strong> {{profile?.userPrincipalName}}</p>
        <p><strong>Id: </strong> {{profile?.id}}</p>
    </div>
    

Terminar sessão

Atualize o código em src/app/app.component.html para exibir condicionalmente um Logout botão:

<mat-toolbar color="primary">
  <a class="title" href="/">{{ title }}</a>

  <div class="toolbar-spacer"></div>

  <a mat-button [routerLink]="['profile']">Profile</a>

  <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
  <button mat-raised-button *ngIf="loginDisplay" (click)="logout()">Logout</button>

</mat-toolbar>
<div class="container">
  <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe -->
  <router-outlet *ngIf="!isIframe"></router-outlet>
</div>

Inscreva-se usando redirecionamentos

Atualize o código em src/app/app.component.ts para assinar um utilizador usando redirecionamentos:

import { Component, OnInit, Inject } from '@angular/core';
import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'msal-angular-tutorial';
  isIframe = false;
  loginDisplay = false;
  private readonly _destroying$ = new Subject<void>();

  constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }

  ngOnInit() {
    this.isIframe = window !== window.parent && !window.opener;

    this.broadcastService.inProgress$
    .pipe(
      filter((status: InteractionStatus) => status === InteractionStatus.None),
      takeUntil(this._destroying$)
    )
    .subscribe(() => {
      this.setLoginDisplay();
    })
  }

  login() {
    if (this.msalGuardConfig.authRequest){
      this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
    } else {
      this.authService.loginRedirect();
    }
  }

  logout() { // Add log out function here
    this.authService.logoutRedirect({
      postLogoutRedirectUri: 'http://localhost:4200'
    });
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
}

Assine usando popups

Atualize o código em src/app/app.component.ts para assinar um utilizador utilizando popups:

import { Component, OnInit, Inject } from '@angular/core';
import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
import { InteractionStatus, PopupRequest } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'msal-angular-tutorial';
  isIframe = false;
  loginDisplay = false;
  private readonly _destroying$ = new Subject<void>();

  constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }

  ngOnInit() {
    this.isIframe = window !== window.parent && !window.opener;

    this.broadcastService.inProgress$
    .pipe(
      filter((status: InteractionStatus) => status === InteractionStatus.None),
      takeUntil(this._destroying$)
    )
    .subscribe(() => {
      this.setLoginDisplay();
    })
  }

  login() {
    if (this.msalGuardConfig.authRequest){
      this.authService.loginPopup({...this.msalGuardConfig.authRequest} as PopupRequest)
        .subscribe({
          next: (result) => {
            console.log(result);
            this.setLoginDisplay();
          },
          error: (error) => console.log(error)
        });
    } else {
      this.authService.loginPopup()
        .subscribe({
          next: (result) => {
            console.log(result);
            this.setLoginDisplay();
          },
          error: (error) => console.log(error)
        });
    }
  }

  logout() { // Add log out function here
    this.authService.logoutPopup({
      mainWindowRedirectUri: "/"
    });
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
}

Teste o seu código

  1. Inicie o servidor web para ouvir a porta executando os seguintes comandos num pedido de linha de comando da pasta de aplicação:

    npm install
    npm start
    
  2. No seu navegador, insira http://localhost:4200 http://localhost:{port} ou, onde a porta é a porta que o seu servidor web está a ouvir. Devia ver uma página que se parece com a de baixo.

    Web browser exibindo diálogo de insusimento

A primeira vez que começa a iniciar o seu contrato de inscrição, é-lhe pedido que lhe conceda acesso ao seu perfil e o deixe entrar:

Diálogo de conteúdo exibido no navegador web

Se consentir com as permissões solicitadas, a aplicação web mostra uma página de login bem sucedida:

Resultados de um sucesso de sind in no navegador web

Ligue para a API do Gráfico

Depois de iniciar sessão, selecione Profile para visualizar as informações do perfil do utilizador devolvidas na resposta da chamada para a API do Gráfico da Microsoft:

Informações de perfil do Microsoft Graph exibidas no navegador

Adicionar âmbitos e permissões delegadas

A API do Microsoft Graph requer que o âmbito user.Read leia o perfil de um utilizador. O âmbito User.Read é adicionado automaticamente a todos os registos de aplicações que criar no portal Azure. Outros APIs para o Microsoft Graph, bem como APIs personalizados para o seu servidor back-end, podem necessitar de âmbitos adicionais. Por exemplo, a API do Gráfico microsoft requer o âmbito Mail.Read para listar o e-mail do utilizador.

À medida que adiciona âmbitos, os seus utilizadores poderão ser solicitados a fornecer consentimento adicional para os âmbitos adicionados.

Nota

O utilizador poderá ser solicitado para obter consentimentos adicionais à medida que aumenta o número de âmbitos.

Ajuda e suporte

Se precisar de ajuda, quer reportar um problema ou quer saber sobre as suas opções de suporte, consulte ajuda e suporte para programadores.

Passos seguintes

Aprofundar o desenvolvimento de aplicações de uma página única (SPA) na plataforma de identidade da Microsoft na nossa série de artigos multi-partes.