Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/29736-mobile-sign-in-handoff-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@objectstack/spec': minor
---

feat(spec): declare the mobile sign-in hand-off surface (one-time-token hand-off and verify, and the registered app-scheme list)

Clause-②: yes
Metadata-protocol: added

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:

- **`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.
- **`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.
- **`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.

No `/auth/config` feature flag is added, because nothing reads one yet.

Nothing to do for existing configurations. To offer the hand-off, list the app's scheme in `trustedAppSchemes`.
57 changes: 55 additions & 2 deletions content/docs/references/api/auth-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ the canonical API contract.
## TypeScript Usage

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

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

---

## OneTimeTokenHandoffQuery

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **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 |
| **code_challenge** | `string` | ✅ | RFC 7636 S256 code challenge: the unpadded base64url SHA-256 of the verifier the app keeps (43 characters) |
| **code_challenge_method** | `'S256'` | optional | RFC 7636 challenge method; only S256 is accepted (plain is refused), and absent means S256 |


---

## OneTimeTokenVerifyRequest

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **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 |


---

## OneTimeTokenVerifyResponse

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **session** | `{ id: string; expiresAt: string; token?: string; ipAddress?: string; … }` | ✅ | The session the token was minted from |
| **user** | `{ id: string; email: string; emailVerified?: boolean; name: string; … }` | ✅ | The signed-in user |

### Nested Shape: `OneTimeTokenVerifyResponse.user`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **id** | `string` | ✅ | User ID |
| **email** | `string` | ✅ | Email address |
| **emailVerified** | `boolean` | optional (default: `false`) | Is email verified? |
| **name** | `string` | ✅ | Display name |
| **image** | `string \| null` | optional | Avatar URL |
| **username** | `string` | optional | Username (optional) |
| **roles** | `string[]` | optional (default: `[]`) | Assigned role IDs |
| **tenantId** | `string` | optional | Current tenant ID |
| **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). |
| **timezone** | `string` | optional | Preferred timezone |
| **createdAt** | `string` | optional | |
| **updatedAt** | `string` | optional | |


---

16 changes: 8 additions & 8 deletions content/docs/references/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Protocol reference — every schema by module
navTitle: Protocol Reference
description: Every schema published by @objectstack/spec — 1605 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1609 schemas across 14 protocol modules
---

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

---

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

## API Protocol

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

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

Expand All @@ -72,7 +72,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`analytics.zod.ts`](/docs/references/api/analytics) | `AnalyticsEndpoint`, `AnalyticsMetadataResponse`, `AnalyticsQueryRequest`, `AnalyticsResultResponse`, `AnalyticsSqlResponse`, `DatasetCompareTo`, `DatasetSelection`, `DatasetTotals`, `GetAnalyticsMetaRequest` |
| [`app-bundle.zod.ts`](/docs/references/api/app-bundle) | `AppMetadataBundleResponse` |
| [`auth.zod.ts`](/docs/references/api/auth) | `AuthProvider`, `LoginRequest`, `LoginType`, `MePermissionsAnonymousResponse`, `MePermissionsAuthenticatedResponse`, `MePermissionsResponse`, `RefreshTokenRequest`, `RegisterRequest`, `Session`, `SessionResponse`, `SessionUser`, `UserProfileResponse` |
| [`auth-endpoints.zod.ts`](/docs/references/api/auth-endpoints) | `AuthEndpoint`, `AuthFeaturesConfig`, `AuthProviderInfo`, `DeviceRequestResponse`, `DeviceTokenResponse`, `EmailPasswordConfigPublic`, `GetAuthConfigResponse` |
| [`auth-endpoints.zod.ts`](/docs/references/api/auth-endpoints) | `AuthEndpoint`, `AuthFeaturesConfig`, `AuthProviderInfo`, `DeviceRequestResponse`, `DeviceTokenResponse`, `EmailPasswordConfigPublic`, `GetAuthConfigResponse`, `OneTimeTokenHandoffQuery`, `OneTimeTokenVerifyRequest`, `OneTimeTokenVerifyResponse` |
| [`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` |
| [`batch.zod.ts`](/docs/references/api/batch) | `BatchConfig`, `BatchOperationResult`, `BatchOperationType`, `BatchOptions`, `BatchRecord`, `BatchUpdateRequest`, `BatchUpdateResponse`, `CrossObjectBatchDroppedFields`, `CrossObjectBatchOperation`, `CrossObjectBatchRequest`, `CrossObjectBatchResponse`, `DeleteManyRequest`, `UpdateManyRecord`, `UpdateManyRequest` |
| [`contract.zod.ts`](/docs/references/api/contract) | `ApiError`, `BaseResponse`, `BatchLoadingStrategy`, `BulkRequest`, `BulkResponse`, `CreateRequest`, `DataLoaderConfig`, `DeleteResponse`, `ExportRequest`, `IdRequest`, `ListRecordResponse`, `ModificationResult`, `QueryOptimizationConfig`, `RecordData`, `SingleRecordResponse`, `UpdateRequest` |
Expand Down Expand Up @@ -325,14 +325,14 @@ Studio designer metadata — the authoring surfaces for the protocols above.

## System Protocol

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

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

| File | Schemas |
| :--- | :--- |
| [`app-install.zod.ts`](/docs/references/system/app-install) | `AppCompatibilityCheck`, `AppInstallRequest`, `AppInstallResult`, `AppManifest` |
| [`auth-config.zod.ts`](/docs/references/system/auth-config) | `AdvancedAuthConfig`, `AudienceConfig`, `AuthConfig`, `AuthPluginConfig`, `AuthProviderConfig`, `EmailAndPasswordConfig`, `EmailVerificationConfig`, `MutualTLSConfig`, `OidcProviderConfig`, `OidcProvidersConfig`, `SocialProviderConfig` |
| [`auth-config.zod.ts`](/docs/references/system/auth-config) | `AdvancedAuthConfig`, `AudienceConfig`, `AuthConfig`, `AuthPluginConfig`, `AuthProviderConfig`, `EmailAndPasswordConfig`, `EmailVerificationConfig`, `MutualTLSConfig`, `OidcProviderConfig`, `OidcProvidersConfig`, `SocialProviderConfig`, `TrustedAppScheme` |
| [`book.zod.ts`](/docs/references/system/book) | `Book`, `BookAudience`, `BookGroup`, `BookInclude`, `BookNode`, `ResolvedBook`, `ResolvedEntry`, `ResolvedGroup` |
| [`cache.zod.ts`](/docs/references/system/cache) | `CacheAvalanchePrevention`, `CacheConfig`, `CacheConsistency`, `CacheInvalidation`, `CacheStrategy`, `CacheTier`, `CacheWarmup`, `DistributedCacheConfig` |
| [`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` |
Expand Down
16 changes: 13 additions & 3 deletions content/docs/references/system/auth-config.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Auth Config schema — System Protocol reference
navTitle: Auth Config
description: "Auth Config schemas of the ObjectStack System Protocol: AdvancedAuthConfig and 10 more — each property with its type, default and a TypeScript example."
description: "Auth Config schemas of the ObjectStack System Protocol: AdvancedAuthConfig and 11 more — each property with its type, default and a TypeScript example."
---

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

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

// Validate data
const result = AdvancedAuthConfigSchema.parse(data);
Expand Down Expand Up @@ -73,6 +73,7 @@ Advanced / low-level Better-Auth options
| **plugins** | `{ organization?: boolean; twoFactor?: boolean; passkeys?: boolean; passwordRejectBreached?: boolean; … }` | optional | |
| **session** | `{ expiresIn?: number; updateAge?: number }` | optional | |
| **trustedOrigins** | `string[]` | optional | Trusted origins for CSRF protection. Supports wildcards (e.g. "https://*.example.com"). The baseUrl origin is always trusted implicitly. |
| **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 |
| **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, …). |
| **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. |
| **emailAndPassword** | `{ enabled?: boolean; disableSignUp?: boolean; requireEmailVerification?: boolean; minPasswordLength?: number; … }` | optional | Email and password authentication options forwarded to better-auth |
Expand Down Expand Up @@ -339,3 +340,12 @@ Social/OAuth provider map forwarded to better-auth socialProviders. Keys are pro

---

## TrustedAppScheme

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)

**Type:** `string`


---

Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ hand-patch a number here** — fix the code or the verdict and regenerate.

| Dir | Sites |
|---|---|
| `api/` | 490 |
| `api/` | 493 |
7 changes: 7 additions & 0 deletions packages/spec/api-surface/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,13 @@
"ObjectDefinitionResponseSchema (const)",
"ObjectSortability (type)",
"ObjectSortabilitySchema (const)",
"OneTimeTokenHandoffQuery (type)",
"OneTimeTokenHandoffQuerySchema (const)",
"OneTimeTokenVerifyRequest (type)",
"OneTimeTokenVerifyRequestSchema (const)",
"OneTimeTokenVerifyResponse (type)",
"OneTimeTokenVerifyResponseParsed (type)",
"OneTimeTokenVerifyResponseSchema (const)",
"OpenApiGenerationConfig (type)",
"OpenApiGenerationConfigParsed (type)",
"OpenApiGenerationConfigSchema (const)",
Expand Down
2 changes: 2 additions & 0 deletions packages/spec/api-surface/system.json
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,8 @@
"TranslationDiffStatusSchema (const)",
"TranslationItem (type)",
"TranslationItemSchema (const)",
"TrustedAppScheme (type)",
"TrustedAppSchemeSchema (const)",
"UserActivityStatus (const)",
"UserActivityStatus (type)",
"UserDevice (type)",
Expand Down
6 changes: 6 additions & 0 deletions packages/spec/authorable-surface/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,12 @@
"api/ObjectDefinitionResponse:meta",
"api/ObjectDefinitionResponse:success",
"api/ObjectSortability:fields",
"api/OneTimeTokenHandoffQuery:code_challenge",
"api/OneTimeTokenHandoffQuery:code_challenge_method",
"api/OneTimeTokenHandoffQuery:redirect_uri",
"api/OneTimeTokenVerifyRequest:token",
"api/OneTimeTokenVerifyResponse:session",
"api/OneTimeTokenVerifyResponse:user",
"api/OpenApiGenerationConfig:apiVersion",
"api/OpenApiGenerationConfig:contact",
"api/OpenApiGenerationConfig:description",
Expand Down
1 change: 1 addition & 0 deletions packages/spec/authorable-surface/system.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"system/AuthConfig:session",
"system/AuthConfig:socialProviders",
"system/AuthConfig:ssoOnlyMode",
"system/AuthConfig:trustedAppSchemes",
"system/AuthConfig:trustedOrigins",
"system/AuthConfig:uiBasePath",
"system/AuthPluginConfig:admin",
Expand Down
Loading
Loading