diff --git a/libs/auth/src/lib/pages/FaydaCallbackPage.tsx b/libs/auth/src/lib/pages/FaydaCallbackPage.tsx index d3f9ac1d1..81314beb2 100644 --- a/libs/auth/src/lib/pages/FaydaCallbackPage.tsx +++ b/libs/auth/src/lib/pages/FaydaCallbackPage.tsx @@ -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) => { diff --git a/libs/auth/src/lib/pages/SignupPage.tsx b/libs/auth/src/lib/pages/SignupPage.tsx index 7045d9777..29b03e6c8 100644 --- a/libs/auth/src/lib/pages/SignupPage.tsx +++ b/libs/auth/src/lib/pages/SignupPage.tsx @@ -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(() => faydaSession.peekResult()); const [faydaStarting, setFaydaStarting] = useState(false); - const [authorizeTrigger] = useApiLazyQuery<{ + const [startTrigger] = useApiMutation<{ authorizationUrl: string; state: string; transactionToken: string; expiresIn: number; }>(); - const [linkTrigger] = useApiMutation(); 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); diff --git a/libs/auth/src/lib/utils/fayda-session.ts b/libs/auth/src/lib/utils/fayda-session.ts index 2319e67fc..d61db63c1 100644 --- a/libs/auth/src/lib/utils/fayda-session.ts +++ b/libs/auth/src/lib/utils/fayda-session.ts @@ -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