Tutorial: Conectar usuários e chamar a API do Microsoft Graph em um SPA (aplicativo de página única) Angular usando o fluxo de código de autenticação

Neste tutorial, você criará um SPA (aplicativo de página única) Angular que conecta usuários e chama a API do Microsoft Graph usando o fluxo de código de autorização com PKCE (Chave de Prova para Troca de Código). O SPA que você criará usa a MSAL (Biblioteca de Autenticação da Microsoft) para Angular v2.

Neste tutorial:

  • Registrar o aplicativo no centro de administração do Microsoft Entra
  • Crie um projeto do Angular com npm
  • Adicionar código para entrada e saída do usuário
  • Adicionar código para chamar a API do Microsoft Graph
  • Testar o aplicativo

A MSAL Angular v2 usa o fluxo de código de autorização com PKCE no navegador, aprimorando com relação ao MSAL Angular v1, que usava o fluxo de concessão implícita. É recomendável usar o fluxo de código de autorização com PKCE para SPAs (aplicativos de página única) porque ele é mais seguro do que o fluxo implícito. A MSAL Angular v2 NÃO dá suporte ao fluxo implícito.

Pré-requisitos

Como o aplicativo de exemplo funciona

Diagrama mostrando o fluxo do código de autorização em um aplicativo de página única

O aplicativo de exemplo criado neste tutorial permite que um SPA Angular consulte a API do Microsoft Graph ou uma API Web que aceita tokens emitidos pela plataforma de identidade da Microsoft. Ele usa a MSAL (Biblioteca de Autenticação da Microsoft) para o Angular v2, um wrapper da biblioteca MSAL.js v2. A MSAL Angular permite que os aplicativos Angular 9+ autentiquem usuários corporativos usando a ID do Microsoft Entra e também usuários com as contas Microsoft e identidades sociais como Facebook, Google e LinkedIn. A biblioteca também permite que os aplicativos tenham acesso aos serviços em nuvem da Microsoft e ao Microsoft Graph.

Nesse cenário, depois que um usuário se conecta, um token de acesso é adicionado às solicitações HTTP por meio do cabeçalho de autorização. A aquisição e a renovação de tokens são manipuladas pela MSAL.

Bibliotecas

Este tutorial usa as seguintes bibliotecas:

Biblioteca Descrição
MSAL Angular Biblioteca de Autenticação da Microsoft para Wrapper Angular JavaScript
MSAL para Navegador Biblioteca de Autenticação da Microsoft para o pacote de navegador do JavaScript v2.0

Você pode encontrar o código-fonte para todas as bibliotecas MSAL.js no repositório microsoft-authentication-library-for-js no GitHub.

Obter o exemplo de código concluído

Você prefere baixar o projeto de exemplo concluído para este tutorial? Clone o ms-identity-javascript-angular-spa

git clone https://github.com/Azure-Samples/ms-identity-javascript-angular-spa.git

Para continuar com o tutorial e criar o aplicativo por conta própria, acesse a próxima seção, Registrar o aplicativo e os identificadores de registro.

Registrar o aplicativo e os identificadores de registro

Dica

As etapas neste artigo podem variar ligeiramente com base no portal do qual você começa.

Para concluir o registro, forneça um nome ao aplicativo, especifique os tipos de conta com suporte e adicione um URI de redirecionamento. Depois de registrada, o painel Visão geral do aplicativo exibirá os identificadores necessários no código-fonte do aplicativo.

  1. Faça login no Centro de administração do Microsoft Entra como pelo menos um Desenvolvedor de aplicativos.
  2. Se você tiver acesso a vários locatários, use o ícone de Configurações no menu superior para alternar para o locatário no qual deseja registrar o aplicativo no menu Diretórios + assinaturas.
  3. Navegue até Identidade>Aplicativos>Registros do aplicativo.
  4. Selecione Novo registro.
  5. Insira um Nome para o aplicativo, como Angular-SPA-auth-code.
  6. Para Tipos de contas com suporte, selecione Contas somente neste diretório organizacional. Para obter informações sobre diferentes tipos de conta, selecione a opção Ajudar-me a escolher.
  7. Em URI de redirecionamento (opcional), use o menu suspenso para selecionar SPA (aplicativo de página única) e insira http://localhost:4200 na caixa de texto.
  8. Selecione Registrar.
  9. O painel Visão geral do aplicativo é exibido quando o registro for concluído. Registre a ID do Diretório (locatário) e a ID do aplicativo (cliente) a ser usada no código-fonte do aplicativo.

Criar seu projeto

  1. Abra o Visual Studio Code.

  2. Selecione Arquivo>Abrir pasta.... Navegue e selecione o local no qual criar seu projeto.

  3. Abra um novo terminal selecione Terminal>Novo Terminal.

    1. Talvez seja necessário alternar os tipos de terminal. Selecione a seta para baixo ao lado do ícone + no terminal e selecione Prompt de Comando.
  4. Execute os comandos a seguir para criar um projeto Angular com o nome msal-angular-tutorial, instalar bibliotecas de componentes Angular Material, MSAL Browser, MSAL Angular e gerar componentes de página inicial e de perfil.

    npm install -g @angular/cli
    ng new msal-angular-tutorial --routing=true --style=css --strict=false
    cd msal-angular-tutorial
    npm install @angular/material @angular/cdk
    npm install @azure/msal-browser @azure/msal-angular
    ng generate component home
    ng generate component profile
    

Configure o aplicativo e edite a interface do usuário base

  1. Abra src/app/app.module.ts. O MsalModule e MsalInterceptor precisam ser adicionados ao imports junto com a constante isIE. Você também adicionará os módulos de material. Substitua todo o conteúdo do arquivo pelo trecho de código a seguir:

    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";
    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", // Application (client) ID from the app registration
              authority:
                "Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here", // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
              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, MsalRedirectComponent],
    })
    export class AppModule {}
    
  2. Substitua os valores a seguir pelos valores obtidos do centro de administração do Microsoft Entra. Para saber mais sobre opções configuráveis disponíveis, confira Inicializar aplicativos cliente.

    • clientId – O identificador do aplicativo, também conhecido como o cliente. Substitua Enter_the_Application_Id_Here pelo valor da ID do Aplicativo (cliente) que foi registrado anteriormente na página de visão geral do aplicativo registrado.
    • authority – Isso é composto de duas partes:
      • A Instância é o ponto de extremidade do provedor de nuvem. Para a nuvem principal ou global do Azure, insira https://login.microsoftonline.com. Verifique com os diferentes pontos de extremidade disponíveis em Nuvens nacionais.
      • A ID do Locatário é o identificador do locatário em que o aplicativo está registrado. Substitua _Enter_the_Tenant_Info_Here pelo valor da ID do Diretório (locatário) que foi registrado anteriormente na página de visão geral do aplicativo registrado.
    • redirectUri - o local para onde o servidor de autorização envia o usuário uma vez que o aplicativo foi autorizado com sucesso e recebeu um código de autorização ou token de acesso. Substitua Enter_the_Redirect_Uri_Here por http://localhost:4200.
  3. Abra src/app/app-routing.module.ts e adicione rotas aos componentes página inicial e perfil. Substitua todo o conteúdo do arquivo pelo trecho de código a seguir:

    import { NgModule } from "@angular/core";
    import { Routes, RouterModule } from "@angular/router";
    import { BrowserUtils } from "@azure/msal-browser";
    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, {
          // Don't perform initial navigation in iframes or popups
          initialNavigation:
            !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
              ? "enabledNonBlocking"
              : "disabled", // Set to enabledBlocking to use Angular Universal
        }),
      ],
      exports: [RouterModule],
    })
    export class AppRoutingModule {}
    
  4. Abra src/app/app.component.html e substitua o código existente pelo snippet a seguir:

    <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>
    
  5. Abra src/style.css para definir o 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%;
    }
    
  6. Abra src/app/app.component.css para adicionar estilo CSS ao aplicativo:

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

Entrar usando pop-ups

  1. Abra src/app/app.component.ts e substitua o conteúdo do arquivo pelo seguinte snippet para conectar um usuário usando uma janela pop-up:

    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;
      }
    }
    

Conectar-se usando redirecionamentos

  1. Atualize src/app/app.module.ts para inicializar o MsalRedirectComponent. Este é um componente de redirecionamento dedicado que tratará os redirecionamentos. Altere a importação MsalModule e a inicialização AppComponent para se assemelhar ao seguinte snippet:

    ...
    import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import
    ...
      bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
    ...
    
  2. Abra src/index.html e substitua todo o conteúdo do arquivo pelo trecho de código a seguir, que adiciona o seletor <app-redirect>:

    <!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. Abra src/app/app.component.ts e substitua o código pelo seguinte snippet para conectar um usuário usando 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. Abra src/app/home/home.component.ts e substitua todo o conteúdo do arquivo pelo snippet a seguir para assinar o evento LOGIN_SUCCESS:

    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 garantir que determinados componentes da interface do usuário sejam exibidos apenas para usuários autenticados, os componentes devem se inscrever no MsalBroadcastService para verificar se os usuários estão conectados e se a interação foi concluída.

  1. Adicione o MsalBroadcastService a src/app/app.component.ts e assine o inProgress$ observável para verificar se a interação foi concluída e se uma conta está conectada antes de renderizar a interface do usuário. Agora, o código deverá ter esta aparência:

    import { Component, OnInit, OnDestroy } 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, OnDestroy {
      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 também verificar se a interação deve ser concluída antes da atualização da interface do usuário. Agora, o código deverá ter esta aparência:

    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 com as seguintes exibições condicionais:

    <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>
    

Implementar o Angular Guard

A classe MsalGuard é aquela que você pode usar para proteger rotas e exigir autenticação antes de acessar a rota protegida. As etapas a seguir adicionam o MsalGuard à rota Profile. Proteger a rota Profile significa que, mesmo que um usuário não se conecte usando o botão Login, se ele tentar acessar a rota Profile ou selecionar o botão Profile, o MsalGuard solicitará que o usuário se autentique por meio de uma janela pop-up ou de redirecionamento antes de mostrar a página Profile.

MsalGuard é uma classe de conveniência que você pode usar para melhorar a experiência do usuário, mas não deve ser usada para segurança. Os invasores podem contornar a proteção do lado do cliente, e você deve garantir que o servidor não retorne dados que o usuário não deve acessar.

  1. Adicione a classe MsalGuard como um provedor no seu aplicativo em src/app/app.module.ts e adicione as configurações para o MsalGuard. Os escopos necessários para adquirir tokens posteriormente podem ser fornecidos no authRequest, e o tipo de interação para o Guard pode ser definido como ou Redirect ou Popup. Seu código deve se parecer com o seguinte snippet:

    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. Defina o MsalGuard nas rotas que deseja proteger em src/app/app-routing.module.ts:

    import { NgModule } from "@angular/core";
    import { Routes, RouterModule } from "@angular/router";
    import { BrowserUtils } from "@azure/msal-browser";
    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, {
          // Don't perform initial navigation in iframes or popups
          initialNavigation:
            !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
              ? "enabledNonBlocking"
              : "disabled", // Set to enabledBlocking to use Angular Universal
        }),
      ],
      exports: [RouterModule],
    })
    export class AppRoutingModule {}
    
  3. Ajuste as chamadas de logon em src/app/app.component.ts para levar em conta o authRequest definido nas configurações de proteção. Seu código agora deve se parecer com o seguinte snippet:

    import { Component, OnInit, OnDestroy, 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, OnDestroy {
      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

Interceptor Angular

A MSAL Angular fornece uma classe Interceptor que adquire automaticamente tokens para solicitações de saída que usam o cliente http do Angular para recursos protegidos conhecidos.

  1. Adicione a classe Interceptor como um provedor ao seu aplicativo em src/app/app.module.ts, com as respectivas configurações. Seu código agora deve se parecer com o seguinte snippet:

    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. As URLs fornecidas na coleção protectedResourceMap diferenciam maiúsculas de minúsculas. Para cada recurso, adicione os escopos solicitados para retorno no token de acesso.

    Por exemplo:

    • ["user.read"] para Microsoft Graph
    • ["<Application ID URL>/scope"] para APIs Web personalizadas (ou seja, api://<Application ID>/access_as_user)

    Modifique os valores no protectedResourceMap, conforme descrito aqui:

    • Enter_the_Graph_Endpoint_Here é a instância da API do Microsoft Graph com a qual o aplicativo deve se comunicar. Para o ponto de extremidade global da API do Microsoft Graph, substitua essa cadeia de caracteres por https://graph.microsoft.com. Para pontos de extremidade em implantações de nuvens nacionais, confira Implantações de nuvens nacionais na documentação do Microsoft Graph.
  2. Substitua o código em src/app/profile/profile.component.ts para recuperar o perfil de um usuário com uma solicitação HTTP e substitua o GRAPH_ENDPOINT pelo ponto de extremidade do Microsoft Graph:

    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 interface do usuário em src/app/profile/profile.component.html para exibir as 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>
    

Sair

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

    <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>
    

Sair do serviço usando redirecionamentos

  1. Atualize o código em src/app/app.component.ts para desconectar um usuário usando redirecionamentos:

    import { Component, OnInit, OnDestroy, 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, OnDestroy {
      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();
      }
    }
    

Sair usando pop-ups

  1. Atualize o código em src/app/app.component.ts para desconectar um usuário usando pop-ups:

    import { Component, OnInit, OnDestroy, 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, OnDestroy {
      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();
      }
    }
    

Testar seu código

  1. Inicie o servidor Web para escutar a porta executando os seguintes comandos em um prompt de linha de comando na pasta do aplicativo:

    npm install
    npm start
    
  2. Em seu navegador, digite http://localhost:4200, e você verá uma página semelhante à seguinte.

    Navegador da Web exibindo caixa de diálogo de entrada

  3. Selecione Aceitar para conceder as permissões do aplicativo ao seu perfil. Isso acontecerá na primeira vez que você começar a entrar.

    Caixa de diálogo de conteúdo exibida no navegador da Web

  4. Se você consentir com as permissões solicitadas, o aplicativo Web mostrará uma página de logon bem-sucedido.

    Resultados de uma entrada bem-sucedida no navegador da Web

  5. Selecione Perfil para exibir as informações do perfil do usuário retornadas na resposta da chamada à API do Microsoft Graph:

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

Adicionar escopos e permissões delegadas

A API do Microsoft Graph requer o escopo User.Read para ler o perfil do usuário. O escopo User.Read é adicionado automaticamente a todos os registros de aplicativo. Outras APIs do Microsoft Graph e APIs personalizadas do servidor de back-end podem exigir outros escopos. Por exemplo, a API do Microsoft Graph requer o escopo Mail.Read para listar o email do usuário.

Conforme você adiciona escopos, seus usuários podem ser solicitados a fornecer consentimento extra para os escopos adicionados.

Observação

Talvez o usuário precise fornecer autorizações adicionais à medida que o número de escopos aumentar.

Ajuda e suporte

Se precisar de ajuda, quiser relatar um problema ou desejar saber mais sobre as opções de suporte, confira Ajuda e suporte para desenvolvedores.

Próximas etapas

  • Saiba mais criando um Aplicativo de Página Única (SPA) React que conecta usuários na série de tutoriais de várias partes a seguir.