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. O SPA que você criará usa a MSAL (Biblioteca de Autenticação da Microsoft) para Angular v2.
Neste tutorial:
- Crie um projeto do Angular com
npm - Registrar o aplicativo no portal do Azure
- 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 traz aprimoramentos em relação à MSAL Angular v1 dando suporte ao fluxo do código de autorização no navegador em vez do fluxo de concessão implícita. 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 aplicativos Angular 9 e posterior autentiquem usuários corporativos usando o Azure AD (Azure Active Directory), bem como usuários com as contas Microsoft e usuários com identidades sociais, como o Facebook, o Google e o 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 |
Encontre o código-fonte de todas as bibliotecas MSAL.js no repositório AzureAD/microsoft-authentication-library-for-js no GitHub.
Criar seu projeto
Depois de instalar o Node.js, abra uma janela do terminal e execute os seguintes comandos para gerar um novo aplicativo 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
Registre seu aplicativo
Siga as instruções para registrar um aplicativo de página única no portal do Azure.
Na página Visão geral do aplicativo do seu registro, anote o valor da ID do aplicativo (cliente) para uso posterior.
Registre o valor do URI de Redirecionamento como http://localhost:4200/ e digite-o como 'SPA'.
Configurar o aplicativo
Na pasta src/app, edite o app.module.ts e adicione
MsalModuleeMsalInterceptoraimports, bem como a constanteisIE. Seu código deve ficar assim: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', // 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] }) export class AppModule { }Substitua esses valores:
Nome do valor Sobre o Enter_the_Application_Id_Here Na página Visão Geral do seu registro de aplicativo, esta é seu valor ID do Aplicativo (cliente) . Enter_the_Cloud_Instance_Id_Here Essa é a instância da nuvem do Azure. Para a nuvem principal ou global do Azure, insira https://login.microsoftonline.com. Para nuvens nacionais (por exemplo, China), confira Nuvens nacionais. Enter_the_Tenant_Info_Here Defina como uma das seguintes opções: Se o aplicativo der suporte a contas neste diretório organizacional, substitua esse valor pela ID do diretório (locatário) ou pelo nome do locatário (por exemplo, contoso.microsoft.com). Se o aplicativo for compatível com as contas em qualquer diretório organizacional, substitua esse valor por organizações. Se o seu aplicativo for compatível com as contas em qualquer diretório organizacional e contas pessoais da Microsoft, substitua esse valor por comum. Para restringir o suporte a contas pessoais da Microsoft, substitua esse valor por consumidores. Enter_the_Redirect_Uri_Here Substitua por http://localhost:4200 . Para saber mais sobre opções configuráveis disponíveis, confira Inicializar aplicativos cliente.
Adicione rotas aos componentes inicial e de perfil em src/app/app-routing.module.ts. O código será parecido com 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 { }
Substituir a interface do usuário base
Substitua o código de espaço reservado em src/app/app.component.html pelo 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>Adicione módulos materiais a src/app/app.module.ts. O
AppModuleserá parecido com este: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 { }(OPCIONAL) Adicione o CSS a src/style.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%; }(OPCIONAL) Adicione o CSS a src/app/app.component.css:
.toolbar-spacer { flex: 1 1 auto; } a.title { color: white; }
Conectar um usuário
Adicione o código das seguintes seções para invocar o logon usando uma janela pop-up ou um redirecionamento de quadro completo:
Entrar usando pop-ups
Altere o código em src/app/app.component.ts para o seguinte 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; } }
Observação
O restante deste tutorial usará o método loginRedirect com o Microsoft Internet Explorer devido a um problema conhecido relacionado ao tratamento de janelas pop-up pelo Internet Explorer.
Conectar-se usando redirecionamentos
Atualize src/app/app.module.ts para inicializar o
MsalRedirectComponent. Este é um componente de redirecionamento dedicado que tratará os redirecionamentos. Agora, o código deverá ter esta aparência: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 { }Adicione o seletor
<app-redirect>a src/index.html. Esse seletor é usado peloMsalRedirectComponent. O src/index.html será parecido com este:<!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>Substitua o código em src/app/app.component.ts pelo seguinte 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; } }Substitua o código existente em src/app/home/home.component.ts para assinar o evento
LOGIN_SUCCESS. Isso permitirá que você acesse o resultado do logon bem-sucedido com o redirecionamento. Seu código deve ficar 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 { 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 renderizar uma interface do usuário somente para usuários autenticados, os componentes precisam assinar o MsalBroadcastService para ver se os usuários se conectaram e se a interação foi concluída.
Adicione o
MsalBroadcastServicea 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>
Como proteger as rotas
Angular Guard
A MSAL Angular fornece o MsalGuard, uma classe que você pode usar para proteger rotas e exigir autenticação antes de acessar a rota protegida. As etapas abaixo adicionam o MsalGuard à rota Profile. A proteção da 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 clicar no botão Profile, o MsalGuard solicitará que o usuário se autentique por meio do item pop-up ou do redirecionamento antes de mostrar a página Profile.
MsalGuard é uma classe de conveniência que você pode usar para aprimorar 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
MsalGuardcomo 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 ouRedirectouPopup. O código será parecido com 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 { }Defina o
MsalGuardnas rotas que deseja proteger em src/app/app-routing.module.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 { }Ajuste as chamadas de logon em src/app/app.component.ts para levar em conta o
authRequestdefinido nas configurações de proteção. Agora, o código será parecido com o seguinte: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
Interceptorcomo um provedor ao seu aplicativo em src/app/app.module.ts, com as respectivas configurações. Agora, o código deverá ter esta aparência: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çãoprotectedResourceMapdiferenciam 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:Nome do valor Sobre o Enter_the_Graph_Endpoint_HereA 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 as duas instâncias dessa 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.Substitua o código em src/app/profile/profile.component.ts para recuperar o perfil de um usuário com uma solicitação 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; }); } }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 startNo seu navegador, digite http://localhost:4200 ou http://localhost:{port}, em que port é a porta que o servidor Web está escutando. Você verá uma página semelhante à mostrada abaixo.
Fornecer autorização para acesso de aplicativo
Na primeira vez que começar a entrar no aplicativo, você deverá permitir acesso ao seu perfil e permitir que ele conecte você:
Se você consentir com as permissões solicitadas, o aplicativo Web mostrará uma página de logon bem-sucedida:
Chamar a API do Graph
Depois de entrar, selecione Perfil para ver 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 criados no portal do Azure. Outras APIs do Microsoft Graph, bem como APIs personalizadas do servidor de back-end, podem exigir escopos adicionais. Por exemplo, a API do Microsoft Graph requer o escopo Mail.Read para listar o email do usuário.
À medida que você adiciona escopos, os usuários podem ser solicitados a fornecer consentimento adicional 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
Aprofunde-se no desenvolvimento de SPAs (aplicativos de página única) na plataforma de identidade da Microsoft em nossa série de artigos de várias partes.