Latihan - Menambahkan autentikasi pengguna

Selesai

Aplikasi web daftar belanja Anda memerlukan autentikasi pengguna. Dalam latihan ini, Anda akan menerapkan masuk dan keluar di aplikasi Anda, dan menampilkan status masuk pengguna saat ini.

Dalam latihan ini, Anda akan menyelesaikan langkah berikut:

  1. Instal Azure Static Web Apps CLI untuk pengembangan lokal.
  2. Jalankan aplikasi dan API secara lokal dengan emulasi autentikasi lokal.
  3. Tambahkan tombol masuk untuk beberapa penyedia autentikasi.
  4. Tambahkan tombol keluar jika pengguna masuk.
  5. Tampilkan status masuk pengguna.
  6. Uji alur kerja autentikasi secara lokal.
  7. Sebarkan aplikasi yang diperbarui.

Bersiap untuk pengembangan lokal

Azure Static Web Apps CLI, juga dikenal sebagai SWA CLI, adalah alat pengembangan lokal yang memungkinkan Anda menjalankan aplikasi web dan API Anda secara lokal dan meniru server autentikasi dan otorisasi.

  1. Buka terminal pada komputer Anda.

  2. Menginstal SWA CLI yang menjalankan perintah berikut.

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

Menjalankan aplikasi secara lokal

Sekarang jalankan aplikasi dan API secara lokal dengan server pengembangan. Dengan cara ini, Anda akan dapat melihat dan menguji perubahan yang dibuat, saat membuat kodenya.

  1. Buka proyek di Visual Studio Code.

  2. Di Visual Studio Code, buka palet perintah dengan menekan F1.

  3. Masuk dan pilih Terminal: Buat Terminal Terpadu Baru.

  4. Buka folder kerangka kerja front-end pilihan Anda, sebagai berikut:

    cd angular-app
    
    cd react-app
    
    cd svelte-app
    
    cd vue-app
    
  5. Jalankan aplikasi klien ujung depan menggunakan server pengembangan.

    npm start
    
    npm start
    
    npm run dev
    
    npm run serve
    

    Biarkan server ini berjalan di latar-belakang. Sekarang jalankan API dan emulator server autentikasi menggunakan SWA CLI.

  6. Di Visual Studio Code, buka palet perintah dengan menekan F1.

  7. Masuk dan pilih Terminal: Buat Terminal Terpadu Baru.

  8. Jalankan CLI SWA dengan menjalankan perintah berikut:

    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. Telusuri http://localhost:4280.

Port akhir yang digunakan oleh SWA CLI berbeda dari yang pernah Anda lihat sebelumnya, karena ini menggunakan proksi terbalik untuk meneruskan permintaan ke tiga komponen berbeda:

  • Server pengembangan kerangka kerja Anda
  • Emulator autentikasi dan otorisasi
  • API yang dihost oleh runtime Functions

Screenshot of the Static Web Apps CLI architecture.

Biarkan aplikasi tetap berjalan saat Anda memodifikasi kode.

Mendapatkan status masuk pengguna

Pertama, Anda perlu mengakses status masuk pengguna dengan membuat kueri ke /.auth/me di klien.

  1. Buat file angular-app/src/app/core/models/user-info.ts dan tambahkan kode berikut untuk merepresentasikan antarmuka untuk informasi pengguna.

    export interface UserInfo {
      identityProvider: string;
      userId: string;
      userDetails: string;
      userRoles: string[];
    }
    
  2. Edit file angular-app/src/app/core/components/nav.component.ts, dan tambahkan metode berikut di kelas NavComponent.

    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. Buat properti kelas baru userInfo dan simpan hasil fungsi asinkron getUserInfo() saat komponen diinisialisasi. Terapkan antarmuka OnInit dan perbarui pernyataan impor untuk mengimpor OnInit dan UserInfo. Kode ini mengambil informasi pengguna ketika komponen diinisialisasi.

    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. Edit file react-app/src/components/NavBar.js dan tambahkan kode berikut di bagian atas fungsi. Kode ini mengambil informasi pengguna ketika komponen memuat dan menyimpan informasi pengguna ke dalam 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. Edit file svelte-app/src/components/NavBar.svelte dan tambahkan kode berikut di bagian skrip. Kode ini mengambil informasi pengguna ketika komponen memuat.

    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. Edit file vue-app/src/components/nav-bar.vue, dan tambahkan userInfo ke objek data.

     data() {
       return {
         userInfo: {
           type: Object,
           default() {},
         },
       };
     },
    
  2. Tambahkan metode getUserInfo() ke bagian metode.

    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. Tambahkan hook siklus hidup created ke komponen.

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

    Ketika komponen dibuat, informasi pengguna diambil secara otomatis.

Menambahkan tombol masuk dan keluar

Informasi pengguna akan menjadi undefined jika pengguna tidak masuk, sehingga perubahan tidak akan terlihat untuk saat ini. Saatnya menambahkan tombol masuk untuk penyedia yang berbeda.

  1. Edit file angular-app/src/app/core/components/nav.component.ts untuk menambahkan daftar penyedia di kelas NavComponent.

    providers = ['twitter', 'github', 'aad'];
    
  2. Tambahkan properti redirect berikut untuk menangkap URL saat ini untuk pengalihan pasca masuk.

    redirect = window.location.pathname;
    
  3. Tambahkan kode berikut ke templat setelah elemen </nav> pertama untuk menampilkan tombol masuk dan keluar.

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

    Jika pengguna tidak masuk, Anda menampilkan tombol masuk untuk setiap penyedia. Setiap tombol ditautkan ke /.auth/login/<AUTH_PROVIDER>, dan menyetel URL pengalihan ke halaman saat ini.

    Jika tidak, saat pengguna sudah masuk, tombol keluar menampilkan yang ditautkan ke /.auth/logout dan juga mengatur URL pengalihan ke halaman saat ini.

Anda kini akan melihat halaman web ini di browser.

Screenshot of the Angular web app with login buttons.

  1. Edit file react-app/src/components/NavBar.js untuk menambahkan daftar penyedia di bagian atas fungsi.

    const providers = ['twitter', 'github', 'aad'];
    
  2. Tambahkan variabel redirect ​​berikut di bagian variabel pertama guna menangkap URL saat ini untuk pengalihan pasca masuk.

    const redirect = window.location.pathname;
    
  3. Tambahkan kode berikut ke templat JSX setelah elemen </nav> pertama untuk menampilkan tombol masuk dan keluar.

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

    Jika pengguna tidak masuk, Anda menampilkan tombol masuk untuk setiap penyedia. Setiap tombol ditautkan ke /.auth/login/<AUTH_PROVIDER>, dan menyetel URL pengalihan ke halaman saat ini.

    Jika tidak, saat pengguna sudah masuk, Anda akan menampilkan tombol keluar yang ditautkan ke /.auth/logout dan juga menyetel URL pengalihan ke halaman saat ini.

Anda kini akan melihat halaman web ini di browser.

Screenshot of the React web app with login buttons.

  1. Edit file svelte-app/src/components/NavBar.svelte untuk menambahkan daftar penyedia di bagian atas skrip.

    const providers = ['twitter', 'github', 'aad'];
    
  2. Tambahkan variabel redirect ​​berikut di bagian variabel pertama guna menangkap URL saat ini untuk pengalihan pasca masuk.

    const redirect = window.location.pathname;
    
  3. Tambahkan kode berikut ke templat setelah elemen </nav> pertama untuk menampilkan tombol masuk dan keluar.

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

    Jika pengguna tidak masuk, Anda menampilkan tombol masuk untuk setiap penyedia. Setiap tombol ditautkan ke /.auth/login/<AUTH_PROVIDER>, dan menyetel URL pengalihan ke halaman saat ini.

    Jika tidak, saat pengguna sudah masuk, Anda akan menampilkan tombol keluar yang ditautkan ke /.auth/logout dan juga menyetel URL pengalihan ke halaman saat ini.

Anda kini akan melihat halaman web ini di browser.

Screenshot of the Svelte web app with login buttons.

  1. Edit file vue-app/src/components/nav-bar.vue, dan tambahkan daftar penyedia ke objek data.

     providers: ['twitter', 'github', 'aad'],
    
  2. Tambahkan properti redirect berikut guna menangkap URL saat ini untuk pengalihan pasca masuk.

     redirect: window.location.pathname,
    
  3. Tambahkan kode berikut ke templat setelah elemen </nav> pertama untuk menampilkan tombol masuk dan keluar.

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

    Jika pengguna tidak masuk, Anda menampilkan tombol masuk untuk setiap penyedia. Setiap tombol ditautkan ke /.auth/login/<AUTH_PROVIDER>, dan menyetel URL pengalihan ke halaman saat ini.

    Jika tidak, saat pengguna sudah masuk, Anda akan menampilkan tombol keluar yang ditautkan ke /.auth/logout dan juga menyetel URL pengalihan ke halaman saat ini.

Anda kini akan melihat halaman web ini di browser.

Screenshot of the Vue web app with login buttons.

Menampilkan status masuk pengguna

Sebelum menguji alur kerja autentikasi, tampilkan detail pengguna terkait pengguna yang masuk.

Edit file angular-app/src/app/core/components/nav.component.ts dan tambahkan kode ini ke bagian bawah templat setelah tag </nav> penutup terakhir.

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

Catatan

Properti userDetails dapat berupa nama pengguna atau alamat email, tergantung identitas yang disediakan untuk masuk.

File Anda yang sudah selesai sekarang akan terlihat seperti berikut:

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 = ['twitter', '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;
    }
  }
}

Edit file react-app/src/components/NavBar.js dan tambahkan kode ini ke bagian bawah templat JSX setelah tag </nav> penutup terakhir, untuk menampilkan status masuk.

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

Catatan

Properti userDetails dapat berupa nama pengguna atau alamat email, tergantung identitas yang disediakan untuk masuk.

File Anda yang sudah selesai sekarang akan terlihat seperti berikut:

import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';

const NavBar = (props) => {
  const providers = ['twitter', '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;

Edit file svelte-app/src/components/NavBar.svelte dan tambahkan kode ini ke bagian bawah templat setelah tag </nav> penutup terakhir, untuk menampilkan status masuk.

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

Catatan

Properti userDetails dapat berupa nama pengguna atau alamat email, tergantung identitas yang disediakan untuk masuk.

File Anda yang sudah selesai sekarang akan terlihat seperti berikut:

<script>
  import { onMount } from 'svelte';
  import { Link } from 'svelte-routing';

  const providers = ['twitter', '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>

Edit file vue-app/src/components/nav-bar.vue, dan tambahkan kode ini ke bagian bawah templat setelah tag </nav> penutup terakhir, untuk menampilkan status masuk:

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

Catatan

Properti userDetails dapat berupa nama pengguna atau alamat email, tergantung identitas yang disediakan untuk masuk.

File Anda yang sudah selesai sekarang akan terlihat seperti berikut:

<script>
  export default {
    name: 'NavBar',
    data() {
      return {
        userInfo: {
          type: Object,
          default() {},
        },
        providers: ['twitter', '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>

Menguji autentikasi secara lokal

Semuanya sekarang sudah diatur. Langkah terakhir adalah menguji apakah semuanya berfungsi seperti yang diharapkan.

  1. Di aplikasi web Anda, pilih salah satu penyedia identitas untuk masuk.

  2. Anda akan diarahkan ke halaman ini:

    Screenshot showing SWA CLI fake authentication screen.

    Ini adalah layar autentikasi palsu, yang disediakan oleh CLI SWA, memungkinkan Anda menguji autentikasi secara lokal dengan memberikan detail pengguna kepada diri Anda sendiri.

  3. Masukkan mslearn sebagai nama pengguna dan 1234 untuk ID pengguna.

  4. Pilih Masuk.

    Setelah masuk, Anda diarahkan ke halaman sebelumnya. Anda dapat melihat tombol masuk telah diganti dengan tombol keluar. Anda juga dapat melihat nama pengguna dan penyedia yang dipilih di bawah tombol keluar.

    Sekarang setelah Anda memeriksa semuanya bekerja seperti yang diharapkan secara lokal, saatnya untuk menyebarkan perubahan Anda.

  5. Anda dapat menghentikan aplikasi dan API yang sedang berjalan dengan menekan Ctrl-C di kedua terminal.

Menyebarkan perubahan Anda

  1. Di Visual Studio Code, buka palet perintah dengan menekan F1.

  2. Masuk dan pilih Git: Commit All.

  3. Masukkan Add authentication sebagai pesan penerapan, dan tekan Enter.

  4. Buka palet perintah dengan menekan F1.

  5. Masuk dan pilih Git: Push, dan tekan Enter.

Setelah Anda mendorong perubahan, tunggu hingga proses build dan penerapan berjalan. Perubahan akan terlihat di aplikasi yang Anda sebarkan setelah itu.

Langkah berikutnya

Aplikasi Anda sekarang mendukung autentikasi pengguna dan langkah Anda berikutnya adalah membatasi beberapa bagian aplikasi untuk pengguna yang tidak diautentikasi.