Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Aplica-se a: Locatários de força de trabalho
Locatários externos (saiba mais)
Neste tutorial, você aprenderá a conectar usuários em um SPA (aplicativo de página única) do React usando a autenticação nativa.
Neste tutorial, você:
- Atualize o aplicativo React para conectar usuários com nome de usuário (email) e senha.
- Testar o fluxo de login.
Pré-requisitos
- Conclua as etapas descritas no Tutorial: Configurar o servidor proxy CORS para gerenciar cabeçalhos CORS para a autenticação nativa.
Definir tipos de chamadas que o aplicativo faz para a API de autenticação nativa
Durante o fluxo de entrada, o aplicativo faz várias chamadas para a API de autenticação nativa, como iniciar uma solicitação de entrada, selecionar um método de autenticação e solicitar tokens de segurança.
Para definir essas chamadas, abra o arquivo scr/client/RequestTypes.ts do e acrescente o seguinte snippet de código:
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;
}
Definir o tipo de resposta que o aplicativo recebe da API de autenticação nativa
Para definir o tipo de respostas que o aplicativo pode receber da API de autenticação nativa para a operação de entrada, abra o arquivo src/client/ResponseTypes.ts e acrescente o seguinte snippet de código:
export interface TokenResponseType {
token_type: string;
scope: string;
expires_in: number;
access_token: string;
refresh_token: string;
id_token: string;
}
Processar solicitações de login
Nesta seção, você adicionará um código que processa solicitações de fluxo de login. Exemplos dessas solicitações são iniciar um fluxo de entrada, selecionar um método de autenticação ou solicitar um token de segurança.
Para fazer isso, crie um arquivo chamado src/client/SignInService.tse adicione o seguinte snippet de código:
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);
};
A propriedade challenge_type
mostra os métodos de autenticação aos quais o aplicativo cliente dá suporte. O aplicativo se conecta usando o email com senha, portanto, o valor do tipo de desafio é redirecionamento de OOB de senha. Leia mais sobre tipos de desafios .
Criar componentes de interface do usuário
Durante o processo de login, este aplicativo coleta as credenciais do usuário, o nome de usuário (email) e a senha, para autenticar o usuário. Depois que o usuário entrar com êxito, o aplicativo exibirá os detalhes do usuário.
Crie uma pasta chamada /pages/signin na pasta src.
Para criar, exibir e enviar o formulário de entrada, crie um arquivo src/pages/signin/SignIn.tsxe adicione o seguinte código:
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> ); };
Para exibir os detalhes do usuário após uma entrada bem-sucedida:
Crie um arquivo chamado cliente/Utils.tse adicione o seguinte snippet de código:
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); }
Crie uma pasta chamada usuário na pasta src/pages.
Crie um arquivo chamado src/pages/user/UserInfo.tsxe adicione o seguinte snippet de código:
// 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> ); };
Adicionar rotas de aplicativo
Abra o arquivo src/AppRoutes.tsx e substitua seu conteúdo pelo seguinte código:
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>
);
};
Executar e testar seu aplicativo
Use as etapas descritas em Executar e testar seu aplicativo para executar seu aplicativo, mas, desta vez, teste o fluxo de entrada usando a conta de usuário com a qual você se inscreveu anteriormente.