Process Flows
This page documents the complex multi-step authentication and routing flows that span multiple handlers and external services. Each section includes a diagram, a summary, and a numbered step-by-step breakdown referencing the source handlers.
See Auth Worker for the full handler reference, and Links Worker for deeplink infrastructure detail.
Username / Password Login
The standard mobile login flow.
Castle provides a pre-login fraud check before credentials are forwarded to Auth0.
If Auth0 determines MFA is required the response carries an mfa_token so the client can continue with the MFA enrollment or OOB challenge flow.
Steps
-
Client sends credentials — the mobile app POSTs
{ email, password }plus the Castle device fingerprint (x-castle-request-token) toPOST /oauth/tokenonauth.floatme.io. Handler:login.ts. -
Pre-login Castle filter —
login.tscallsPOST /v1/filteron Castle with the device fingerprint and IP. Castle returns apolicy.action:-
allow— proceed;challengeRequiredset tofalsein the Auth0 payload. -
challenge— proceed;challengeRequiredset totrueso Auth0 will enforce MFA. -
deny— ifENABLE_CASTLE_BLOCKING=true(production), the handler returns401 { error: "unauthorized" }immediately and the Auth0 call is skipped.
-
-
Auth0 password-realm grant — the worker forwards the credentials plus
client_id,client_secret,audience,realm,scope, and the client IP viaauth0-forwarded-fortoPOST /oauth/tokenon Auth0. -
MFA branch — if Auth0 returns
mfa_required, the error is passed through transparently to the client (403with anmfa_token). The client must then call either MFA Enrollment (first-time) or the OOB challenge sub-flow (returning user). -
Successful login — on a successful Auth0 response the worker calls
POST /v1/riskon Castle (authenticated risk scoring including the user ID). The full token set (access_token,refresh_token,id_token) is returned to the client with200 OK.
In the test environment, the QA bypass (qa-helper.ts) intercepts logins with integrationtest.com / integrationtest.floatme.io emails that contain a +state suffix and provisions the user on demand before forwarding to Auth0.
|
MFA Enrollment
The MFA enrollment flow covers two scenarios: first-time enrollment (user has never set up MFA) and the challenge/submit cycle (user already has an authenticator and needs to prove identity via OOB code).
Both paths rely on a short-lived MFA token scoped exclusively to enroll read:authenticators.
Steps
-
Acquire an MFA token — the client calls
POST /mfa/tokenwith the user’s email and password. Handler:get-mfa-token.ts. The worker performs a password-realm grant against Auth0 using the MFA audience (AUTH0_MFA_AUDIENCE). Auth0 returns a short-livedmfa_tokenscoped toenroll read:authenticatorsonly. -
First enrollment — the client calls
POST /mfa/first-enroll(or the aliasPOST /mfa/associate) with{ email, oob_channel, mfa_token }. Handler:enroll-first-mfa-authenticator.ts. +-
For
oob_channel=sms(default): the worker fetches the user’s canonical phone number from the user-service (GET /user/phone?email=…) using IAM-signed requests. The client cannot supply an arbitrary number; the phone number comes from the user’s profile. Auth0’sPOST /mfa/associateis called withoob_channels=["sms"]and the fetched phone number. Response:{ oob_code, masked_phone_number }(last 4 digits only). -
For
oob_channel=email: the worker callsPOST /mfa/associatewithoob_channels=["email"]. Response:{ oob_code, masked_email }(masked viahelpers/email.ts). + Auth0 delivers the OOB code to the user via SMS or email.
-
-
List authenticators (optional) — the client may call
GET /mfa/authenticatorswith the MFA token in theAuthorizationheader. Handler:get-mfa-authenticators.ts. The worker proxies toGET /mfa/authenticatorson Auth0, then filters the response to OOB authenticators only and returns a normalized array of{ id, name, active, oob_channel }. -
Challenge — the client calls
POST /mfa/challengewith{ authenticator_id, mfa_token }. Handler:challenge-mfa-oob-authenticator.ts. The worker forwards toPOST /mfa/challengeon Auth0 withchallenge_type=oob, injectingclient_idandclient_secret. Auth0 delivers a new OOB code to the registered channel. Response:{ oob_code }. + NOTE: Auth0 rate-limits OOB delivery. The errorToo many SMS sent by the useris normalized to{ error: "too_many_sms" }. -
Submit challenge — the client calls
POST /mfa/confirmwith{ oob_code, binding_code, mfa_token }. Handler:submit-mfa-oob-challenge.ts. The worker forwards toPOST /oauth/tokenwithgrant_type=http://auth0.com/oauth/grant-type/mfa-oob. On success, a post-challenge Castle risk call (POST /v1/risk) is made with the authenticated user context. If Castle blocks the request,401 { error: "unauthorized" }is returned. Otherwise the full token set (access_token,refresh_token,id_token) is returned to the client. + NOTE: If Auth0 omitsrefresh_tokenin the response (edge case), the worker substitutes a placeholder value to prevent null-reference errors in downstream clients. This is a known bug — the handler should surface a hard failure rather than returning an invalid token.
Signup
The signup flow creates a new user in Auth0 and returns a minimal profile payload. Email verification and FloatMe account provisioning (user-service) are handled separately after signup completes. Castle provides both pre-signup and post-signup risk scoring.
Steps
-
Client sends signup data — the mobile app POSTs
{ email, password, username, given_name, family_name }plus the Castle device fingerprint toPOST /signup. Handler:signup.ts. -
Pre-signup Castle filter — the worker calls
POST /v1/filteron Castle with the email, username, phone (in E.164 format), and device fingerprint. If Castle returnsdenyandENABLE_CASTLE_BLOCKING=true, the handler returns401 { error: "unauthorized", message: "blocked by security policy" }immediately. -
Auth0 user creation — the worker calls
POST /dbconnections/signupon Auth0 with the user data.verify_emailis always set tofalse; Auth0 will not send a verification email automatically. Verification email delivery is triggered separately by the client viaPOST /email/verify. + On success, Auth0 returns{ _id, email }. + On failure, the error is normalized:user_existsbecomes{ error: "invalid_signup" }(400); other coded errors pass through with their original status. -
Post-signup Castle risk — after successful Auth0 user creation the worker calls
POST /v1/riskon Castle, now including the Auth0-assigned user ID and the full address fields from the signup request. If Castle blocks post-signup (and blocking is enabled),401 { error: "unauthorized", message: "blocked by security policy" }is returned even though the Auth0 record was already created. + WARNING: This leaves an orphaned Auth0 user record. There is currently no automatic cleanup for these orphaned records. A user hitting this path will encounter a "user already exists" error on retry. Resolution requires manual Auth0 user deletion or support intervention. This is a known gap; a recommended fix is to move the Castle check before Auth0 user creation, or add a compensating delete on Castle block. -
Response to client — on success,
200 { id, email }is returned. The mobile app then drives email verification (POST /email/verify) and user-service account provisioning as subsequent steps in the onboarding funnel.
Social Login
The social login flow uses Auth0’s OAuth2 authorization code grant with a Google connection.
The auth worker handles both the initial redirect and the OAuth callback, then issues a custom-scheme deeplink so the mobile app can complete sign-in.
The signup flag in the deeplink tells the app whether to show an account-creation flow.
Steps
-
Initiate social signin — the mobile app calls
GET /social/signin. Handler:social-signin.ts. The worker builds an Auth0/authorizeURL withconnection=google-oauth2, fullscope(openid email profile offline_access), the configuredaudience, and aredirect_uripointing back to/social/completeon the same worker domain (prod or test). The response is a302redirect to that Auth0 URL; the mobile browser or in-app browser follows it. -
Google authentication — Auth0 redirects the browser to Google’s OAuth2 consent screen. The user authenticates with their Google account. Google returns an authorization code to Auth0’s callback, and Auth0 then redirects to the worker’s
/social/completeendpoint with acodequery parameter. -
Callback token exchange — Auth0 redirects to
GET /social/complete?code=…. Handler:social-callback.ts. The worker extracts thecodefrom the query string and callsPOST /oauth/tokenon Auth0 withgrant_type=authorization_code, injectingclient_id,client_secret, and the sameredirect_uri. Auth0 returns{ access_token, refresh_token, id_token }. -
ID token validation — the worker validates the
id_tokenagainst Auth0’s JWKs (viaservices/auth0/token-validator.ts). If validation fails, the handler returns401 Unauthorized. Theemailclaim is extracted from the validated token. -
User existence check — using the extracted email, the worker calls
POST /users-by-emailon the user-service (IAM-signed). A404response means the FloatMe account does not yet exist; the worker setssignup=truein the redirect parameters. A200response means the account exists;signup=false. -
Deeplink redirect — the worker issues a
302redirect tofloatme://links.floatme.io/social?access_token=…&refresh_token=…&id_token=…&signup=true|false. The mobile OS intercepts this custom-scheme URL and opens the FloatMe app, which consumes the tokens and (ifsignup=true) continues through account setup. + WARNING: Passing tokens as URL query parameters is a known security risk — tokens can appear in URL logs, browser history, and referrer headers. The recommended migration is to Authorization Code + PKCE, where the app exchanges a short-lived code for tokens via a secure backend endpoint rather than receiving them directly in the URL.
Deeplink Routing
links.floatme.io is the Universal Link (iOS) and App Link (Android) domain for the FloatMe mobile app.
The links worker’s primary job is to serve the .well-known verification files that allow the OS to associate this domain with the app.
The actual deeplink navigation is OS-handled and does not pass through the worker at runtime.
Steps
-
Domain verification (one-time) — when the FloatMe app is installed (or periodically refreshed), iOS downloads
/.well-known/apple-app-site-associationand Android downloads/.well-known/assetlinks.jsonfromlinks.floatme.io. Handler:index.ts→wellknown.ts. The worker serves the correct static payload for the environment (WORKER_ENV=prodortest). + The AASA declaresapplinksfor path patterns/link/*and/login/\*, pluswebcredentialsfor AutoFill. Theassetlinks.jsondeclaresdelegate_permission/common.handle_all_urlsfor the same domain. + Once verified, the OS registerslinks.floatme.ioas a trusted domain for the app. -
User taps a deeplink — a user taps a URL of the form
https://links.floatme.io/link/...orhttps://links.floatme.io/login/...(for example, in an email, SMS, or shared referral link). -
OS intercepts navigation — because the domain was previously verified, the OS intercepts the tap before making any network request to the worker. If the FloatMe app is installed, the OS opens it directly and passes the full URL. No HTTP request reaches the links worker during normal operation.
-
App-not-installed fallback — if the app is not installed, the OS allows the browser to navigate to the URL. The links worker returns
200 OKwith an empty body for all non-.well-knownpaths, so the browser renders a blank page. + NOTE: A future improvement noted in the links worker would redirect unarmed deeplinks (app not installed) to the App Store or Play Store. -
Social callback deeplink — the social login flow (see Social Login) issues a redirect to
floatme://links.floatme.io/social?…. This uses thefloatme://custom scheme directly, not thehttps://Universal Link path. Custom scheme URLs bypass the.well-knownmechanism; the OS routes them directly to the registered app handler.
See Also
-
Auth Worker — full handler reference with Castle, Auth0, and user-service detail
-
Links Worker —
.well-knownhosting, route table, and environment configuration -
Auth0 Actions — post-login MFA enforcement and IP metadata actions
-
Feature Summary — capability overview with links to this page