Självstudie: Logga in användare i en React-ensidesapp med inbyggd autentisering (förhandsversion)

Gäller för: Grön cirkel med en vit bockmarkeringssymbol som anger att följande innehåll gäller för externa klienter. Externa klienter (läs mer)

I den här självstudien får du lära dig hur du loggar in användare i en React-ensidesapp (SPA) med inbyggd autentisering.

I den här handledningen kommer du att:

  • Uppdatera React-appen för att logga in användare med användarnamn(e-post) och lösenord.
  • Testa inloggningsflödet.

Förutsättningar

Definiera typer av anrop som appen gör till det interna autentiserings-API:et

Under inloggningsflödet gör appen flera anrop till det interna autentiserings-API:et, till exempel starta en inloggningsbegäran, välja en autentiseringsmetod och begära säkerhetstoken.

Om du vill definiera dessa anrop öppnar du filen scr/client/RequestTypes.ts och lägger sedan till följande kodfragment:

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

Definiera vilken typ av svar appen tar emot från det interna autentiserings-API:et

Om du vill definiera vilken typ av svar appen kan ta emot från det interna autentiserings-API:et för inloggningsåtgärden öppnar du filen src/client/ResponseTypes.ts och lägger sedan till följande kodfragment:

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

Bearbeta inloggningsbegäranden

I det här avsnittet lägger du till kod som bearbetar inloggningsflödesbegäranden. Exempel på dessa begäranden är att starta ett inloggningsflöde, välja en autentiseringsmetod eller begära en säkerhetstoken.

Det gör du genom att skapa en fil med namnet src/client/SignInService.tsoch sedan lägga till följande kodfragment:

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

Egenskapen challenge_type visar de autentiseringsmetoder som klientappen stöder. Den här appen loggar in med e-post med lösenord, så värdet för utmaningstypen är lösenords-oob-omdirigering. Läs mer om utmaningstyper.

Skapa gränssnittskomponenter

Under inloggningsflödet samlar den här appen in användarens autentiseringsuppgifter, användarnamn (e-post) och lösenord för att logga in användaren. När användaren har loggat in visar appen användarens information.

  1. Skapa en mapp med namnet /pages/signin i mappen src.

  2. Om du vill skapa, visa och skicka inloggningsformuläret skapar du en fil src/pages/signin/SignIn.tsxoch lägger sedan till följande kod:

        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. Så här visar du användarens information efter en lyckad inloggning:

    1. Skapa en fil med namnet klient/Utils.tsoch lägg sedan till följande kodfragment:

          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. Skapa en mapp med namnet användare i mappen src/pages.

    3. Skapa en fil med namnet src/pages/user/UserInfo.tsxoch lägg sedan till följande kodfragment:

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

Lägga till appvägar

Öppna filen src/AppRoutes.tsx och ersätt innehållet med följande kod:

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

Köra och testa din app

Använd stegen i Kör och testa din app för att köra din app, men den här gången testar du inloggningsflödet med hjälp av det användarkonto som du registrerade dig tidigare.

Aktivera inloggning med ett alias eller användarnamn

Du kan tillåta att användare som loggar in med en e-postadress och ett lösenord även loggar in med ett användarnamn och lösenord. Användarnamnet kallas även för en alternativ inloggningsidentifierare, kan vara ett kund-ID, kontonummer eller en annan identifierare som du väljer att använda som användarnamn.

Du kan tilldela användarnamn till användarkontot manuellt via administrationscentret för Microsoft Entra eller automatisera det i din app via Microsoft Graph API.

Använd stegen i artikeln Logga in med ett alias eller användarnamn så att användarna kan logga in med ett användarnamn i programmet:

  1. Aktivera användarnamn vid inloggning.
  2. Skapa användare med användarnamn i administrationscentret eller uppdatera befintliga användare till genom att lägga till ett användarnamn. Du kan också automatisera skapandet och uppdateringen av användare i din app med hjälp av Microsoft Graph API.

Nästa steg