Oefening: gebruikersverificatie toevoegen

Voltooid

Uw boodschappenlijstweb-app heeft gebruikersverificatie nodig. In deze oefening implementeert u aanmelding en afmelding in uw app en geeft u de huidige aanmeldingsstatus van de gebruiker weer.

In deze oefening voert u de volgende stappen uit:

  1. Installeer de Static Web Apps CLI voor lokale ontwikkeling.
  2. Voer de app en API lokaal uit met lokale verificatie-emulatie.
  3. Voeg aanmeldingsknoppen toe voor meerdere verificatieproviders.
  4. Voeg een afmeldingsknop toe als de gebruiker is aangemeld.
  5. De aanmeldingsstatus van de gebruiker weergeven.
  6. Test de verificatiewerkstroom lokaal.
  7. Implementeer de bijgewerkte app.

Voorbereiden op lokale ontwikkeling

De Static Web Apps CLI, ook wel SWA CLI genoemd, is een lokaal ontwikkelprogramma waarmee u uw web-app en API lokaal kunt uitvoeren en verificatie- en autorisatieservers kunt emuleren.

  1. Open een terminal op uw computer.

  2. Installeer de SWA CLI met de volgende opdracht.

    npm install -g @azure/static-web-apps-cli
    

De app lokaal uitvoeren

Voer nu de app en API lokaal uit met een ontwikkelserver. Op deze manier kunt u uw wijzigingen zien en testen terwijl u ze in de code aanbrengt.

  1. Open het project in Visual Studio Code.

  2. Open in Visual Studio Code het opdrachtpalet door op F1 te drukken.

  3. Voer Terminal in en selecteer Deze: Nieuwe geïntegreerde terminal maken.

  4. Ga als volgt naar de map van uw favoriete front-endframework:

    cd angular-app
    
    cd react-app
    
    cd svelte-app
    
    cd vue-app
    
  5. Voer de front-endclienttoepassing uit met behulp van een ontwikkelserver.

    npm start
    
    npm start
    
    npm run dev
    
    npm run serve
    

    Laat deze server op de achtergrond draaien. Voer nu de API- en verificatieserveremulator uit met behulp van de SWA CLI.

  6. Open in Visual Studio Code het opdrachtpalet door op F1 te drukken.

  7. Voer Terminal in en selecteer Deze: Nieuwe geïntegreerde terminal maken.

  8. Voer de SWA CLI uit door de volgende opdracht uit te voeren:

    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
    
  9. Blader naar http://localhost:4280.

De laatste poort die door de SWA CLI wordt gebruikt, verschilt van de poort die u eerder hebt gezien, omdat er een omgekeerde proxy wordt gebruikt om aanvragen door te sturen naar de drie verschillende onderdelen:

  • Uw frameworkontwikkelingsserver
  • De verificatie- en autorisatieemulator
  • De API die wordt gehost door de Functions-runtime

Schermopname van de CLI-architectuur van Static Web Apps.

Laat de toepassing actief blijven terwijl u de code wijzigt.

De aanmeldingsstatus van de gebruiker ophalen

Eerst moet u toegang krijgen tot de aanmeldingsstatus van de gebruiker door een query naar de client te /.auth/me maken.

  1. Maak het bestand angular-app/src/app/core/models/user-info.ts en voeg de volgende code toe om de interface voor de gebruikersgegevens weer te geven.

    export interface UserInfo {
      identityProvider: string;
      userId: string;
      userDetails: string;
      userRoles: string[];
    }
    
  2. Bewerk het bestand angular-app/src/app/core/components/nav.component.tsen voeg de volgende methode toe aan de NavComponent klasse.

    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;
      }
    }
    
  3. Maak een nieuwe klasse-eigenschap userInfoen sla het resultaat van de asynchrone functie getUserInfo() op wanneer het onderdeel wordt geïnitialiseerd. Implementeer de OnInit interface en werk de importinstructies bij om te importeren OnInit en UserInfo. Met deze code worden de gebruikersgegevens opgehaald wanneer het onderdeel wordt geïnitialiseerd.

    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();
      }
      // ...
    }
    
  1. Bewerk het bestand react-app/src/components/NavBar.jsen voeg de volgende code toe boven aan de functie. Met deze code worden de gebruikersgegevens opgehaald wanneer het onderdeel wordt geladen en opgeslagen in de status.

    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 (
      // ...
    
  1. Bewerk het bestand svelte-app/src/components/NavBar.svelteen voeg de volgende code toe in de scriptsectie. Met deze code worden de gebruikersgegevens opgehaald wanneer het onderdeel wordt geladen.

    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;
      }
    }
    
  1. Bewerk het bestand vue-app/src/components/nav-bar.vueen voeg dit toe userInfo aan het gegevensobject.

     data() {
       return {
         userInfo: {
           type: Object,
           default() {},
         },
       };
     },
    
  2. Voeg de getUserInfo() methode toe aan de sectie Methoden .

    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;
        }
      },
    },
    
  3. Voeg de created levenscyclushook toe aan het onderdeel.

    async created() {
      this.userInfo = await this.getUserInfo();
    },
    

    Wanneer het onderdeel wordt gemaakt, worden de gebruikersgegevens automatisch opgehaald.

Aanmeldings- en afmeldingsknoppen toevoegen

De gebruikersgegevens zijn undefined als ze niet zijn aangemeld, zodat de wijzigingen voorlopig niet zichtbaar zijn. Het is tijd om aanmeldingsknoppen toe te voegen voor de verschillende providers.

  1. Bewerk het bestand angular-app/src/app/core/components/nav.component.ts om een lijst met providers toe te voegen in de NavComponent klasse.

    providers = ['x', 'github', 'aad'];
    
  2. Voeg de volgende redirect eigenschap toe om de huidige URL voor de omleiding na aanmelding vast te leggen.

    redirect = window.location.pathname;
    
  3. Voeg de volgende code toe aan de sjabloon na het eerste </nav> element om de aanmeldings- en afmeldingsknoppen weer te geven.

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

    Als de gebruiker niet is aangemeld, geeft u de aanmeldingsknop voor elke provider weer. Elke knop wordt gekoppeld aan /.auth/login/<AUTH_PROVIDER>en stelt de omleidings-URL in op de huidige pagina.

    Als de gebruiker al is aangemeld, wordt met een afmeldingsknop deze koppelingen weergegeven /.auth/logouten wordt ook de omleidings-URL ingesteld op de huidige pagina.

U ziet deze webpagina nu in uw browser.

Schermopname van de Angular-web-app met aanmeldingsknoppen.

  1. Bewerk het bestand react-app/src/components/NavBar.js om een lijst met providers boven aan de functie toe te voegen.

    const providers = ['x', 'github', 'aad'];
    
  2. Voeg de volgende redirect variabele toe onder de eerste variabele om de huidige URL voor de omleiding na aanmelding vast te leggen.

    const redirect = window.location.pathname;
    
  3. Voeg de volgende code toe aan de JSX-sjabloon na het eerste </nav> element om de aanmeldings- en afmeldingsknoppen weer te geven.

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

    Als de gebruiker niet is aangemeld, geeft u de aanmeldingsknop voor elke provider weer. Elke knop wordt gekoppeld aan /.auth/login/<AUTH_PROVIDER>en stelt de omleidings-URL in op de huidige pagina.

    Als de gebruiker al is aangemeld, geeft u een afmeldingsknop weer waarnaar wordt verwezen /.auth/logouten stelt u ook de omleidings-URL in op de huidige pagina.

U ziet deze webpagina nu in uw browser.

Schermopname van de React-web-app met aanmeldingsknoppen.

  1. Bewerk het bestand svelte-app/src/components/NavBar.svelte om een lijst met providers boven aan het script toe te voegen.

    const providers = ['x', 'github', 'aad'];
    
  2. Voeg de volgende redirect variabele toe onder de eerste variabele om de huidige URL voor de omleiding na aanmelding vast te leggen.

    const redirect = window.location.pathname;
    
  3. Voeg de volgende code toe aan de sjabloon na het eerste </nav> element om de aanmeldings- en afmeldingsknoppen weer te geven.

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

    Als de gebruiker niet is aangemeld, geeft u de aanmeldingsknop voor elke provider weer. Elke knop wordt gekoppeld aan /.auth/login/<AUTH_PROVIDER>en stelt de omleidings-URL in op de huidige pagina.

    Als de gebruiker al is aangemeld, geeft u een afmeldingsknop weer waarnaar wordt verwezen /.auth/logouten stelt u ook de omleidings-URL in op de huidige pagina.

U ziet deze webpagina nu in uw browser.

Schermopname van de Svelte-web-app met aanmeldingsknoppen.

  1. Bewerk het bestand vue-app/src/components/nav-bar.vueen voeg een lijst met providers toe aan het gegevensobject.

     providers: ['x', 'github', 'aad'],
    
  2. Voeg de volgenderedirect eigenschap toe om de huidige URL voor de omleiding na aanmelding vast te leggen.

     redirect: window.location.pathname,
    
  3. Voeg de volgende code toe aan de sjabloon na het eerste </nav> element om de aanmeldings- en afmeldingsknoppen weer te geven.

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

    Als de gebruiker niet is aangemeld, geeft u de aanmeldingsknop voor elke provider weer. Elke knop wordt gekoppeld aan /.auth/login/<AUTH_PROVIDER>en stelt de omleidings-URL in op de huidige pagina.

    Als de gebruiker al is aangemeld, geeft u een afmeldingsknop weer waarnaar wordt verwezen /.auth/logouten stelt u ook de omleidings-URL in op de huidige pagina.

U ziet deze webpagina nu in uw browser.

Schermopname van de Vue-web-app met aanmeldingsknoppen.

De aanmeldingsstatus van de gebruiker weergeven

Voordat u de verificatiewerkstroom test, laten we de gebruikersdetails over de aangemelde gebruiker weergeven.

Bewerk het bestand angular-app/src/app/core/components/nav.component.tsen voeg deze code onder aan de sjabloon toe na de laatste afsluitende </nav> tag.

<div class="user" *ngIf="userInfo">
  <p>Welcome</p>
  <p>{{ userInfo?.userDetails }}</p>
  <p>{{ userInfo?.identityProvider }}</p>
</div>

Notitie

De userDetails eigenschap kan een gebruikersnaam of e-mailadres zijn, afhankelijk van de identiteit die is opgegeven om u aan te melden.

Het voltooide bestand ziet er nu als volgt uit:

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

Bewerk het bestand react-app/src/components/NavBar.jsen voeg deze code toe aan de onderkant van de JSX-sjabloon na de laatste slottag </nav> om de aanmeldingsstatus weer te geven.

{
  userInfo && (
    <div>
      <div className="user">
        <p>Welcome</p>
        <p>{userInfo && userInfo.userDetails}</p>
        <p>{userInfo && userInfo.identityProvider}</p>
      </div>
    </div>
  )
}

Notitie

De userDetails eigenschap kan een gebruikersnaam of e-mailadres zijn, afhankelijk van de identiteit die is opgegeven om u aan te melden.

Het voltooide bestand ziet er nu als volgt uit:

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;

Bewerk het bestand svelte-app/src/components/NavBar.svelteen voeg deze code onder aan de sjabloon toe na de laatste afsluitende </nav> tag om de aanmeldingsstatus weer te geven.

{#if userInfo}
<div class="user">
  <p>Welcome</p>
  <p>{userInfo && userInfo.userDetails}</p>
  <p>{userInfo && userInfo.identityProvider}</p>
</div>
{/if}

Notitie

De userDetails eigenschap kan een gebruikersnaam of e-mailadres zijn, afhankelijk van de identiteit die is opgegeven om u aan te melden.

Het voltooide bestand ziet er nu als volgt uit:

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

Bewerk het bestand vue-app/src/components/nav-bar.vueen voeg deze code onder aan de sjabloon toe na de laatste afsluitende </nav> tag om de aanmeldingsstatus weer te geven:

<div class="user" v-if="userInfo">
  <p>Welcome</p>
  <p>{{ userInfo.userDetails }}</p>
  <p>{{ userInfo.identityProvider }}</p>
</div>

Notitie

De userDetails eigenschap kan een gebruikersnaam of e-mailadres zijn, afhankelijk van de identiteit die is opgegeven om u aan te melden.

Het voltooide bestand ziet er nu als volgt uit:

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

Verificatie lokaal testen

Alles is nu aanwezig. De laatste stap is om te testen of alles werkt zoals verwacht.

  1. Selecteer in uw web-app een van de id-providers om u aan te melden.

  2. U wordt omgeleid naar deze pagina:

    Schermopname van swa CLI nep verificatiescherm.

    Dit is een nepverificatiescherm, geleverd door de SWA CLI, zodat u verificatie lokaal kunt testen door uzelf gebruikersgegevens op te geven.

  3. Voer mslearn in als gebruikersnaam en 1234 voor de gebruikers-id.

  4. Selecteer Aanmelden.

    Na de aanmelding wordt u omgeleid naar de vorige pagina. U kunt zien dat de aanmeldingsknoppen zijn vervangen door een afmeldingsknop. U kunt ook uw gebruikersnaam en de geselecteerde provider onder de afmeldingsknop zien.

    Nu u hebt gecontroleerd of alles lokaal werkt zoals verwacht, is het tijd om uw wijzigingen te implementeren.

  5. U kunt de actieve app en API stoppen door op Ctrl-C in beide terminals te drukken.

Uw wijzigingen implementeren

  1. Open in Visual Studio Code het opdrachtpalet door op F1 te drukken.

  2. Voer Git in en selecteer Alles doorvoeren.

  3. Voer het Add authentication doorvoerbericht in en druk op Enter.

  4. Druk op F1 om het opdrachtenpalet te openen.

  5. Enter en selecteer Git: Push en druk op Enter.

Nadat u de wijzigingen hebt gepusht, wacht u tot het build- en implementatieproces is uitgevoerd. De wijzigingen moeten daarna zichtbaar zijn in uw geïmplementeerde app.

Volgende stappen

Uw app ondersteunt nu gebruikersverificatie en de volgende stap is het beperken van sommige onderdelen van de app tot niet-geverifieerde gebruikers.