@oxyhq/contracts 0.12.0 → 0.13.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.
@@ -1,28 +1,16 @@
1
1
  "use strict";
2
2
  /**
3
- * Device-first bootstrap & token contracts (auth centralization, wave 1).
3
+ * First-party login result contract.
4
4
  *
5
- * SINGLE SOURCE OF TRUTH for the wire shape of the new device-first session
6
- * bootstrap: the top-level `#oxy_boot=…` fragment the API hands back from
7
- * `GET /auth/device/bootstrap`, the token bundle a boot code / web-session
8
- * fast-path exchanges into, the persisted-refresh rotation, the native
9
- * device-token issuance, the IdP chooser's device-resolve, and the first-party
10
- * password login result (2FA arm vs. session arm). The API validates its OUTPUT
11
- * against these schemas; every consumer (`@oxyhq/core`'s device-boot mixin, the
12
- * SDK cold boot, the IdP chooser) validates its INPUT against the same
13
- * definitions, so producer and consumers cannot drift.
5
+ * SINGLE SOURCE OF TRUTH for the first-party password login result (2FA arm vs.
6
+ * session arm). The API validates its OUTPUT against this schema; every consumer
7
+ * (`@oxyhq/core`'s auth mixin) validates its INPUT against the same definition,
8
+ * so producer and consumers cannot drift.
14
9
  *
15
- * Design anchors (from the auth-centralization plan):
16
- * - The bootstrap fragment carries NO tokens and NO deviceId — only a
17
- * `state` echo (CSRF), a `reason`, a short-lived single-use `code`, and an
18
- * opaque `deviceToken`. Tokens are obtained by exchanging the `code` at
19
- * `POST /auth/device/exchange` (origin-bound GETDEL burn).
20
- * - Refresh is ONE rotating, single-use family shared by web and native.
21
- * - `loginResult` mirrors what `POST /auth/login` returns today
22
- * (`buildSessionAuthResponse` in the API's `session.controller.ts`): either a
23
- * 2FA challenge (`{ twoFactorRequired: true, loginToken }`) or a session
24
- * payload. The session arm matches `SessionAuthResponse` EXACTLY, plus an
25
- * optional `refreshToken` the new server adds for the persisted-refresh lane.
10
+ * The device transport is `deviceId` + `deviceSecret` + `POST /session/device/token`
11
+ * (see `deviceSession.ts`). The legacy cookie/bootstrap/refresh-family lanes were
12
+ * removed in the zero-cookie cutover — nothing here carries a refresh token or a
13
+ * boot fragment.
26
14
  *
27
15
  * Nested-object response shapes are declared as explicit `interface`s with the
28
16
  * runtime schema annotated `z.ZodType<Interface>` — the same rationale as
@@ -35,131 +23,27 @@
35
23
  * `require()`).
36
24
  */
37
25
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.deviceResolveResponseSchema = exports.deviceResolveRequestSchema = exports.loginResultSchema = exports.deviceTokenIssueResponseSchema = exports.tokenRefreshResponseSchema = exports.tokenRefreshRequestSchema = exports.webSessionResultSchema = exports.authTokenBundleSchema = exports.deviceExchangeRequestSchema = exports.deviceBootFragmentSchema = exports.deviceBootReasonSchema = void 0;
26
+ exports.loginResultSchema = void 0;
39
27
  const zod_1 = require("zod");
40
- const userResponse_1 = require("./userResponse");
41
- /* -------------------------------------------------------------------------- */
42
- /* Bootstrap fragment */
43
- /* -------------------------------------------------------------------------- */
44
- /**
45
- * Why the bootstrap hop resolved the way it did.
46
- * - `session` — the device cookie resolved an active session; a `code` is
47
- * present to exchange for tokens.
48
- * - `no_session` — the device is known but has no active session; no `code`.
49
- * - `new_device` — first contact; the cookie was just planted, no session yet.
50
- */
51
- exports.deviceBootReasonSchema = zod_1.z.enum(['session', 'no_session', 'new_device']);
52
- /**
53
- * The `#oxy_boot=<json>` fragment `GET /auth/device/bootstrap` appends to the
54
- * `return_to` URL. Carries the CSRF `state` echo, the resolution `reason`, the
55
- * opaque `deviceToken`, and — ONLY on the `session` arm — the single-use
56
- * exchange `code`. NEVER carries tokens or a deviceId.
57
- *
58
- * Discriminated on `reason` so the code↔reason coupling is enforced by the
59
- * schema, not by the consumer: the `session` arm REQUIRES `code`, and the
60
- * `no_session` / `new_device` arms omit it (a stray `code` on those arms is
61
- * stripped). A `session` fragment WITHOUT a code therefore fails to parse and
62
- * is treated as "no usable fragment" rather than half-processed.
63
- */
64
- const deviceBootFragmentBase = {
65
- v: zod_1.z.literal(1),
66
- state: zod_1.z.string().min(1).max(256),
67
- deviceToken: zod_1.z.string().min(20).max(512),
68
- };
69
- exports.deviceBootFragmentSchema = zod_1.z.discriminatedUnion('reason', [
70
- zod_1.z.object({
71
- ...deviceBootFragmentBase,
72
- reason: zod_1.z.literal('session'),
73
- code: zod_1.z.string().min(20).max(128),
74
- }),
75
- zod_1.z.object({
76
- ...deviceBootFragmentBase,
77
- reason: zod_1.z.literal('no_session'),
78
- }),
79
- zod_1.z.object({
80
- ...deviceBootFragmentBase,
81
- reason: zod_1.z.literal('new_device'),
82
- }),
83
- ]);
84
- /* -------------------------------------------------------------------------- */
85
- /* Boot-code exchange */
86
- /* -------------------------------------------------------------------------- */
87
- /** Request body for `POST /auth/device/exchange` — the single-use boot code. */
88
- exports.deviceExchangeRequestSchema = zod_1.z.object({
89
- code: zod_1.z.string().min(20).max(128),
90
- });
91
- exports.authTokenBundleSchema = zod_1.z.object({
92
- sessionId: zod_1.z.string(),
93
- accessToken: zod_1.z.string(),
94
- refreshToken: zod_1.z.string(),
95
- expiresAt: zod_1.z.string(),
96
- user: userResponse_1.userResponseSchema,
97
- deviceSecret: zod_1.z.string().optional(),
98
- });
99
- // Internal (unexported) arm schemas — PLAIN `z.object` literals (no
100
- // `z.ZodType<>` annotation) so `z.discriminatedUnion` can introspect the
101
- // `reason` discriminator. The node10 `.d.ts`-degradation safety comes from the
102
- // EXPORTED symbols instead: the public types are explicit interfaces
103
- // (`WebSessionSession` / `WebSessionNoSession` / `WebSessionResult`) and the
104
- // exported schema is annotated `z.ZodType<WebSessionResult>` below, so the
105
- // emitted declaration states the shape literally rather than a degradable
106
- // `z.infer<>` of the nested `session` object.
107
- const webSessionSessionSchema = zod_1.z.object({
108
- reason: zod_1.z.literal('session'),
109
- session: exports.authTokenBundleSchema,
110
- deviceToken: zod_1.z.string().min(1),
111
- });
112
- const webSessionNoSessionSchema = zod_1.z.object({
113
- reason: zod_1.z.enum(['no_session', 'new_device']),
114
- deviceToken: zod_1.z.string().min(1),
115
- });
116
- // Discriminated on `reason` — a true `discriminatedUnion` (not `z.union`): it
117
- // dispatches on the discriminator instead of sequentially probing each arm,
118
- // giving precise per-arm errors.
119
- exports.webSessionResultSchema = zod_1.z.discriminatedUnion('reason', [
120
- webSessionSessionSchema,
121
- webSessionNoSessionSchema,
122
- ]);
123
- /* -------------------------------------------------------------------------- */
124
- /* Refresh-token rotation (web + native, one implementation) */
125
- /* -------------------------------------------------------------------------- */
126
- /** Request body for `POST /auth/refresh-token` — the current refresh token. */
127
- exports.tokenRefreshRequestSchema = zod_1.z.object({
128
- refreshToken: zod_1.z.string().min(20),
129
- });
130
- /**
131
- * Wire shape of `POST /auth/refresh-token`: the rotated (single-use) family —
132
- * a new access token, the next refresh token, the new access-token expiry, and
133
- * the owning session id. `expiresAt` is an ISO string.
134
- */
135
- exports.tokenRefreshResponseSchema = zod_1.z.object({
136
- accessToken: zod_1.z.string(),
137
- refreshToken: zod_1.z.string(),
138
- expiresAt: zod_1.z.string(),
139
- sessionId: zod_1.z.string(),
140
- });
141
- /* -------------------------------------------------------------------------- */
142
- /* Native device-token issuance */
143
- /* -------------------------------------------------------------------------- */
144
- /**
145
- * Wire shape of `POST /auth/device/token` — issues (or rotates) the opaque
146
- * device token for the native channel. The deviceId is taken from the bearer
147
- * JWT claims server-side; only the token comes back.
148
- */
149
- exports.deviceTokenIssueResponseSchema = zod_1.z.object({
150
- deviceToken: zod_1.z.string(),
151
- });
152
28
  const loginTwoFactorRequiredSchema = zod_1.z.object({
153
29
  twoFactorRequired: zod_1.z.literal(true),
154
30
  loginToken: zod_1.z.string(),
155
31
  });
32
+ const securityAlertSchema = zod_1.z.object({
33
+ message: zod_1.z.string(),
34
+ anomalies: zod_1.z.array(zod_1.z.object({
35
+ type: zod_1.z.string(),
36
+ reason: zod_1.z.string(),
37
+ details: zod_1.z.string().optional(),
38
+ })),
39
+ });
156
40
  const loginSessionResultSchema = zod_1.z.object({
157
41
  sessionId: zod_1.z.string(),
158
42
  deviceId: zod_1.z.string(),
159
43
  expiresAt: zod_1.z.string(),
160
44
  accessToken: zod_1.z.string().optional(),
161
- refreshToken: zod_1.z.string().optional(),
162
45
  deviceSecret: zod_1.z.string().optional(),
46
+ securityAlert: securityAlertSchema.optional(),
163
47
  user: zod_1.z.object({
164
48
  id: zod_1.z.string(),
165
49
  username: zod_1.z.string().optional(),
@@ -170,24 +54,3 @@ exports.loginResultSchema = zod_1.z.union([
170
54
  loginTwoFactorRequiredSchema,
171
55
  loginSessionResultSchema,
172
56
  ]);
173
- /* -------------------------------------------------------------------------- */
174
- /* IdP chooser device-resolve */
175
- /* -------------------------------------------------------------------------- */
176
- /**
177
- * Request body for `POST /auth/device/resolve` (X-Oxy-Internal, called by the
178
- * IdP chooser) — the device key the chooser read from the first-party
179
- * `oxy_device` cookie.
180
- */
181
- exports.deviceResolveRequestSchema = zod_1.z.object({
182
- deviceKey: zod_1.z.string().min(20),
183
- });
184
- const deviceResolveAccountSchema = zod_1.z.object({
185
- user: userResponse_1.userResponseSchema,
186
- sessionId: zod_1.z.string(),
187
- accessToken: zod_1.z.string(),
188
- expiresAt: zod_1.z.string(),
189
- });
190
- exports.deviceResolveResponseSchema = zod_1.z.object({
191
- activeAccountId: zod_1.z.string().nullable(),
192
- accounts: zod_1.z.array(deviceResolveAccountSchema),
193
- });
package/dist/cjs/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.personhoodBreakdownSchema = exports.personhoodVouchRecordSchema = exports.validationVoteResultSchema = exports.validationRequestSummarySchema = exports.validationOpenResultSchema = exports.validationOpenRequestSchema = exports.validationVerdictRecordSchema = exports.realLifeAttestationResultSchema = exports.realLifeAttestationRecordSchema = exports.signedPublicCardSchema = exports.publicCardSchema = exports.logPageResponseSchema = exports.chainHeadResponseSchema = exports.oxySignedRecordTypeSchema = exports.exportBundleSchema = exports.exportAttestationSchema = exports.authMethodsResponseSchema = exports.authMethodEntrySchema = exports.domainVerificationInstructionsSchema = exports.domainVerificationRequestSchema = exports.verifiedDomainSchema = exports.signedRecordEnvelopeSchema = exports.didDocumentSchema = exports.didServiceSchema = exports.verificationMethodSchema = exports.appAffinityEventsIngestSchema = exports.appAffinityEventSchema = exports.appAffinityEventTypeSchema = exports.appUserSignalIngestSchema = exports.appInterestInputSchema = exports.appEndorsementInputSchema = exports.recommendationResponseSchema = exports.recommendationItemSchema = exports.recommendationCountSchema = exports.recommendationRequestSchema = exports.recommendationSignalWeightsSchema = exports.recommendationBoostSchema = exports.recommendationExcludeTypeSchema = exports.fedcmTokenPayloadSchema = exports.sessionStatusSchema = exports.publicApplicationSchema = exports.applicationTypeSchema = exports.safeParseContract = exports.resolveUserId = exports.deviceLinkedSessionsResponseSchema = exports.deviceLinkedSessionSchema = exports.currentUserResponseSchema = exports.userProfileUpdateSchema = exports.userResponseSchema = exports.userNameSchema = void 0;
15
- exports.deviceResolveResponseSchema = exports.deviceResolveRequestSchema = exports.loginResultSchema = exports.deviceTokenIssueResponseSchema = exports.tokenRefreshResponseSchema = exports.tokenRefreshRequestSchema = exports.webSessionResultSchema = exports.authTokenBundleSchema = exports.deviceExchangeRequestSchema = exports.deviceBootFragmentSchema = exports.deviceBootReasonSchema = exports.deviceTokenMintResponseSchema = exports.deviceTokenMintRequestSchema = exports.deviceSessionSyncSchema = exports.activeTokenSchema = exports.deviceSessionStateSchema = exports.sessionAccountSchema = exports.linkPreviewResponseSchema = exports.linkPreviewBatchResponseSchema = exports.linkPreviewBatchRequestSchema = exports.linkPreviewSchema = exports.credentialVerifyResultSchema = exports.credentialListResultSchema = exports.credentialIssueResultSchema = exports.verifiableCredentialResponseSchema = exports.credentialRecordSchema = exports.vouchResultSchema = exports.personhoodStatusResultSchema = void 0;
15
+ exports.loginResultSchema = exports.deviceTokenMintResponseSchema = exports.deviceTokenMintRequestSchema = exports.deviceSessionSyncSchema = exports.activeTokenSchema = exports.deviceSessionStateSchema = exports.sessionAccountSchema = exports.linkPreviewResponseSchema = exports.linkPreviewBatchResponseSchema = exports.linkPreviewBatchRequestSchema = exports.linkPreviewSchema = exports.credentialVerifyResultSchema = exports.credentialListResultSchema = exports.credentialIssueResultSchema = exports.verifiableCredentialResponseSchema = exports.credentialRecordSchema = exports.vouchResultSchema = exports.personhoodStatusResultSchema = void 0;
16
16
  var userResponse_1 = require("./userResponse");
17
17
  // Schemas
18
18
  Object.defineProperty(exports, "userNameSchema", { enumerable: true, get: function () { return userResponse_1.userNameSchema; } });
@@ -103,14 +103,4 @@ Object.defineProperty(exports, "deviceTokenMintRequestSchema", { enumerable: tru
103
103
  Object.defineProperty(exports, "deviceTokenMintResponseSchema", { enumerable: true, get: function () { return deviceSession_1.deviceTokenMintResponseSchema; } });
104
104
  var deviceBoot_1 = require("./deviceBoot");
105
105
  // Schemas
106
- Object.defineProperty(exports, "deviceBootReasonSchema", { enumerable: true, get: function () { return deviceBoot_1.deviceBootReasonSchema; } });
107
- Object.defineProperty(exports, "deviceBootFragmentSchema", { enumerable: true, get: function () { return deviceBoot_1.deviceBootFragmentSchema; } });
108
- Object.defineProperty(exports, "deviceExchangeRequestSchema", { enumerable: true, get: function () { return deviceBoot_1.deviceExchangeRequestSchema; } });
109
- Object.defineProperty(exports, "authTokenBundleSchema", { enumerable: true, get: function () { return deviceBoot_1.authTokenBundleSchema; } });
110
- Object.defineProperty(exports, "webSessionResultSchema", { enumerable: true, get: function () { return deviceBoot_1.webSessionResultSchema; } });
111
- Object.defineProperty(exports, "tokenRefreshRequestSchema", { enumerable: true, get: function () { return deviceBoot_1.tokenRefreshRequestSchema; } });
112
- Object.defineProperty(exports, "tokenRefreshResponseSchema", { enumerable: true, get: function () { return deviceBoot_1.tokenRefreshResponseSchema; } });
113
- Object.defineProperty(exports, "deviceTokenIssueResponseSchema", { enumerable: true, get: function () { return deviceBoot_1.deviceTokenIssueResponseSchema; } });
114
106
  Object.defineProperty(exports, "loginResultSchema", { enumerable: true, get: function () { return deviceBoot_1.loginResultSchema; } });
115
- Object.defineProperty(exports, "deviceResolveRequestSchema", { enumerable: true, get: function () { return deviceBoot_1.deviceResolveRequestSchema; } });
116
- Object.defineProperty(exports, "deviceResolveResponseSchema", { enumerable: true, get: function () { return deviceBoot_1.deviceResolveResponseSchema; } });
@@ -17,8 +17,7 @@
17
17
  *
18
18
  * Faithful to the producers:
19
19
  * - `packages/api/src/utils/userTransform.ts` `formatUserResponse` — the
20
- * canonical serialization used by the device-first bootstrap/exchange
21
- * endpoints (`deviceAuth.ts`), login/signup, device sessions, etc.
20
+ * canonical serialization used by login/signup, device sessions, etc.
22
21
  * Emits `id` (NOT `_id`), forwards `username` verbatim (may be absent), and
23
22
  * emits `name` as the structured `{ first, last, full, displayName }`
24
23
  * subdocument.