Condividi tramite


Esercitazione: Accesso degli utenti in un'app React a pagina singola usando l'autenticazione integrata (anteprima)

Si applica a: cerchio bianco con un simbolo X grigio . dipendenti tenant cerchio verde con simbolo di spunta bianco. tenant esterni (ulteriori informazioni)

In questa esercitazione si apprenderà come consentire agli utenti di accedere a un'app a pagina singola React usando l'autenticazione nativa.

In questa esercitazione, farai:

  • Aggiornare l'app React per consentire agli utenti di accedere con nome utente (indirizzo di posta elettronica) e password.
  • Test del flusso di autenticazione.

Prerequisiti

Definire i tipi di chiamate effettuate dall'app all'API di autenticazione nativa

Durante il flusso di accesso, l'app effettua più chiamate all'API di autenticazione nativa, ad esempio l'avvio di una richiesta di accesso, la selezione di un metodo di autenticazione e la richiesta di token di sicurezza.

Per definire queste chiamate, aprire il file scr/client/RequestTypes.ts, quindi aggiungere il frammento di codice seguente:

   export interface TokenRequestType {
        continuation_token: string;
        client_id: string;
        grant_type: string;
        scope: string;
        password?: string;
        oob?: string;
        challenge_type?: string;
    }

    // Sign in
    export interface TokenSignInType {
        continuation_token: string;
        grant_type: string;
        password?: string;
        oob?: string;
    }

    export interface ChallengeRequest {
        client_id: string;
        challenge_type: string;
        continuation_token: string;
    }

    export interface SignInStartRequest {
        client_id: string;
        challenge_type: string;
        username: string;
    }

    export interface SignInTokenRequest {
        client_id: string;
        grant_type: string;
        continuation_token: string;
        scope: string;
        challenge_type?: string;
        password?: string;
        oob?: string;
    }

Definire il tipo di risposte che l'app riceve dall'API di autenticazione nativa

Per definire il tipo di risposte che l'app può ricevere dall'API di autenticazione nativa per l'operazione di accesso, aprire il file src/client/ResponseTypes.ts, quindi aggiungere il frammento di codice seguente:

    export interface TokenResponseType {
        token_type: string;
        scope: string;
        expires_in: number;
        access_token: string;
        refresh_token: string;
        id_token: string;
    }

Elaborare le richieste di accesso

In questa sezione viene aggiunto codice che elabora le richieste di flusso di accesso. Esempi di queste richieste sono l'avvio di un flusso di accesso, la selezione di un metodo di autenticazione o la richiesta di un token di sicurezza.

A tale scopo, creare un file denominato src/client/SignInService.ts, quindi aggiungere il frammento di codice seguente:

    import { CLIENT_ID, ENV } from "../config";
    import { postRequest } from "./RequestClient";
    import { ChallengeRequest, SignInStartRequest, TokenRequestType, TokenSignInType } from "./RequestTypes";
    import { TokenResponseType } from "./ResponseTypes";

    export const signInStart = async ({ username }: { username: string }) => {
        const payloadExt: SignInStartRequest = {
            username,
            client_id: CLIENT_ID,
            challenge_type: "password oob redirect",
        };

        return await postRequest(ENV.urlOauthInit, payloadExt);
    };

    export const signInChallenge = async ({ continuation_token }: { continuation_token: string }) => {
        const payloadExt: ChallengeRequest = {
            continuation_token,
            client_id: CLIENT_ID,
            challenge_type: "password oob redirect",
        };

        return await postRequest(ENV.urlOauthChallenge, payloadExt);
    };

    export const signInTokenRequest = async (request: TokenSignInType): Promise<TokenResponseType> => {
        const payloadExt: TokenRequestType = {
            ...request,
            client_id: CLIENT_ID,
            challenge_type: "password oob redirect",
            scope: "openid offline_access",
        };

        if (request.grant_type === "password") {
            payloadExt.password = request.password;
        }

        if (request.grant_type === "oob") {
            payloadExt.oob = request.oob;
        }

        return await postRequest(ENV.urlOauthToken, payloadExt);
    };

La proprietà challenge_type mostra i metodi di autenticazione supportati dall'app client. Questa app accede utilizzando l'email con password, quindi il valore del tipo di sfida è password oob reindirizzamento. Ulteriori informazioni sui tipi di challenge .

Creare componenti dell'interfaccia utente

Durante il flusso di accesso, questa app raccoglie le credenziali, il nome utente (indirizzo di posta elettronica) e la password dell'utente per accedere all'utente. Dopo l'accesso dell'utente, l'app visualizza i dettagli dell'utente.

  1. Creare una cartella denominata /pages/signin nella cartella src.

  2. Per creare, visualizzare e inviare il modulo di accesso, creare un file src/pages/signin/SignIn.tsx, quindi aggiungere il codice seguente:

        import React, { useState } from "react";
        import { Link as LinkTo, useNavigate } from "react-router-dom";
        import { signInStart, signInChallenge, signInTokenRequest } from "../../client/SignInService";
        import { ErrorResponseType } from "../../client/ResponseTypes";
    
        export const SignIn: React.FC = () => {
          const [email, setEmail] = useState<string>("");
          const [password, setPassword] = useState<string>("");
          const [error, setError] = useState<string>("");
          const [isLoading, setIsloading] = useState<boolean>(false);
    
          const navigate = useNavigate();
          const validateEmail = (email: string): boolean => {
            const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            return re.test(String(email).toLowerCase());
          };
    
          const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
            e.preventDefault();
            if (!validateEmail(email)) {
              setError("Invalid email format");
              return;
            }
            setError("");
            setIsloading(true);
            try {
              const res1 = await signInStart({
                username: email,
              });
              const res2 = await signInChallenge({ continuation_token: res1.continuation_token });
              const res3 = await signInTokenRequest({
                continuation_token: res2.continuation_token,
                grant_type: "password",
                password: password,
              });
              navigate("/user", { state: res3 });
            } catch (err) {
              setError("An error has occured " + (err as ErrorResponseType).error_description);
            } finally {
              setIsloading(false);
            }
          };
    
          return (
            <div className="login-form">
              <form onSubmit={handleSubmit}>
                <h2>Login</h2>
                <div className="form-group">
                  <label>Email:</label>
                  <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
                </div>
                <div className="form-group">
                  <label>Password:</label>
                  <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required />
                </div>
                {error && <div className="error">{error}</div>}
                {isLoading && <div className="warning">Sending request...</div>}
                <button type="submit" disabled={isLoading}>Login</button>
              </form>
            </div>
          );
        };
    
  3. Per visualizzare i dettagli dell'utente dopo un accesso riuscito:

    1. Creare un file denominato client/Utils.ts, quindi aggiungere il frammento di codice seguente:

          export function parseJwt(token: string) {
              var base64Url = token.split(".")[1];
              var base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
              var jsonPayload = decodeURIComponent(
                  window
                  .atob(base64)
                  .split("")
                  .map(function (c) {
                      return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
                  })
                  .join("")
              );
              return JSON.parse(jsonPayload);
          }
      
    2. Creare una cartella denominata utente nella cartella src/pages.

    3. Creare un file denominato src/pages/user/UserInfo.tsx, quindi aggiungere il frammento di codice seguente:

      // User.tsx
      import React from "react";
      import { useLocation } from "react-router-dom";
      import { parseJwt } from "../../client/Utils";
      
      export const UserInfo: React.FC = () => {
        const { state } = useLocation();
        const decodedToken = parseJwt(state.access_token);
        const { given_name, scp, family_name, unique_name: email } = decodedToken;
      
        console.log(decodedToken);
        const familyName = family_name;
        const givenName = given_name;
        const tokenExpireTime = state.expires_in;
        const scopes = state.scope;
      
        return (
          <div className="user-info">
            <h2>User Information</h2>
            <div className="info-group">
              <label>Given Name:</label>
              <span>{givenName}</span>
            </div>
            <div className="info-group">
              <label>Family Name:</label>
              <span>{familyName}</span>
            </div>
            <div className="info-group">
              <label>Email:</label>
              <span>{email}</span>
            </div>
            <div className="info-group">
              <label>Token Expire Time:</label>
              <span>{tokenExpireTime}</span>
            </div>
            <div className="info-group">
              <label>Scopes:</label>
              <span>{scopes}</span>
            </div>
            <div className="info-group">
              <label>Token payload:</label>
              <span><pre>{JSON.stringify(decodedToken, null, 2)}</pre></span>
            </div>
          </div>
        );
      };
      

Aggiungere percorsi dell'app

Aprire il file src/AppRoutes.tsx, quindi sostituirlo con il codice seguente:

import { Route, Routes } from "react-router-dom";
import { SignIn } from "./pages/SignIn/SignIn";
import { UserInfo } from "./pages/User/UserInfo";
import { SignUp } from "./pages/SignUp/SignUp";
import { SignUpChallenge } from "./pages/SignUp/SignUpChallenge";
import { SignUpCompleted } from "./pages/SignUp/SignUpCompleted";
//For password reset
//import { ResetPassword } from "./pages/ResetAccount/ResetPassword";

export const AppRoutes = () => {
  return (
    <Routes>
      <Route path="/" element={<SignUp />} />
      <Route path="/signin" element={<SignIn />} />
      <Route path="/user" element={<UserInfo />} />
      <Route path="/signup" element={<SignUp />} />
      <Route path="/signup/challenge" element={<SignUpChallenge />} />
      <Route path="/signup/completed" element={<SignUpCompleted />} />
      //For password reset
      //<Route path="/reset" element={<ResetPassword />} />
    </Routes>
  );
};

Eseguire e testare l'app

Usare i passaggi descritti in Eseguire e testare l'app per eseguire l'app, ma questa volta testare il flusso di accesso utilizzando l'account utente con cui ti sei registrato in precedenza.

Passaggio successivo