@oxyhq/contracts 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -91,6 +91,7 @@ export const authTokenBundleSchema = z.object({
91
91
  refreshToken: z.string(),
92
92
  expiresAt: z.string(),
93
93
  user: userResponseSchema,
94
+ deviceSecret: z.string().optional(),
94
95
  });
95
96
  // Internal (unexported) arm schemas — PLAIN `z.object` literals (no
96
97
  // `z.ZodType<>` annotation) so `z.discriminatedUnion` can introspect the
@@ -155,6 +156,7 @@ const loginSessionResultSchema = z.object({
155
156
  expiresAt: z.string(),
156
157
  accessToken: z.string().optional(),
157
158
  refreshToken: z.string().optional(),
159
+ deviceSecret: z.string().optional(),
158
160
  user: z.object({
159
161
  id: z.string(),
160
162
  username: z.string().optional(),
@@ -20,3 +20,30 @@ export const deviceSessionSyncSchema = z.object({
20
20
  state: deviceSessionStateSchema,
21
21
  activeToken: activeTokenSchema.nullable(),
22
22
  });
23
+ /* -------------------------------------------------------------------------- */
24
+ /* Device-secret token mint (phase 2c — zero-cookie transport) */
25
+ /* -------------------------------------------------------------------------- */
26
+ /**
27
+ * Request body for `POST /session/device/token` — the client presents the
28
+ * `deviceId` it stored first-party plus the opaque `deviceSecret`. NO bearer:
29
+ * possession of the secret IS the proof of device ownership. The server matches
30
+ * `sha256(deviceSecret)` against the device's stored `secretHash` (constant-time)
31
+ * and mints a short access token for the device's active account.
32
+ */
33
+ export const deviceTokenMintRequestSchema = z.object({
34
+ deviceId: z.string().min(1),
35
+ deviceSecret: z.string().min(1),
36
+ });
37
+ /**
38
+ * Wire shape of a successful `POST /session/device/token`: the freshly-minted
39
+ * short access token for the active account, its expiry, the NEXT rotating
40
+ * device secret the client must persist (rotation-in-use — the presented secret
41
+ * stays valid for a short grace so multi-tab races don't lock out), and the
42
+ * projected device-session state.
43
+ */
44
+ export const deviceTokenMintResponseSchema = z.object({
45
+ accessToken: z.string(),
46
+ expiresAt: z.string(),
47
+ nextDeviceSecret: z.string(),
48
+ state: deviceSessionStateSchema,
49
+ });
@@ -1,6 +1,16 @@
1
1
  /**
2
2
  * Canonical contract for the FedCM ID-token JWT payload.
3
3
  *
4
+ * DEAD SINCE THE WAVE-2 FEDCM DELETION (found during the comment sweep, not
5
+ * fixed here — this is a published `@oxyhq/contracts` export, so removing it
6
+ * is a breaking change/major-version decision, out of scope for a comment
7
+ * fix): `POST /fedcm/exchange`, `fedcm.service.exchangeIdToken`, and
8
+ * `packages/auth/server/index.ts`'s `mintSessionForClient` — every consumer
9
+ * and producer this file describes — are all deleted. No current code
10
+ * imports `fedcmTokenPayloadSchema` / `FedcmTokenPayload` outside this
11
+ * package. Flag for a follow-up major-version cleanup.
12
+ *
13
+ * Original doc (historical, describes the now-deleted system):
4
14
  * SINGLE SOURCE OF TRUTH for the decoded claims of the HS256 ID token the auth
5
15
  * IdP (`auth.oxy.so`) signs and `POST /fedcm/exchange` consumes. The API decodes
6
16
  * the JWT, verifies its signature, then validates the resulting claim object
package/dist/esm/index.js CHANGED
@@ -11,7 +11,7 @@
11
11
  */
12
12
  export {
13
13
  // Schemas
14
- userNameSchema, userResponseSchema, userProfileUpdateSchema, currentUserResponseSchema, deviceSessionAccountSchema, deviceSessionsResponseSchema,
14
+ userNameSchema, userResponseSchema, userProfileUpdateSchema, currentUserResponseSchema, deviceLinkedSessionSchema, deviceLinkedSessionsResponseSchema,
15
15
  // Helpers
16
16
  resolveUserId, safeParseContract, } from './userResponse.js';
17
17
  export {
@@ -40,7 +40,7 @@ credentialRecordSchema, verifiableCredentialResponseSchema, credentialIssueResul
40
40
  export {
41
41
  // Schemas
42
42
  linkPreviewSchema, linkPreviewBatchRequestSchema, linkPreviewBatchResponseSchema, linkPreviewResponseSchema, } from './links.js';
43
- export { sessionAccountSchema, deviceSessionStateSchema, activeTokenSchema, deviceSessionSyncSchema, } from './deviceSession.js';
43
+ export { sessionAccountSchema, deviceSessionStateSchema, activeTokenSchema, deviceSessionSyncSchema, deviceTokenMintRequestSchema, deviceTokenMintResponseSchema, } from './deviceSession.js';
44
44
  export {
45
45
  // Schemas
46
46
  deviceBootReasonSchema, deviceBootFragmentSchema, deviceExchangeRequestSchema, authTokenBundleSchema, webSessionResultSchema, tokenRefreshRequestSchema, tokenRefreshResponseSchema, deviceTokenIssueResponseSchema, loginResultSchema, deviceResolveRequestSchema, deviceResolveResponseSchema, } from './deviceBoot.js';
@@ -19,9 +19,10 @@
19
19
  * Faithful to the producers:
20
20
  * - `packages/api/src/utils/serializeApplication.ts` `serializePublicApplication`
21
21
  * — the ONLY shape returned to an unauthenticated consent UI. Optional fields
22
- * (`description`, `icon`, `websiteUrl`, `developerName`) are OMITTED when
23
- * absent (never serialized as `null`), so they are `.optional()` — NOT
24
- * `.nullable()`. `type` is the `Application.type` enum.
22
+ * (`description`, `icon`, `websiteUrl`, `privacyPolicyUrl`, `termsUrl`,
23
+ * `developerName`) are OMITTED when absent (never serialized as `null`), so
24
+ * they are `.optional()` — NOT `.nullable()`. `type` is the `Application.type`
25
+ * enum.
25
26
  * - `packages/api/src/routes/auth.ts` `GET /session/status/:sessionToken` — the
26
27
  * inner object of the API's `{ data: ... }` success envelope. The handler
27
28
  * ALWAYS emits `status`, `authorized` (`status === 'authorized'`),
@@ -52,9 +53,10 @@ export const applicationTypeSchema = z.enum([
52
53
  * `GET /auth/oauth/client/:clientId` (OAuth code flow).
53
54
  *
54
55
  * Optional fields are `.optional()` (NOT `.nullable()`): the serializer OMITS
55
- * `description` / `icon` / `websiteUrl` / `developerName` when the underlying
56
- * value is absent — it never writes `null` for them. `developerName` is only
57
- * attached for non-official apps when a name could be resolved.
56
+ * `description` / `icon` / `websiteUrl` / `privacyPolicyUrl` / `termsUrl` /
57
+ * `developerName` when the underlying value is absent — it never writes `null`
58
+ * for them. `developerName` is only attached for non-official apps when a name
59
+ * could be resolved.
58
60
  */
59
61
  export const publicApplicationSchema = z.object({
60
62
  id: z.string(),
@@ -62,6 +64,8 @@ export const publicApplicationSchema = z.object({
62
64
  description: z.string().optional(),
63
65
  icon: z.string().optional(),
64
66
  websiteUrl: z.string().optional(),
67
+ privacyPolicyUrl: z.string().optional(),
68
+ termsUrl: z.string().optional(),
65
69
  type: applicationTypeSchema,
66
70
  isOfficial: z.boolean(),
67
71
  isInternal: z.boolean(),
@@ -16,7 +16,8 @@
16
16
  *
17
17
  * Faithful to the producers:
18
18
  * - `packages/api/src/utils/userTransform.ts` `formatUserResponse` — the
19
- * canonical serialization used by `/auth/refresh-all`, device sessions, etc.
19
+ * canonical serialization used by the device-first bootstrap/exchange
20
+ * endpoints (`deviceAuth.ts`), login/signup, device sessions, etc.
20
21
  * Emits `id` (NOT `_id`), forwards `username` verbatim (may be absent), and
21
22
  * emits `name` as the structured `{ first, last, full, displayName }`
22
23
  * subdocument.
@@ -24,8 +25,6 @@
24
25
  * `''`; `full` and `displayName` are Mongoose VIRTUALS. Formatted API
25
26
  * responses compose both fields, while raw-document responses may omit the
26
27
  * virtuals if the query did not materialise them.
27
- * - The `/auth/refresh-all` handler in `packages/api/src/routes/auth.ts`, whose
28
- * per-slot `authuser` is the numeric `oxy_rt_${authuser}` cookie slot.
29
28
  *
30
29
  * Platform-agnostic — zod only, no react/react-native/expo. ESM-safe (no
31
30
  * `require()`).
@@ -146,13 +145,13 @@ export const currentUserResponseSchema = z.object({
146
145
  * session). Backs the multi-account chooser. The embedded user mirrors
147
146
  * `formatUserResponse`; it is nullable on slots that lost their user document.
148
147
  */
149
- export const deviceSessionAccountSchema = z.object({
148
+ export const deviceLinkedSessionSchema = z.object({
150
149
  sessionId: z.string(),
151
150
  isCurrent: z.boolean().optional(),
152
151
  user: userResponseSchema.nullable().optional(),
153
152
  });
154
153
  /** Wire shape of `GET /session/device/sessions/:sessionId` (an array). */
155
- export const deviceSessionsResponseSchema = z.array(deviceSessionAccountSchema);
154
+ export const deviceLinkedSessionsResponseSchema = z.array(deviceLinkedSessionSchema);
156
155
  /**
157
156
  * Safely parse a value against a contract schema. Returns the parsed (typed)
158
157
  * value, or `null` when validation fails — the same ergonomics the auth app's