@opengeni/contracts 2.5.0-canary.2 → 2.6.0-canary.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.
@@ -0,0 +1,208 @@
1
+ import { z } from "zod";
2
+
3
+ export const MANAGED_AUTH_SESSION_SET_MAX_SLOTS = 8 as const;
4
+ export const MANAGED_AUTH_TRANSACTION_TTL_SECONDS = 600 as const;
5
+ export const MANAGED_AUTH_RETURN_INTENT_MAX_BYTES = 2_048 as const;
6
+ export const MANAGED_AUTH_SESSION_SET_API_CONTRACT_HEADER = "x-opengeni-api-contract" as const;
7
+ export const MANAGED_AUTH_SESSION_SET_API_CONTRACT_REVISION =
8
+ "2026-08-personal-only-organization-setup-v1" as const;
9
+
10
+ export const ManagedAuthSessionSetMode = z.enum(["legacy", "dual", "broker"]);
11
+ export type ManagedAuthSessionSetMode = z.infer<typeof ManagedAuthSessionSetMode>;
12
+
13
+ export const ManagedAuthLoginSlotState = z.enum(["active", "reauth_required"]);
14
+ export type ManagedAuthLoginSlotState = z.infer<typeof ManagedAuthLoginSlotState>;
15
+
16
+ export const ManagedAuthVerifiedClaim = z.object({
17
+ kind: z.literal("email"),
18
+ value: z.string().email().max(320),
19
+ });
20
+ export type ManagedAuthVerifiedClaim = z.infer<typeof ManagedAuthVerifiedClaim>;
21
+
22
+ /** Secret-free, browser-safe summary. Provider session ids and tokens never enter this shape. */
23
+ export const ManagedAuthLoginSlot = z.object({
24
+ id: z.string().uuid(),
25
+ displayName: z.string().min(1).max(256),
26
+ verifiedClaim: ManagedAuthVerifiedClaim,
27
+ state: ManagedAuthLoginSlotState,
28
+ });
29
+ export type ManagedAuthLoginSlot = z.infer<typeof ManagedAuthLoginSlot>;
30
+
31
+ export const ManagedAuthSessionSetState = z.enum(["ready", "actor_change_required"]);
32
+ export type ManagedAuthSessionSetState = z.infer<typeof ManagedAuthSessionSetState>;
33
+
34
+ export const ManagedAuthSessionSetProjection = z.object({
35
+ mode: ManagedAuthSessionSetMode,
36
+ generation: z.string().regex(/^[1-9][0-9]*$/),
37
+ actorEpoch: z.string().regex(/^[1-9][0-9]*$/),
38
+ csrfToken: z.string().min(32).max(512),
39
+ selectedSlotId: z.string().uuid().nullable(),
40
+ state: ManagedAuthSessionSetState,
41
+ slots: z.array(ManagedAuthLoginSlot).max(MANAGED_AUTH_SESSION_SET_MAX_SLOTS),
42
+ });
43
+ export type ManagedAuthSessionSetProjection = z.infer<typeof ManagedAuthSessionSetProjection>;
44
+
45
+ export const ManagedAuthOperationIdentity = z.object({
46
+ operationId: z.string().uuid(),
47
+ expectedGeneration: z.string().regex(/^[1-9][0-9]*$/),
48
+ });
49
+ export type ManagedAuthOperationIdentity = z.infer<typeof ManagedAuthOperationIdentity>;
50
+
51
+ export const BootstrapManagedAuthSessionSetRequest = ManagedAuthOperationIdentity;
52
+ export type BootstrapManagedAuthSessionSetRequest = z.infer<
53
+ typeof BootstrapManagedAuthSessionSetRequest
54
+ >;
55
+
56
+ export const ManagedAuthReturnIntent = z
57
+ .string()
58
+ .min(1)
59
+ .superRefine((value, context) => {
60
+ if (new TextEncoder().encode(value).byteLength > MANAGED_AUTH_RETURN_INTENT_MAX_BYTES) {
61
+ context.addIssue({ code: "custom", message: "return intent exceeds its UTF-8 byte limit" });
62
+ }
63
+ if (
64
+ !value.startsWith("/") ||
65
+ value.startsWith("//") ||
66
+ value.includes("?") ||
67
+ value.includes("#") ||
68
+ /[\u0000-\u001f\u007f\\]/.test(value)
69
+ ) {
70
+ context.addIssue({
71
+ code: "custom",
72
+ message: "return intent must be a safe same-origin path",
73
+ });
74
+ return;
75
+ }
76
+ try {
77
+ const parsed = new URL(value, "https://opengeni.invalid");
78
+ const uuid = "[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";
79
+ const supportedPath = new RegExp(
80
+ `^/(?:sessions/${uuid}|workspaces/${uuid}(?:/sessions(?:/${uuid})?)?)$`,
81
+ "i",
82
+ );
83
+ if (
84
+ parsed.origin !== "https://opengeni.invalid" ||
85
+ parsed.username ||
86
+ parsed.password ||
87
+ parsed.search ||
88
+ parsed.hash ||
89
+ !supportedPath.test(value) ||
90
+ parsed.pathname !== value
91
+ ) {
92
+ context.addIssue({
93
+ code: "custom",
94
+ message: "return intent contains unsafe authority data",
95
+ });
96
+ }
97
+ } catch {
98
+ context.addIssue({
99
+ code: "custom",
100
+ message: "return intent is not a valid same-origin path",
101
+ });
102
+ }
103
+ });
104
+
105
+ export const BeginManagedAuthLoginTransactionRequest = ManagedAuthOperationIdentity.extend({
106
+ kind: z.enum(["add", "reauth"]),
107
+ slotId: z.string().uuid().optional(),
108
+ returnIntent: ManagedAuthReturnIntent.optional(),
109
+ }).superRefine((value, context) => {
110
+ if ((value.kind === "reauth") !== Boolean(value.slotId)) {
111
+ context.addIssue({
112
+ code: "custom",
113
+ path: ["slotId"],
114
+ message: "reauth requires exactly one slot id and add forbids one",
115
+ });
116
+ }
117
+ });
118
+ export type BeginManagedAuthLoginTransactionRequest = z.infer<
119
+ typeof BeginManagedAuthLoginTransactionRequest
120
+ >;
121
+
122
+ export const ManagedAuthLoginTransaction = z.object({
123
+ id: z.string().uuid(),
124
+ kind: z.enum(["add", "reauth"]),
125
+ expiresAt: z.string().datetime(),
126
+ returnIntentId: z.string().uuid().nullable(),
127
+ });
128
+ export type ManagedAuthLoginTransaction = z.infer<typeof ManagedAuthLoginTransaction>;
129
+
130
+ export const CompleteManagedAuthEmailPasswordTransactionRequest =
131
+ ManagedAuthOperationIdentity.extend({
132
+ transactionId: z.string().uuid(),
133
+ email: z.string().email().max(320),
134
+ password: z.string().min(1).max(1_024),
135
+ });
136
+ export type CompleteManagedAuthEmailPasswordTransactionRequest = z.infer<
137
+ typeof CompleteManagedAuthEmailPasswordTransactionRequest
138
+ >;
139
+
140
+ export const CompleteManagedAuthLoginTransactionResponse = z.object({
141
+ projection: ManagedAuthSessionSetProjection,
142
+ returnIntent: ManagedAuthReturnIntent.nullable(),
143
+ });
144
+ export type CompleteManagedAuthLoginTransactionResponse = z.infer<
145
+ typeof CompleteManagedAuthLoginTransactionResponse
146
+ >;
147
+
148
+ export const CancelManagedAuthLoginTransactionRequest = ManagedAuthOperationIdentity.extend({
149
+ transactionId: z.string().uuid(),
150
+ });
151
+ export type CancelManagedAuthLoginTransactionRequest = z.infer<
152
+ typeof CancelManagedAuthLoginTransactionRequest
153
+ >;
154
+
155
+ export const SelectManagedAuthLoginSlotRequest = ManagedAuthOperationIdentity.extend({
156
+ slotId: z.string().uuid(),
157
+ });
158
+ export type SelectManagedAuthLoginSlotRequest = z.infer<typeof SelectManagedAuthLoginSlotRequest>;
159
+
160
+ export const LogoutManagedAuthLoginSlotRequest = ManagedAuthOperationIdentity.extend({
161
+ slotId: z.string().uuid(),
162
+ replacementSlotId: z.string().uuid().nullable(),
163
+ });
164
+ export type LogoutManagedAuthLoginSlotRequest = z.infer<typeof LogoutManagedAuthLoginSlotRequest>;
165
+
166
+ export const LogoutManagedAuthSessionSetRequest = ManagedAuthOperationIdentity;
167
+ export type LogoutManagedAuthSessionSetRequest = z.infer<typeof LogoutManagedAuthSessionSetRequest>;
168
+
169
+ export const ManagedAuthLogoutAllReceipt = z.object({
170
+ generation: z.string().regex(/^[1-9][0-9]*$/),
171
+ actorEpoch: z.string().regex(/^[1-9][0-9]*$/),
172
+ state: z.literal("logged_out"),
173
+ });
174
+ export type ManagedAuthLogoutAllReceipt = z.infer<typeof ManagedAuthLogoutAllReceipt>;
175
+
176
+ export const ResolveManagedAuthDeepLinkRequest = z.object({
177
+ path: ManagedAuthReturnIntent,
178
+ });
179
+ export type ResolveManagedAuthDeepLinkRequest = z.infer<typeof ResolveManagedAuthDeepLinkRequest>;
180
+
181
+ export const ManagedAuthDeepLinkResolution = z.discriminatedUnion("kind", [
182
+ z.object({ kind: z.literal("current") }),
183
+ z.object({ kind: z.literal("switch_required"), slot: ManagedAuthLoginSlot }),
184
+ z.object({ kind: z.literal("unavailable") }),
185
+ ]);
186
+ export type ManagedAuthDeepLinkResolution = z.infer<typeof ManagedAuthDeepLinkResolution>;
187
+
188
+ export const ManagedAuthSessionSetErrorCode = z.enum([
189
+ "actor_change_required",
190
+ "actor_mutation_in_flight",
191
+ "api_contract_changed",
192
+ "browser_session_set_required",
193
+ "browser_session_set_unavailable",
194
+ "generation_conflict",
195
+ "invalid_browser_session_set_request",
196
+ "invalid_transaction",
197
+ "managed_authentication_required",
198
+ "managed_authentication_unavailable",
199
+ "operation_outcome_unknown",
200
+ "operation_reused",
201
+ "origin_rejected",
202
+ "login_transaction_rate_limited",
203
+ "provider_route_blocked",
204
+ "slot_limit_reached",
205
+ "slot_already_exists",
206
+ "slot_unavailable",
207
+ ]);
208
+ export type ManagedAuthSessionSetErrorCode = z.infer<typeof ManagedAuthSessionSetErrorCode>;
@@ -0,0 +1,186 @@
1
+ import { z } from "zod";
2
+
3
+ const Uuid = z.string().uuid();
4
+ const Timestamp = z.string().datetime({ offset: true });
5
+
6
+ export const OrganizationRecoveryPolicyState = z.enum([
7
+ "pending_acceptance",
8
+ "active",
9
+ "degraded",
10
+ "superseded",
11
+ "disabled",
12
+ ]);
13
+ export type OrganizationRecoveryPolicyState = z.infer<typeof OrganizationRecoveryPolicyState>;
14
+
15
+ export const OrganizationRecoveryOperationState = z.enum([
16
+ "collecting",
17
+ "cooling",
18
+ "executed",
19
+ "cancelled",
20
+ "expired",
21
+ "superseded",
22
+ ]);
23
+ export type OrganizationRecoveryOperationState = z.infer<typeof OrganizationRecoveryOperationState>;
24
+
25
+ export const OrganizationRecoveryUnavailableReason = z.enum([
26
+ "no_policy",
27
+ "pending_acceptance",
28
+ "degraded",
29
+ "disabled",
30
+ "identity_unavailable",
31
+ ]);
32
+ export type OrganizationRecoveryUnavailableReason = z.infer<
33
+ typeof OrganizationRecoveryUnavailableReason
34
+ >;
35
+
36
+ export const OrganizationRecoveryMemberSummary = z
37
+ .object({
38
+ membershipId: Uuid,
39
+ name: z.string().trim().min(1).max(1024).nullable(),
40
+ email: z.string().email().max(320).nullable(),
41
+ })
42
+ .strict();
43
+ export type OrganizationRecoveryMemberSummary = z.infer<typeof OrganizationRecoveryMemberSummary>;
44
+
45
+ export const OrganizationRecoveryCustodian = OrganizationRecoveryMemberSummary.extend({
46
+ ordinal: z.number().int().min(1).max(3),
47
+ enrollmentState: z.enum(["pending_acceptance", "accepted", "ineligible"]),
48
+ acceptedAt: Timestamp.nullable(),
49
+ }).strict();
50
+ export type OrganizationRecoveryCustodian = z.infer<typeof OrganizationRecoveryCustodian>;
51
+
52
+ export const OrganizationRecoveryPolicy = z
53
+ .object({
54
+ id: Uuid,
55
+ organizationId: Uuid,
56
+ revision: z.number().int().positive(),
57
+ state: OrganizationRecoveryPolicyState,
58
+ custodians: z.array(OrganizationRecoveryCustodian).length(3),
59
+ createdAt: Timestamp,
60
+ updatedAt: Timestamp,
61
+ })
62
+ .strict();
63
+ export type OrganizationRecoveryPolicy = z.infer<typeof OrganizationRecoveryPolicy>;
64
+
65
+ export const OrganizationRecoveryApproval = OrganizationRecoveryMemberSummary.extend({
66
+ approvedAt: Timestamp,
67
+ }).strict();
68
+ export type OrganizationRecoveryApproval = z.infer<typeof OrganizationRecoveryApproval>;
69
+
70
+ export const OrganizationRecoveryOperation = z
71
+ .object({
72
+ id: Uuid,
73
+ organizationId: Uuid,
74
+ policyId: Uuid,
75
+ policyRevision: z.number().int().positive(),
76
+ revision: z.number().int().positive(),
77
+ state: OrganizationRecoveryOperationState,
78
+ target: OrganizationRecoveryMemberSummary,
79
+ approvals: z.array(OrganizationRecoveryApproval).max(3),
80
+ approvalCount: z.number().int().min(0).max(3),
81
+ quorumAt: Timestamp.nullable(),
82
+ executableAt: Timestamp.nullable(),
83
+ expiresAt: Timestamp,
84
+ executedAt: Timestamp.nullable(),
85
+ cancelledAt: Timestamp.nullable(),
86
+ notificationJournaled: z.boolean(),
87
+ createdAt: Timestamp,
88
+ updatedAt: Timestamp,
89
+ })
90
+ .strict();
91
+ export type OrganizationRecoveryOperation = z.infer<typeof OrganizationRecoveryOperation>;
92
+
93
+ export const OrganizationRecoveryCapabilities = z
94
+ .object({
95
+ configure: z.boolean(),
96
+ accept: z.boolean(),
97
+ disable: z.boolean(),
98
+ start: z.boolean(),
99
+ approve: z.boolean(),
100
+ cancel: z.boolean(),
101
+ execute: z.boolean(),
102
+ })
103
+ .strict();
104
+ export type OrganizationRecoveryCapabilities = z.infer<typeof OrganizationRecoveryCapabilities>;
105
+
106
+ export const OrganizationRecoveryOverview = z
107
+ .object({
108
+ organizationId: Uuid,
109
+ availability: z.enum(["available", "recovery_unavailable"]),
110
+ unavailableReason: OrganizationRecoveryUnavailableReason.nullable(),
111
+ recentReauthenticationAt: Timestamp.nullable(),
112
+ eligibleMembers: z.array(OrganizationRecoveryMemberSummary).max(1000),
113
+ policy: OrganizationRecoveryPolicy.nullable(),
114
+ operation: OrganizationRecoveryOperation.nullable(),
115
+ capabilities: OrganizationRecoveryCapabilities,
116
+ })
117
+ .strict();
118
+ export type OrganizationRecoveryOverview = z.infer<typeof OrganizationRecoveryOverview>;
119
+
120
+ const ExpectedPolicyRevision = z.number().int().nonnegative();
121
+ const ExpectedOperationRevision = z.number().int().positive();
122
+ const CommandOperationId = Uuid;
123
+
124
+ export const ConfigureOrganizationRecoveryPolicyRequest = z
125
+ .object({
126
+ custodianMembershipIds: z.tuple([Uuid, Uuid, Uuid]).superRefine((ids, context) => {
127
+ if (new Set(ids).size !== 3) {
128
+ context.addIssue({
129
+ code: "custom",
130
+ message: "recovery custodians must be three distinct memberships",
131
+ });
132
+ }
133
+ }),
134
+ expectedPolicyRevision: ExpectedPolicyRevision,
135
+ operationId: CommandOperationId,
136
+ })
137
+ .strict();
138
+ export type ConfigureOrganizationRecoveryPolicyRequest = z.infer<
139
+ typeof ConfigureOrganizationRecoveryPolicyRequest
140
+ >;
141
+
142
+ export const AcceptOrganizationRecoveryCustodyRequest = z
143
+ .object({
144
+ expectedPolicyRevision: ExpectedPolicyRevision,
145
+ operationId: CommandOperationId,
146
+ })
147
+ .strict();
148
+ export type AcceptOrganizationRecoveryCustodyRequest = z.infer<
149
+ typeof AcceptOrganizationRecoveryCustodyRequest
150
+ >;
151
+
152
+ export const DisableOrganizationRecoveryPolicyRequest = AcceptOrganizationRecoveryCustodyRequest;
153
+ export type DisableOrganizationRecoveryPolicyRequest = z.infer<
154
+ typeof DisableOrganizationRecoveryPolicyRequest
155
+ >;
156
+
157
+ export const StartOrganizationRecoveryOperationRequest = z
158
+ .object({
159
+ targetMembershipId: Uuid,
160
+ expectedPolicyRevision: z.number().int().positive(),
161
+ operationId: CommandOperationId,
162
+ })
163
+ .strict();
164
+ export type StartOrganizationRecoveryOperationRequest = z.infer<
165
+ typeof StartOrganizationRecoveryOperationRequest
166
+ >;
167
+
168
+ export const OrganizationRecoveryOperationCommandRequest = z
169
+ .object({
170
+ expectedOperationRevision: ExpectedOperationRevision,
171
+ operationId: CommandOperationId,
172
+ })
173
+ .strict();
174
+ export type OrganizationRecoveryOperationCommandRequest = z.infer<
175
+ typeof OrganizationRecoveryOperationCommandRequest
176
+ >;
177
+
178
+ export const OrganizationRecoveryMutationResponse = z
179
+ .object({
180
+ replay: z.boolean(),
181
+ overview: OrganizationRecoveryOverview,
182
+ })
183
+ .strict();
184
+ export type OrganizationRecoveryMutationResponse = z.infer<
185
+ typeof OrganizationRecoveryMutationResponse
186
+ >;