Skip to content

[Epic] React Native mobile template #40

Closed

Description

@Bccorb

Tracking issue for shipping a React Native mobile template, and the work it needs across
the other repositories.

What the spike established

A working Expo spike drove the local auth API directly and answered the two questions
that were blocking this. Both came back yes, and neither needed a change to
seamless-auth-api
.

  1. A native client can authenticate with bearer tokens only. Registration returns a
    typ=ephemeral JWT that carries the multi step flow; completing it returns an access
    JWT plus an opaque refresh token. No endpoint issued a Set-Cookie at any point.
    POST /refresh takes the opaque token and rotates both, and replaying a spent token
    returns 401 refresh_token_reused and revokes the whole session chain.
  2. Native passkeys pass the API's WebAuthn verification. Registration and login both
    completed on the iOS 26.5 simulator and on an API 36 Google Play emulator, and were
    accepted by verifyRegistrationResponse and verifyAuthenticationResponse. The
    ORIGINS list is not URL validated, so the android:apk-key-hash:<base64url> origin
    format passes through unchanged.

The cookie machinery in @seamless-auth/core turns out to be a browser adapter wrapped
around tokens the API was already issuing, not part of the protocol.

Effect on the estimate

The pre spike estimate assumed a bearer transport had to be built in the server SDK, at
two to four weeks of security critical work. That work already exists. Revised total is
roughly 7 to 11 engineer weeks, of which only about 1 to 1.5 land in this repository.
The auth view rebuild is now the critical path and the least reducible part.

The prerequisite that is not code

Browser WebAuthn treats localhost as a secure context, which is why the web templates
work the moment seamless init finishes. Native passkey APIs have no such exemption on
either platform. Before a passkey works at all, including on a simulator, an adopter must
publish an association file over HTTPS on a domain they control.

  • iOS additionally needs an Apple Developer team (a free Personal Team is enough), a
    bundle identifier, and an associatedDomains entitlement.
  • Android additionally needs a Google account signed in on the device and a device screen
    lock.

The template can shrink this to a checklist. It cannot remove it. Tracked in
#39.

Child issues

Template and packaging, this repository:

Client SDK, seamless-auth-react. Packaging as a second package in that repository was
chosen so the framework agnostic client core can be shared rather than duplicated, which
is what fells-code/seamless-auth-js#64 already proposes:

Server SDK:

Auth API:

CLI:

Suggested order

  1. feat(guards): accept bearer tokens in requireAuth and getSeamlessUser seamless-auth-server#147 and Dev compose hardcodes RPID and system config re-seeds on boot seamless-auth-api#142 first. Both
    are small, and the second removes a persistent source of confusion for anyone else
    working on passkeys locally.
  2. Stand up a durable relying party domain before further passkey work. The spike used an
    ephemeral tunnel, which changes hostname on restart and invalidates every passkey
    registered against the old one.
  3. Start feat(mobile): rebuild the auth views on React Native primitives seamless-auth-js#126 early, since it is the critical path.
  4. Registry, CLI, and template work can proceed in parallel once the kind is agreed.

Still uncertain

Activity

  1. added
    enhancementNew feature or request
    epicLarge tracking issue spanning multiple tasks
    on Aug 1, 2026
  2. Bccorb commented on Sep 13, 2026

    @Bccorb
    ContributorAuthor

    Status as of 2026-09-13: every piece of the plan is open as a PR, and the pilot app exists end to end. The design changed in one respect from the spike: the mobile client reaches auth through the server adapter in a new bearer transport mode (header x-seamless-auth-transport: bearer, plus POST /auth/refresh) rather than the auth API directly, because a real deployment (RoxTarget) keeps its auth server private and owns email delivery in the adapter.

    Template and packaging, this repository:

    Client SDK, seamless-auth-react:

    Server SDK:

    Auth API and types:

    CLI:

    Docs: fells-code/seamless-auth-docs#43 (React Native SDK and Mobile Passkeys pages, plus the server SDK, secure routes, React SDK, ecosystem, system config and CLI updates).

    Pilot: fells-code/roxtarget-mobile #1 to #6 (stacked drafts) carry every screen of the web app on the new SDK, verified on the iOS simulator against the local stack for sign-in, sign-up, magic links via the URL scheme, onboarding, logging, and every tab. Native passkeys remain to be exercised on the staging domain once fells-code/roxtarget-web#1 (association files) is deployed.

    Release order once merged: server, react monorepo, types, api, templates (tag), cli (REF bump), then the RoxTarget repos switch their lockfiles to the published versions and leave draft.

    Still uncertain, unchanged: PRF on react-native-passkeys is shipped flagged experimental and unexercised.

  3. Bccorb commented on Sep 13, 2026

    @Bccorb
    ContributorAuthor

    Closing status as of 2026-09-13: every PR in the plan is merged, and every package it produced is published except the last CLI release, which is one Version Packages merge away.

    Released:

    Pilot: fells-code/roxtarget-api#2 (bearer guards and /auth in bearer mode), fells-code/roxtarget-web#1 (association files under /.well-known/), and fells-code/roxtarget-mobile#1 to #6 are merged. The app carries every screen of the web app on the new SDK and was driven on the iOS simulator against the local stack: sign-up and sign-in with an email code, the magic link (polled, and completed through the app's URL scheme standing in for the universal link), onboarding, logging, every tab, and the sessions list.

    Verified since the last status: seamless init --yes --mobile=expo against templates v0.15.0 scaffolds mobile/ on @seamless-auth/react-native 0.1.0 next to an API starter on @seamless-auth/express 0.16.0, with EXPO_PUBLIC_API_URL set and services.mobile recorded. The bearer contract through the adapter holds end to end: login, OTP verify, users/me, refresh rotation, and a replayed refresh token answered with 401 refresh_token_reused and the chain revoked. The published @seamless-auth/client bundle carries the two fixes found on the simulator (a Headers instance is flattened before it reaches Expo's fetch, and authorizedFetch sends the transport header for URLs under the adapter mount).

    What remains, none of it a code change in this repository:

    • Native passkeys on a device. The spike established the format; this effort has not yet served the template's generated association files from a real domain. It needs RoxTarget's staging deploy of fells-code/roxtarget-web#1, the auth server's RPID and ORIGINS set for that domain, and then the register / sign-in / step-up delete checks from the plan.
    • feat(mobile): rebuild the auth views on React Native primitives seamless-auth-js#126, prebuilt views on React Native primitives. The RN package is headless; RoxTarget's screens are its own, as the template's are.
    • PRF on react-native-passkeys is shipped flagged experimental and still unexercised.
    • Session lifetime stays tenant-wide. RoxTarget sets its own TTLs; a per-client profile is a later decision.

    With fells-code/seamless-cli#207 merged and the open children closed against their PRs (#37, #38, #39; fells-code/seamless-auth-js#64, #124, #125, #127, #128), what is left of this epic is #126 and the device check above.

  4. Bccorb commented on Oct 6, 2026

    @Bccorb
    ContributorAuthor

    Closing. Every child in this repository and the server, API, CLI, and client repositories is merged and released, and the Expo starter shipped in v0.15.0.

    The two items left open in the last status are settled:

    • Native passkeys on a real device are confirmed. The mobile app runs on a physical phone against a real domain serving the association files, so the device check from the plan is done.
    • feat(mobile): rebuild the auth views on React Native primitives seamless-auth-js#126 (prebuilt auth views on React Native primitives) is split off and tracked on its own. The React Native package stays headless and the starter keeps its own screens, so it does not block this epic.

    Still open and unchanged from the last status: PRF on react-native-passkeys is flagged experimental and unexercised, and session lifetime stays tenant-wide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestepicLarge tracking issue spanning multiple tasks

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions