Skip to content

feat: trigger the deep-link login handoff via flow=deeplink and a UUID request id#430

Merged
LautaroPetaccio merged 3 commits into
mainfrom
feat/deep-link-login-by-uuid
Jul 13, 2026
Merged

feat: trigger the deep-link login handoff via flow=deeplink and a UUID request id#430
LautaroPetaccio merged 3 commits into
mainfrom
feat/deep-link-login-by-uuid

Conversation

@LautaroPetaccio

Copy link
Copy Markdown
Collaborator

Summary

Reworks the /auth/requests/:requestId deep-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=deeplink query param (compared case-insensitively), and the route requestId must be a valid UUID v4 — the client-generated correlation id — which is forwarded to the client as the deep link's authRequestId.

When flow=deeplink is 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 as authRequestId. Never calls recover.
  • ?flow=deeplink + non-UUID id → rejected up front with the error view ("The sign-in link is invalid."), no identity post, no recover.
  • No flow param → unchanged normal request flow (recover/verify/transaction).

Removed

  • The client-login pseudo request id (CLIENT_LOGIN_REQUEST_ID).
  • The recover-based deep-link sign-in variant: the VerifySignIn deep-link copy, the DEEP_LINK_SIGN_IN analytics type, and the now-unreachable isDeepLinkFlow argument of the auth clients' recover().
  • The dead request.deep_link_confirm / request.sign_in translation keys (all 5 locales). login.sign_in is untouched.

Implementation

  • shared/locations.ts: new isDeepLinkFlowEnabled (case-insensitive) and isValidUuidV4 helpers; buildRequestPageUrl reuses the shared param constants.
  • RequestPage.tsx: keys the handoff off isDeepLinkFlow; guards a malformed id in both effects; forwards the route UUID as the signin authRequestId.
  • httpClient.ts / wsClient.ts: drop the unused recover param (analytics for the recover path is now always VERIFY_SIGN_IN).

Testing

  • Unit: RequestPage.spec.tsx and locations.spec.ts updated to the new contract (case-insensitive flow, UUID validation, route-UUID-as-authRequestId, invalid-id error). Full suite: 788 passing.
  • E2E (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.
  • tsc and eslint clean.

🤖 Generated with Claude Code

…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.
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
auth Ready Ready Preview, Comment Jul 13, 2026 4:59pm

Request Review

- 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 decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 digit 4 and 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 through URLSearchParams in getSigninDeeplink, which encodes values. No injection risk.
  • Flow gating (RequestPage.tsx): isInvalidDeepLinkId correctly short-circuits both effects before any profile work or identity posting. The loadRequest() path is only reached when isDeepLinkFlow is false, so removing the old && !isDeepLinkFlow guard on the skipSetup check 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_IN analytics type, isDeepLinkFlow prop on VerifySignIn, and translation keys across 5 locales — all confirmed zero remaining references.
  • Consumer impact: CLIENT_LOGIN_REQUEST_ID and the client-login string have no external consumers across the decentraland org. 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:93buildRequestPageUrl 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.
@LautaroPetaccio LautaroPetaccio merged commit a168e3f into main Jul 13, 2026
13 checks passed
@LautaroPetaccio LautaroPetaccio deleted the feat/deep-link-login-by-uuid branch July 13, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants