feat: trigger the deep-link login handoff via flow=deeplink and a UUID request id#430
Conversation
…D request id The `/auth/requests/:requestId` deep-link login handoff (post the signed identity, then open the client via `open?signin=<identityId>`, skipping the recover/verify flow) is now keyed on the `?flow=deeplink` query param (compared case-insensitively) instead of the `client-login` magic request id. The route id must be a valid UUID v4 — the client-generated id that correlates the login with the instance that requested it — and is forwarded to the client as the deep link's `authRequestId`. A `flow=deeplink` request whose id is not a valid UUID v4 is rejected up front with the error view (no identity post, no recover). This removes the need to retrieve an id from, or request, the auth-server when the flag is set. The `client-login` pseudo request id and the recover-based deep-link sign-in variant (VerifySignIn deep-link copy, DEEP_LINK_SIGN_IN tracking, the now-unused `recover` isDeepLinkFlow argument) are removed, along with their dead translation keys. - Add `isDeepLinkFlowEnabled` (case-insensitive) and `isValidUuidV4` to shared/locations; drop `CLIENT_LOGIN_REQUEST_ID`. - RequestPage keys the handoff off `isDeepLinkFlow`, guards a malformed id with an explanatory error, and forwards the route UUID as the signin authRequestId. - Drop the unreachable `isDeepLinkFlow` param from the auth clients' `recover`. - Update unit and e2e specs to the new contract.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- A UUID v4 id without flow=deeplink goes through the normal recover flow (the handoff is gated on the flag, not the id shape) and posts no identity. - A malformed deep-link id skips the profile consistency check. - isDeepLinkFlowEnabled: a bare `?flow` flag (no value) is not the deep-link flow. - isValidUuidV4: reject surrounding whitespace and a trailing newline (guards the regex anchor, since the id is forwarded to the native client).
decentraland-bot
left a comment
There was a problem hiding this comment.
Review — PR #430
feat: trigger the deep-link login handoff via flow=deeplink and a UUID request id
Clean, well-structured PR. The migration from the magic client-login request ID to ?flow=deeplink + UUID v4 validation is correct and thoroughly tested. No security, correctness, or architectural concerns.
What was checked
- UUID v4 regex (
locations.ts): correctly anchored (^/$, no multiline flag), validates version digit4and variant bits[89ab], case-insensitive. No bypass vectors (trailing newline, whitespace, wrong version/variant all rejected — confirmed by tests). - Deep link injection surface: the validated UUID (only
[0-9a-fA-F-]chars) is passed throughURLSearchParamsingetSigninDeeplink, which encodes values. No injection risk. - Flow gating (
RequestPage.tsx):isInvalidDeepLinkIdcorrectly short-circuits both effects before any profile work or identity posting. TheloadRequest()path is only reached whenisDeepLinkFlowis false, so removing the old&& !isDeepLinkFlowguard on theskipSetupcheck is safe. recover()param removal (httpClient.ts,wsClient.ts): clean parallel change, no callers still pass the removed param.- Dead code removal:
CLIENT_LOGIN_REQUEST_ID,DEEP_LINK_SIGN_INanalytics type,isDeepLinkFlowprop onVerifySignIn, and translation keys across 5 locales — all confirmed zero remaining references. - Consumer impact:
CLIENT_LOGIN_REQUEST_IDand theclient-loginstring have no external consumers across thedecentralandorg. Safe to remove. - Git conventions: title (
feat: ...) and branch (feat/deep-link-login-by-uuid) follow semantic commit / ADR-6. - CI: all checks passing (tests, e2e, audit, CodeQL, lint).
Findings
[P2] RequestPage.spec.tsx:93 — buildRequestPageUrl mock discards options
The mock (requestId, targetConfigId) => ... ignores the third options argument, so tests cannot verify that flow=deeplink, bridgeOnly, and authRequestId are correctly preserved through login round-trip redirects. Not introduced by this PR — pre-existing.
[P2] locations.spec.ts — UUID variant coverage
The valid-UUID test only exercises variant bit a (a456). Adding a case with variant 8, 9, or b would confirm the regex accepts all valid variants.
[P2] Pre-existing — loginMethodParam missing from Effect 2 deps (RequestPage.tsx:~807)
loginMethodParam is read inside Effect 2 but absent from its dependency array. Safe in practice (derived from searchParams) but would trigger the exhaustive-deps lint rule. Not introduced by this PR.
Verdict
Approved — no P0 or P1 findings. The three P2s are pre-existing or minor test-coverage suggestions.
Reviewed by Jarvis 🤖 · Requested by Lautaro Petaccio (<@U5RLBKGB3>) via Slack
Thread the route UUID (the client's correlation id) onto the deep-link handoff's analytics so a login can be tied to the instance that requested it in Segment, and add a failure event for the paths that previously emitted nothing. - postIdentity accepts an `authRequestId` and includes it on the DEEP_LINK_AUTH_SUCCESS event; the desktop handoff passes the route UUID. - IDENTITY_DEEP_LINK_OPENED carries the authRequestId when the deep link opens. - New DEEP_LINK_AUTH_FAILED event fires on both failure paths: a malformed deep-link id (reason `invalid_request_id`) and a failed identity POST (reason `post_identity_failed`), each carrying the received id. - Extend TrackingData with `authRequestId` / `reason`. Adds unit coverage for the forwarded id (RequestPage + httpClient) and both failure events.
Summary
Reworks the
/auth/requests/:requestIddeep-link login handoff so the Explorer team can correlate a login with the instance that requested it.Previously the handoff (post the signed identity → open the client via
decentraland://open?signin=<identityId>, skipping the recover/verify flow) was triggered by a magic request id,client-login. Now it is triggered by the?flow=deeplinkquery param (compared case-insensitively), and the routerequestIdmust be a valid UUID v4 — the client-generated correlation id — which is forwarded to the client as the deep link'sauthRequestId.When
flow=deeplinkis set there is no longer any need to retrieve an id from, or make a request to, the auth-server.Behavior
?flow=deeplink+ valid UUID v4 id → post identity, open the client, forward the UUID asauthRequestId. Never callsrecover.?flow=deeplink+ non-UUID id → rejected up front with the error view ("The sign-in link is invalid."), no identity post, no recover.flowparam → unchanged normal request flow (recover/verify/transaction).Removed
client-loginpseudo request id (CLIENT_LOGIN_REQUEST_ID).VerifySignIndeep-link copy, theDEEP_LINK_SIGN_INanalytics type, and the now-unreachableisDeepLinkFlowargument of the auth clients'recover().request.deep_link_confirm/request.sign_intranslation keys (all 5 locales).login.sign_inis untouched.Implementation
shared/locations.ts: newisDeepLinkFlowEnabled(case-insensitive) andisValidUuidV4helpers;buildRequestPageUrlreuses the shared param constants.RequestPage.tsx: keys the handoff offisDeepLinkFlow; guards a malformed id in both effects; forwards the route UUID as the signinauthRequestId.httpClient.ts/wsClient.ts: drop the unusedrecoverparam (analytics for the recover path is now alwaysVERIFY_SIGN_IN).Testing
RequestPage.spec.tsxandlocations.spec.tsupdated to the new contract (case-insensitive flow, UUID validation, route-UUID-as-authRequestId, invalid-id error). Full suite: 788 passing.request-edge-cases.spec.ts, real browser): valid-UUID handoff (posts identity, no recover) and invalid-UUID error view, plus the untouched different-account / expired / fulfilled cases.tscand eslint clean.🤖 Generated with Claude Code