Skip to content
AXOWLDevelopers
Quickstart

Account switching

When one person controls multiple email-based accounts (e.g. a personal account and a company-email account from corporate onboarding), they can link them and switch without signing in again. Base path: /api/auth/linked-identities.

Linking (proof of ownership on both sides)

Section titled “Linking (proof of ownership on both sides)”
Method · RoutePurpose
POST /initiateSigned-in user A sends { "email": "<B>" }; Axowl emails B a single-use confirm link (valid 15 minutes). An address with no Axowl account gets the same 200 answer and no email, so the response does not reveal whether an account exists. One request per minute per caller and address — a second one answers 400 rate_limited. Other errors: 400 self_link, 400 already_linked.
GET /confirm?token=B opens the emailed link and sees a confirmation page with a Link accounts button. Opening the link changes nothing and does not use up the token.
POST /confirmSent by the button on that page with { "token": "…" }: creates the sealed link (A↔B) and uses up the token. The token is the proof; no session is needed. Returns { message, redirectUrl }. Errors (400): invalid_token, expired_token, account_gone, already_linked.
GET /List the current user’s active links (linkId, userId, email, displayName, linkedAt) — for an account-switcher menu.
POST /{id}/revokeUnlink. Either side can revoke; the link record is marked revoked and kept, never hard-deleted.

/initiate and /{id}/revoke only work from a sealed device.

POST /switch re-issues the session as the linked identity and swaps the cookie — gated by a fresh passkey step-up of the current session owner. Body: { "targetUserId", "assertionResponseJson" }, where assertionResponseJson is the result of a fresh navigator.credentials.get() for the current account.

  • targetUserId must be a valid id (400 invalid_target) and a link must already exist between caller and target, else 404 not_linked.
  • A passkey assertion is required (403 step_up_required if absent).
  • The assertion must verify and belong to the caller’s own account, so a stolen cookie + an attacker’s own passkey cannot pivot (403 step_up_failed).
  • On success, the response sets the session cookie of the target account and returns its userId, email and organizationId.

Linking requires explicit proof of ownership of both accounts — accounts are never linked automatically.