Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Применяется:
Внешние клиенты (дополнительные сведения)
В этом руководстве вы узнаете, как создать одностраничные приложения React, которое регистрирует пользователей с помощью собственной проверки подлинности.
Изучив это руководство, вы:
- Создайте проект React.
- Добавьте компоненты пользовательского интерфейса приложения.
- Настройте проект для регистрации пользователя с помощью имени пользователя (электронной почты) и пароля.
Необходимые условия
- Выполните шаги, указанные в быстром старте: Вход пользователей в пример одностраничного приложения React с использованием API для родной аутентификации. В этом кратком руководстве показано, как подготовить внешнего арендатора и запустить образец примера кода React.
- Visual Studio Code или другой редактор программного кода.
- Node.js.
Создание проекта React и установка зависимостей
В выбранном расположении на компьютере выполните следующие команды, чтобы создать проект React с именем reactspa, перейдите в папку проекта, а затем установите пакеты:
npm config set legacy-peer-deps true
npx create-react-app reactspa --template typescript
cd reactspa
npm install ajv
npm install react-router-dom
npm install
Добавление файла конфигурации для приложения
Создайте файл с именем src/config.js, а затем добавьте следующий код:
// App Id obatained from the Microsoft Entra portal
export const CLIENT_ID = "Enter_the_Application_Id_Here";
// URL of the CORS proxy server
const BASE_API_URL = `http://localhost:3001/api`;
// Endpoints URLs for Native Auth APIs
export const ENV = {
urlSignupStart: `${BASE_API_URL}/signup/v1.0/start`,
urlSignupChallenge: `${BASE_API_URL}/signup/v1.0/challenge`,
urlSignupContinue: `${BASE_API_URL}/signup/v1.0/continue`,
}
Найдите значение
Enter_the_Application_Id_Hereи замените его на идентификатор приложения (clientId) приложения, которое вы зарегистрировали в Центре администрирования Microsoft Entra.BASE_API_URLуказывает на прокси-сервер общего доступа к ресурсам (CORS), который мы настроим позже в этой серии руководств. API собственной проверки подлинности не поддерживает CORS, поэтому мы настроим прокси-сервер CORS между React SPA и API собственной проверки подлинности для управления заголовками CORS.
Настройка приложения React для вызова собственного API проверки подлинности и обработки ответа
Чтобы завершить поток проверки подлинности, например поток регистрации, с собственными API проверки подлинности, приложение делает вызов dn обрабатывает ответ. Например, приложение инициирует поток регистрации и ожидает ответа, а затем отправляет атрибуты пользователя и ожидает повторно, пока пользователь не будет успешно зарегистрирован.
Настройте вызов клиента к встроенному API проверки подлинности
В этом разделе описано, как осуществлять вызовы к встроенной аутентификации и как обрабатывать ответы:
Создайте папку с именем client в "src".
Создайте файл с именем scr/client/RequestClient.ts, а затем добавьте следующий фрагмент кода:
import { ErrorResponseType } from "./ResponseTypes"; export const postRequest = async (url: string, payloadExt: any) => { const body = new URLSearchParams(payloadExt as any); const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body, }); if (!response.ok) { try { const errorData: ErrorResponseType = await response.json(); throw errorData; } catch (jsonError) { const errorData = { error: response.status, description: response.statusText, codes: [], timestamp: "", trace_id: "", correlation_id: "", }; throw errorData; } } return await response.json(); };Этот код определяет, как приложение вызывает собственный API проверки подлинности и обрабатывает ответы. Всякий раз, когда приложению необходимо инициировать поток проверки подлинности, он использует функцию
postRequest, указав URL-адрес и полезные данные.
Определение типов вызовов приложения в собственный API проверки подлинности
Во время потока регистрации приложение выполняет несколько вызовов к собственному API проверки подлинности.
Чтобы определить эти вызовы, создайте файл с именем scr/client/RequestTypes.ts, а затем добавьте следующий фрагмент кода:
//SignUp
export interface SignUpStartRequest {
client_id: string;
username: string;
challenge_type: string;
password?: string;
attributes?: Object;
}
export interface SignUpChallengeRequest {
client_id: string;
continuation_token: string;
challenge_type?: string;
}
export interface SignUpFormPassword {
name: string;
surname: string;
username: string;
password: string;
}
//OTP
export interface ChallengeForm {
continuation_token: string;
oob?: string;
password?: string;
}
Определите тип ответов, которые приложение получает из собственного API аутентификации.
Чтобы определить тип ответов, которые приложение может получить из собственного API проверки подлинности для операции регистрации, создайте файл с именем src/client/ResponseTypes.ts, а затем добавьте следующий фрагмент кода:
export interface SuccessResponseType {
continuation_token?: string;
challenge_type?: string;
}
export interface ErrorResponseType {
error: string;
error_description: string;
error_codes: number[];
timestamp: string;
trace_id: string;
correlation_id: string;
}
export interface ChallengeResponse {
binding_method: string;
challenge_channel: string;
challenge_target_label: string;
challenge_type: string;
code_length: number;
continuation_token: string;
interval: number;
}
Обработка запросов на регистрацию
В этом разделе вы добавите код, обрабатывающий запросы потока регистрации. Примеры этих запросов — запуск потока регистрации, выбор метода проверки подлинности и отправка одноразового секретного кода.
Для этого создайте файл с именем src/client/SignUpService.ts, а затем добавьте следующий фрагмент кода:
import { CLIENT_ID, ENV } from "../config";
import { postRequest } from "./RequestClient";
import { ChallengeForm, SignUpChallengeRequest, SignUpFormPassword, SignUpStartRequest } from "./RequestTypes";
import { ChallengeResponse } from "./ResponseTypes";
//handle start a sign-up flow
export const signupStart = async (payload: SignUpFormPassword) => {
const payloadExt: SignUpStartRequest = {
attributes: JSON.stringify({
given_name: payload.name,
surname: payload.surname,
}),
username: payload.username,
password: payload.password,
client_id: CLIENT_ID,
challenge_type: "password oob redirect",
};
return await postRequest(ENV.urlSignupStart, payloadExt);
};
//handle selecting an authentication method
export const signupChallenge = async (payload: ChallengeForm):Promise<ChallengeResponse> => {
const payloadExt: SignUpChallengeRequest = {
client_id: CLIENT_ID,
challenge_type: "password oob redirect",
continuation_token: payload.continuation_token,
};
return await postRequest(ENV.urlSignupChallenge, payloadExt);
};
//handle submit one-time passcode
export const signUpSubmitOTP = async (payload: ChallengeForm) => {
const payloadExt = {
client_id: CLIENT_ID,
continuation_token: payload.continuation_token,
oob: payload.oob,
grant_type: "oob",
};
return await postRequest(ENV.urlSignupContinue, payloadExt);
};
Свойство challenge_type показывает методы проверки подлинности, поддерживаемые клиентским приложением. Вход в приложение осуществляется с помощью электронной почты и пароля, поэтому значение типа задачи - перенаправление oob пароля. Узнайте больше о типах испытаний.
Создание компонентов пользовательского интерфейса
Это приложение собирает сведения о пользователе, такие как имя, имя пользователя (электронная почта) и пароль и одноразовый секретный код пользователя. Таким образом, приложение должно иметь форму единовременной регистрации и единовременной формы сбора секретных кодов.
Создайте папку с именем /pages/SignUp в папке src.
Чтобы создать, отобразить и отправить форму регистрации, создайте файл src/pages/SignUp/SignUp.tsx, а затем добавьте следующий код:
import React, { useState } from 'react'; import { signupChallenge, signupStart } from '../../client/SignUpService'; import { useNavigate } from 'react-router-dom'; import { ErrorResponseType } from "../../client/ResponseTypes"; export const SignUp: React.FC = () => { const [name, setName] = useState<string>(''); const [surname, setSurname] = useState<string>(''); const [email, setEmail] = 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 (!name || !surname || !email) { setError('All fields are required'); return; } if (!validateEmail(email)) { setError('Invalid email format'); return; } setError(''); try { setIsloading(true); const res1 = await signupStart({ name, surname, username: email, password }); const res2 = await signupChallenge({ continuation_token: res1.continuation_token }); navigate('/signup/challenge', { state: { ...res2} }); } catch (err) { setError("An error occurred during sign up " + (err as ErrorResponseType).error_description); } finally { setIsloading(false); } }; return ( <div className="sign-up-form"> <form onSubmit={handleSubmit}> <h2>Sign Up</h2> <div className="form-group"> <label>Name:</label> <input type="text" value={name} onChange={(e) => setName(e.target.value)} required /> </div> <div className="form-group"> <label>Last Name:</label> <input type="text" value={surname} onChange={(e) => setSurname(e.target.value)} required /> </div> <div className="form-group"> <label>Email:</label> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required /> </div> {error && <div className="error">{error}</div>} {isLoading && <div className="warning">Sending request...</div>} <button type="submit">Sign Up</button> </form> </div> ); };Чтобы создать, отобразить и отправить одноразовую форму секретного кода, создайте файл src/pages/signup/SignUpChallenge.tsx, а затем добавьте следующий код:
import React, { useState } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { signUpSubmitOTP } from "../../client/SignUpService"; import { ErrorResponseType } from "../../client/ResponseTypes"; export const SignUpChallenge: React.FC = () => { const { state } = useLocation(); const navigate = useNavigate(); const { challenge_target_label, challenge_type, continuation_token, code_length } = state; const [code, setCode] = useState<string>(""); const [error, setError] = useState<string>(""); const [isLoading, setIsloading] = useState<boolean>(false); const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); if (!code) { setError("All fields are required"); return; } setError(""); try { setIsloading(true); const res = await signUpSubmitOTP({ continuation_token, oob: code }); navigate("/signup/completed"); } catch (err) { setError("An error occurred during sign up " + (err as ErrorResponseType).error_description); } finally { setIsloading(false); } }; return ( <div className="sign-up-form"> <form onSubmit={handleSubmit}> <h2>Insert your one time code received at {challenge_target_label}</h2> <div className="form-group"> <label>Code:</label> <input maxLength={code_length} type="text" value={code} onChange={(e) => setCode(e.target.value)} required /> </div> {error && <div className="error">{error}</div>} {isLoading && <div className="warning">Sending request...</div>} <button type="submit">Sign Up</button> </form> </div> ); };Создайте файл src/pages/signup/SignUpCompleted.tsx, а затем добавьте следующий код:
import React from 'react'; import { Link } from 'react-router-dom'; export const SignUpCompleted: React.FC = () => { return ( <div className="sign-up-completed"> <h2>Sign Up Completed</h2> <p>Your sign-up process is complete. You can now log in.</p> <Link to="/signin" className="login-link">Go to Login</Link> </div> ); };На этой странице отображается сообщение об успешном выполнении и кнопка для входа пользователя на страницу входа после успешной регистрации.
Откройте файл src/App.tsx, а затем замените его содержимое следующим кодом:
import React from "react"; import { BrowserRouter, Link } from "react-router-dom"; import "./App.css"; import { AppRoutes } from "./AppRoutes"; function App() { return ( <div className="App"> <BrowserRouter> <header> <nav> <ul> <li> <Link to="/signup">Sign Up</Link> </li> <li> <Link to="/signin">Sign In</Link> </li> <li> <Link to="/reset">Reset Password</Link> </li> </ul> </nav> </header> <AppRoutes /> </BrowserRouter> </div> ); } export default App;Чтобы правильно отобразить приложение React, выполните следующие действия.
Откройте файл src/App.css, а затем добавьте следующее свойство в класс
App-header:min-height: 100vh;Откройте файл src/Index.css, а затем замените его содержимое кодом из src/index.css
Добавление маршрутов приложений
Создайте файл с именем src/AppRoutes.tsx, а затем добавьте следующий код:
import { Route, Routes } from "react-router-dom";
import { SignUp } from "./pages/SignUp/SignUp";
import { SignUpChallenge } from "./pages/SignUp/SignUpChallenge";
import { SignUpCompleted } from "./pages/SignUp/SignUpCompleted";
export const AppRoutes = () => {
return (
<Routes>
<Route path="/" element={<SignUp />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/signup/challenge" element={<SignUpChallenge />} />
<Route path="/signup/completed" element={<SignUpCompleted />} />
</Routes>
);
};
На этом этапе приложение React может отправлять запросы на регистрацию в собственный API проверки подлинности, но нам нужно настроить прокси-сервер CORS для управления заголовками CORS.