Exercice : ajouter l’authentification utilisateur
Votre application web de liste d’achats a besoin d’une authentification utilisateur. Dans cet exercice, vous allez implémenter la connexion et la déconnexion dans votre application et afficher l’état de la connexion de l’utilisateur actuel.
Dans cet exercice, vous allez effectuer les étapes suivantes :
- Installer l’interface CLI Static Web Apps pour le développement local.
- Exécuter l’application et l’API localement avec l’émulation d’authentification locale.
- Ajouter des boutons de connexion pour plusieurs fournisseurs d’authentification.
- Ajouter un bouton de déconnexion si l’utilisateur est connecté.
- Afficher l’état de la connexion de l’utilisateur.
- Tester le workflow d’authentification localement.
- Déployer l’application mise à jour.
Se préparer pour le développement local
L’interface CLI Static Web Apps, également appelée interface CLI SWA, est un outil de développement local qui vous permet d’exécuter votre application web et votre API localement et d’émuler les serveurs d’authentification et d’autorisation.
Ouvrez un terminal sur votre ordinateur.
Installez l’interface CLI SWA en exécutant la commande suivante.
npm install -g @azure/static-web-apps-cli
Exécutez l’application localement.
Nous exécutons maintenant l’application et l’API localement avec un serveur de développement. De cette façon, vous serez en mesure de voir et de tester vos modifications au fur et à mesure.
Ouvrez le projet dans Visual Studio Code.
Dans Visual Studio Code, ouvrez la palette de commandes en appuyant sur F1.
Entrez et sélectionnez Terminal : Créer un terminal intégré.
Accédez au dossier de votre framework front-end par défaut comme suit :
cd angular-app
cd react-app
cd svelte-app
cd vue-app
Exécutez l’application cliente front-end à l’aide d’un serveur de développement.
npm start
npm start
npm run dev
npm run serve
Laissez ce serveur en cours d’exécution en arrière-plan. Nous exécutons maintenant l’API et l’émulateur du serveur d’authentification à l’aide de l’interface CLI SWA.
Dans Visual Studio Code, ouvrez la palette de commandes en appuyant sur F1.
Entrez et sélectionnez Terminal : Créer un terminal intégré.
Installez l’interface CLI SWA en exécutant la commande suivante :
swa start http://localhost:4200 --api-location ./api
swa start http://localhost:3000 --api-location ./api
swa start http://localhost:5000 --api-location ./api
swa start http://localhost:8080 --api-location ./api
Accédez à
http://localhost:4280
.
Le port final utilisé par l’interface CLI SWA est différent de celui que vous avez vu précédemment, car il utilise un proxy inverse pour transférer des requêtes vers les trois composants différents :
- votre serveur de développement d’infrastructure
- l’émulateur d'authentification et d'autorisation
- l’API hébergée par le runtime Functions
Laissez l’application s’exécuter pendant que vous modifiez le code.
Obtenir l’état de connexion de l’utilisateur
Tout d’abord, vous devez accéder à l’état de la connexion de l’utilisateur en exécutant une requête pour /.auth/me
dans le client.
Créez le fichier
angular-app/src/app/core/models/user-info.ts
et ajoutez le code suivant pour représenter l’interface des informations utilisateur.export interface UserInfo { identityProvider: string; userId: string; userDetails: string; userRoles: string[]; }
Ouvrez le fichier
angular-app/src/app/core/components/nav.component.ts
et ajoutez la méthode suivante dans la classeNavComponent
.async getUserInfo() { try { const response = await fetch('/.auth/me'); const payload = await response.json(); const { clientPrincipal } = payload; return clientPrincipal; } catch (error) { console.error('No profile could be found'); return undefined; } }
Créez une propriété de classe
userInfo
et stockez le résultat de la fonction asyncgetUserInfo()
quand le composant est initialisé. Implémentez l’interfaceOnInit
et mettez à jour les instructions d’importation pour importerOnInit
etUserInfo
. Ce code extrait les informations utilisateur quand le composant est initialisé.import { Component, OnInit } from '@angular/core'; import { UserInfo } from '../model/user-info'; export class NavComponent implements OnInit { userInfo: UserInfo; async ngOnInit() { this.userInfo = await this.getUserInfo(); } // ... }
Modifiez le fichier
react-app/src/components/NavBar.js
et ajoutez le code suivant en haut de la fonction. Ce code extrait les informations utilisateur quand le composant se charge et le stocke dans l’état.import React, { useState, useEffect } from 'react'; import { NavLink } from 'react-router-dom'; const NavBar = (props) => { const [userInfo, setUserInfo] = useState(); useEffect(() => { (async () => { setUserInfo(await getUserInfo()); })(); }, []); async function getUserInfo() { try { const response = await fetch('/.auth/me'); const payload = await response.json(); const { clientPrincipal } = payload; return clientPrincipal; } catch (error) { console.error('No profile could be found'); return undefined; } } return ( // ...
Modifiez le fichier
svelte-app/src/components/NavBar.svelte
et ajoutez le code suivant dans la section de script. Ce code extrait les informations utilisateur quand le composant se charge.import { onMount } from 'svelte'; let userInfo = undefined; onMount(async () => (userInfo = await getUserInfo())); async function getUserInfo() { try { const response = await fetch('/.auth/me'); const payload = await response.json(); const { clientPrincipal } = payload; return clientPrincipal; } catch (error) { console.error('No profile could be found'); return undefined; } }
Ouvrez le fichier
vue-app/src/components/nav-bar.vue
et ajoutezuserInfo
à l’objet de données.data() { return { userInfo: { type: Object, default() {}, }, }; },
Ajoutez la méthode
getUserInfo()
à la section methods.methods: { async getUserInfo() { try { const response = await fetch('/.auth/me'); const payload = await response.json(); const { clientPrincipal } = payload; return clientPrincipal; } catch (error) { console.error('No profile could be found'); return undefined; } }, },
Ajoutez le hook de cycle de vie
created
au composant.async created() { this.userInfo = await this.getUserInfo(); },
Une fois le composant créé, les informations utilisateur sont automatiquement récupérées.
Ajouter des boutons de connexion et de déconnexion
Les informations utilisateur sont undefined
si vous n’êtes pas connecté : les changements ne sont donc pas visibles pour le moment. Il est temps d’ajouter des boutons de connexion pour les différents fournisseurs.
Ouvrez le fichier
angular-app/src/app/core/components/nav.component.ts
pour ajouter une liste de fournisseurs dans la classeNavComponent
.providers = ['x', 'github', 'aad'];
Ajoutez la propriété
redirect
suivante afin de capturer l’URL actuelle pour la redirection après connexion.redirect = window.location.pathname;
Ajoutez le code suivant dans le modèle après le premier élément
</nav>
pour afficher les boutons de connexion et de déconnexion.<nav class="menu auth"> <p class="menu-label">Auth</p> <div class="menu-list auth"> <ng-container *ngIf="!userInfo; else logout"> <ng-container *ngFor="let provider of providers"> <a href="/.auth/login/{{provider}}?post_login_redirect_uri={{redirect}}">{{provider}}</a> </ng-container> </ng-container> <ng-template #logout> <a href="/.auth/logout?post_logout_redirect_uri={{redirect}}">Logout</a> </ng-template> </div> </nav>
Si l’utilisateur n’est pas connecté, vous affichez le bouton de connexion pour chaque fournisseur. Chaque bouton est lié à
/.auth/login/<AUTH_PROVIDER>
et définit l’URL de redirection vers la page actuelle.Sinon, si l’utilisateur est déjà connecté, s’affiche un bouton de déconnexion qui est lié à
/.auth/logout
et qui définit également l’URL de redirection vers la page actuelle.
Vous devez maintenant voir cette page web dans votre navigateur.
Ouvrez le fichier
react-app/src/components/NavBar.js
pour ajouter une liste de fournisseurs en haut de la fonction.const providers = ['x', 'github', 'aad'];
Ajoutez la variable
redirect
suivante en dessous de la première variable afin de capturer l’URL actuelle pour la redirection après connexion.const redirect = window.location.pathname;
Ajoutez le code suivant dans le modèle JSX après le premier élément
</nav>
pour afficher les boutons de connexion et de déconnexion.<nav className="menu auth"> <p className="menu-label">Auth</p> <div className="menu-list auth"> {!userInfo && providers.map((provider) => ( <a key={provider} href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}> {provider} </a> ))} {userInfo && <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>Logout</a>} </div> </nav>
Si l’utilisateur n’est pas connecté, vous affichez le bouton de connexion pour chaque fournisseur. Chaque bouton est lié à
/.auth/login/<AUTH_PROVIDER>
et définit l’URL de redirection vers la page actuelle.Sinon, si l’utilisateur est déjà connecté, vous affichez un bouton de déconnexion qui est lié à
/.auth/logout
et qui définit également l’URL de redirection vers la page actuelle.
Vous devez maintenant voir cette page web dans votre navigateur.
Ouvrez le fichier
svelte-app/src/components/NavBar.svelte
pour ajouter une liste de fournisseurs en haut du script.const providers = ['x', 'github', 'aad'];
Ajoutez la variable
redirect
suivante en dessous de la première variable afin de capturer l’URL actuelle pour la redirection après connexion.const redirect = window.location.pathname;
Ajoutez le code suivant dans le modèle après le premier élément
</nav>
pour afficher les boutons de connexion et de déconnexion.<nav class="menu auth"> <p class="menu-label">Auth</p> <div class="menu-list auth"> {#if !userInfo} {#each providers as provider (provider)} <a href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}> {provider} </a> {/each} {/if} {#if userInfo} <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}> Logout </a> {/if} </div> </nav>
Si l’utilisateur n’est pas connecté, vous affichez le bouton de connexion pour chaque fournisseur. Chaque bouton est lié à
/.auth/login/<AUTH_PROVIDER>
et définit l’URL de redirection vers la page actuelle.Sinon, si l’utilisateur est déjà connecté, vous affichez un bouton de déconnexion qui est lié à
/.auth/logout
et qui définit également l’URL de redirection vers la page actuelle.
Vous devez maintenant voir cette page web dans votre navigateur.
Ouvrez le fichier
vue-app/src/components/nav-bar.vue
et ajoutez une liste de fournisseurs à l’objet de données.providers: ['x', 'github', 'aad'],
Ajoutez la propriété
redirect
suivante afin de capturer l’URL actuelle pour la redirection après connexion.redirect: window.location.pathname,
Ajoutez le code suivant dans le modèle après le premier élément
</nav>
pour afficher les boutons de connexion et de déconnexion.<nav class="menu auth"> <p class="menu-label">Auth</p> <div class="menu-list auth"> <template v-if="!userInfo"> <template v-for="provider in providers"> <a :key="provider" :href="`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`"> {{ provider }} </a> </template> </template> <a v-if="userInfo" :href="`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`"> Logout </a> </div> </nav>
Si l’utilisateur n’est pas connecté, vous affichez le bouton de connexion pour chaque fournisseur. Chaque bouton est lié à
/.auth/login/<AUTH_PROVIDER>
et définit l’URL de redirection vers la page actuelle.Sinon, si l’utilisateur est déjà connecté, vous affichez un bouton de déconnexion qui est lié à
/.auth/logout
et qui définit également l’URL de redirection vers la page actuelle.
Vous devez maintenant voir cette page web dans votre navigateur.
Afficher l’état de connexion de l’utilisateur
Avant de tester le workflow d’authentification, affichons les détails de l’utilisateur connecté.
Modifiez le fichier angular-app/src/app/core/components/nav.component.ts
et ajoutez ce code en bas du modèle après l’étiquette de fermeture finale </nav>
.
<div class="user" *ngIf="userInfo">
<p>Welcome</p>
<p>{{ userInfo?.userDetails }}</p>
<p>{{ userInfo?.identityProvider }}</p>
</div>
Notes
La propriété userDetails
peut être un nom d’utilisateur ou une adresse e-mail, en fonction de l’identité fournie utilisée pour la connexion.
Votre fichier terminé doit maintenant ressembler à ceci :
import { Component, OnInit } from '@angular/core';
import { UserInfo } from '../model/user-info';
@Component({
selector: 'app-nav',
template: `
<nav class="menu">
<p class="menu-label">Menu</p>
<ul class="menu-list">
<a routerLink="/products" routerLinkActive="router-link-active">
<span>Products</span>
</a>
<a routerLink="/about" routerLinkActive="router-link-active">
<span>About</span>
</a>
</ul>
</nav>
<nav class="menu auth">
<p class="menu-label">Auth</p>
<div class="menu-list auth">
<ng-container *ngIf="!userInfo; else logout">
<ng-container *ngFor="let provider of providers">
<a href="/.auth/login/{{ provider }}?post_login_redirect_uri={{ redirect }}">{{ provider }}</a>
</ng-container>
</ng-container>
<ng-template #logout>
<a href="/.auth/logout?post_logout_redirect_uri={{ redirect }}">Logout</a>
</ng-template>
</div>
</nav>
<div class="user" *ngIf="userInfo">
<p>Welcome</p>
<p>{{ userInfo?.userDetails }}</p>
<p>{{ userInfo?.identityProvider }}</p>
</div>
`,
})
export class NavComponent implements OnInit {
providers = ['x', 'github', 'aad'];
redirect = window.location.pathname;
userInfo: UserInfo;
async ngOnInit() {
this.userInfo = await this.getUserInfo();
}
async getUserInfo() {
try {
const response = await fetch('/.auth/me');
const payload = await response.json();
const { clientPrincipal } = payload;
return clientPrincipal;
} catch (error) {
console.error('No profile could be found');
return undefined;
}
}
}
Modifiez le fichier react-app/src/components/NavBar.js
et ajoutez ce code en bas du modèle JSX, après l’étiquette de fermeture finale </nav>
, pour afficher l’état de connexion.
{
userInfo && (
<div>
<div className="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
</div>
)
}
Notes
La propriété userDetails
peut être un nom d’utilisateur ou une adresse e-mail, en fonction de l’identité fournie utilisée pour la connexion.
Votre fichier terminé doit maintenant ressembler à ceci :
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const NavBar = (props) => {
const providers = ['x', 'github', 'aad'];
const redirect = window.location.pathname;
const [userInfo, setUserInfo] = useState();
useEffect(() => {
(async () => {
setUserInfo(await getUserInfo());
})();
}, []);
async function getUserInfo() {
try {
const response = await fetch('/.auth/me');
const payload = await response.json();
const { clientPrincipal } = payload;
return clientPrincipal;
} catch (error) {
console.error('No profile could be found');
return undefined;
}
}
return (
<div className="column is-2">
<nav className="menu">
<p className="menu-label">Menu</p>
<ul className="menu-list">
<NavLink to="/products" activeClassName="active-link">
Products
</NavLink>
<NavLink to="/about" activeClassName="active-link">
About
</NavLink>
</ul>
{props.children}
</nav>
<nav className="menu auth">
<p className="menu-label">Auth</p>
<div className="menu-list auth">
{!userInfo &&
providers.map((provider) => (
<a key={provider} href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}>
{provider}
</a>
))}
{userInfo && <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>Logout</a>}
</div>
</nav>
{userInfo && (
<div>
<div className="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
</div>
)}
</div>
);
};
export default NavBar;
Modifiez le fichier svelte-app/src/components/NavBar.svelte
et ajoutez ce code en bas du modèle, après l’étiquette de fermeture finale </nav>
, pour afficher l’état de connexion.
{#if userInfo}
<div class="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
{/if}
Notes
La propriété userDetails
peut être un nom d’utilisateur ou une adresse e-mail, en fonction de l’identité fournie utilisée pour la connexion.
Votre fichier terminé doit maintenant ressembler à ceci :
<script>
import { onMount } from 'svelte';
import { Link } from 'svelte-routing';
const providers = ['x', 'github', 'aad'];
const redirect = window.location.pathname;
let userInfo = undefined;
onMount(async () => (userInfo = await getUserInfo()));
async function getUserInfo() {
try {
const response = await fetch('/.auth/me');
const payload = await response.json();
const { clientPrincipal } = payload;
return clientPrincipal;
} catch (error) {
console.error('No profile could be found');
return undefined;
}
}
function getProps({ href, isPartiallyCurrent, isCurrent }) {
const isActive = href === '/' ? isCurrent : isPartiallyCurrent || isCurrent;
// The object returned here is spread on the anchor element's attributes
if (isActive) {
return { class: 'router-link-active' };
}
return {};
}
</script>
<div class="column is-2">
<nav class="menu">
<p class="menu-label">Menu</p>
<ul class="menu-list">
<Link to="/products" {getProps}>Products</Link>
<Link to="/about" {getProps}>About</Link>
</ul>
</nav>
<nav class="menu auth">
<p class="menu-label">Auth</p>
<div class="menu-list auth">
{#if !userInfo}
{#each providers as provider (provider)}
<a href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}>
{provider}
</a>
{/each}
{/if}
{#if userInfo}
<a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>
Logout
</a>
{/if}
</div>
</nav>
{#if userInfo}
<div class="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
{/if}
</div>
Modifiez le fichier vue-app/src/components/nav-bar.vue
et ajoutez ce code en bas du modèle, après l’étiquette de fermeture finale </nav>
, pour afficher l’état de connexion :
<div class="user" v-if="userInfo">
<p>Welcome</p>
<p>{{ userInfo.userDetails }}</p>
<p>{{ userInfo.identityProvider }}</p>
</div>
Notes
La propriété userDetails
peut être un nom d’utilisateur ou une adresse e-mail, en fonction de l’identité fournie utilisée pour la connexion.
Votre fichier terminé doit maintenant ressembler à ceci :
<script>
export default {
name: 'NavBar',
data() {
return {
userInfo: {
type: Object,
default() {},
},
providers: ['x', 'github', 'aad'],
redirect: window.location.pathname,
};
},
methods: {
async getUserInfo() {
try {
const response = await fetch('/.auth/me');
const payload = await response.json();
const { clientPrincipal } = payload;
return clientPrincipal;
} catch (error) {
console.error('No profile could be found');
return undefined;
}
},
},
async created() {
this.userInfo = await this.getUserInfo();
},
};
</script>
<template>
<div column is-2>
<nav class="menu">
<p class="menu-label">Menu</p>
<ul class="menu-list">
<router-link to="/products">Products</router-link>
<router-link to="/about">About</router-link>
</ul>
</nav>
<nav class="menu auth">
<p class="menu-label">Auth</p>
<div class="menu-list auth">
<template v-if="!userInfo">
<template v-for="provider in providers">
<a :key="provider" :href="`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`">{{ provider }}</a>
</template>
</template>
<a v-if="userInfo" :href="`/.auth/logout?post_logout_redirect_uri=${redirect}`">Logout</a>
</div>
</nav>
<div class="user" v-if="userInfo">
<p>Welcome</p>
<p>{{ userInfo.userDetails }}</p>
<p>{{ userInfo.identityProvider }}</p>
</div>
</div>
</template>
Tester l’authentification localement
Tout est maintenant en place. L’étape finale consiste à tester si tout fonctionne comme prévu.
Dans votre application web, sélectionnez l’un des fournisseurs d’identité à connecter.
Vous serez redirigé vers cette page :
C’est un écran d’authentification fictif fourni par l’interface CLI SWA, qui vous permet de tester l’authentification localement en fournissant vous-même les détails de l’utilisateur.
Entrez
mslearn
comme nom d'utilisateur et1234
pour l’identifiant utilisateur.Sélectionnez Connexion.
Après la connexion, vous êtes redirigé vers la page précédente. Vous pouvez voir que les boutons de connexion ont été remplacés par un bouton de déconnexion. Vous pouvez également voir votre nom d’utilisateur et le fournisseur sélectionné sous le bouton de déconnexion.
Maintenant que vous avez vérifié que tout fonctionne comme prévu localement, il est temps de déployer vos modifications.
Vous pouvez arrêter l’application en cours d’exécution et l’API en appuyant sur Ctrl-C dans les deux terminaux.
Déployez vos modifications
Dans Visual Studio Code, ouvrez la palette de commandes en appuyant sur F1.
Entrez et sélectionnez Git: Commit All.
Entrez
Add authentication
comme message de commit, puis appuyez sur Entrée.Ouvrez la palette de commandes en appuyant sur F1.
Entrez et sélectionnez Git: Push, puis appuyez sur Entrée.
Après avoir envoyé vos modifications, attendez que le processus de génération et de déploiement s’exécute. Par la suite, les modifications doivent être visibles sur votre application déployée.
Étapes suivantes
Votre application prend désormais en charge l’authentification utilisateur et l’étape suivante consiste à limiter certaines parties de l’application aux utilisateurs non authentifiés.