@seamless-auth/types 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @seamless-auth/types
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 9ae3092: No contract changes. Schemas, types, and exports are untouched.
8
+
9
+ Packaging: the non-test sources under `src` now ship in the tarball. The published `dist` has
10
+ always included declaration maps and source maps, but the sources they pointed at were not in the
11
+ package, so the maps were dangling. Go to Definition now lands on the actual schema instead of a
12
+ `.d.ts`.
13
+
14
+ Documentation: the README now carries npm, CI, Node, and license badges, a requirements section
15
+ that spells out the module resolution the subpath exports need, a conventions section covering the
16
+ schema and type alias pairing, and sections on versioning, supply chain, and security.
17
+ `SECURITY.md` states which versions are supported and what is in scope for this repository.
18
+
3
19
  ## 0.4.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -1,24 +1,35 @@
1
1
  # @seamless-auth/types
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@seamless-auth/types?logo=npm&color=cb3837)](https://www.npmjs.com/package/@seamless-auth/types)
4
+ [![CI](https://github.com/fells-code/seamless-auth-types/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fells-code/seamless-auth-types/actions/workflows/ci.yml)
5
+ [![Node](https://img.shields.io/node/v/@seamless-auth/types?logo=node.js&logoColor=white)](.nvmrc)
6
+ [![License: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-blue.svg)](LICENSE)
7
+
3
8
  Shared TypeScript types and Zod schemas for the SeamlessAuth ecosystem.
4
9
 
5
- This package provides a single source of truth for domain models like Users, Credentials, Sessions, and Auth Events, ensuring consistency across:
10
+ This package is the single source of truth for the SeamlessAuth domain models (Users,
11
+ Credentials, Sessions, Auth, Auth Events, Organizations) and for the request and response
12
+ contracts built on top of them. Every layer of the stack validates against the same schemas:
6
13
 
7
14
  - API servers
8
- - SDKs
15
+ - SDKs and server adapters
9
16
  - Frontend applications
10
17
  - CLI tools
11
18
 
12
- ---
19
+ Because everything downstream imports these contracts, a change here is treated as
20
+ breaking by default. See [Versioning and stability](#versioning-and-stability).
13
21
 
14
- ## Features
22
+ ## Requirements
15
23
 
16
- - Zod-first schemas with runtime validation and type inference
17
- - Strict typing across all models
18
- - Framework-agnostic
19
- - Minimal dependencies (Zod only)
24
+ | | |
25
+ | -------------------- | ------------------------- |
26
+ | Node | 24 (`>=24 <25`) |
27
+ | TypeScript | 5.x |
28
+ | Runtime dependencies | `zod@^4` and nothing else |
20
29
 
21
- ---
30
+ The package ships as ESM only. The root import works under any module resolution, but the
31
+ [subpath exports](#subpath-exports) are resolved from `package.json` `exports`, so consumers
32
+ that use them need `moduleResolution` set to `node16`, `nodenext`, or `bundler`.
22
33
 
23
34
  ## Installation
24
35
 
@@ -26,24 +37,47 @@ This package provides a single source of truth for domain models like Users, Cre
26
37
  npm install @seamless-auth/types
27
38
  ```
28
39
 
29
- ---
40
+ ## Quick start
41
+
42
+ ```ts
43
+ import { UserSchema, type User } from '@seamless-auth/types';
44
+
45
+ // Validate untrusted input and get a fully typed value back.
46
+ const user: User = UserSchema.parse(await response.json());
47
+ ```
30
48
 
31
49
  ## Usage
32
50
 
33
- ### Import schemas
51
+ ### Validate data
52
+
53
+ Every model is a Zod schema, so you get runtime validation and the static type from one
54
+ declaration:
34
55
 
35
56
  ```ts
36
- import { UserSchema } from '@seamless-auth/types';
57
+ import { LoginRequestSchema } from '@seamless-auth/types';
58
+
59
+ const parsed = LoginRequestSchema.safeParse(req.body);
60
+ if (!parsed.success) {
61
+ return res.status(400).json({ error: parsed.error.issues });
62
+ }
37
63
  ```
38
64
 
39
- ### Validate data
65
+ ### Infer types
40
66
 
41
67
  ```ts
42
- const user = UserSchema.parse(data);
68
+ import type { User } from '@seamless-auth/types';
69
+
70
+ function handleUser(user: User) {
71
+ console.log(user.email);
72
+ }
43
73
  ```
44
74
 
45
- User role schemas accept plain roles such as `admin` and colon-separated scoped roles such as
46
- `admin:read` and `admin:write`. Whitespace, underscores, slashes, and backslashes are rejected.
75
+ ### Roles
76
+
77
+ Role schemas accept plain roles such as `admin` and colon-separated scoped roles such as
78
+ `admin:read` and `admin:write`. Whitespace, underscores, slashes, and backslashes are
79
+ rejected, so a role name is always safe to embed in a JWT claim, a URL path segment, or a
80
+ config file without escaping.
47
81
 
48
82
  ### Match roles without Zod
49
83
 
@@ -62,22 +96,10 @@ if (!hasScopedRole(user.roles, 'billing:invoices:read')) {
62
96
  `@seamless-auth/types/role` adds `RoleNameSchema` on top of the same matchers, and the package
63
97
  root still exports all of them, so existing imports keep working.
64
98
 
65
- ### Infer types
66
-
67
- ```ts
68
- import type { User } from '@seamless-auth/types';
69
-
70
- function handleUser(user: User) {
71
- console.log(user.email);
72
- }
73
- ```
74
-
75
- ---
76
-
77
99
  ## Modules
78
100
 
79
- Every module is re-exported from the package root, so import from
80
- `@seamless-auth/types` unless one of the subpath exports below fits better.
101
+ Every module is re-exported from the package root, so import from `@seamless-auth/types`
102
+ unless one of the subpath exports below fits better.
81
103
 
82
104
  | Module | Covers |
83
105
  | -------------- | ------------------------------------------------------------------- |
@@ -106,69 +128,105 @@ Every module is re-exported from the package root, so import from
106
128
  | `@seamless-auth/types/role` | Role name schema plus the matchers |
107
129
  | `@seamless-auth/types/role/matching` | The matchers alone, with no Zod import |
108
130
 
109
- ---
110
-
111
- ## Zod as the Source of Truth
131
+ ## Conventions
112
132
 
113
- All models are defined using Zod:
133
+ **Zod is the source of truth.** Models are declared as schemas and the TypeScript type is
134
+ inferred from the schema. No type in this package is hand-written alongside a schema that
135
+ already describes it:
114
136
 
115
137
  ```ts
116
138
  export const UserSchema = z.object({
117
- id: z.string().uuid(),
118
- email: z.string().email(),
139
+ id: z.uuid(),
140
+ email: z.email(),
119
141
  });
120
- ```
121
-
122
- Types are inferred:
123
142
 
124
- ```ts
125
143
  export type User = z.infer<typeof UserSchema>;
126
144
  ```
127
145
 
128
- ---
146
+ **Every exported schema has a matching type alias.** `XSchema` is always paired with `X`, so
147
+ you never have to write `z.infer<typeof XSchema>` at a call site. This is enforced by a test,
148
+ not by convention alone.
149
+
150
+ **Framework-agnostic.** No server, browser, or Node-only APIs, and no runtime dependency
151
+ beyond Zod, so the same contracts load in an API process, a browser bundle, and a CLI.
152
+
153
+ **Strict typing throughout.** No `any` and no non-null assertions. Wire-facing schemas are
154
+ `.strict()` where unknown keys should be rejected rather than silently dropped.
129
155
 
130
- ## Versioning
156
+ ## Versioning and stability
131
157
 
132
158
  This package follows semantic versioning:
133
159
 
134
- - PATCH for fixes and non-breaking improvements
135
- - MINOR for additive changes such as new fields or models
136
- - MAJOR for breaking changes such as field removals or type changes
160
+ - **PATCH** for fixes with no effect on the contract, including docs and internal tidying
161
+ - **MINOR** for additive changes such as a new module, export, or optional field
162
+ - **MAJOR** for breaking changes such as a removed or renamed export, a field whose type
163
+ changes, or a schema that now rejects input it used to accept
164
+
165
+ A widening change (a wider enum, a field that becomes nullable) is not breaking at runtime,
166
+ but it does change the inferred TypeScript type, so it is called out in the release notes.
167
+
168
+ Releases are managed with [Changesets](https://github.com/changesets/changesets). See
169
+ [RELEASES.md](RELEASES.md) for the release flow and [CHANGELOG.md](CHANGELOG.md) for the
170
+ history.
137
171
 
138
- ---
172
+ ## Supply chain
173
+
174
+ - Published from CI only, from a tagged release on `main`, never from a developer machine.
175
+ - Every release carries [npm provenance](https://docs.npmjs.com/generating-provenance-statements),
176
+ so the published tarball is cryptographically linked to the commit and workflow that built it.
177
+ - One runtime dependency (`zod`). Dependency updates are automated and reviewed like any other
178
+ change.
179
+ - The published tarball is limited to `dist`, the non-test sources under `src`, `README.md`,
180
+ `CHANGELOG.md`, `LICENSE`, and `package.json`. `npm run check-npm-build` prints the contents in
181
+ CI on every push and release.
182
+ - Sources ship alongside the declaration maps and source maps, so Go to Definition in your editor
183
+ lands on the actual schema rather than a `.d.ts`.
184
+
185
+ ## Security
186
+
187
+ Do not open a public issue for a vulnerability. Report it privately to
188
+ **security@seamlessauth.com**. See [SECURITY.md](SECURITY.md) for what to include, our
189
+ acknowledgement window, and the coordinated disclosure process.
139
190
 
140
191
  ## Development
141
192
 
142
193
  ```bash
143
- # build
144
- npm run build
194
+ nvm use # Node 24, pinned by .nvmrc
195
+ npm ci
145
196
 
146
- # watch mode
147
- npm run dev
148
-
149
- # type check
197
+ npm run build # compile to dist/
198
+ npm run dev # tsc --watch
150
199
  npm run typecheck
200
+ npm run lint
201
+ npm run format:check
202
+ npm test
151
203
  ```
152
204
 
153
205
  ## Contributing
154
206
 
155
- Contributions are welcome.
207
+ Contributions are welcome. Start with the organization
208
+ [contributing guide](https://github.com/fells-code/.github/blob/main/CONTRIBUTING.md) and the
209
+ [repository standards](https://github.com/fells-code/.github/blob/main/REPO-STANDARDS.md).
210
+ [AGENTS.md](AGENTS.md) documents the repository layout and the working standards in more detail.
156
211
 
157
- Please ensure:
212
+ For a change in this repository specifically:
158
213
 
159
- - strict TypeScript compliance
160
- - schemas remain Zod-based
161
- - no framework-specific dependencies are introduced
214
+ 1. Add or change the schema under `src/schemas/<model>/`.
215
+ 2. Export it from `src/index.ts` if it is part of the public API.
216
+ 3. Run `npm run typecheck`, `npm run lint`, `npm run format:check`, `npm test`, and `npm run build`.
217
+ 4. Run `npm run changeset` and write the notes for the repositories that consume these
218
+ contracts, not for the implementation. A change to `src/` without a changeset ships nothing.
162
219
 
163
- ---
220
+ Keep schemas Zod-based, keep the package framework-agnostic, and treat any change to an
221
+ existing schema as breaking until you can argue otherwise.
164
222
 
165
223
  ## License
166
224
 
167
- AGPL-3.0 © Fells Code, LLC
168
-
169
- ---
225
+ [AGPL-3.0-only](LICENSE) © Fells Code, LLC
170
226
 
171
227
  ## Links
172
228
 
173
- - GitHub: [https://github.com/fells-code/seamless-auth-types](https://github.com/fells-code/seamless-auth-types)
174
- - SeamlessAuth: [https://seamlessauth.com](https://seamlessauth.com)
229
+ - [npm package](https://www.npmjs.com/package/@seamless-auth/types)
230
+ - [GitHub repository](https://github.com/fells-code/seamless-auth-types)
231
+ - [SeamlessAuth](https://seamlessauth.com)
232
+ - [Fells Code](https://github.com/fells-code)
@@ -9,8 +9,8 @@ export declare const WebAuthnPrfRequestSchema: z.ZodObject<{
9
9
  }, z.core.$strip>;
10
10
  export type WebAuthnPrfRequest = z.infer<typeof WebAuthnPrfRequestSchema>;
11
11
  export declare const WebAuthnRegisterStartQuerySchema: z.ZodObject<{
12
- requestPrf: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodOptional<z.ZodBoolean>>;
13
- requirePrf: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodOptional<z.ZodBoolean>>;
12
+ requestPrf: z.ZodPreprocess<z.ZodOptional<z.ZodBoolean>>;
13
+ requirePrf: z.ZodPreprocess<z.ZodOptional<z.ZodBoolean>>;
14
14
  }, z.core.$strip>;
15
15
  export type WebAuthnRegisterStartQuery = z.infer<typeof WebAuthnRegisterStartQuerySchema>;
16
16
  export declare const WebAuthnAssertionStartSchema: z.ZodDefault<z.ZodObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamless-auth/types",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Shared TypeScript types and Zod schemas for SeamlessAuth.",
5
5
  "author": "Fells Code, LLC",
6
6
  "license": "AGPL-3.0-only",
@@ -24,6 +24,8 @@
24
24
  "sideEffects": false,
25
25
  "files": [
26
26
  "dist",
27
+ "src",
28
+ "!src/**/*.test.ts",
27
29
  "README.md",
28
30
  "CHANGELOG.md",
29
31
  "LICENSE"
@@ -58,7 +60,6 @@
58
60
  "prepublishOnly": "npm run build",
59
61
  "test": "vitest run",
60
62
  "test:watch": "vitest",
61
- "coverage": "vitest run --coverage",
62
63
  "changeset": "changeset",
63
64
  "version-packages": "changeset version",
64
65
  "release:stable": "npm run build && changeset publish",
@@ -78,15 +79,14 @@
78
79
  },
79
80
  "devDependencies": {
80
81
  "@changesets/cli": "^2.31.1",
81
- "@commitlint/cli": "^20.5.0",
82
- "@commitlint/config-conventional": "^20.5.0",
82
+ "@commitlint/cli": "^21.2.1",
83
+ "@commitlint/config-conventional": "^21.2.0",
83
84
  "@typescript-eslint/eslint-plugin": "^8.57.2",
84
85
  "@typescript-eslint/parser": "^8.57.2",
85
- "@vitest/coverage-v8": "^4.1.10",
86
86
  "eslint": "^10.1.0",
87
87
  "eslint-config-prettier": "^10.1.8",
88
88
  "husky": "^9.1.7",
89
- "lint-staged": "^16.4.0",
89
+ "lint-staged": "^17.2.0",
90
90
  "prettier": "^3.8.1",
91
91
  "typescript": "^5.9.3",
92
92
  "vitest": "^4.1.1"
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export * from './shared.js';
2
+
3
+ export * from './schemas/common/schema.js';
4
+ export * from './schemas/role/schema.js';
5
+ export * from './schemas/user/schema.js';
6
+ export * from './schemas/credential/schema.js';
7
+ export * from './schemas/session/schema.js';
8
+ export * from './schemas/authEvent/schema.js';
9
+ export * from './schemas/messaging/schema.js';
10
+ export * from './schemas/systemConfig/schema.js';
11
+ export * from './schemas/auth/auth.schema.js';
12
+ export * from './schemas/oauth/schema.js';
13
+ export * from './schemas/webauthn/schema.js';
14
+ export * from './schemas/totp/schema.js';
15
+ export * from './schemas/stepUp/schema.js';
16
+ export * from './schemas/organization/schema.js';
17
+ export * from './schemas/me/schema.js';
18
+ export * from './schemas/metrics/schema.js';
19
+ export * from './schemas/admin/schema.js';
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ import { AuthEventSchema } from '../authEvent/schema.js';
3
+ import { CredentialResponseSchema } from '../credential/schema.js';
4
+ import { SessionSchema } from '../session/schema.js';
5
+ import { ApiUserSchema } from '../user/schema.js';
6
+
7
+ export const UserIdParamSchema = z.object({
8
+ userId: z.string(),
9
+ });
10
+
11
+ export type UserIdParam = z.infer<typeof UserIdParamSchema>;
12
+
13
+ /**
14
+ * What an operator clears when a user replaces a lost device. Each step is
15
+ * opt-out rather than opt-in so a hurried recovery does not leave the old
16
+ * device's credentials in place.
17
+ */
18
+ export const DeviceReplacementRecoverySchema = z
19
+ .object({
20
+ revokeSessions: z.boolean().default(true),
21
+ removePasskeys: z.boolean().default(true),
22
+ disableTotp: z.boolean().default(true),
23
+ })
24
+ .strict();
25
+
26
+ export type DeviceReplacementRecoveryRequest = z.infer<typeof DeviceReplacementRecoverySchema>;
27
+
28
+ export const DeviceReplacementRecoveryResponseSchema = z.object({
29
+ userId: z.string(),
30
+ revokedSessions: z.number().int().nonnegative(),
31
+ removedCredentials: z.number().int().nonnegative(),
32
+ disabledTotpCredentials: z.number().int().nonnegative(),
33
+ });
34
+
35
+ export type DeviceReplacementRecoveryResponse = z.infer<
36
+ typeof DeviceReplacementRecoveryResponseSchema
37
+ >;
38
+
39
+ export const AdminUserDetailResponseSchema = z.object({
40
+ user: ApiUserSchema,
41
+ sessions: z.array(SessionSchema),
42
+ credentials: z.array(CredentialResponseSchema),
43
+ events: z.array(AuthEventSchema),
44
+ });
45
+
46
+ export type AdminUserDetailResponse = z.infer<typeof AdminUserDetailResponseSchema>;
47
+
48
+ export const AdminUserAnomaliesResponseSchema = z.object({
49
+ suspiciousEvents: z.array(AuthEventSchema),
50
+ relatedIps: z.array(z.string()),
51
+ relatedAgents: z.array(z.string()),
52
+ });
53
+
54
+ export type AdminUserAnomaliesResponse = z.infer<typeof AdminUserAnomaliesResponseSchema>;
@@ -0,0 +1,139 @@
1
+ import z from 'zod';
2
+ import { AuthDeliverySchema } from '../messaging/schema.js';
3
+ import { LoginMethodSchema } from '../systemConfig/schema.js';
4
+
5
+ export const IdentifierTypeSchema = z.enum(['email', 'phone']);
6
+
7
+ export type IdentifierType = z.infer<typeof IdentifierTypeSchema>;
8
+
9
+ export const LoginRequestSchema = z.object({
10
+ identifier: z.string(),
11
+ passkeyAvailable: z.boolean().optional(),
12
+ });
13
+
14
+ export type LoginRequest = z.infer<typeof LoginRequestSchema>;
15
+
16
+ export const LoginSuccessResponseSchema = z.object({
17
+ message: z.string(),
18
+ token: z.string().optional(),
19
+ sub: z.string().optional(),
20
+ identifierType: IdentifierTypeSchema.optional(),
21
+ loginMethods: z.array(LoginMethodSchema).optional(),
22
+ ttl: z.number().optional(),
23
+ });
24
+
25
+ export type LoginSuccessResponse = z.infer<typeof LoginSuccessResponseSchema>;
26
+
27
+ export const RefreshTokenRequestSchema = z.object({});
28
+
29
+ export type RefreshTokenRequest = z.infer<typeof RefreshTokenRequestSchema>;
30
+
31
+ /** @deprecated Use {@link RefreshTokenRequestSchema}. */
32
+ export const RefreshRequestSchema = RefreshTokenRequestSchema;
33
+
34
+ /** @deprecated Use {@link RefreshTokenRequest}. */
35
+ export type RefreshRequest = z.infer<typeof RefreshRequestSchema>;
36
+
37
+ /**
38
+ * The token envelope every completed authentication returns, whatever the
39
+ * method. `sessionId` is only present where the flow names the session it
40
+ * created; OTP, magic link, and OAuth completions omit it.
41
+ */
42
+ export const RefreshSuccessResponseSchema = z.object({
43
+ message: z.string(),
44
+ token: z.string().optional(),
45
+ refreshToken: z.string().optional(),
46
+ sub: z.string().optional(),
47
+ sessionId: z.string().optional(),
48
+ organizationId: z.string().nullable().optional(),
49
+ roles: z.array(z.string()).optional(),
50
+ email: z.string().optional(),
51
+ phone: z.string().nullable().optional(),
52
+ ttl: z.number().optional(),
53
+ refreshTtl: z.number().optional(),
54
+ });
55
+
56
+ export type RefreshSuccessResponse = z.infer<typeof RefreshSuccessResponseSchema>;
57
+
58
+ export const LogoutScopeSchema = z.enum(['current_session', 'all_sessions']);
59
+
60
+ export type LogoutScope = z.infer<typeof LogoutScopeSchema>;
61
+
62
+ export const RegistrationRequestSchema = z.object({
63
+ email: z.email(),
64
+ // Registration only needs an email. A phone can be added and verified later.
65
+ phone: z.string().nullish(),
66
+ });
67
+
68
+ export type RegistrationRequest = z.infer<typeof RegistrationRequestSchema>;
69
+
70
+ export const RegistrationSuccessSchema = z.object({
71
+ message: z.string(),
72
+ sub: z.string().optional(),
73
+ token: z.string().optional(),
74
+ ttl: z.string().optional(),
75
+ delivery: AuthDeliverySchema.optional(),
76
+ });
77
+
78
+ export type RegistrationSuccessResponse = z.infer<typeof RegistrationSuccessSchema>;
79
+
80
+ export const RegisterPhoneRequestSchema = z.object({
81
+ phone: z.string(),
82
+ });
83
+
84
+ export type RegisterPhoneRequest = z.infer<typeof RegisterPhoneRequestSchema>;
85
+
86
+ export const RegisterPhoneSuccessSchema = z.object({
87
+ message: z.string(),
88
+ phone: z.string(),
89
+ delivery: AuthDeliverySchema.optional(),
90
+ });
91
+
92
+ export type RegisterPhoneSuccess = z.infer<typeof RegisterPhoneSuccessSchema>;
93
+
94
+ export const VerifyOTPRequestSchema = z.object({
95
+ verificationToken: z.string(),
96
+ });
97
+
98
+ export type VerifyOTPRequest = z.infer<typeof VerifyOTPRequestSchema>;
99
+
100
+ export const OTPVerifyTokenSuccessSchema = RefreshSuccessResponseSchema.omit({
101
+ sessionId: true,
102
+ });
103
+
104
+ export type OTPVerifyTokenSuccess = z.infer<typeof OTPVerifyTokenSuccessSchema>;
105
+
106
+ export const MagicLinkVerifyParamsSchema = z.object({
107
+ token: z.string(),
108
+ });
109
+
110
+ export type MagicLinkVerifyParams = z.infer<typeof MagicLinkVerifyParamsSchema>;
111
+
112
+ export const MagicLinkPollSuccessSchema = RefreshSuccessResponseSchema.omit({
113
+ sessionId: true,
114
+ organizationId: true,
115
+ });
116
+
117
+ export type MagicLinkPollSuccess = z.infer<typeof MagicLinkPollSuccessSchema>;
118
+
119
+ /** Body for endpoints that acknowledge a request and may hand back a delivery. */
120
+ export const AuthMessageResponseSchema = z.object({
121
+ message: z.string(),
122
+ token: z.string().optional(),
123
+ delivery: AuthDeliverySchema.optional(),
124
+ });
125
+
126
+ export type AuthMessageResponse = z.infer<typeof AuthMessageResponseSchema>;
127
+
128
+ /** The access token's claims, as a resource server reads them off a request. */
129
+ export const SeamlessAuthUserSchema = z.object({
130
+ id: z.string(),
131
+ roles: z.array(z.string()),
132
+ email: z.string().optional(),
133
+ phone: z.string().nullable().optional(),
134
+ iat: z.number().optional(),
135
+ exp: z.number().optional(),
136
+ token: z.string().optional(),
137
+ });
138
+
139
+ export type SeamlessAuthUser = z.infer<typeof SeamlessAuthUserSchema>;
@@ -0,0 +1,120 @@
1
+ import z from 'zod';
2
+ import { IsoDate } from '../../shared.js';
3
+
4
+ export const AUTH_EVENT_TYPES = [
5
+ 'admin_device_replacement_recovery',
6
+ 'admin_session_revoked',
7
+ 'auth_action_incremented',
8
+ 'bearer_token_failed',
9
+ 'bearer_token_success',
10
+ 'bearer_token_suspicious',
11
+ 'credentials_deleted',
12
+ 'informational',
13
+ 'internal_user_updated_by_owner',
14
+ 'jwks_failed',
15
+ 'jwks_success',
16
+ 'jwks_suspicious',
17
+ 'login_challenge',
18
+ 'login_failed',
19
+ 'login_success',
20
+ 'login_suspicious',
21
+ 'logout_failed',
22
+ 'logout_success',
23
+ 'logout_suspicious',
24
+ 'magic_link_failed',
25
+ 'magic_link_poll_completed_successfully',
26
+ 'magic_link_requested',
27
+ 'magic_link_success',
28
+ 'mfa_otp_failed',
29
+ 'mfa_otp_success',
30
+ 'mfa_otp_suspicious',
31
+ 'notification_sent',
32
+ 'oauth_login_failed',
33
+ 'oauth_login_started',
34
+ 'oauth_login_success',
35
+ 'otp_failed',
36
+ 'otp_success',
37
+ 'otp_suspicious',
38
+ 'recovery_otp_failed',
39
+ 'recovery_otp_success',
40
+ 'recovery_otp_suspicious',
41
+ 'refresh_token_failed',
42
+ 'refresh_token_success',
43
+ 'refresh_token_suspicious',
44
+ 'registration_failed',
45
+ 'registration_success',
46
+ 'registration_suspicious',
47
+ 'request_suspicious',
48
+ 'service_token_failed',
49
+ 'service_token_rotated',
50
+ 'service_token_success',
51
+ 'service_token_suspicious',
52
+ 'step_up_challenge',
53
+ 'step_up_failed',
54
+ 'step_up_success',
55
+ 'step_up_suspicious',
56
+ 'system_config_error',
57
+ 'system_config_read',
58
+ 'system_config_updated',
59
+ 'totp_disabled',
60
+ 'totp_enrollment_started',
61
+ 'totp_enrollment_success',
62
+ 'totp_failed',
63
+ 'totp_success',
64
+ 'totp_suspicious',
65
+ 'user_created',
66
+ 'user_data_failed',
67
+ 'user_data_success',
68
+ 'user_data_suspicious',
69
+ 'user_deleted',
70
+ 'verify_otp_failed',
71
+ 'verify_otp_success',
72
+ 'verify_otp_suspicious',
73
+ 'webauthn_login_failed',
74
+ 'webauthn_login_success',
75
+ 'webauthn_login_suspicious',
76
+ 'webauthn_registration_failed',
77
+ 'webauthn_registration_success',
78
+ 'webauthn_registration_suspicious',
79
+ ] as const;
80
+
81
+ export const AuthEventTypeEnum = z.enum(AUTH_EVENT_TYPES);
82
+
83
+ export type AuthEventType = z.infer<typeof AuthEventTypeEnum>;
84
+
85
+ // `type` stays a plain string: a stored event written by a newer server must
86
+ // still parse on an older consumer. Use AuthEventTypeEnum to validate input.
87
+ export const AuthEventSchema = z.object({
88
+ id: z.string(),
89
+ user_id: z.string().nullable().optional(),
90
+ type: z.string(),
91
+ ip_address: z.string().nullable().optional(),
92
+ user_agent: z.string().nullable().optional(),
93
+ metadata: z.record(z.string(), z.unknown()).nullable(),
94
+ created_at: IsoDate,
95
+ updated_at: IsoDate,
96
+ });
97
+
98
+ export type AuthEvent = z.infer<typeof AuthEventSchema>;
99
+
100
+ export const AuthEventQuerySchema = z.object({
101
+ limit: z.coerce.number().min(1).max(100).default(10),
102
+ offset: z.coerce.number().min(0).default(0),
103
+
104
+ userId: z.string().optional(),
105
+ type: z
106
+ .union([AuthEventTypeEnum, z.string(), z.array(z.union([AuthEventTypeEnum, z.string()]))])
107
+ .optional(),
108
+
109
+ from: z.string().optional(),
110
+ to: z.string().optional(),
111
+ });
112
+
113
+ export type AuthEventQuery = z.infer<typeof AuthEventQuerySchema>;
114
+
115
+ export const AuthEventsResponseSchema = z.object({
116
+ events: z.array(AuthEventSchema),
117
+ total: z.number(),
118
+ });
119
+
120
+ export type AuthEventsResponse = z.infer<typeof AuthEventsResponseSchema>;