Delen via


Zelfstudie: Gebruikers aanmelden bij een React-app met één pagina met behulp van systeemeigen verificatie (preview)

Van toepassing op: Witte cirkel met een grijs X-symbool. Werkplekhuren Groene cirkel met een wit vinkje. Externe huurders (meer informatie)

In deze zelfstudie leert u hoe u gebruikers kunt aanmelden bij een React-app met één pagina (SPA) met behulp van systeemeigen verificatie.

In deze handleiding leert u:

  • Werk de React-app bij om gebruikers aan te melden met gebruikersnaam(e-mailadres) en wachtwoord.
  • Aanmeldingsstroom testen.

Voorwaarden

Typen aanroepen definiëren die door de app worden uitgevoerd naar de systeemeigen verificatie-API

Tijdens de aanmeldingsstroom voert de app meerdere aanroepen uit naar de systeemeigen verificatie-API, zoals het starten van een aanmeldingsaanvraag, het selecteren van een verificatiemethode en het aanvragen van beveiligingstokens.

Als u deze aanroepen wilt definiëren, opent u het bestand scr/client/RequestTypes.ts en voegt u het volgende codefragment toe:

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

Het type antwoorden definiëren dat de app ontvangt van de systeemeigen verificatie-API

Als u het type antwoorden wilt definiëren dat de app kan ontvangen van de systeemeigen verificatie-API voor de aanmeldingsbewerking, opent u het bestand src/client/ResponseTypes.ts en voegt u vervolgens het volgende codefragment toe:

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

Aanmeldingsaanvragen verwerken

In deze sectie voegt u code toe waarmee aanmeldingsstroomaanvragen worden verwerkt. Voorbeelden van deze aanvragen zijn het starten van een aanmeldingsstroom, het selecteren van een verificatiemethode of het aanvragen van een beveiligingstoken.

Hiervoor maakt u een bestand met de naam src/client/SignInService.tsen voegt u het volgende codefragment toe:

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

De eigenschap challenge_type toont de verificatiemethoden die door de client-app worden ondersteund. Deze app maakt gebruik van e-mail met wachtwoord voor aanmelding, dus de waarde van het vraagtype is wachtwoord oob-omleiding. Lees meer over uitdagingstypen.

UI-onderdelen maken

Tijdens het aanmeldingsproces verzamelt deze app de inloggegevens van de gebruiker, waaronder hun gebruikersnaam (e-mailadres) en wachtwoord, om de gebruiker aan te melden. Nadat de gebruiker zich heeft aangemeld, worden de gegevens van de gebruiker weergegeven in de app.

  1. Maak een map met de naam /pages/signin in de map src.

  2. Als u het aanmeldingsformulier wilt maken, weergeven en verzenden, maakt u een bestand src/pages/signin/SignIn.tsxen voegt u de volgende code toe:

        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. De details van de gebruiker weergeven na een geslaagde aanmelding:

    1. Maak een bestand met de naam client/Utils.tsen voeg vervolgens het volgende codefragment toe:

          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. Maak een map met de naam gebruiker in de map src/pages.

    3. Maak een bestand met de naam src/pages/user/UserInfo.tsxen voeg vervolgens het volgende codefragment toe:

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

App-routes toevoegen

Open het bestand src/AppRoutes.tsx en vervang de inhoud door de volgende code:

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

Uw app uitvoeren en testen

Gebruik de stappen in De app uitvoeren en testen om uw app uit te voeren, maar test deze keer het aanmeldproces met het gebruikersaccount dat u eerder registreerde.

Volgende stap