Tutorial: Inicio de sesión de usuarios y llamada a Microsoft Graph API desde una aplicación de página única (SPA) de Angular mediante el uso del flujo de código de autenticación
En este tutorial, compilarás una aplicación de página única (SPA) de Angular que inicia la sesión de los usuarios y llama a la API de Microsoft Graph mediante el flujo de código de autorización con clave de prueba para intercambio de código (PKCE). La aplicación de página única que cree usa la biblioteca de autenticación de Microsoft (MSAL) para Angular v2.
En este tutorial, aprenderá a:
- Registro de la aplicación en el centro de administración de Microsoft Entra
- Crear un proyecto de Angular con
npm
- Agregar código para admitir el inicio y el cierre de sesión de usuario
- Agregar código para llamar a Microsoft Graph API
- Prueba de la aplicación
MSAL Angular v2 utiliza el flujo de código de autorización con PKCE en el explorador, lo que supone una mejora respecto a MSAL Angular v1, que utilizaba el flujo de concesión implícita. Se recomienda usar el flujo de código de autorización con PKCE para aplicaciones de página única (SPA) porque es más seguro que el flujo implícito. MSAL Angular v2 no admite el flujo implícito.
Prerrequisitos
- Node.js para ejecutar un servidor web local.
- Visual Studio Code u otro editor para modificar archivos de proyecto.
Funcionamiento de la aplicación de ejemplo
La aplicación de ejemplo que se crea con este tutorial permite que una aplicación de página única de Angular haga consultas a Microsoft Graph API o a una API web que acepte los tokens que emite la Plataforma de identidad de Microsoft. Usa la Biblioteca de autenticación de Microsoft (MSAL) para Angular v2, un contenedor de la biblioteca principal de MSAL.js v2. MSAL Angular permite que las aplicaciones de Angular 9 y versiones posteriores autentiquen a usuarios empresariales mediante Microsoft Entra ID, a usuarios de cuentas de Microsoft y a usuarios de identidades de redes sociales como Facebook, Google y LinkedIn. La biblioteca también permite que las aplicaciones obtengan acceso a los servicios en la nube de Microsoft y a Microsoft Graph.
En este escenario, después de que un usuario inicia sesión, se solicita un token de acceso y se agrega a las solicitudes HTTP mediante el encabezado de autorización. MSAL controla la adquisición y la renovación de tokens.
Bibliotecas
En este tutorial se usan las siguientes bibliotecas:
Biblioteca | Descripción |
---|---|
MSAL Angular | Biblioteca de autenticación de Microsoft para el contenedor de Angular de JavaScript |
Explorador MSAL | Biblioteca de autenticación de Microsoft para el paquete del explorador de JavaScript v2 |
Puede encontrar el código fuente de todas las bibliotecas MSAL.js en el repositorio microsoft-authentication-library-for-js
en GitHub.
Obtención del ejemplo de código completado
¿Prefiere descargar el proyecto de muestra completado para este tutorial? Clonación del ms-identity-javascript-angular-spa
git clone https://github.com/Azure-Samples/ms-identity-javascript-angular-spa.git
Para continuar con el tutorial y compilar la aplicación, ve a la siguiente sección, Registro de la aplicación y registro de los identificadores .
Registro de la aplicación y los identificadores de registro
Sugerencia
Los pasos de este artículo pueden variar ligeramente en función del portal desde donde comienza.
Para completar el registro, proporcione un nombre a la aplicación, especifique los tipos de cuenta admitidos y agregue un URI de redireccionamiento. Una vez registrada, el panel Información general de la aplicación muestra los identificadores necesarios en el código fuente de la aplicación.
- Inicie sesión en el Centro de administración de Microsoft Entra al menos como Desarrollador de aplicaciones.
- Si tiene acceso a varios inquilinos, use el icono Configuración del menú superior para cambiar al inquilino en el que desea registrar la aplicación desde el menú Directorios y suscripciones.
- Vaya aIdentidad>Aplicaciones>Registros de aplicaciones.
- Seleccione Nuevo registro.
- Escriba un nombre para la aplicación, como Angular-SPA-auth-code.
- Para la opción Tipos de cuenta admitidos, seleccione Solo las cuentas de este directorio organizativo. Para obtener información sobre los distintos tipos de cuenta, selecciona la opción Ayudarme a elegir.
- En URI de redireccionamiento (opcional), use el menú desplegable para seleccionar Aplicación de página única (SPA) y escriba
http://localhost:4200
en el cuadro de texto. - Seleccione Registrar.
- El panel Información general de la aplicación se muestra cuando se completa el registro. Registrar el id. de directorio (inquilino) y el id. de aplicación (cliente) que se usará en el código fuente de la aplicación.
Creación del proyecto
Abra Visual Studio Code.
Selecciona Archivo>Abrir carpeta.... Navega y selecciona la ubicación en la que se va a crear el proyecto.
Abra una terminal nueva seleccionando Terminal >Crear terminal.
- Es posible que tenga que modificar los tipos de terminal. Seleccione la flecha abajo situada junto al icono + del terminal y seleccione Símbolo del sistema.
Ejecute los siguientes comandos para crear un nuevo proyecto de Angular con el nombre
msal-angular-tutorial
, instale bibliotecas de componentes de material de Angular, MSAL Browser, MSAL Angular y genere componentes de inicio y 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
Configuración de la aplicación y edición de la interfaz de usuario base
Abra src/app/app.module.ts. El
MsalModule
yMsalInterceptor
deberán agregarse aimports
junto con la constanteisIE
. También agregarás los módulos de material. Reemplace todo el contenido del archivo de código por el siguiente fragmento de código: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 {}
Reemplace los siguientes valores por los valores obtenidos del Centro de administración de Microsoft Entra. Para más información acerca de las opciones configurables disponibles, consulte Inicializar aplicaciones cliente.
clientId
- El identificador de la aplicación, también denominado cliente. ReemplaceEnter_the_Application_Id_Here
por el valor de Id. de la aplicación (cliente) que se registró anteriormente en la página de resumen de la aplicación registrada.authority
- Se compone de dos partes:- La instancia es el punto de conexión del proveedor de nube. En el caso de la nube principal o global de Azure, escriba
https://login.microsoftonline.com
. Consulte los diferentes puntos de conexión disponibles en nubes nacionales. - El Id. de inquilino es el identificador del inquilino en el que está registrada la solicitud. Reemplace el
_Enter_the_Tenant_Info_Here
con el valor de Id. de directorio (inquilino) que se registró anteriormente en la página de resumen de la aplicación registrada.
- La instancia es el punto de conexión del proveedor de nube. En el caso de la nube principal o global de Azure, escriba
redirectUri
: la ubicación a la que el servidor de autorización envía al usuario una vez que se haya autorizado correctamente a la aplicación y se haya concedido un código de autorización o un token de acceso. ReemplaceEnter_the_Redirect_Uri_Here
porhttp://localhost:4200
.
Abra src/app/app-routing.module.ts y agregue rutas a los componentes de inicio y perfil. Reemplace todo el contenido del archivo de código por el siguiente fragmento de código:
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 {}
Abre src/app/app.component.html y reemplaza el código existente por el siguiente fragmento de código:
<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 el 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 agregar estilos CSS a la aplicación:
.toolbar-spacer { flex: 1 1 auto; } a.title { color: white; }
Inicio de sesión mediante elementos emergentes
Abre src/app/app.component.ts y reemplaza el contenido del archivo por el siguiente fragmento de código para iniciar sesión a un usuario mediante una ventana emergente:
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; } }
Inicio de sesión mediante redireccionamientos
Actualice src/app/app.module.ts para arrancar
MsalRedirectComponent
. Se trata de un componente de redireccionamiento dedicado, el cual controla las redirecciones. Cambia la importaciónMsalModule
y el arranqueAppComponent
para que se parezcan al siguiente fragmento de código:... import { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import ... bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here ...
Abra src/index.html y reemplace todo el contenido del archivo por el siguiente fragmento de código, el cual agrega el selector
<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>
Abre src/app/app.component.ts y reemplaza el código por el siguiente fragmento de código para iniciar sesión a un usuario mediante una redirección de marco 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; } }
Abre src/app/home/home.component.ts y reemplaza todo el contenido del archivo por el siguiente fragmento de código para suscribirte al 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); }); } }
Representación condicional
Para asegurarte de que determinados componentes de la interfaz de usuario (IU) solo se muestran para los usuarios autenticados, los componentes deben suscribirse a MsalBroadcastService para comprobar si los usuarios han iniciado sesión y si la interacción está completa.
Agregue
MsalBroadcastService
a src/app/app.component.ts y suscríbase al objeto observableinProgress$
para comprobar si la interacción se ha completado y una cuenta ha iniciado sesión antes de representar la interfaz de usuario. Ahora el código debería ser similar a este: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(); } }
Actualice el código de src/app/home/home.component.ts para comprobar también si la interacción se completará antes de actualizar la interfaz de usuario. Ahora el código debería ser similar a este:
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; } }
Reemplace el código de src/app/home/home.component.html por las siguientes pantallas condicionales:
<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>
Implementación de Angular Guard
La clase MsalGuard
es una que puede usar para proteger las rutas y requerir autenticación antes de acceder a la ruta protegida. En los pasos siguientes, agregue MsalGuard
a la ruta Profile
. Proteger la ruta Profile
significa que, aunque un usuario no inicie sesión con el botón Login
, si intenta acceder a la ruta Profile
o seleccionar el botón Profile
, MsalGuard
le solicitará que se autentique mediante un elemento emergente o un redireccionamiento antes de mostrar la página Profile
.
MsalGuard
es una clase de utilidad que puede usar para mejorar la experiencia del usuario, pero no debería basarse en ella por seguridad. Los atacantes podría moverse por las protecciones del lado cliente y usted debería asegurarse de que el servidor no devuelva ningún dato al que el usuario no debiera acceder.
Agregue la clase
MsalGuard
como proveedor en la aplicación en src/app/app.module.tsy agregue las configuraciones deMsalGuard
. Los ámbitos necesarios para adquirir tokens más adelante se pueden proporcionar enauthRequest
y el tipo de interacción para la protección se puede establecer enRedirect
oPopup
. El código debería parecerse al siguiente fragmento de código: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 {}
Establezca
MsalGuard
las rutas que desea proteger en 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 {}
Ajusta las llamadas de inicio de sesión en src/app/app.component.ts para tener en cuenta el conjunto de
authRequest
en las configuraciones de restricción. El código debería parecerse ahora al siguiente fragmento de código: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(); } }
Adquisición de un token
Interceptor de Angular
MSAL Angular proporciona una clase Interceptor
que adquiere automáticamente tokens para las solicitudes salientes que usan el cliente http
de Angular con los recursos protegidos conocidos.
Agregue la clase
Interceptor
como proveedor a la aplicación en src/app/app.module.ts, con sus configuraciones. El código debería parecerse ahora al siguiente fragmento de código: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 {}
Los recursos protegidos se proporcionan como
protectedResourceMap
. Las direcciones URL que proporciona en la colecciónprotectedResourceMap
distinguen mayúsculas de minúsculas. Para cada recurso, agregue los ámbitos cuya devolución se solicita en el token de acceso.Por ejemplo:
["user.read"]
para Microsoft Graph["<Application ID URL>/scope"]
para las API web personalizadas (es decir,api://<Application ID>/access_as_user
)
Modifique los valores en la sección
protectedResourceMap
, como se describe aquí:Enter_the_Graph_Endpoint_Here
es la instancia de Microsoft Graph API con la que la aplicación debe comunicarse. En el caso del punto de conexión de Microsoft Graph API global, puede reemplazar esta cadena porhttps://graph.microsoft.com
. Para obtener información sobre los puntos de conexión en las implementaciones de nubes nacionales, consulte Implementaciones de nube nacionales en la documentación de Microsoft Graph.
Reemplace el código de src/app/profile/profile.component.ts para recuperar el perfil de un usuario con una solicitud HTTP, y reemplace
GRAPH_ENDPOINT
por el punto de conexión de 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; }); } }
Reemplace la interfaz de usuario de src/app/profile/profile.component.html para mostrar la información del 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>
Cerrar sesión
Actualice el código de src/app/app.component.html para mostrar condicionalmente un botón
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>
Cierre de sesión mediante redireccionamientos
Actualice el código de src/app/app.component.ts para cerrar la sesión de un usuario mediante redirecciones:
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(); } }
Cierre de sesión mediante elementos emergentes
Actualice el código de src/app/app.component.ts para cerrar la sesión de un usuario mediante elementos emergentes:
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(); } }
Prueba del código
Inicie el servidor web para que escuche el puerto; para ello, ejecute los siguientes comandos desde la carpeta de la aplicación en un símbolo del sistema de la línea de comandos:
npm install npm start
En el explorador, escriba
http://localhost:4200
y, a continuación, debería ver una página similar a la siguiente.Seleccione Aceptar para conceder los permisos de aplicación al perfil. Esto ocurrirá la primera vez que empieces a iniciar sesión.
Si das tu consentimiento a los permisos solicitados, la aplicación web mostrará una página que indica que el inicio de sesión se ha realizado correctamente.
Seleccione Perfil para ver la información de perfil de usuario devuelta en la respuesta de la llamada a Microsoft Graph API:
Incorporación de ámbitos y permisos delegados
La API de Microsoft Graph requiere el ámbito User.Read para leer el perfil de un usuario. El ámbito User.Read se agrega automáticamente a cada registro de aplicación. Otras API de Microsoft Graph, así como las API personalizadas para el servidor back-end, podrían requerir otros ámbitos. Por ejemplo, la API de Microsoft Graph requiere el ámbito Mail.Read para mostrar el correo electrónico del usuario.
A medida que se agreguen ámbitos, puede que se pida a los usuarios que proporcionen consentimiento adicional para los ámbitos agregados.
Nota:
Es posible que se pida al usuario algún consentimiento adicional a medida que aumente el número de ámbitos.
Ayuda y soporte técnico
Si necesita ayuda, desea informar de un problema o desea obtener información sobre las opciones de soporte técnico, consulte Opciones de ayuda y soporte técnico para desarrolladores.
Pasos siguientes
- Para obtener más información, cree una aplicación de página única (SPA) de React que inicie sesión de los usuarios en la siguiente serie de tutoriales de varias partes.