Skip to content

feat(auth): native-app sign-in handoff via one-time code - #66

Open
rigwig wants to merge 1 commit into
jetkvm:devfrom
rigwig:feat/native-app-auth
Open

feat(auth): native-app sign-in handoff via one-time code#66
rigwig wants to merge 1 commit into
jetkvm:devfrom
rigwig:feat/native-app-auth

Conversation

@rigwig

@rigwig rigwig commented Sep 5, 2026

Copy link
Copy Markdown

Related: #7, #46, jetkvm/kvm#650, jetkvm/kvm#520

Summary

Adds an opt-in way for native apps to sign in through the system browser instead of an embedded web view. Today the only way a native client can get a cloud session is to run the OIDC flow inside a WKWebView and scrape the cookie — which rules out passkeys (WebKit doesn't expose WebAuthn to embedded views for third-party origins like accounts.google.com) and is the embedded-OAuth pattern Google discourages. Disabled unless NATIVE_APP_SCHEMES is set, so hosted behaviour is unchanged until you opt in.

Flow

  1. The app opens ${APP_HOSTNAME}/login?returnTo=jetpilot://auth in the system browser (ASWebAuthenticationSession on iOS, Custom Tabs on Android). The existing login page already forwards returnTo into the form, so no frontend change is needed.
  2. After sign-in, /oidc/callback sees a returnTo on a registered scheme, redirects to jetpilot://auth?code=… with a single-use code (5-minute TTL, in-memory like activeConnections), and clears the browser session so nothing is left behind in Safari.
  3. The app calls POST /auth/exchange with { "code": "…" } and stores the session / session.sig cookies from the response. From there it's an ordinary session.

Native returnTo URLs are honoured only on the plain sign-in path; the device-adoption path (deviceId) is untouched.

Notes for review

Checklist

  • npm run build and npm test pass locally (8 new tests in test/native-auth.test.ts: scheme allowlist on/off, single-use codes, /auth/exchange success and failure modes)
  • Linked to issues above
  • One problem per PR
  • Tricky parts are commented in code; README and .env.example document the setting

Context: I maintain JetPilot, an iOS client for JetKVM. Users regularly ask why Google passkeys don't work in the in-app sign-in; this is the server half of fixing that.

Native clients can't read the session cookie out of the system browser, so
today an app has to embed a web view for the OIDC flow and scrape the cookie.
That rules out passkeys (WebKit doesn't expose WebAuthn to embedded views for
third-party origins) and is the embedded-OAuth pattern Google discourages.

Add an opt-in handoff, disabled unless NATIVE_APP_SCHEMES is set:

- a native app opens the normal login page with a returnTo on one of its
  registered URL schemes, e.g. /login?returnTo=jetpilot://auth;
- after sign-in, /oidc/callback redirects there with a single-use code
  (5 minutes, kept in memory like activeConnections) and clears the browser
  session so nothing is left behind in the system browser;
- POST /auth/exchange { code } establishes a regular session cookie.

Native returnTo URLs are only honoured on the plain sign-in path; the
device-adoption path (deviceId) is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Sep 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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.

Open redirect via returnTo

2 participants