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
- Node.js para executar um servidor Web local.
- Visual Studio Code ou outro editor para modificar arquivos de projeto.
Como o aplicativo de exemplo funciona
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.
- Faça login no Centro de administração do Microsoft Entra como pelo menos um Desenvolvedor de aplicativos.
- 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.
- Navegue até Identidade>Aplicativos>Registros do aplicativo.
- Selecione Novo registro.
- Insira um Nome para o aplicativo, como Angular-SPA-auth-code.
- 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.
- 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. - Selecione Registrar.
- 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
Abra o Visual Studio Code.
Selecione Arquivo>Abrir pasta.... Navegue e selecione o local no qual criar seu projeto.
Abra um novo terminal selecione Terminal>Novo Terminal.
- 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.
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
Abra src/app/app.module.ts. O
MsalModule
eMsalInterceptor
precisam ser adicionados aoimports
junto com a constanteisIE
. 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 {}
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. SubstituaEnter_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.
- A Instância é o ponto de extremidade do provedor de nuvem. Para a nuvem principal ou global do Azure, insira
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. SubstituaEnter_the_Redirect_Uri_Here
porhttp://localhost:4200
.
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 {}
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>
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%; }
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
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
Atualize src/app/app.module.ts para inicializar o
MsalRedirectComponent
. Este é um componente de redirecionamento dedicado que tratará os redirecionamentos. Altere a importaçãoMsalModule
e a inicializaçãoAppComponent
para se assemelhar ao seguinte snippet:... import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import ... bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here ...
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>
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; } }
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.
Adicione o
MsalBroadcastService
a src/app/app.component.ts e assine oinProgress$
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(); } }
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; } }
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.
Adicione a classe
MsalGuard
como um provedor no seu aplicativo em src/app/app.module.ts e adicione as configurações para oMsalGuard
. Os escopos necessários para adquirir tokens posteriormente podem ser fornecidos noauthRequest
, e o tipo de interação para o Guard pode ser definido como ouRedirect
ouPopup
. 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 {}
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 {}
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.
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çãoprotectedResourceMap
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 porhttps://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.
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; }); } }
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
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
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
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
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
Em seu navegador, digite
http://localhost:4200
, e você verá uma página semelhante à seguinte.Selecione Aceitar para conceder as permissões do aplicativo ao seu perfil. Isso acontecerá na primeira vez que você começar a entrar.
Se você consentir com as permissões solicitadas, o aplicativo Web mostrará uma página de logon bem-sucedido.
Selecione Perfil para exibir as informações do perfil do usuário retornadas na resposta da chamada à API do Microsoft Graph:
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.