mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
refactor(signup): drive Fayda through register-with-fayda
Follows the API back to a single endpoint. The page posts { action: 'start' }
to open the attempt, the callback page posts { action: 'verify', ... } to get
the verified identity, and the existing signup submission is unchanged.
Drops the post-signup link call: there is no longer a link endpoint, and the
account is created by the signup endpoint that already owns user creation.
This commit is contained in:
@@ -63,9 +63,11 @@ export function FaydaCallbackPage() {
|
||||
}
|
||||
|
||||
callbackTrigger({
|
||||
url: '/auth/fayda/callback',
|
||||
url: '/auth/register-with-fayda',
|
||||
method: 'POST',
|
||||
body: { code, state, transactionToken: request.transactionToken },
|
||||
// `verify` returns the identity without creating an account — the
|
||||
// existing signup endpoint still does that.
|
||||
body: { action: 'verify', code, state, transactionToken: request.transactionToken },
|
||||
})
|
||||
.unwrap()
|
||||
.then((result) => {
|
||||
|
||||
@@ -31,7 +31,7 @@ import { z } from 'zod';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useApiLazyQuery, useApiMutation } from '@ema-platform/api';
|
||||
import { useApiMutation } from '@ema-platform/api';
|
||||
import { useErrorHandler, passwordSchema, PasswordRequirements, phoneNumber, PhoneInput } from '@ema-platform/ui';
|
||||
import { AuthShell } from '../components/AuthShell';
|
||||
import { loginSuccess, setUser } from '../store/auth.slice';
|
||||
@@ -70,13 +70,12 @@ export function SignupPage() {
|
||||
// Fayda is optional: the form below works exactly as before without it.
|
||||
const [fayda, setFayda] = useState<FaydaResult | null>(() => faydaSession.peekResult());
|
||||
const [faydaStarting, setFaydaStarting] = useState(false);
|
||||
const [authorizeTrigger] = useApiLazyQuery<{
|
||||
const [startTrigger] = useApiMutation<{
|
||||
authorizationUrl: string;
|
||||
state: string;
|
||||
transactionToken: string;
|
||||
expiresIn: number;
|
||||
}>();
|
||||
const [linkTrigger] = useApiMutation<void>();
|
||||
|
||||
const verified = (field: string) => fayda?.verifiedFields.includes(field) ?? false;
|
||||
const conflicted = (field: string) => fayda?.conflicts.includes(field) ?? false;
|
||||
@@ -174,7 +173,7 @@ export function SignupPage() {
|
||||
// are always the applicant's to choose, and Fayda supplies neither.
|
||||
useEffect(() => {
|
||||
if (!fayda) return;
|
||||
const { email, phoneNumber: phone, nameEn, nameAm } = fayda.prefill;
|
||||
const { email, phoneNumber: phone, nameEn, nameAm } = fayda.identity;
|
||||
if (email) setValue('email', email);
|
||||
if (phone) setValue('phoneNumber', phone);
|
||||
if (nameEn) setValue('nameEn', nameEn);
|
||||
@@ -185,8 +184,12 @@ export function SignupPage() {
|
||||
setServerError(null);
|
||||
setFaydaStarting(true);
|
||||
try {
|
||||
const { authorizationUrl, transactionToken, state } = await authorizeTrigger({
|
||||
url: '/auth/fayda/authorize',
|
||||
// Same endpoint the registration itself uses; `start` only opens the
|
||||
// attempt and hands back where to send the user.
|
||||
const { authorizationUrl, transactionToken, state } = await startTrigger({
|
||||
url: '/auth/register-with-fayda',
|
||||
method: 'POST',
|
||||
body: { action: 'start' },
|
||||
}).unwrap();
|
||||
|
||||
faydaSession.saveRequest({ transactionToken, state });
|
||||
@@ -231,23 +234,7 @@ export function SignupPage() {
|
||||
const me = await meTrigger({ url: '/auth/me', method: 'GET' }).unwrap();
|
||||
dispatch(setUser(me));
|
||||
|
||||
// Records the Fayda identity on the account that was just created. The
|
||||
// registration endpoint is shared platform code and drops fields it does
|
||||
// not know, so the link has to be a separate call. It is best-effort: the
|
||||
// account is already usable, and the worst case is that it is not marked
|
||||
// as Fayda-verified.
|
||||
if (fayda) {
|
||||
try {
|
||||
await linkTrigger({
|
||||
url: '/auth/fayda/link',
|
||||
method: 'POST',
|
||||
body: { verificationToken: fayda.verificationToken },
|
||||
}).unwrap();
|
||||
} catch {
|
||||
/* deliberately ignored — signup already succeeded */
|
||||
}
|
||||
faydaSession.clearResult();
|
||||
}
|
||||
faydaSession.clearResult();
|
||||
|
||||
if (data.isPhoneNumberVerified) {
|
||||
navigate(loginRedirectPath);
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* survive it lives in sessionStorage: same tab, same origin, gone when the tab
|
||||
* closes.
|
||||
*
|
||||
* Nothing secret is kept here. `transactionToken` and `verificationToken` are
|
||||
* signed by the API and are useless without it — the PKCE verifier and the
|
||||
* client secret never leave the backend.
|
||||
* Nothing secret is kept here. The `transactionToken` is signed by the API and
|
||||
* useless without it — the PKCE verifier, the nonce and the client key never
|
||||
* leave the backend.
|
||||
*/
|
||||
|
||||
const REQUEST_KEY = 'fayda:request';
|
||||
@@ -26,16 +26,14 @@ export interface FaydaPrefill {
|
||||
address?: string;
|
||||
}
|
||||
|
||||
/** Shape of `POST /auth/register-with-fayda` with `action: "verify"`. */
|
||||
export interface FaydaResult {
|
||||
prefill: FaydaPrefill;
|
||||
/** Always true when the API returned a result at all. */
|
||||
identity: FaydaPrefill;
|
||||
faydaVerified: boolean;
|
||||
/** Signup fields Fayda vouched for. */
|
||||
verifiedFields: string[];
|
||||
/** Prefilled fields already taken by another account. */
|
||||
conflicts: string[];
|
||||
/** Posted to /auth/fayda/link once the account exists. */
|
||||
verificationToken: string;
|
||||
}
|
||||
|
||||
// Private browsing and locked-down browsers can throw on access, and a failure
|
||||
|
||||
Reference in New Issue
Block a user