Files
TREK/shared/src/native-auth/native-auth.schema.ts
T
Maurice 9832d12751 feat(auth): hand a browser sign-in over to the native app
The native apps show the server's own UI in a WebView, where neither SSO nor
passkeys can run. They sign in through the system browser instead and need a
way to carry that session back.

The app keeps a random verifier and sends only its sha256 into the browser.
Once signed in there, the browser trades that challenge for a one-time code
(cookie session only, so an API or MCP token cannot mint itself a browser
session), and the WebView redeems the code together with the verifier. The
code rides through a URL scheme any app could claim, so on its own it is
worthless; it is single use and good for a minute.

Also: /api/health/features reports nativeApp so the apps can refuse servers
that predate this, and hashed files under assets/ are served immutable,
which the iOS app needs to start offline without a service worker.
2026-09-24 12:19:10 +02:00

31 lines
1.2 KiB
TypeScript

import { z } from 'zod';
/**
* Sign-in hand-off for the native apps, /api/auth/native.
*
* The app shows the TREK web UI inside its own WebView, but SSO and passkeys
* cannot run there, so it signs in through the system browser instead and has
* to carry that session back. The app picks a random verifier and sends only
* its sha256 (the challenge) into the browser. The browser, once signed in,
* trades the challenge for a one-time code, and the WebView redeems the code
* together with the verifier. The code travels through a URL scheme any app
* could claim, so without the verifier it is worth nothing.
*/
const hexSha256 = z.string().regex(/^[0-9a-f]{64}$/);
export const nativeHandoffRequestSchema = z.object({
challenge: hexSha256,
});
export type NativeHandoffRequest = z.infer<typeof nativeHandoffRequestSchema>;
export const nativeHandoffResponseSchema = z.object({
code: z.string(),
});
export type NativeHandoffResponse = z.infer<typeof nativeHandoffResponseSchema>;
export const nativeExchangeRequestSchema = z.object({
code: z.string().min(1).max(128),
verifier: z.string().min(43).max(128),
});
export type NativeExchangeRequest = z.infer<typeof nativeExchangeRequestSchema>;