@seamless-auth/types 0.4.0 → 0.5.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.
- package/CHANGELOG.md +30 -0
- package/README.md +121 -63
- package/dist/schemas/systemConfig/schema.d.ts +23 -0
- package/dist/schemas/systemConfig/schema.d.ts.map +1 -1
- package/dist/schemas/systemConfig/schema.js +16 -0
- package/dist/schemas/systemConfig/schema.js.map +1 -1
- package/dist/schemas/webauthn/schema.d.ts +2 -2
- package/package.json +6 -6
- package/src/index.ts +19 -0
- package/src/schemas/admin/schema.ts +54 -0
- package/src/schemas/auth/auth.schema.ts +139 -0
- package/src/schemas/authEvent/schema.ts +120 -0
- package/src/schemas/common/schema.ts +33 -0
- package/src/schemas/credential/schema.ts +103 -0
- package/src/schemas/me/schema.ts +32 -0
- package/src/schemas/messaging/schema.ts +86 -0
- package/src/schemas/metrics/schema.ts +115 -0
- package/src/schemas/oauth/schema.ts +70 -0
- package/src/schemas/organization/schema.ts +135 -0
- package/src/schemas/role/matching.ts +76 -0
- package/src/schemas/role/schema.ts +15 -0
- package/src/schemas/session/schema.ts +26 -0
- package/src/schemas/stepUp/schema.ts +27 -0
- package/src/schemas/systemConfig/schema.ts +215 -0
- package/src/schemas/totp/schema.ts +34 -0
- package/src/schemas/user/schema.ts +75 -0
- package/src/schemas/webauthn/schema.ts +82 -0
- package/src/shared.ts +3 -0
|
@@ -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>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const PaginationQuerySchema = z.object({
|
|
4
|
+
limit: z.coerce.number().min(1).max(100).optional().default(50),
|
|
5
|
+
offset: z.coerce.number().min(0).optional().default(0),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export type PaginationQuery = z.infer<typeof PaginationQuerySchema>;
|
|
9
|
+
|
|
10
|
+
/** Body for endpoints that only acknowledge the request. */
|
|
11
|
+
export const MessageResponseSchema = z.object({
|
|
12
|
+
message: z.string(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export type MessageResponse = z.infer<typeof MessageResponseSchema>;
|
|
16
|
+
|
|
17
|
+
export const ErrorResponseSchema = z.object({
|
|
18
|
+
message: z.string().optional(),
|
|
19
|
+
error: z.string(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export type ErrorResponse = z.infer<typeof ErrorResponseSchema>;
|
|
23
|
+
|
|
24
|
+
export const InvalidPayloadResponseSchema = z.object({
|
|
25
|
+
error: z.string(),
|
|
26
|
+
details: z.unknown().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export type InvalidPayloadResponse = z.infer<typeof InvalidPayloadResponseSchema>;
|
|
30
|
+
|
|
31
|
+
export const MetadataSchema = z.record(z.string(), z.unknown()).nullable().optional();
|
|
32
|
+
|
|
33
|
+
export type Metadata = z.infer<typeof MetadataSchema>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import { IsoDate } from '../../shared.js';
|
|
3
|
+
|
|
4
|
+
// Mirrors WebAuthn's AuthenticatorTransportFuture. Authenticators report
|
|
5
|
+
// transports the browser passes through verbatim, so anything narrower rejects
|
|
6
|
+
// credentials the platform considers valid.
|
|
7
|
+
export const TransportSchema = z.enum([
|
|
8
|
+
'ble',
|
|
9
|
+
'cable',
|
|
10
|
+
'hybrid',
|
|
11
|
+
'internal',
|
|
12
|
+
'nfc',
|
|
13
|
+
'smart-card',
|
|
14
|
+
'usb',
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export type Transport = z.infer<typeof TransportSchema>;
|
|
18
|
+
|
|
19
|
+
export const DeviceTypeSchema = z.enum(['singleDevice', 'multiDevice']);
|
|
20
|
+
|
|
21
|
+
export type DeviceType = z.infer<typeof DeviceTypeSchema>;
|
|
22
|
+
|
|
23
|
+
export const CredentialSchema = z.object({
|
|
24
|
+
id: z.string(),
|
|
25
|
+
userId: z.string(),
|
|
26
|
+
|
|
27
|
+
publicKey: z.string(),
|
|
28
|
+
counter: z.number(),
|
|
29
|
+
|
|
30
|
+
transports: z.array(TransportSchema).optional(),
|
|
31
|
+
|
|
32
|
+
deviceType: DeviceTypeSchema.optional(),
|
|
33
|
+
|
|
34
|
+
backedUp: z.boolean(),
|
|
35
|
+
|
|
36
|
+
friendlyName: z.string().nullable().optional(),
|
|
37
|
+
lastUsedAt: IsoDate.nullable().optional(),
|
|
38
|
+
|
|
39
|
+
platform: z.string().nullable().optional(),
|
|
40
|
+
browser: z.string().nullable().optional(),
|
|
41
|
+
deviceInfo: z.string().nullable().optional(),
|
|
42
|
+
|
|
43
|
+
createdAt: IsoDate,
|
|
44
|
+
updatedAt: IsoDate.optional(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export type Credential = z.infer<typeof CredentialSchema>;
|
|
48
|
+
|
|
49
|
+
export const UpdateCredentialRequestSchema = z.object({
|
|
50
|
+
id: z.string(),
|
|
51
|
+
friendlyName: z.string().min(1).max(128).optional(),
|
|
52
|
+
deviceInfo: z.string().max(256).optional(),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export type UpdateCredentialRequest = z.infer<typeof UpdateCredentialRequestSchema>;
|
|
56
|
+
|
|
57
|
+
export const DeleteCredentialRequestSchema = z.object({
|
|
58
|
+
id: z.string(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export type DeleteCredentialRequest = z.infer<typeof DeleteCredentialRequestSchema>;
|
|
62
|
+
|
|
63
|
+
export const CredentialApiSchema = CredentialSchema.pick({
|
|
64
|
+
id: true,
|
|
65
|
+
transports: true,
|
|
66
|
+
deviceType: true,
|
|
67
|
+
backedUp: true,
|
|
68
|
+
counter: true,
|
|
69
|
+
friendlyName: true,
|
|
70
|
+
lastUsedAt: true,
|
|
71
|
+
platform: true,
|
|
72
|
+
browser: true,
|
|
73
|
+
deviceInfo: true,
|
|
74
|
+
createdAt: true,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export type CredentialApi = z.infer<typeof CredentialApiSchema>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A credential as the API returns it. `backedup` is the historical lowercase
|
|
81
|
+
* spelling kept alongside `backedUp` so older SDK builds keep working; new
|
|
82
|
+
* consumers should read `backedUp`.
|
|
83
|
+
*/
|
|
84
|
+
export const CredentialResponseSchema = CredentialApiSchema.extend({
|
|
85
|
+
backedup: z.boolean(),
|
|
86
|
+
backedUp: z.boolean(),
|
|
87
|
+
prfCapable: z.boolean().optional(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export type CredentialResponse = z.infer<typeof CredentialResponseSchema>;
|
|
91
|
+
|
|
92
|
+
export const CredentialUpdateResponseSchema = z.object({
|
|
93
|
+
message: z.string(),
|
|
94
|
+
credential: CredentialResponseSchema,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export type CredentialUpdateResponse = z.infer<typeof CredentialUpdateResponseSchema>;
|
|
98
|
+
|
|
99
|
+
export const CredentialCountResponseSchema = z.object({
|
|
100
|
+
count: z.number(),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export type CredentialCountResponse = z.infer<typeof CredentialCountResponseSchema>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { IsoDate } from '../../shared.js';
|
|
3
|
+
import { CredentialResponseSchema } from '../credential/schema.js';
|
|
4
|
+
import { OrganizationSchema } from '../organization/schema.js';
|
|
5
|
+
import { RoleNameSchema } from '../role/schema.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The caller's own user record. `lastLogin` is null until the first login, and
|
|
9
|
+
* `activeOrganizationId` is null when the access token carries no org context.
|
|
10
|
+
*/
|
|
11
|
+
export const MeUserSchema = z.object({
|
|
12
|
+
id: z.string(),
|
|
13
|
+
email: z.email(),
|
|
14
|
+
phone: z.string().nullable(),
|
|
15
|
+
roles: z.array(RoleNameSchema),
|
|
16
|
+
lastLogin: IsoDate.nullable().optional(),
|
|
17
|
+
activeOrganizationId: z.string().nullable().optional(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type MeUser = z.infer<typeof MeUserSchema>;
|
|
21
|
+
|
|
22
|
+
/** @deprecated Use {@link MeUser}. */
|
|
23
|
+
export type SeamlessUser = MeUser;
|
|
24
|
+
|
|
25
|
+
export const MeResponseSchema = z.object({
|
|
26
|
+
user: MeUserSchema,
|
|
27
|
+
credentials: z.array(CredentialResponseSchema),
|
|
28
|
+
organizations: z.array(OrganizationSchema).optional(),
|
|
29
|
+
activeOrganization: OrganizationSchema.nullable().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type MeResponse = z.infer<typeof MeResponseSchema>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const MessagingChannelSchema = z.enum(['email', 'sms']);
|
|
4
|
+
|
|
5
|
+
export type MessagingChannel = z.infer<typeof MessagingChannelSchema>;
|
|
6
|
+
|
|
7
|
+
export const DeliveryResultSchema = z.object({
|
|
8
|
+
accepted: z.boolean(),
|
|
9
|
+
provider: z.string(),
|
|
10
|
+
channel: MessagingChannelSchema,
|
|
11
|
+
messageId: z.string().optional(),
|
|
12
|
+
raw: z.unknown().optional(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export type DeliveryResult = z.infer<typeof DeliveryResultSchema>;
|
|
16
|
+
|
|
17
|
+
export const EmailMessageSchema = z.object({
|
|
18
|
+
to: z.string(),
|
|
19
|
+
from: z.string().optional(),
|
|
20
|
+
subject: z.string(),
|
|
21
|
+
text: z.string(),
|
|
22
|
+
html: z.string().optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export type EmailMessage = z.infer<typeof EmailMessageSchema>;
|
|
26
|
+
|
|
27
|
+
export const SmsMessageSchema = z.object({
|
|
28
|
+
to: z.string(),
|
|
29
|
+
from: z.string().optional(),
|
|
30
|
+
body: z.string(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export type SmsMessage = z.infer<typeof SmsMessageSchema>;
|
|
34
|
+
|
|
35
|
+
export const SendOtpEmailInputSchema = z.object({
|
|
36
|
+
to: z.string(),
|
|
37
|
+
token: z.string(),
|
|
38
|
+
from: z.string().optional(),
|
|
39
|
+
subject: z.string().optional(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export type SendOtpEmailInput = z.infer<typeof SendOtpEmailInputSchema>;
|
|
43
|
+
|
|
44
|
+
export const SendOtpSmsInputSchema = z.object({
|
|
45
|
+
to: z.string(),
|
|
46
|
+
token: z.union([z.string(), z.number()]),
|
|
47
|
+
from: z.string().optional(),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export type SendOtpSmsInput = z.infer<typeof SendOtpSmsInputSchema>;
|
|
51
|
+
|
|
52
|
+
export const SendMagicLinkEmailInputSchema = z.object({
|
|
53
|
+
to: z.string(),
|
|
54
|
+
magicLinkUrl: z.string(),
|
|
55
|
+
token: z.string().optional(),
|
|
56
|
+
from: z.string().optional(),
|
|
57
|
+
subject: z.string().optional(),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export type SendMagicLinkEmailInput = z.infer<typeof SendMagicLinkEmailInputSchema>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* What the auth API tells a self-hosted server to deliver. The API never sends
|
|
64
|
+
* the message itself when the adopter owns delivery, so this is the contract
|
|
65
|
+
* between the two.
|
|
66
|
+
*/
|
|
67
|
+
export const AuthDeliverySchema = z.discriminatedUnion('kind', [
|
|
68
|
+
z.object({
|
|
69
|
+
kind: z.literal('otp_email'),
|
|
70
|
+
to: z.string(),
|
|
71
|
+
token: z.string(),
|
|
72
|
+
}),
|
|
73
|
+
z.object({
|
|
74
|
+
kind: z.literal('otp_sms'),
|
|
75
|
+
to: z.string(),
|
|
76
|
+
token: z.union([z.string(), z.number()]),
|
|
77
|
+
}),
|
|
78
|
+
z.object({
|
|
79
|
+
kind: z.literal('magic_link_email'),
|
|
80
|
+
to: z.string(),
|
|
81
|
+
token: z.string().optional(),
|
|
82
|
+
magicLinkUrl: z.string(),
|
|
83
|
+
}),
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
export type AuthDeliveryInstruction = z.infer<typeof AuthDeliverySchema>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AuthEventSchema } from '../authEvent/schema.js';
|
|
3
|
+
|
|
4
|
+
const MAX_METRICS_WINDOW_MS = 1000 * 60 * 60 * 24 * 366; // ~1 year
|
|
5
|
+
|
|
6
|
+
export const MetricsIntervalSchema = z.enum(['hour', 'day']);
|
|
7
|
+
|
|
8
|
+
export type MetricsInterval = z.infer<typeof MetricsIntervalSchema>;
|
|
9
|
+
|
|
10
|
+
export const MetricsQuerySchema = z
|
|
11
|
+
.object({
|
|
12
|
+
userId: z.string().optional(),
|
|
13
|
+
from: z.string().optional(),
|
|
14
|
+
to: z.string().optional(),
|
|
15
|
+
interval: MetricsIntervalSchema.optional().default('hour'),
|
|
16
|
+
})
|
|
17
|
+
.superRefine((data, ctx) => {
|
|
18
|
+
const fromDate = data.from ? new Date(data.from) : undefined;
|
|
19
|
+
const toDate = data.to ? new Date(data.to) : undefined;
|
|
20
|
+
|
|
21
|
+
const fromValid = fromDate !== undefined && !Number.isNaN(fromDate.getTime());
|
|
22
|
+
const toValid = toDate !== undefined && !Number.isNaN(toDate.getTime());
|
|
23
|
+
|
|
24
|
+
if (data.from !== undefined && !fromValid) {
|
|
25
|
+
ctx.addIssue({ code: 'custom', path: ['from'], message: 'Invalid from date' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (data.to !== undefined && !toValid) {
|
|
29
|
+
ctx.addIssue({ code: 'custom', path: ['to'], message: 'Invalid to date' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!fromValid || !toValid || !fromDate || !toDate) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (fromDate.getTime() > toDate.getTime()) {
|
|
37
|
+
ctx.addIssue({ code: 'custom', path: ['to'], message: 'from must be on or before to' });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Unbounded windows let a single request scan the whole event table.
|
|
42
|
+
if (toDate.getTime() - fromDate.getTime() > MAX_METRICS_WINDOW_MS) {
|
|
43
|
+
ctx.addIssue({
|
|
44
|
+
code: 'custom',
|
|
45
|
+
path: ['to'],
|
|
46
|
+
message: 'time range exceeds the maximum window',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export type MetricsQuery = z.infer<typeof MetricsQuerySchema>;
|
|
52
|
+
|
|
53
|
+
export const AuthEventSummaryItemSchema = z.object({
|
|
54
|
+
type: z.string(),
|
|
55
|
+
count: z.number(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export type AuthEventSummaryItem = z.infer<typeof AuthEventSummaryItemSchema>;
|
|
59
|
+
|
|
60
|
+
export const AuthEventSummaryResponseSchema = z.object({
|
|
61
|
+
summary: z.array(AuthEventSummaryItemSchema),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export type AuthEventSummaryResponse = z.infer<typeof AuthEventSummaryResponseSchema>;
|
|
65
|
+
|
|
66
|
+
export const AuthEventTimeseriesPointSchema = z.object({
|
|
67
|
+
bucket: z.string(),
|
|
68
|
+
success: z.number(),
|
|
69
|
+
failed: z.number(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export type AuthEventTimeseriesPoint = z.infer<typeof AuthEventTimeseriesPointSchema>;
|
|
73
|
+
|
|
74
|
+
export const AuthEventTimeseriesResponseSchema = z.object({
|
|
75
|
+
timeseries: z.array(AuthEventTimeseriesPointSchema),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export type AuthEventTimeseriesResponse = z.infer<typeof AuthEventTimeseriesResponseSchema>;
|
|
79
|
+
|
|
80
|
+
export const LoginStatsResponseSchema = z.object({
|
|
81
|
+
success: z.number(),
|
|
82
|
+
failed: z.number(),
|
|
83
|
+
successRate: z.number(),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export type LoginStatsResponse = z.infer<typeof LoginStatsResponseSchema>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Anomaly rows come straight out of the event store, where a partially written
|
|
90
|
+
* event is still worth surfacing, so every field is optional here.
|
|
91
|
+
*/
|
|
92
|
+
export const PartialAuthEventSchema = AuthEventSchema.partial();
|
|
93
|
+
|
|
94
|
+
export type PartialAuthEvent = z.infer<typeof PartialAuthEventSchema>;
|
|
95
|
+
|
|
96
|
+
export const SecurityAnomaliesResponseSchema = z.object({
|
|
97
|
+
suspiciousEvents: z.array(PartialAuthEventSchema),
|
|
98
|
+
total: z.number().int().nonnegative(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export type SecurityAnomaliesResponse = z.infer<typeof SecurityAnomaliesResponseSchema>;
|
|
102
|
+
|
|
103
|
+
export const DashboardMetricsResponseSchema = z.object({
|
|
104
|
+
totalUsers: z.number(),
|
|
105
|
+
activeSessions: z.number(),
|
|
106
|
+
newUsers24h: z.number(),
|
|
107
|
+
loginSuccess24h: z.number(),
|
|
108
|
+
loginFailed24h: z.number(),
|
|
109
|
+
successRate24h: z.number(),
|
|
110
|
+
otpUsage24h: z.number(),
|
|
111
|
+
passkeyUsage24h: z.number(),
|
|
112
|
+
databaseSize: z.number(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
export type DashboardMetricsResponse = z.infer<typeof DashboardMetricsResponseSchema>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { RefreshSuccessResponseSchema } from '../auth/auth.schema.js';
|
|
3
|
+
import { OAuthProviderIdSchema } from '../systemConfig/schema.js';
|
|
4
|
+
|
|
5
|
+
export const OAuthProviderParamSchema = z.object({
|
|
6
|
+
providerId: OAuthProviderIdSchema,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export type OAuthProviderParam = z.infer<typeof OAuthProviderParamSchema>;
|
|
10
|
+
|
|
11
|
+
/** A provider as an unauthenticated client may see it: no client or secret material. */
|
|
12
|
+
export const PublicOAuthProviderSchema = z.object({
|
|
13
|
+
id: z.string(),
|
|
14
|
+
name: z.string(),
|
|
15
|
+
scopes: z.array(z.string()),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type PublicOAuthProvider = z.infer<typeof PublicOAuthProviderSchema>;
|
|
19
|
+
|
|
20
|
+
export const OAuthProvidersResponseSchema = z.object({
|
|
21
|
+
providers: z.array(PublicOAuthProviderSchema),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type OAuthProvidersResponse = z.infer<typeof OAuthProvidersResponseSchema>;
|
|
25
|
+
|
|
26
|
+
export const StartOAuthLoginRequestSchema = z.object({
|
|
27
|
+
redirectUri: z.url().optional(),
|
|
28
|
+
returnTo: z.url().optional(),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export type StartOAuthLoginRequest = z.infer<typeof StartOAuthLoginRequestSchema>;
|
|
32
|
+
|
|
33
|
+
export const StartOAuthLoginResponseSchema = z.object({
|
|
34
|
+
provider: PublicOAuthProviderSchema,
|
|
35
|
+
state: z.string(),
|
|
36
|
+
authorizationUrl: z.url(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export type StartOAuthLoginResponse = z.infer<typeof StartOAuthLoginResponseSchema>;
|
|
40
|
+
|
|
41
|
+
export const FinishOAuthLoginRequestSchema = z.object({
|
|
42
|
+
code: z.string().trim().min(1),
|
|
43
|
+
state: z.string().trim().min(1),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type FinishOAuthLoginRequest = z.infer<typeof FinishOAuthLoginRequestSchema>;
|
|
47
|
+
|
|
48
|
+
export const OAuthLoginSuccessResponseSchema = RefreshSuccessResponseSchema.omit({
|
|
49
|
+
sessionId: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export type OAuthLoginSuccessResponse = z.infer<typeof OAuthLoginSuccessResponseSchema>;
|
|
53
|
+
|
|
54
|
+
export const OAUTH_ERROR_CODES = [
|
|
55
|
+
'oauth_missing_email',
|
|
56
|
+
'oauth_email_not_verified',
|
|
57
|
+
'oauth_missing_subject',
|
|
58
|
+
] as const;
|
|
59
|
+
|
|
60
|
+
export const OAuthErrorCodeSchema = z.enum(OAUTH_ERROR_CODES);
|
|
61
|
+
|
|
62
|
+
export type OAuthErrorCode = z.infer<typeof OAuthErrorCodeSchema>;
|
|
63
|
+
|
|
64
|
+
export const OAuthLoginErrorResponseSchema = z.object({
|
|
65
|
+
message: z.string().optional(),
|
|
66
|
+
error: z.string(),
|
|
67
|
+
code: OAuthErrorCodeSchema.optional(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export type OAuthLoginErrorResponse = z.infer<typeof OAuthLoginErrorResponseSchema>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { IsoDate } from '../../shared.js';
|
|
3
|
+
import { MetadataSchema } from '../common/schema.js';
|
|
4
|
+
|
|
5
|
+
export const OrganizationIdParamSchema = z.object({
|
|
6
|
+
organizationId: z.uuid(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export type OrganizationIdParam = z.infer<typeof OrganizationIdParamSchema>;
|
|
10
|
+
|
|
11
|
+
export const OrganizationMemberParamSchema = OrganizationIdParamSchema.extend({
|
|
12
|
+
userId: z.uuid(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export type OrganizationMemberParam = z.infer<typeof OrganizationMemberParamSchema>;
|
|
16
|
+
|
|
17
|
+
export const CreateOrganizationRequestSchema = z.object({
|
|
18
|
+
name: z.string().trim().min(1).max(120),
|
|
19
|
+
slug: z.string().trim().min(1).max(100).optional(),
|
|
20
|
+
metadata: MetadataSchema,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type CreateOrganizationRequest = z.infer<typeof CreateOrganizationRequestSchema>;
|
|
24
|
+
|
|
25
|
+
export const UpdateOrganizationRequestSchema = z.object({
|
|
26
|
+
name: z.string().trim().min(1).max(120).optional(),
|
|
27
|
+
slug: z.string().trim().min(1).max(100).optional(),
|
|
28
|
+
metadata: MetadataSchema,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export type UpdateOrganizationRequest = z.infer<typeof UpdateOrganizationRequestSchema>;
|
|
32
|
+
|
|
33
|
+
const MembershipRoleSchema = z.string().trim().min(1).max(80);
|
|
34
|
+
const MembershipScopeSchema = z.string().trim().min(1).max(120);
|
|
35
|
+
|
|
36
|
+
export const AddOrganizationMemberRequestSchema = z
|
|
37
|
+
.object({
|
|
38
|
+
userId: z.uuid().optional(),
|
|
39
|
+
email: z.email().optional(),
|
|
40
|
+
roles: z.array(MembershipRoleSchema).optional(),
|
|
41
|
+
scopes: z.array(MembershipScopeSchema).optional(),
|
|
42
|
+
})
|
|
43
|
+
.refine((value) => Boolean(value.userId || value.email), {
|
|
44
|
+
message: 'userId or email is required',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export type AddOrganizationMemberRequest = z.infer<typeof AddOrganizationMemberRequestSchema>;
|
|
48
|
+
|
|
49
|
+
export const UpdateOrganizationMemberRequestSchema = z.object({
|
|
50
|
+
roles: z.array(MembershipRoleSchema).optional(),
|
|
51
|
+
scopes: z.array(MembershipScopeSchema).optional(),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export type UpdateOrganizationMemberRequest = z.infer<typeof UpdateOrganizationMemberRequestSchema>;
|
|
55
|
+
|
|
56
|
+
const OrganizationMembershipUserSchema = z.object({
|
|
57
|
+
id: z.string(),
|
|
58
|
+
email: z.email(),
|
|
59
|
+
phone: z.string().nullable(),
|
|
60
|
+
roles: z.array(z.string()),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const OrganizationMembershipSchema = z.object({
|
|
64
|
+
id: z.string(),
|
|
65
|
+
organizationId: z.string(),
|
|
66
|
+
userId: z.string(),
|
|
67
|
+
roles: z.array(z.string()),
|
|
68
|
+
scopes: z.array(z.string()),
|
|
69
|
+
createdAt: IsoDate,
|
|
70
|
+
updatedAt: IsoDate,
|
|
71
|
+
user: OrganizationMembershipUserSchema.optional(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export type OrganizationMembership = z.infer<typeof OrganizationMembershipSchema>;
|
|
75
|
+
|
|
76
|
+
export const OrganizationSchema = z.object({
|
|
77
|
+
id: z.string(),
|
|
78
|
+
name: z.string(),
|
|
79
|
+
slug: z.string(),
|
|
80
|
+
createdByUserId: z.string().nullable(),
|
|
81
|
+
metadata: z.record(z.string(), z.unknown()).nullable(),
|
|
82
|
+
createdAt: IsoDate,
|
|
83
|
+
updatedAt: IsoDate,
|
|
84
|
+
membership: OrganizationMembershipSchema.optional(),
|
|
85
|
+
memberCount: z.number().int().nonnegative().optional(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export type Organization = z.infer<typeof OrganizationSchema>;
|
|
89
|
+
|
|
90
|
+
export const OrganizationEnvelopeResponseSchema = z.object({
|
|
91
|
+
organization: OrganizationSchema,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export type OrganizationEnvelopeResponse = z.infer<typeof OrganizationEnvelopeResponseSchema>;
|
|
95
|
+
|
|
96
|
+
export const OrganizationListResponseSchema = z.object({
|
|
97
|
+
organizations: z.array(OrganizationSchema),
|
|
98
|
+
activeOrganizationId: z.string().nullable(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export type OrganizationListResponse = z.infer<typeof OrganizationListResponseSchema>;
|
|
102
|
+
|
|
103
|
+
export const AdminOrganizationListResponseSchema = z.object({
|
|
104
|
+
organizations: z.array(OrganizationSchema),
|
|
105
|
+
total: z.number().int().nonnegative(),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export type AdminOrganizationListResponse = z.infer<typeof AdminOrganizationListResponseSchema>;
|
|
109
|
+
|
|
110
|
+
export const OrganizationMembersResponseSchema = z.object({
|
|
111
|
+
members: z.array(OrganizationMembershipSchema),
|
|
112
|
+
total: z.number().int().nonnegative(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
export type OrganizationMembersResponse = z.infer<typeof OrganizationMembersResponseSchema>;
|
|
116
|
+
|
|
117
|
+
export const OrganizationMembershipEnvelopeResponseSchema = z.object({
|
|
118
|
+
membership: OrganizationMembershipSchema,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export type OrganizationMembershipEnvelopeResponse = z.infer<
|
|
122
|
+
typeof OrganizationMembershipEnvelopeResponseSchema
|
|
123
|
+
>;
|
|
124
|
+
|
|
125
|
+
export const OrganizationSwitchResponseSchema = z.object({
|
|
126
|
+
message: z.string(),
|
|
127
|
+
token: z.string(),
|
|
128
|
+
sub: z.string(),
|
|
129
|
+
sessionId: z.string(),
|
|
130
|
+
organizationId: z.string(),
|
|
131
|
+
organization: OrganizationSchema,
|
|
132
|
+
ttl: z.number(),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export type OrganizationSwitchResponse = z.infer<typeof OrganizationSwitchResponseSchema>;
|