Skip to content

Commit 55a4898

Browse files
feat(spec): declare the mobile sign-in hand-off surface (one-time-token hand-off and verify, registered app schemes) (#29804)
<!-- claude-projects-attribution: {"github_login":"os-warren"} --> _Requested by **Warren** · [project thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8PKyTDyCuaiBJ67xQB1FoTM)_ Fixes #29736 Clause-②: yes Metadata-protocol: added Skills-docs: none — no file under `skills/**` or the hand-written docs teaches `trustedOrigins`, the auth endpoint paths or a mobile sign-in, so nothing taught becomes false and there is no page the new key belongs on yet; the generated reference pages (`content/docs/references/api/auth-endpoints.mdx`, `system/auth-config.mdx`) carry it. ## 维护者速读 - 改了什么:在 `packages/spec` 声明移动端登录交接的契约:两条路由(`/one-time-token/handoff` 与 better-auth 自带的 `/one-time-token/verify`)及其请求/响应 schema,和新的可编写键 `AuthConfig.trustedAppSchemes`(允许回跳的原生 App scheme 列表)。 - 为什么改:部分身份提供方(如 Google)拒绝内嵌 WebView,移动端要走系统浏览器登录再把会话交回 WebView;契约先行,服务端卡 #29714 等本卡落地才转 ready。 - 风险与回滚:只加不减(新键可选、新导出),既有配置零变化;回滚即 revert 本 PR。最敏感处是 scheme 校验规则写严了(必须小写、含点、不带 `://`),错写的条目会被拒而不是被修正。 - 先看:`packages/spec/src/system/auth-config.zod.ts`(`TrustedAppSchemeSchema` 正则与 `trustedAppSchemes` 文档块)、`packages/spec/src/api/auth-endpoints.zod.ts`(交接 query 与 verify 绑定方式)、`.changeset/29736-mobile-sign-in-handoff-spec.md`。 - 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run src/system/auth-config.test.ts src/api/auth-endpoints.test.ts`,预期全部通过,其中 `myapp`、`https`、`com.example.app://` 三种写法都被拒。 - 需要你做的:无,席位落地。 ## What changes The spec half of the mobile sign-in hand-off. The server half is the #29714 card (draft until this lands). The shapes below were agreed with that card's thread before writing. They were built against better-auth 1.7.12, the version `plugin-auth` pins, and measured from its `dist/plugins/one-time-token`. - **`AuthEndpointPaths.oneTimeTokenHandoff`** = `/one-time-token/handoff` (GET, ObjectStack's own route) with **`OneTimeTokenHandoffQuerySchema`**. It is a strict, closed set: - `redirect_uri`; - `code_challenge`, an RFC 7636 S256 challenge (43 base64url characters); - `code_challenge_method`, optional, only `S256`. The docblock states the answers. On success, a `302` to `redirect_uri` with a `token` query parameter, `no-store`, valid for one minute. A `400 VALIDATION_ERROR` with no redirect for an unknown or repeated parameter, an unregistered scheme or a bad challenge. A `401` without a session. - **`AuthEndpointPaths.oneTimeTokenVerify`** = `/one-time-token/verify` (POST, better-auth's route name and body) with **`OneTimeTokenVerifyRequestSchema`** and **`OneTimeTokenVerifyResponseSchema`**. - The interception binding rides inside better-auth's own `token` field: the token, a dot, then the RFC 7636 verifier. The stock client keeps working, and a callback intercepted by another app cannot be redeemed without the verifier. The vendor token alphabet (`a-z A-Z 0-9 - _`, measured from `generateRandomString`) never contains a dot, so the server splits on the first one. - The response is better-auth's unwrapped `{ session, user }`, reusing `SessionSchema` / `SessionUserSchema` from `api/auth.zod.ts`. - better-auth's `GET /one-time-token/generate` is mounted but refuses client requests (`disableClientRequest`), so it is deliberately not declared. - **`AuthConfig.trustedAppSchemes`**: a new authorable key, an optional array of **`TrustedAppSchemeSchema`**. The rule is strict and never normalised: a lowercase RFC 3986 scheme written bare, with at least one dot. RFC 8252 section 7.1 requires a private-use scheme to be a reverse domain name. That requirement also rules out, by construction, every web scheme (`http`, `https`, `ws`, `wss`, `javascript`, `data`, `file`, `blob`, `about`, `vbscript`), because none of them contains a dot. A bad entry is refused, never dropped. The docblock states that a hand-off to an unregistered scheme is refused with `400` and no redirect. The server half refuses the same entries at boot, since `plugin-auth` receives plain objects. - **No `/auth/config` feature flag.** Nothing in the tree reads one. `PUBLIC_AUTH_FEATURES` precedent (passkeys withdrawn) says not to advertise ahead of a consumer, so `kernel/public-auth-features.ts` is untouched. - Regenerated: `api-surface/`, `export-origins/`, `declaration-map/`, `authorable-surface/`, `json-schema.manifest/`, the strictness-ledger counts and two reference pages. Three isomorphic alias pins were added (ADR-0122), 819 → 822. **Key-name precedent (the seat's call under the veto window):** `trustedAppSchemes` sits beside `trustedOrigins` on `AuthConfigSchema` and follows its `trusted…` + plural-noun form. It is a separate key on purpose. better-auth trusts an origin in `trustedOrigins` for every redirect it makes, while a scheme here is trusted only as the hand-off's destination. The query parameter names are the OAuth 2.0 / RFC 7636 wire names, kept verbatim. ## Why Some identity providers (Google among them) refuse embedded WebViews, so a mobile user signing in with one cannot sign in to the app today. The maintainer ruled option B: reuse better-auth's one-time-token plugin. Triage's first touch on the server card asks for the public surface to be declared in `packages/spec` first. ## Tests - `src/api/auth-endpoints.test.ts`, a new `mobile sign-in hand-off (one-time token)` block: - the two paths are declared and generate is not; - the hand-off query is accepted with and without the method; - an unknown parameter is refused as `unrecognized_keys`; - `plain`, a short challenge, a padded challenge and a missing challenge are refused at their path; - the verify body is accepted with the binding (including a verifier with dots and tildes) and refused without it or with a short verifier; - the vendor verify body parses. - `src/system/auth-config.test.ts`: reverse-domain schemes are accepted and the key is absent by default. A table refuses eight shapes at `['trustedAppSchemes', 1]`: `https`, `javascript`, `myapp`, `com.example.app://`, `com.example.app:`, `Com.Example.App`, `com..app` and `com.example.`. - `src/type-alias-convention.pin.test.ts`: three new `Iso_*` pins and the count. ## Acceptance notes - No ablation: the diff adds schemas and changes no tested behaviour. ## Verification All readings below were taken on head `b73cfb24d` (objectstack-ai/objectstack), after the final commit. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 102 families from the change set (19 paths at derivation, under the 3,000-line threshold). All 102 exit 0. `dispatch-gates --ran` reconciles: 102 run, 0 NOT MEASURED. - Also run, outside the derivation: `pnpm --filter @objectstack/spec test` (803 files, 23811 tests passed, 1 todo), `pnpm --filter @objectstack/spec typecheck` (exit 0) and `pnpm lint` (exit 0). - `pnpm --filter @objectstack/spec check:generated` exit 0: every artifact is current. - The first pass on `4b54dade` was red on `check:spec-parsed-alias` (the three new aliases needed `Iso_*` pins) and on `check:generated` (declaration map). Both were fixed in this PR. Four gates answered exit 3 (prerequisite: unbuilt packages, or a pinned commit outside the shallow clone) and were then run on the final head after building `formula`, `lint`, `objectql`, `client` and `client-react` and fetching the fixture commit. All four are green there. <details><summary>Regeneration commands</summary> `pnpm --filter @objectstack/spec check:generated --fix` regenerated `api-surface/`, `export-origins/`, `declaration-map/`, `content/docs/references/**` and the strictness-ledger counts. `gen:schema` (via `check:authorable-surface`) wrote `authorable-surface/` and `json-schema.manifest/`. `authorable-surface.base.json` is untouched. </details> ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. - [x] 2. Every name I added uses the casing for its kind (the snake_case query keys are the RFC wire names, stated above). - [x] 3. No formatter ran; `pnpm lint` passes. - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. --- _Generated by [Claude Code](https://claude.ai/code/session_01JQwxbXghRkYmSR5dVh8PFG)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a7e6e07 commit 55a4898

20 files changed

Lines changed: 397 additions & 17 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): declare the mobile sign-in hand-off surface (one-time-token hand-off and verify, and the registered app-scheme list)
6+
7+
Clause-②: yes
8+
Metadata-protocol: added
9+
10+
Some identity providers refuse embedded WebViews, so a mobile app signs in through the system browser and needs the session handed back to its WebView. This declares the contract the auth plugin serves for that, before the plugin enables it:
11+
12+
- **`AuthEndpointPaths.oneTimeTokenHandoff`** (`GET /one-time-token/handoff`) with `OneTimeTokenHandoffQuerySchema`: a closed query of `redirect_uri` (an absolute URI), an RFC 7636 S256 `code_challenge` and an optional `code_challenge_method` (only `S256`). A signed-in session is redirected to `redirect_uri` with a single-use `token` that expires after one minute. An unregistered scheme, an unknown or repeated parameter, or a bad challenge is refused with `400 VALIDATION_ERROR` and no redirect.
13+
- **`AuthEndpointPaths.oneTimeTokenVerify`** (`POST /one-time-token/verify`, better-auth's own route) with `OneTimeTokenVerifyRequestSchema` and `OneTimeTokenVerifyResponseSchema`. The body is `{ token }`, where the token is followed by a dot and the code verifier. A callback intercepted by another app cannot be redeemed without the verifier. The response is better-auth's unwrapped `{ session, user }` with the session cookie. better-auth's `GET /one-time-token/generate` refuses client requests, so it is not declared.
14+
- **`AuthConfig.trustedAppSchemes`** (new authorable key, beside `trustedOrigins`): the native-app schemes the hand-off may redirect to. Each entry is a `TrustedAppSchemeSchema`: a lowercase reverse-domain scheme written bare: two or more non-empty labels joined by single dots, the first starting with a letter (RFC 8252 section 7.1), such as `com.example.app`. A malformed entry is refused, not dropped or rewritten. That includes a `://` suffix, an uppercase letter, a dotless name such as `myapp`, and every web scheme such as `https`. If the list is absent or empty, the hand-off is not offered.
15+
16+
No `/auth/config` feature flag is added, because nothing reads one yet.
17+
18+
Nothing to do for existing configurations. To offer the hand-off, list the app's scheme in `trustedAppSchemes`.

‎content/docs/references/api/auth-endpoints.mdx‎

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ the canonical API contract.
2222
## TypeScript Usage
2323

2424
```typescript
25-
import { AuthEndpointSchema, AuthFeaturesConfigSchema, AuthProviderInfoSchema, DeviceRequestResponseSchema, DeviceTokenResponseSchema, EmailPasswordConfigPublicSchema, GetAuthConfigResponseSchema } from '@objectstack/spec/api';
26-
import type { AuthEndpoint, AuthFeaturesConfig, AuthProviderInfo, DeviceRequestResponse, DeviceTokenResponse, EmailPasswordConfigPublic, GetAuthConfigResponse } from '@objectstack/spec/api';
25+
import { AuthEndpointSchema, AuthFeaturesConfigSchema, AuthProviderInfoSchema, DeviceRequestResponseSchema, DeviceTokenResponseSchema, EmailPasswordConfigPublicSchema, GetAuthConfigResponseSchema, OneTimeTokenHandoffQuerySchema, OneTimeTokenVerifyRequestSchema, OneTimeTokenVerifyResponseSchema } from '@objectstack/spec/api';
26+
import type { AuthEndpoint, AuthFeaturesConfig, AuthProviderInfo, DeviceRequestResponse, DeviceTokenResponse, EmailPasswordConfigPublic, GetAuthConfigResponse, OneTimeTokenHandoffQuery, OneTimeTokenVerifyRequest, OneTimeTokenVerifyResponse } from '@objectstack/spec/api';
2727

2828
// Validate data
2929
const result = AuthEndpointSchema.parse(data);
@@ -215,3 +215,56 @@ This schema accepts one of the following structures:
215215

216216
---
217217

218+
## OneTimeTokenHandoffQuery
219+
220+
### Properties
221+
222+
| Property | Type | Required | Description |
223+
| :--- | :--- | :--- | :--- |
224+
| **redirect_uri** | `string` | ✅ | Where to send the token: an absolute URI whose scheme is registered in AuthConfig.trustedAppSchemes (e.g. com.example.app://auth/callback); any other scheme is refused with 400 and no redirect |
225+
| **code_challenge** | `string` | ✅ | RFC 7636 S256 code challenge: the unpadded base64url SHA-256 of the verifier the app keeps (43 characters) |
226+
| **code_challenge_method** | `'S256'` | optional | RFC 7636 challenge method; only S256 is accepted (plain is refused), and absent means S256 |
227+
228+
229+
---
230+
231+
## OneTimeTokenVerifyRequest
232+
233+
### Properties
234+
235+
| Property | Type | Required | Description |
236+
| :--- | :--- | :--- | :--- |
237+
| **token** | `string` | ✅ | The hand-off token, a dot, then the RFC 7636 code verifier (43-128 characters) whose S256 challenge was sent to the hand-off |
238+
239+
240+
---
241+
242+
## OneTimeTokenVerifyResponse
243+
244+
### Properties
245+
246+
| Property | Type | Required | Description |
247+
| :--- | :--- | :--- | :--- |
248+
| **session** | `{ id: string; expiresAt: string; token?: string; ipAddress?: string; … }` | ✅ | The session the token was minted from |
249+
| **user** | `{ id: string; email: string; emailVerified?: boolean; name: string; … }` | ✅ | The signed-in user |
250+
251+
### Nested Shape: `OneTimeTokenVerifyResponse.user`
252+
253+
| Property | Type | Required | Description |
254+
| :--- | :--- | :--- | :--- |
255+
| **id** | `string` | ✅ | User ID |
256+
| **email** | `string` | ✅ | Email address |
257+
| **emailVerified** | `boolean` | optional (default: `false`) | Is email verified? |
258+
| **name** | `string` | ✅ | Display name |
259+
| **image** | `string \| null` | optional | Avatar URL |
260+
| **username** | `string` | optional | Username (optional) |
261+
| **roles** | `string[]` | optional (default: `[]`) | Assigned role IDs |
262+
| **tenantId** | `string` | optional | Current tenant ID |
263+
| **language** | `never` | optional | [REMOVED] `SessionUser.language` was removed in @objectstack/spec 17.4.0 (ADR-0049 enforce-or-remove) — it was declared with a permanent default of `'en'` and never produced by any session endpoint nor read by any client, so a reader keying on it saw a constant, not the user's language. Delete the key. Read the signed-in user's language from `GET /auth/me/localization` (`locale`: the user's own `sys_user.locale` when set → the request's `Accept-Language` → the deployment default). |
264+
| **timezone** | `string` | optional | Preferred timezone |
265+
| **createdAt** | `string` | optional | |
266+
| **updatedAt** | `string` | optional | |
267+
268+
269+
---
270+

‎content/docs/references/index.mdx‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Protocol reference — every schema by module
33
navTitle: Protocol Reference
4-
description: Every schema published by @objectstack/spec — 1605 schemas across 14 protocol modules
4+
description: Every schema published by @objectstack/spec — 1609 schemas across 14 protocol modules
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -21,7 +21,7 @@ counts are sums of the rows they head. Regenerate with
2121
| Module | Pages | Schemas | Description |
2222
| :--- | ---: | ---: | :--- |
2323
| [AI Protocol](/docs/references/ai) | 12 | 68 | Agents, tools, skills, RAG and knowledge sources, model registry, conversations. |
24-
| [API Protocol](/docs/references/api) | 35 | 474 | REST contracts, endpoints, routing, realtime, batch, discovery. |
24+
| [API Protocol](/docs/references/api) | 35 | 477 | REST contracts, endpoints, routing, realtime, batch, discovery. |
2525
| [Automation Protocol](/docs/references/automation) | 15 | 78 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
2626
| [Data Protocol](/docs/references/data) | 31 | 175 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
2727
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
@@ -32,9 +32,9 @@ counts are sums of the rows they head. Regenerate with
3232
| [Security Protocol](/docs/references/security) | 6 | 37 | Permission sets, row-level security, sharing rules, tenancy posture. |
3333
| [Shared Protocol](/docs/references/shared) | 10 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
3434
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
35-
| [System Protocol](/docs/references/system) | 39 | 292 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
35+
| [System Protocol](/docs/references/system) | 39 | 293 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3636
| [UI Protocol](/docs/references/ui) | 16 | 175 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
37-
| **Total** | **208** | **1605** | 14 protocol modules |
37+
| **Total** | **208** | **1609** | 14 protocol modules |
3838

3939
---
4040

@@ -63,7 +63,7 @@ Agents, tools, skills, RAG and knowledge sources, model registry, conversations.
6363

6464
## API Protocol
6565

66-
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **35 pages, 474 schemas**
66+
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **35 pages, 477 schemas**
6767

6868
REST contracts, endpoints, routing, realtime, batch, discovery.
6969

@@ -72,7 +72,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
7272
| [`analytics.zod.ts`](/docs/references/api/analytics) | `AnalyticsEndpoint`, `AnalyticsMetadataResponse`, `AnalyticsQueryRequest`, `AnalyticsResultResponse`, `AnalyticsSqlResponse`, `DatasetCompareTo`, `DatasetSelection`, `DatasetTotals`, `GetAnalyticsMetaRequest` |
7373
| [`app-bundle.zod.ts`](/docs/references/api/app-bundle) | `AppMetadataBundleResponse` |
7474
| [`auth.zod.ts`](/docs/references/api/auth) | `AuthProvider`, `LoginRequest`, `LoginType`, `MePermissionsAnonymousResponse`, `MePermissionsAuthenticatedResponse`, `MePermissionsResponse`, `RefreshTokenRequest`, `RegisterRequest`, `Session`, `SessionResponse`, `SessionUser`, `UserProfileResponse` |
75-
| [`auth-endpoints.zod.ts`](/docs/references/api/auth-endpoints) | `AuthEndpoint`, `AuthFeaturesConfig`, `AuthProviderInfo`, `DeviceRequestResponse`, `DeviceTokenResponse`, `EmailPasswordConfigPublic`, `GetAuthConfigResponse` |
75+
| [`auth-endpoints.zod.ts`](/docs/references/api/auth-endpoints) | `AuthEndpoint`, `AuthFeaturesConfig`, `AuthProviderInfo`, `DeviceRequestResponse`, `DeviceTokenResponse`, `EmailPasswordConfigPublic`, `GetAuthConfigResponse`, `OneTimeTokenHandoffQuery`, `OneTimeTokenVerifyRequest`, `OneTimeTokenVerifyResponse` |
7676
| [`automation-api.zod.ts`](/docs/references/api/automation-api) | `AutomationApiErrorCode`, `AutomationFlowPathParams`, `AutomationRunPathParams`, `CloneFlowRequest`, `CloneFlowResponse`, `CreateFlowRequest`, `CreateFlowResponse`, `DeleteFlowRequest`, `DeleteFlowResponse`, `FlowFailureDetails`, `FlowInnerRefusal`, `GetFlowRequest`, `GetFlowResponse`, `GetRunRequest`, `GetRunResponse`, `ListRunsRequest`, `ListRunsResponse`, `ResumeFailureDetails`, `ToggleFlowRequest`, `ToggleFlowResponse`, `TriggerFlowRequest`, `TriggerFlowResponse`, `UpdateFlowRequest`, `UpdateFlowResponse` |
7777
| [`batch.zod.ts`](/docs/references/api/batch) | `BatchConfig`, `BatchOperationResult`, `BatchOperationType`, `BatchOptions`, `BatchRecord`, `BatchUpdateRequest`, `BatchUpdateResponse`, `CrossObjectBatchDroppedFields`, `CrossObjectBatchOperation`, `CrossObjectBatchRequest`, `CrossObjectBatchResponse`, `DeleteManyRequest`, `UpdateManyRecord`, `UpdateManyRequest` |
7878
| [`contract.zod.ts`](/docs/references/api/contract) | `ApiError`, `BaseResponse`, `BatchLoadingStrategy`, `BulkRequest`, `BulkResponse`, `CreateRequest`, `DataLoaderConfig`, `DeleteResponse`, `ExportRequest`, `IdRequest`, `ListRecordResponse`, `ModificationResult`, `QueryOptimizationConfig`, `RecordData`, `SingleRecordResponse`, `UpdateRequest` |
@@ -325,14 +325,14 @@ Studio designer metadata — the authoring surfaces for the protocols above.
325325

326326
## System Protocol
327327

328-
**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **39 pages, 292 schemas**
328+
**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **39 pages, 293 schemas**
329329

330330
The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance.
331331

332332
| File | Schemas |
333333
| :--- | :--- |
334334
| [`app-install.zod.ts`](/docs/references/system/app-install) | `AppCompatibilityCheck`, `AppInstallRequest`, `AppInstallResult`, `AppManifest` |
335-
| [`auth-config.zod.ts`](/docs/references/system/auth-config) | `AdvancedAuthConfig`, `AudienceConfig`, `AuthConfig`, `AuthPluginConfig`, `AuthProviderConfig`, `EmailAndPasswordConfig`, `EmailVerificationConfig`, `MutualTLSConfig`, `OidcProviderConfig`, `OidcProvidersConfig`, `SocialProviderConfig` |
335+
| [`auth-config.zod.ts`](/docs/references/system/auth-config) | `AdvancedAuthConfig`, `AudienceConfig`, `AuthConfig`, `AuthPluginConfig`, `AuthProviderConfig`, `EmailAndPasswordConfig`, `EmailVerificationConfig`, `MutualTLSConfig`, `OidcProviderConfig`, `OidcProvidersConfig`, `SocialProviderConfig`, `TrustedAppScheme` |
336336
| [`book.zod.ts`](/docs/references/system/book) | `Book`, `BookAudience`, `BookGroup`, `BookInclude`, `BookNode`, `ResolvedBook`, `ResolvedEntry`, `ResolvedGroup` |
337337
| [`cache.zod.ts`](/docs/references/system/cache) | `CacheAvalanchePrevention`, `CacheConfig`, `CacheConsistency`, `CacheInvalidation`, `CacheStrategy`, `CacheTier`, `CacheWarmup`, `DistributedCacheConfig` |
338338
| [`collaboration.zod.ts`](/docs/references/system/collaboration) | `AwarenessEvent`, `AwarenessSession`, `AwarenessUpdate`, `AwarenessUserState`, `CRDTMergeResult`, `CRDTState`, `CRDTType`, `CollaborationMode`, `CollaborationSession`, `CollaborationSessionConfig`, `CollaborativeCursor`, `CounterOperation`, `CursorColorPreset`, `CursorSelection`, `CursorStyle`, `CursorUpdate`, `GCounter`, `LWWRegister`, `ORSet`, `ORSetElement`, `OTComponent`, `OTOperation`, `OTOperationType`, `OTTransformResult`, `PNCounter`, `TextCRDTOperation`, `TextCRDTState`, `UserActivityStatus`, `VectorClock` |

‎content/docs/references/system/auth-config.mdx‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Auth Config schema — System Protocol reference
33
navTitle: Auth Config
4-
description: "Auth Config schemas of the ObjectStack System Protocol: AdvancedAuthConfig and 10 more — each property with its type, default and a TypeScript example."
4+
description: "Auth Config schemas of the ObjectStack System Protocol: AdvancedAuthConfig and 11 more — each property with its type, default and a TypeScript example."
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -13,8 +13,8 @@ description: "Auth Config schemas of the ObjectStack System Protocol: AdvancedAu
1313
## TypeScript Usage
1414

1515
```typescript
16-
import { AdvancedAuthConfigSchema, AudienceConfigSchema, AuthConfigSchema, AuthPluginConfigSchema, AuthProviderConfigSchema, EmailAndPasswordConfigSchema, EmailVerificationConfigSchema, MutualTLSConfigSchema, OidcProviderConfigSchema, OidcProvidersConfigSchema, SocialProviderConfigSchema } from '@objectstack/spec/system';
17-
import type { AdvancedAuthConfig, AudienceConfig, AuthConfig, AuthPluginConfig, AuthProviderConfig, EmailAndPasswordConfig, EmailVerificationConfig, MutualTLSConfig, OidcProviderConfig, OidcProvidersConfig, SocialProviderConfig } from '@objectstack/spec/system';
16+
import { AdvancedAuthConfigSchema, AudienceConfigSchema, AuthConfigSchema, AuthPluginConfigSchema, AuthProviderConfigSchema, EmailAndPasswordConfigSchema, EmailVerificationConfigSchema, MutualTLSConfigSchema, OidcProviderConfigSchema, OidcProvidersConfigSchema, SocialProviderConfigSchema, TrustedAppSchemeSchema } from '@objectstack/spec/system';
17+
import type { AdvancedAuthConfig, AudienceConfig, AuthConfig, AuthPluginConfig, AuthProviderConfig, EmailAndPasswordConfig, EmailVerificationConfig, MutualTLSConfig, OidcProviderConfig, OidcProvidersConfig, SocialProviderConfig, TrustedAppScheme } from '@objectstack/spec/system';
1818

1919
// Validate data
2020
const result = AdvancedAuthConfigSchema.parse(data);
@@ -73,6 +73,7 @@ Advanced / low-level Better-Auth options
7373
| **plugins** | `{ organization?: boolean; twoFactor?: boolean; passkeys?: boolean; passwordRejectBreached?: boolean; … }` | optional | |
7474
| **session** | `{ expiresIn?: number; updateAge?: number }` | optional | |
7575
| **trustedOrigins** | `string[]` | optional | Trusted origins for CSRF protection. Supports wildcards (e.g. "https://*.example.com"). The baseUrl origin is always trusted implicitly. |
76+
| **trustedAppSchemes** | `string[]` | optional | Native-app URI schemes the mobile sign-in hand-off may redirect a one-time token to (e.g. ["com.example.app"]). A hand-off to any other scheme is refused with 400 VALIDATION_ERROR and no redirect; absent or empty means the hand-off is not offered |
7677
| **socialProviders** | `Record<string, { clientId: string; clientSecret: string; enabled?: boolean; scope?: string[] } & Record<string, any>>` | optional | Social/OAuth provider map forwarded to better-auth socialProviders. Keys are provider ids (google, github, apple, …). |
7778
| **oidcProviders** | `{ providerId: string; name?: string; discoveryUrl?: string; issuer?: string; … }[]` | optional | List of OIDC/OAuth2 providers for enterprise SSO. Product or enterprise packages can pass this directly or contribute it through auth:configure. |
7879
| **emailAndPassword** | `{ enabled?: boolean; disableSignUp?: boolean; requireEmailVerification?: boolean; minPasswordLength?: number; … }` | optional | Email and password authentication options forwarded to better-auth |
@@ -339,3 +340,12 @@ Social/OAuth provider map forwarded to better-auth socialProviders. Keys are pro
339340

340341
---
341342

343+
## TrustedAppScheme
344+
345+
A registered native-app URI scheme: lowercase, reverse-domain (two or more non-empty labels joined by single dots, the first starting with a letter), written bare (e.g. com.example.app)
346+
347+
**Type:** `string`
348+
349+
350+
---
351+

‎docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ hand-patch a number here** — fix the code or the verdict and regenerate.
1919

2020
| Dir | Sites |
2121
|---|---|
22-
| `api/` | 490 |
22+
| `api/` | 493 |

‎packages/spec/api-surface/api.json‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,13 @@
762762
"ObjectDefinitionResponseSchema (const)",
763763
"ObjectSortability (type)",
764764
"ObjectSortabilitySchema (const)",
765+
"OneTimeTokenHandoffQuery (type)",
766+
"OneTimeTokenHandoffQuerySchema (const)",
767+
"OneTimeTokenVerifyRequest (type)",
768+
"OneTimeTokenVerifyRequestSchema (const)",
769+
"OneTimeTokenVerifyResponse (type)",
770+
"OneTimeTokenVerifyResponseParsed (type)",
771+
"OneTimeTokenVerifyResponseSchema (const)",
765772
"OpenApiGenerationConfig (type)",
766773
"OpenApiGenerationConfigParsed (type)",
767774
"OpenApiGenerationConfigSchema (const)",

‎packages/spec/api-surface/system.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,8 @@
809809
"TranslationDiffStatusSchema (const)",
810810
"TranslationItem (type)",
811811
"TranslationItemSchema (const)",
812+
"TrustedAppScheme (type)",
813+
"TrustedAppSchemeSchema (const)",
812814
"UserActivityStatus (const)",
813815
"UserActivityStatus (type)",
814816
"UserDevice (type)",

‎packages/spec/authorable-surface/api.json‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,12 @@
12741274
"api/ObjectDefinitionResponse:meta",
12751275
"api/ObjectDefinitionResponse:success",
12761276
"api/ObjectSortability:fields",
1277+
"api/OneTimeTokenHandoffQuery:code_challenge",
1278+
"api/OneTimeTokenHandoffQuery:code_challenge_method",
1279+
"api/OneTimeTokenHandoffQuery:redirect_uri",
1280+
"api/OneTimeTokenVerifyRequest:token",
1281+
"api/OneTimeTokenVerifyResponse:session",
1282+
"api/OneTimeTokenVerifyResponse:user",
12771283
"api/OpenApiGenerationConfig:apiVersion",
12781284
"api/OpenApiGenerationConfig:contact",
12791285
"api/OpenApiGenerationConfig:description",

‎packages/spec/authorable-surface/system.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"system/AuthConfig:session",
7777
"system/AuthConfig:socialProviders",
7878
"system/AuthConfig:ssoOnlyMode",
79+
"system/AuthConfig:trustedAppSchemes",
7980
"system/AuthConfig:trustedOrigins",
8081
"system/AuthConfig:uiBasePath",
8182
"system/AuthPluginConfig:admin",

0 commit comments

Comments
 (0)