@seamless-auth/types 0.3.0 → 0.4.1

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +121 -63
  3. package/dist/schemas/admin/schema.d.ts +1 -0
  4. package/dist/schemas/admin/schema.d.ts.map +1 -1
  5. package/dist/schemas/admin/schema.js.map +1 -1
  6. package/dist/schemas/auth/auth.schema.d.ts +9 -0
  7. package/dist/schemas/auth/auth.schema.d.ts.map +1 -1
  8. package/dist/schemas/auth/auth.schema.js.map +1 -1
  9. package/dist/schemas/common/schema.d.ts +1 -0
  10. package/dist/schemas/common/schema.d.ts.map +1 -1
  11. package/dist/schemas/credential/schema.d.ts +2 -0
  12. package/dist/schemas/credential/schema.d.ts.map +1 -1
  13. package/dist/schemas/credential/schema.js.map +1 -1
  14. package/dist/schemas/metrics/schema.d.ts +4 -0
  15. package/dist/schemas/metrics/schema.d.ts.map +1 -1
  16. package/dist/schemas/metrics/schema.js.map +1 -1
  17. package/dist/schemas/oauth/schema.d.ts +3 -0
  18. package/dist/schemas/oauth/schema.d.ts.map +1 -1
  19. package/dist/schemas/oauth/schema.js.map +1 -1
  20. package/dist/schemas/organization/schema.d.ts +6 -0
  21. package/dist/schemas/organization/schema.d.ts.map +1 -1
  22. package/dist/schemas/organization/schema.js.map +1 -1
  23. package/dist/schemas/role/schema.d.ts +2 -0
  24. package/dist/schemas/role/schema.d.ts.map +1 -1
  25. package/dist/schemas/session/schema.d.ts +1 -0
  26. package/dist/schemas/session/schema.d.ts.map +1 -1
  27. package/dist/schemas/session/schema.js.map +1 -1
  28. package/dist/schemas/systemConfig/schema.d.ts +8 -0
  29. package/dist/schemas/systemConfig/schema.d.ts.map +1 -1
  30. package/dist/schemas/systemConfig/schema.js.map +1 -1
  31. package/dist/schemas/totp/schema.d.ts +1 -0
  32. package/dist/schemas/totp/schema.d.ts.map +1 -1
  33. package/dist/schemas/user/schema.d.ts +2 -0
  34. package/dist/schemas/user/schema.d.ts.map +1 -1
  35. package/dist/schemas/user/schema.js.map +1 -1
  36. package/dist/schemas/webauthn/schema.d.ts +7 -2
  37. package/dist/schemas/webauthn/schema.d.ts.map +1 -1
  38. package/dist/schemas/webauthn/schema.js.map +1 -1
  39. package/package.json +6 -6
  40. package/src/index.ts +19 -0
  41. package/src/schemas/admin/schema.ts +54 -0
  42. package/src/schemas/auth/auth.schema.ts +139 -0
  43. package/src/schemas/authEvent/schema.ts +120 -0
  44. package/src/schemas/common/schema.ts +33 -0
  45. package/src/schemas/credential/schema.ts +103 -0
  46. package/src/schemas/me/schema.ts +32 -0
  47. package/src/schemas/messaging/schema.ts +86 -0
  48. package/src/schemas/metrics/schema.ts +115 -0
  49. package/src/schemas/oauth/schema.ts +70 -0
  50. package/src/schemas/organization/schema.ts +135 -0
  51. package/src/schemas/role/matching.ts +76 -0
  52. package/src/schemas/role/schema.ts +15 -0
  53. package/src/schemas/session/schema.ts +26 -0
  54. package/src/schemas/stepUp/schema.ts +27 -0
  55. package/src/schemas/systemConfig/schema.ts +196 -0
  56. package/src/schemas/totp/schema.ts +34 -0
  57. package/src/schemas/user/schema.ts +75 -0
  58. package/src/schemas/webauthn/schema.ts +82 -0
  59. package/src/shared.ts +3 -0
@@ -0,0 +1,27 @@
1
+ import { z } from 'zod';
2
+
3
+ export const StepUpMethodSchema = z.enum(['webauthn', 'totp']);
4
+
5
+ export type StepUpMethod = z.infer<typeof StepUpMethodSchema>;
6
+
7
+ /** Every field but `fresh` is null until the user has stepped up at least once. */
8
+ export const StepUpStatusSchema = z.object({
9
+ fresh: z.boolean(),
10
+ method: StepUpMethodSchema.nullable(),
11
+ verifiedAt: z.string().nullable(),
12
+ expiresAt: z.string().nullable(),
13
+ maxAgeSeconds: z.number(),
14
+ });
15
+
16
+ export type StepUpStatus = z.infer<typeof StepUpStatusSchema>;
17
+
18
+ export const StepUpSuccessSchema = z.object({
19
+ message: z.string(),
20
+ fresh: z.boolean(),
21
+ method: StepUpMethodSchema,
22
+ verifiedAt: z.string(),
23
+ expiresAt: z.string(),
24
+ maxAgeSeconds: z.number(),
25
+ });
26
+
27
+ export type StepUpSuccessResponse = z.infer<typeof StepUpSuccessSchema>;
@@ -0,0 +1,196 @@
1
+ import { z } from 'zod';
2
+ import { RoleNameSchema } from '../role/schema.js';
3
+
4
+ export const LoginMethodSchema = z.enum([
5
+ 'passkey',
6
+ 'magic_link',
7
+ 'email_otp',
8
+ 'phone_otp',
9
+ 'oauth',
10
+ ]);
11
+
12
+ export type LoginMethod = z.infer<typeof LoginMethodSchema>;
13
+
14
+ export const OAuthProviderIdSchema = z.string().regex(/^[a-z0-9-]{2,40}$/);
15
+
16
+ export type OAuthProviderId = z.infer<typeof OAuthProviderIdSchema>;
17
+
18
+ export const OAuthProviderConfigSchema = z.object({
19
+ id: OAuthProviderIdSchema,
20
+ name: z.string().trim().min(1).max(80),
21
+ enabled: z.boolean().default(true),
22
+ clientId: z.string().trim().min(1),
23
+ // The secret itself never lives in config; this names the environment
24
+ // variable the server reads it from.
25
+ clientSecretEnv: z.string().trim().min(1),
26
+ authorizationUrl: z.url(),
27
+ tokenUrl: z.url(),
28
+ userInfoUrl: z.url(),
29
+ scopes: z.array(z.string().trim().min(1)).default([]),
30
+ redirectUri: z.url().optional(),
31
+ redirectUris: z.array(z.url()).default([]),
32
+ subjectJsonPath: z.string().trim().min(1).default('sub'),
33
+ emailJsonPath: z.string().trim().min(1).default('email'),
34
+ emailVerifiedJsonPath: z.string().trim().min(1).default('email_verified'),
35
+ nameJsonPath: z.string().trim().min(1).optional(),
36
+ allowSignup: z.boolean().default(true),
37
+ accountLinking: z.enum(['email', 'disabled']).default('email'),
38
+ requireEmailVerified: z.boolean().default(false),
39
+ pkce: z.boolean().optional(),
40
+ });
41
+
42
+ export type OAuthProviderConfig = z.infer<typeof OAuthProviderConfigSchema>;
43
+
44
+ export const LockoutPolicySchema = z.object({
45
+ enabled: z.boolean().default(true),
46
+ maxFailures: z.number().int().positive().default(10),
47
+ windowSeconds: z
48
+ .number()
49
+ .int()
50
+ .positive()
51
+ .default(15 * 60),
52
+ lockoutSeconds: z
53
+ .number()
54
+ .int()
55
+ .positive()
56
+ .default(15 * 60),
57
+ });
58
+
59
+ export type LockoutPolicy = z.infer<typeof LockoutPolicySchema>;
60
+
61
+ export const DefaultLockoutPolicy = {
62
+ enabled: true,
63
+ maxFailures: 10,
64
+ windowSeconds: 15 * 60,
65
+ lockoutSeconds: 15 * 60,
66
+ } as const;
67
+
68
+ export const SystemConfigSchema = z.object({
69
+ app_name: z.string().min(3),
70
+ default_roles: z.array(RoleNameSchema).min(1),
71
+ available_roles: z.array(RoleNameSchema).min(1),
72
+ login_methods: z.array(LoginMethodSchema).min(1),
73
+ passkey_login_fallback_enabled: z.boolean(),
74
+ oauth_providers: z.array(OAuthProviderConfigSchema).default([]),
75
+ lockout_policy: LockoutPolicySchema.default(DefaultLockoutPolicy),
76
+
77
+ access_token_ttl: z.string().regex(/^\d+[smhd]$/),
78
+ refresh_token_ttl: z.string().regex(/^\d+[smhd]$/),
79
+
80
+ rate_limit: z.number().int().positive(),
81
+ delay_after: z.number().int().nonnegative(),
82
+
83
+ rpid: z.string().min(1),
84
+ origins: z.array(z.url()).min(1),
85
+
86
+ frontend_url: z.url().optional(),
87
+ });
88
+
89
+ export type SystemConfig = z.infer<typeof SystemConfigSchema>;
90
+
91
+ export const SystemConfigPatchSchema = z
92
+ .object({
93
+ app_name: SystemConfigSchema.shape.app_name.optional(),
94
+ default_roles: SystemConfigSchema.shape.default_roles.optional(),
95
+ available_roles: SystemConfigSchema.shape.available_roles.optional(),
96
+ login_methods: SystemConfigSchema.shape.login_methods.optional(),
97
+ passkey_login_fallback_enabled:
98
+ SystemConfigSchema.shape.passkey_login_fallback_enabled.optional(),
99
+ oauth_providers: z.array(OAuthProviderConfigSchema).optional(),
100
+ lockout_policy: LockoutPolicySchema.optional(),
101
+ access_token_ttl: SystemConfigSchema.shape.access_token_ttl.optional(),
102
+ refresh_token_ttl: SystemConfigSchema.shape.refresh_token_ttl.optional(),
103
+ rate_limit: SystemConfigSchema.shape.rate_limit.optional(),
104
+ delay_after: SystemConfigSchema.shape.delay_after.optional(),
105
+ rpid: SystemConfigSchema.shape.rpid.optional(),
106
+ origins: SystemConfigSchema.shape.origins.optional(),
107
+ })
108
+ .strict();
109
+
110
+ export type SystemConfigPatch = z.infer<typeof SystemConfigPatchSchema>;
111
+
112
+ /**
113
+ * A patch schema bound to the config currently stored, so role edits can be
114
+ * checked against the merged result rather than the patch alone.
115
+ */
116
+ export function createPatchSystemConfigSchema(existing: SystemConfig) {
117
+ return SystemConfigPatchSchema.superRefine((data, ctx) => {
118
+ const nextAvailable = data.available_roles ?? existing.available_roles;
119
+ const nextDefault = data.default_roles ?? existing.default_roles;
120
+
121
+ const removedDefaults =
122
+ data.available_roles !== undefined &&
123
+ existing.default_roles.some((role) => !data.available_roles?.includes(role));
124
+
125
+ if (removedDefaults) {
126
+ ctx.addIssue({
127
+ code: 'custom',
128
+ path: ['available_roles'],
129
+ message: 'Cannot remove roles currently set as default',
130
+ });
131
+ }
132
+
133
+ if (!nextDefault.every((role) => nextAvailable.includes(role))) {
134
+ ctx.addIssue({
135
+ code: 'custom',
136
+ path: ['default_roles'],
137
+ message: 'All default roles must exist in available_roles',
138
+ });
139
+ }
140
+ });
141
+ }
142
+
143
+ export const OAuthProviderCreateSchema = OAuthProviderConfigSchema;
144
+
145
+ export type OAuthProviderCreate = z.infer<typeof OAuthProviderCreateSchema>;
146
+
147
+ export const OAuthProviderIdParamSchema = z.object({
148
+ id: OAuthProviderIdSchema,
149
+ });
150
+
151
+ export type OAuthProviderIdParam = z.infer<typeof OAuthProviderIdParamSchema>;
152
+
153
+ // The id is immutable and taken from the path, so it is omitted here. Every other
154
+ // field is optional so callers can patch a single attribute without resending the
155
+ // whole provider; the merged result is re-validated against the full schema.
156
+ export const OAuthProviderUpdateSchema = OAuthProviderConfigSchema.omit({ id: true })
157
+ .partial()
158
+ .strict();
159
+
160
+ export type OAuthProviderUpdate = z.infer<typeof OAuthProviderUpdateSchema>;
161
+
162
+ export const OAuthProvidersListResponseSchema = z.object({
163
+ providers: z.array(OAuthProviderConfigSchema),
164
+ });
165
+
166
+ export type OAuthProvidersListResponse = z.infer<typeof OAuthProvidersListResponseSchema>;
167
+
168
+ export const OAuthProviderResponseSchema = z.object({
169
+ provider: OAuthProviderConfigSchema,
170
+ });
171
+
172
+ export type OAuthProviderResponse = z.infer<typeof OAuthProviderResponseSchema>;
173
+
174
+ export const OAuthProviderDeletedResponseSchema = z.object({
175
+ success: z.literal(true),
176
+ id: z.string(),
177
+ });
178
+
179
+ export type OAuthProviderDeletedResponse = z.infer<typeof OAuthProviderDeletedResponseSchema>;
180
+
181
+ export const GetSystemConfigResponseSchema = SystemConfigSchema;
182
+
183
+ export type GetSystemConfigResponse = z.infer<typeof GetSystemConfigResponseSchema>;
184
+
185
+ export const UpdateSystemConfigResponseSchema = z.object({
186
+ success: z.boolean(),
187
+ updatedKeys: z.array(z.string()),
188
+ });
189
+
190
+ export type UpdateSystemConfigResponse = z.infer<typeof UpdateSystemConfigResponseSchema>;
191
+
192
+ export const AvailableRolesResponseSchema = z.object({
193
+ roles: z.array(z.string()),
194
+ });
195
+
196
+ export type AvailableRolesResponse = z.infer<typeof AvailableRolesResponseSchema>;
@@ -0,0 +1,34 @@
1
+ import { z } from 'zod';
2
+
3
+ export const TotpVerifyRequestSchema = z.object({
4
+ code: z.string().regex(/^\d{6}$/),
5
+ });
6
+
7
+ export type TotpVerifyRequest = z.infer<typeof TotpVerifyRequestSchema>;
8
+
9
+ export const TotpStatusSchema = z.object({
10
+ enabled: z.boolean(),
11
+ verifiedAt: z.string().nullable(),
12
+ lastUsedAt: z.string().nullable(),
13
+ });
14
+
15
+ export type TotpStatus = z.infer<typeof TotpStatusSchema>;
16
+
17
+ export const TotpEnrollmentStartSchema = z.object({
18
+ message: z.string(),
19
+ secret: z.string(),
20
+ otpauthUrl: z.string(),
21
+ issuer: z.string(),
22
+ accountName: z.string(),
23
+ algorithm: z.literal('SHA1'),
24
+ digits: z.number(),
25
+ period: z.number(),
26
+ });
27
+
28
+ export type TotpEnrollmentStartResponse = z.infer<typeof TotpEnrollmentStartSchema>;
29
+
30
+ export const TotpVerifySuccessSchema = z.object({
31
+ message: z.string(),
32
+ });
33
+
34
+ export type TotpVerifySuccess = z.infer<typeof TotpVerifySuccessSchema>;
@@ -0,0 +1,75 @@
1
+ import { z } from 'zod';
2
+ import { IsoDate } from '../../shared.js';
3
+ import { RoleNameSchema } from '../role/schema.js';
4
+
5
+ export const UserSchema = z.object({
6
+ id: z.uuid(),
7
+ email: z.email(),
8
+ phone: z.string(),
9
+ roles: z.array(RoleNameSchema).default([]),
10
+ revoked: z.boolean().default(false),
11
+ emailVerified: z.boolean().default(false),
12
+ phoneVerified: z.boolean().default(false),
13
+ verified: z.boolean().default(false),
14
+ lastLogin: IsoDate.nullable().optional(),
15
+ createdAt: IsoDate,
16
+ updatedAt: IsoDate.nullable().optional(),
17
+ });
18
+
19
+ export type User = z.infer<typeof UserSchema>;
20
+
21
+ /**
22
+ * The user as it crosses the wire. Looser than {@link UserSchema}: the id is an
23
+ * opaque string rather than a UUID, phone is nullable for email-only accounts,
24
+ * and the verification flags are absent on endpoints that do not expose them.
25
+ */
26
+ export const ApiUserSchema = z
27
+ .object({
28
+ id: z.string(),
29
+ email: z.email(),
30
+ phone: z.string().nullable(),
31
+ roles: z.array(RoleNameSchema).default([]),
32
+ revoked: z.boolean().optional(),
33
+ emailVerified: z.boolean().optional(),
34
+ phoneVerified: z.boolean().optional(),
35
+ verified: z.boolean().optional(),
36
+ lastLogin: IsoDate.nullable().optional(),
37
+ createdAt: IsoDate.optional(),
38
+ updatedAt: IsoDate.optional(),
39
+ })
40
+ .strict();
41
+
42
+ export type ApiUser = z.infer<typeof ApiUserSchema>;
43
+
44
+ export const CreateUserSchema = z.object({
45
+ email: z.email(),
46
+ phone: z.string().min(5).nullish(),
47
+ roles: z.array(RoleNameSchema).min(1),
48
+ });
49
+
50
+ export type CreateUserRequest = z.infer<typeof CreateUserSchema>;
51
+
52
+ export const UpdateUserSchema = z
53
+ .object({
54
+ email: z.email().optional(),
55
+ phone: z.string().min(5).nullish(),
56
+ emailVerified: z.boolean().optional(),
57
+ phoneVerified: z.boolean().optional(),
58
+ roles: z.array(RoleNameSchema).min(1).optional(),
59
+ })
60
+ .strict();
61
+
62
+ export type UpdateUserRequest = z.infer<typeof UpdateUserSchema>;
63
+
64
+ export const UserResponseSchema = z.object({
65
+ user: ApiUserSchema,
66
+ });
67
+
68
+ export type UserResponse = z.infer<typeof UserResponseSchema>;
69
+
70
+ export const UsersListResponseSchema = z.object({
71
+ users: z.array(ApiUserSchema),
72
+ total: z.number(),
73
+ });
74
+
75
+ export type UsersListResponse = z.infer<typeof UsersListResponseSchema>;
@@ -0,0 +1,82 @@
1
+ import { z } from 'zod';
2
+
3
+ const BooleanQuerySchema = z.preprocess((value) => {
4
+ if (value === 'true') return true;
5
+ if (value === 'false') return false;
6
+ return value;
7
+ }, z.boolean().optional());
8
+
9
+ /**
10
+ * PRF salts are validated for length and encoding by the server, which owns the
11
+ * rule. This schema only asserts the wire shape so both sides agree on it.
12
+ */
13
+ export const WebAuthnPrfRequestSchema = z.object({
14
+ salt: z.string(),
15
+ secondSalt: z.string().optional(),
16
+ });
17
+
18
+ export type WebAuthnPrfRequest = z.infer<typeof WebAuthnPrfRequestSchema>;
19
+
20
+ export const WebAuthnRegisterStartQuerySchema = z.object({
21
+ requestPrf: BooleanQuerySchema,
22
+ requirePrf: BooleanQuerySchema,
23
+ });
24
+
25
+ export type WebAuthnRegisterStartQuery = z.infer<typeof WebAuthnRegisterStartQuerySchema>;
26
+
27
+ export const WebAuthnAssertionStartSchema = z
28
+ .object({
29
+ credentialId: z.string().optional(),
30
+ prf: WebAuthnPrfRequestSchema.optional(),
31
+ })
32
+ .default({});
33
+
34
+ export type WebAuthnAssertionStart = z.infer<typeof WebAuthnAssertionStartSchema>;
35
+
36
+ export const WebAuthnCredentialMetadataSchema = z.object({
37
+ friendlyName: z.string().optional(),
38
+ platform: z.string().optional(),
39
+ browser: z.string().optional(),
40
+ deviceInfo: z.string().optional(),
41
+ prfCapable: z.boolean().optional(),
42
+ });
43
+
44
+ export type WebAuthnCredentialMetadata = z.infer<typeof WebAuthnCredentialMetadataSchema>;
45
+
46
+ export const WebAuthnRegisterFinishSchema = z.object({
47
+ attestationResponse: z.record(z.string(), z.unknown()),
48
+ metadata: WebAuthnCredentialMetadataSchema.optional(),
49
+ });
50
+
51
+ export type WebAuthnRegisterFinish = z.infer<typeof WebAuthnRegisterFinishSchema>;
52
+
53
+ export const WebAuthnLoginFinishSchema = z.object({
54
+ assertionResponse: z.record(z.string(), z.unknown()),
55
+ });
56
+
57
+ export type WebAuthnLoginFinish = z.infer<typeof WebAuthnLoginFinishSchema>;
58
+
59
+ /**
60
+ * The credential creation and request options are passed through to the browser
61
+ * verbatim, so they are not narrowed here.
62
+ */
63
+ export const WebAuthnChallengeSchema = z.record(z.string(), z.unknown());
64
+
65
+ export type WebAuthnChallenge = z.infer<typeof WebAuthnChallengeSchema>;
66
+
67
+ export const WebAuthnTokenSuccessSchema = z.object({
68
+ message: z.string(),
69
+ token: z.string().optional(),
70
+ refreshToken: z.string().optional(),
71
+ refreshTokenHash: z.string().optional(),
72
+
73
+ sub: z.string().optional(),
74
+ roles: z.array(z.string()).optional(),
75
+ email: z.string().optional(),
76
+ phone: z.string().nullable().optional(),
77
+
78
+ ttl: z.number().optional(),
79
+ refreshTtl: z.number().optional(),
80
+ });
81
+
82
+ export type WebAuthnTokenSuccessResponse = z.infer<typeof WebAuthnTokenSuccessSchema>;
package/src/shared.ts ADDED
@@ -0,0 +1,3 @@
1
+ import z from 'zod';
2
+
3
+ export const IsoDate = z.coerce.date().transform((d) => d.toISOString());