mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
541 lines
23 KiB
TypeScript
541 lines
23 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {ThemeTypes} from '@fluxer/constants/src/UserConstants';
|
|
import {
|
|
WebAuthnAuthenticationOptions,
|
|
WebAuthnAuthenticationResponse,
|
|
WebAuthnRegistrationResponse,
|
|
} from '@fluxer/schema/src/domains/auth/WebAuthnSchemas';
|
|
import {UserPartialResponse, UserPrivateResponse} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
|
import {
|
|
createNamedStringLiteralUnion,
|
|
createStringType,
|
|
SnowflakeStringType,
|
|
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
|
import {
|
|
EmailType,
|
|
GlobalNameType,
|
|
PasswordType,
|
|
PhoneNumberType,
|
|
UsernameType,
|
|
} from '@fluxer/schema/src/primitives/UserValidators';
|
|
import {z} from 'zod';
|
|
|
|
const RegisterThemeType = createNamedStringLiteralUnion(
|
|
[
|
|
[ThemeTypes.DARK, 'DARK', 'Dark colour theme'],
|
|
[ThemeTypes.DARK_LEGACY, 'DARK_LEGACY', 'Legacy dark colour theme (original neutral grey palette)'],
|
|
[ThemeTypes.COAL, 'COAL', 'Coal/darker colour theme'],
|
|
[ThemeTypes.LIGHT, 'LIGHT', 'Light colour theme'],
|
|
[ThemeTypes.SYSTEM, 'SYSTEM', 'Follow system colour preference'],
|
|
] as const,
|
|
'UI theme preference',
|
|
);
|
|
|
|
export const RegisterRequest = z.object({
|
|
email: EmailType.optional().describe('Email address for the new account'),
|
|
username: UsernameType.optional().describe('Username for the new account (1-32 characters)'),
|
|
global_name: GlobalNameType.optional().describe('Display name shown to other users'),
|
|
password: PasswordType.optional().describe('Password for the new account'),
|
|
date_of_birth: createStringType(10, 10)
|
|
.refine((value) => /^\d{4}-\d{2}-\d{2}$/.test(value), 'Invalid date format')
|
|
.optional()
|
|
.describe('Date of birth in YYYY-MM-DD format'),
|
|
consent: z.boolean().default(false).describe('Whether user consents to terms of service'),
|
|
invite_code: createStringType(0, 256).nullish().describe('Guild invite code to join after registration'),
|
|
registration_url_code: createStringType(1, 256)
|
|
.nullish()
|
|
.describe('Admin-issued registration URL code to use for this registration'),
|
|
theme: RegisterThemeType.optional().describe('Initial UI theme preference for the new account'),
|
|
});
|
|
|
|
export type RegisterRequest = z.infer<typeof RegisterRequest>;
|
|
|
|
export const UsernameSuggestionsRequest = z.object({
|
|
global_name: GlobalNameType.describe('Display name to generate username suggestions from'),
|
|
});
|
|
|
|
export type UsernameSuggestionsRequest = z.infer<typeof UsernameSuggestionsRequest>;
|
|
|
|
export const LoginRequest = z.object({
|
|
email: EmailType.describe('Email address for authentication'),
|
|
password: PasswordType.describe('Account password'),
|
|
invite_code: createStringType(0, 256).nullish().describe('Guild invite code to join after login'),
|
|
});
|
|
|
|
export type LoginRequest = z.infer<typeof LoginRequest>;
|
|
|
|
export const LogoutAuthSessionsRequest = z.object({
|
|
session_id_hashes: z.array(createStringType()).max(100).describe('Array of session ID hashes to log out (max 100)'),
|
|
password: PasswordType.optional().describe('Account password for verification'),
|
|
});
|
|
|
|
export type LogoutAuthSessionsRequest = z.infer<typeof LogoutAuthSessionsRequest>;
|
|
|
|
export const ForgotPasswordRequest = z.object({
|
|
email: EmailType.describe('Email address to send password reset link'),
|
|
});
|
|
|
|
export type ForgotPasswordRequest = z.infer<typeof ForgotPasswordRequest>;
|
|
|
|
export const ResetPasswordRequest = z.object({
|
|
token: createStringType(64, 64).describe('Password reset token from email'),
|
|
password: PasswordType.describe('New password to set'),
|
|
});
|
|
|
|
export type ResetPasswordRequest = z.infer<typeof ResetPasswordRequest>;
|
|
|
|
export const ResetPasswordTokenParam = z.object({
|
|
token: createStringType(64, 64).describe('Password reset token from email'),
|
|
});
|
|
|
|
export type ResetPasswordTokenParam = z.infer<typeof ResetPasswordTokenParam>;
|
|
|
|
export const ValidateResetPasswordTokenResponse = z.object({
|
|
valid: z.boolean().describe('Whether the password reset token is valid and unexpired'),
|
|
});
|
|
|
|
export type ValidateResetPasswordTokenResponse = z.infer<typeof ValidateResetPasswordTokenResponse>;
|
|
|
|
export const EmailRevertRequest = z.object({
|
|
token: createStringType(64, 64).describe('Email revert token from email'),
|
|
password: PasswordType.describe('Account password for verification'),
|
|
});
|
|
|
|
export type EmailRevertRequest = z.infer<typeof EmailRevertRequest>;
|
|
|
|
export const VerifyEmailRequest = z.object({
|
|
token: createStringType(64, 64).describe('Email verification token from email'),
|
|
});
|
|
|
|
export type VerifyEmailRequest = z.infer<typeof VerifyEmailRequest>;
|
|
|
|
export const SudoVerificationSchema = z.object({
|
|
password: PasswordType.optional().describe('Account password for sudo verification'),
|
|
mfa_method: createNamedStringLiteralUnion(
|
|
[
|
|
['totp', 'TOTP', 'Time-based one-time password authentication via authenticator app'],
|
|
['webauthn', 'WebAuthn', 'Security key or biometric authentication'],
|
|
],
|
|
'MFA method to use for verification',
|
|
).optional(),
|
|
mfa_code: createStringType(1, 32).optional().describe('MFA verification code from an authenticator app'),
|
|
webauthn_response: WebAuthnAuthenticationResponse.optional().describe('WebAuthn authentication response'),
|
|
webauthn_challenge: createStringType().optional().describe('WebAuthn challenge string'),
|
|
});
|
|
export const SsoStatusResponse = z.object({
|
|
enabled: z.boolean().describe('Whether SSO is enabled for this instance'),
|
|
enforced: z.boolean().describe('Whether SSO is required for all users'),
|
|
display_name: z.string().nullable().describe('Display name of the SSO provider'),
|
|
redirect_uri: z.string().describe('OAuth redirect URI for SSO'),
|
|
});
|
|
|
|
export type SsoStatusResponse = z.infer<typeof SsoStatusResponse>;
|
|
|
|
export const SsoStartResponse = z.object({
|
|
authorization_url: z.string().describe('URL to redirect user to for SSO authentication'),
|
|
state: z.string().describe('State parameter for CSRF protection'),
|
|
redirect_uri: z.string().describe('OAuth redirect URI used for the SSO provider callback'),
|
|
});
|
|
|
|
export type SsoStartResponse = z.infer<typeof SsoStartResponse>;
|
|
|
|
export const SsoCompleteResponse = z.object({
|
|
token: z.string().describe('Authentication token for the session'),
|
|
user_id: SnowflakeStringType.describe('ID of the authenticated user'),
|
|
user: UserPartialResponse.describe('Partial user data for the authenticated account'),
|
|
redirect_to: z.string().describe('URL to redirect the user to after completion'),
|
|
});
|
|
|
|
export type SsoCompleteResponse = z.infer<typeof SsoCompleteResponse>;
|
|
|
|
export const AuthTokenWithUserIdResponse = z.object({
|
|
token: z.string().describe('Authentication token for API requests'),
|
|
user_id: SnowflakeStringType.describe('ID of the authenticated user'),
|
|
user: UserPartialResponse.describe('Partial user data for the authenticated account'),
|
|
});
|
|
|
|
export type AuthTokenWithUserIdResponse = z.infer<typeof AuthTokenWithUserIdResponse>;
|
|
|
|
export const AuthRegistrationPendingApprovalResponse = z.object({
|
|
registration_pending_approval: z.literal(true).describe('Registration succeeded and is waiting for admin approval'),
|
|
user_id: SnowflakeStringType.describe('ID of the registered account waiting for approval'),
|
|
});
|
|
|
|
export type AuthRegistrationPendingApprovalResponse = z.infer<typeof AuthRegistrationPendingApprovalResponse>;
|
|
|
|
const AuthMfaRequiredResponse = z.object({
|
|
mfa: z.literal(true).describe('Indicates MFA is required to complete authentication'),
|
|
ticket: z.string().describe('MFA ticket to use when completing MFA verification'),
|
|
allowed_methods: z.array(z.string()).max(10).describe('List of allowed MFA methods'),
|
|
totp: z.boolean().describe('Whether TOTP authenticator MFA is available'),
|
|
webauthn: z.boolean().describe('Whether WebAuthn security key MFA is available'),
|
|
backup_codes: z.boolean().describe('Whether the account has at least one unconsumed backup code'),
|
|
});
|
|
|
|
export const AuthLoginResponse = z.union([AuthTokenWithUserIdResponse, AuthMfaRequiredResponse]);
|
|
|
|
export type AuthLoginResponse = z.infer<typeof AuthLoginResponse>;
|
|
|
|
export const AuthRegisterResponse = z.union([
|
|
AuthTokenWithUserIdResponse,
|
|
AuthMfaRequiredResponse,
|
|
AuthRegistrationPendingApprovalResponse,
|
|
]);
|
|
|
|
export type AuthRegisterResponse = z.infer<typeof AuthRegisterResponse>;
|
|
|
|
export const AuthSessionLocation = z.object({
|
|
city: z.string().nullish().describe('The city name reported by the client'),
|
|
region: z.string().nullish().describe('The region reported by the client'),
|
|
country: z.string().nullish().describe('The country reported by the client'),
|
|
});
|
|
|
|
export type AuthSessionLocation = z.infer<typeof AuthSessionLocation>;
|
|
|
|
const AuthSessionClientInfo = z.object({
|
|
platform: z.string().nullish().describe('The platform reported by the client'),
|
|
os: z.string().nullish().describe('The operating system reported by the client'),
|
|
browser: z.string().nullish().describe('The browser reported by the client'),
|
|
device: z.enum(['mobile', 'desktop']).describe('Device class of the session, decided by the server'),
|
|
location: AuthSessionLocation.nullish().describe('The geolocation data sent by the client'),
|
|
});
|
|
|
|
export const AuthSessionResponse = z.object({
|
|
id_hash: z.string().describe('The base64url-encoded session id hash'),
|
|
client_info: AuthSessionClientInfo.nullish().describe('Client metadata recorded for this session'),
|
|
masked_ip: z.string().nullable().describe('Semi-redacted IP address recorded for this session'),
|
|
approx_last_used_at: z.iso.datetime().nullish().describe('Approximate timestamp of the last session activity'),
|
|
current: z.boolean().describe('Whether this is the current session making the request'),
|
|
});
|
|
|
|
export type AuthSessionResponse = z.infer<typeof AuthSessionResponse>;
|
|
|
|
export const AuthSessionsResponse = z.array(AuthSessionResponse);
|
|
|
|
export type AuthSessionsResponse = z.infer<typeof AuthSessionsResponse>;
|
|
|
|
export const WebAuthnAuthenticationOptionsResponse = WebAuthnAuthenticationOptions.omit({
|
|
hints: true,
|
|
extensions: true,
|
|
});
|
|
|
|
export type WebAuthnAuthenticationOptionsResponse = z.infer<typeof WebAuthnAuthenticationOptionsResponse>;
|
|
|
|
export const UsernameSuggestionsResponse = z.object({
|
|
suggestions: z.array(z.string()).max(20).describe('List of suggested usernames'),
|
|
});
|
|
|
|
export type UsernameSuggestionsResponse = z.infer<typeof UsernameSuggestionsResponse>;
|
|
|
|
export const HandoffInitiateResponse = z.object({
|
|
code: z.string().describe('Handoff code to share with the receiving device'),
|
|
expires_at: z.iso.datetime().describe('ISO 8601 timestamp when the handoff code expires'),
|
|
poll_secret: z.string().optional().describe('Secret the initiating device must present to retrieve the token'),
|
|
});
|
|
|
|
export type HandoffInitiateResponse = z.infer<typeof HandoffInitiateResponse>;
|
|
|
|
const HandoffInfoClientInfo = z.object({
|
|
platform: z.string().nullish().describe('The platform of the requesting device'),
|
|
os: z.string().nullish().describe('The operating system of the requesting device'),
|
|
device: z.enum(['mobile', 'desktop']).describe('Device class of the requesting device, decided by the server'),
|
|
location: AuthSessionLocation.nullish().describe('The approximate location of the requesting device'),
|
|
});
|
|
|
|
export const HandoffInfoResponse = z.object({
|
|
status: z.string().describe('Current status of the handoff (pending, expired)'),
|
|
client_info: HandoffInfoClientInfo.nullish().describe('Client information of the initiating device'),
|
|
});
|
|
|
|
export type HandoffInfoResponse = z.infer<typeof HandoffInfoResponse>;
|
|
|
|
export const HandoffStatusResponse = z.object({
|
|
status: z.string().describe('Current status of the handoff (pending, completed, expired)'),
|
|
token: z.string().nullish().describe('Authentication token if handoff is complete'),
|
|
user_id: SnowflakeStringType.nullish().describe('User ID if handoff is complete'),
|
|
user: UserPartialResponse.nullish().describe('Partial user data if handoff is complete'),
|
|
});
|
|
|
|
export type HandoffStatusResponse = z.infer<typeof HandoffStatusResponse>;
|
|
|
|
export const SsoStartRequest = z.object({
|
|
redirect_to: createStringType(0, 2048).nullish().describe('URL to redirect to after SSO completion'),
|
|
redirect_uri: createStringType(0, 2048).nullish().describe('OAuth redirect URI to use for the SSO provider callback'),
|
|
});
|
|
|
|
export type SsoStartRequest = z.infer<typeof SsoStartRequest>;
|
|
|
|
export const SsoCompleteRequest = z.object({
|
|
code: createStringType(1, 4096).describe('Authorization code from the SSO provider'),
|
|
state: createStringType(1, 4096).describe('State parameter for CSRF protection'),
|
|
});
|
|
|
|
export type SsoCompleteRequest = z.infer<typeof SsoCompleteRequest>;
|
|
|
|
export const MfaTotpRequest = z.object({
|
|
code: createStringType().describe('The TOTP code from the authenticator app'),
|
|
ticket: createStringType().describe('The MFA ticket from the login response'),
|
|
});
|
|
|
|
export type MfaTotpRequest = z.infer<typeof MfaTotpRequest>;
|
|
|
|
export const MfaTicketRequest = z.object({
|
|
ticket: createStringType().describe('The MFA ticket from the login response'),
|
|
});
|
|
|
|
export type MfaTicketRequest = z.infer<typeof MfaTicketRequest>;
|
|
|
|
export const AuthorizeIpRequest = z.object({
|
|
token: createStringType().describe('The IP authorization token from email'),
|
|
});
|
|
|
|
export type AuthorizeIpRequest = z.infer<typeof AuthorizeIpRequest>;
|
|
|
|
export const IpAuthorizationPollQuery = z.object({
|
|
ticket: createStringType().describe('The IP authorization ticket'),
|
|
});
|
|
|
|
export type IpAuthorizationPollQuery = z.infer<typeof IpAuthorizationPollQuery>;
|
|
|
|
export const IpAuthorizationPollResponse = z.object({
|
|
completed: z.boolean().describe('Whether the IP authorization has been completed'),
|
|
token: z.string().nullish().describe('Authentication token if authorization is complete'),
|
|
user_id: SnowflakeStringType.nullish().describe('User ID if authorization is complete'),
|
|
user: UserPartialResponse.nullish().describe('Partial user data if authorization is complete'),
|
|
});
|
|
|
|
export type IpAuthorizationPollResponse = z.infer<typeof IpAuthorizationPollResponse>;
|
|
|
|
export const WebAuthnAuthenticateRequest = z.object({
|
|
response: WebAuthnAuthenticationResponse.describe('WebAuthn authentication response'),
|
|
challenge: createStringType().describe('The challenge string from authentication options'),
|
|
});
|
|
|
|
export type WebAuthnAuthenticateRequest = z.infer<typeof WebAuthnAuthenticateRequest>;
|
|
|
|
export const WebAuthnMfaRequest = z.object({
|
|
response: WebAuthnAuthenticationResponse.describe('WebAuthn authentication response'),
|
|
challenge: createStringType().describe('The challenge string from authentication options'),
|
|
ticket: createStringType().describe('The MFA ticket from the login response'),
|
|
});
|
|
|
|
export type WebAuthnMfaRequest = z.infer<typeof WebAuthnMfaRequest>;
|
|
|
|
export const HandoffCompleteRequest = z.object({
|
|
code: createStringType().describe('The handoff code from the initiating session'),
|
|
token: createStringType().optional().describe('The authentication token to transfer'),
|
|
user_id: createStringType().describe('The user ID associated with the authenticated session'),
|
|
});
|
|
|
|
export type HandoffCompleteRequest = z.infer<typeof HandoffCompleteRequest>;
|
|
|
|
export const HandoffCodeParam = z.object({
|
|
code: createStringType().describe('The handoff code'),
|
|
});
|
|
|
|
export type HandoffCodeParam = z.infer<typeof HandoffCodeParam>;
|
|
|
|
export const HandoffStatusRequest = z.object({
|
|
poll_secret: createStringType().describe('The poll secret issued when the handoff was initiated'),
|
|
});
|
|
|
|
export type HandoffStatusRequest = z.infer<typeof HandoffStatusRequest>;
|
|
|
|
export const HandoffCancelRequest = z.object({
|
|
poll_secret: createStringType().describe('The poll secret issued when the handoff was initiated'),
|
|
});
|
|
|
|
export type HandoffCancelRequest = z.infer<typeof HandoffCancelRequest>;
|
|
|
|
export const EnableMfaTotpRequest = z
|
|
.object({
|
|
secret: createStringType(1, 256).describe('The TOTP secret key'),
|
|
code: createStringType(1, 32).describe('The TOTP verification code'),
|
|
})
|
|
.extend(SudoVerificationSchema.shape);
|
|
|
|
export type EnableMfaTotpRequest = z.infer<typeof EnableMfaTotpRequest>;
|
|
|
|
export const DisableTotpRequest = z
|
|
.object({
|
|
code: createStringType(1, 32).describe('The TOTP code to verify'),
|
|
password: PasswordType.optional().describe('Account password for verification'),
|
|
})
|
|
.extend(SudoVerificationSchema.shape);
|
|
|
|
export type DisableTotpRequest = z.infer<typeof DisableTotpRequest>;
|
|
|
|
export const MfaBackupCodesRequest = z
|
|
.object({
|
|
regenerate: z.boolean().describe('Whether to regenerate backup codes'),
|
|
password: PasswordType.optional().describe('Account password for verification'),
|
|
})
|
|
.extend(SudoVerificationSchema.shape);
|
|
|
|
export type MfaBackupCodesRequest = z.infer<typeof MfaBackupCodesRequest>;
|
|
|
|
const MfaBackupCodeResponse = z.object({
|
|
code: z.string().describe('The backup code'),
|
|
consumed: z.boolean().describe('Whether the code has been used'),
|
|
});
|
|
|
|
export const MfaBackupCodesResponse = z.object({
|
|
backup_codes: z.array(MfaBackupCodeResponse).describe('List of backup codes'),
|
|
});
|
|
|
|
export type MfaBackupCodesResponse = z.infer<typeof MfaBackupCodesResponse>;
|
|
|
|
export const MfaBackupCodesChallengeStartResponse = z.object({
|
|
ticket: z.string().describe('Ticket for backup codes challenge actions'),
|
|
code_expires_at: z.string().describe('ISO8601 timestamp when the verification code expires'),
|
|
resend_available_at: z.string().describe('ISO8601 timestamp when the code can be resent'),
|
|
});
|
|
|
|
export type MfaBackupCodesChallengeStartResponse = z.infer<typeof MfaBackupCodesChallengeStartResponse>;
|
|
|
|
export const MfaBackupCodesChallengeResendRequest = z.object({
|
|
ticket: createStringType().describe('Backup codes challenge ticket identifier'),
|
|
});
|
|
|
|
export type MfaBackupCodesChallengeResendRequest = z.infer<typeof MfaBackupCodesChallengeResendRequest>;
|
|
|
|
export const MfaBackupCodesChallengeVerifyRequest = MfaBackupCodesChallengeResendRequest.extend({
|
|
code: createStringType().describe('Verification code sent to the email address'),
|
|
});
|
|
|
|
export type MfaBackupCodesChallengeVerifyRequest = z.infer<typeof MfaBackupCodesChallengeVerifyRequest>;
|
|
|
|
export const MfaBackupCodesChallengeVerifyResponse = MfaBackupCodesResponse.extend({
|
|
verification_proof: z.string().describe('Proof token authorizing backup code regeneration on this ticket'),
|
|
});
|
|
|
|
export type MfaBackupCodesChallengeVerifyResponse = z.infer<typeof MfaBackupCodesChallengeVerifyResponse>;
|
|
|
|
export const MfaBackupCodesChallengeRegenerateRequest = MfaBackupCodesChallengeResendRequest.extend({
|
|
verification_proof: createStringType().describe('Proof token obtained from verifying the email code'),
|
|
});
|
|
|
|
export type MfaBackupCodesChallengeRegenerateRequest = z.infer<typeof MfaBackupCodesChallengeRegenerateRequest>;
|
|
|
|
export const PhoneSendVerificationRequest = z.object({
|
|
phone: PhoneNumberType.describe('Phone number to send verification code'),
|
|
channel: z
|
|
.enum(['sms', 'inbound_challenge'])
|
|
.optional()
|
|
.describe(
|
|
'Channel to deliver the OTP on. Defaults to the first available channel from server policy. Server may override to an available fallback when the requested channel is disabled.',
|
|
),
|
|
});
|
|
|
|
export type PhoneSendVerificationRequest = z.infer<typeof PhoneSendVerificationRequest>;
|
|
|
|
const PhoneSendVerificationDeliveredResponse = z.object({
|
|
channel: z
|
|
.literal('sms')
|
|
.describe('Channel actually used for delivery (may differ from request when server adjusts)'),
|
|
});
|
|
|
|
const PhoneSendVerificationInboundChallengeResponse = z.object({
|
|
channel: z.literal('inbound_challenge').describe('The user must send Fluxer an SMS instead of receiving one'),
|
|
challenge_code: createStringType(4, 12).describe('The numeric code the user must text to our number'),
|
|
our_number: createStringType(4, 32).describe('The Twilio number the user must text the code to (E.164)'),
|
|
expires_at: z.iso.datetime().describe('ISO 8601 timestamp when this inbound challenge expires'),
|
|
reason: z
|
|
.enum(['voip', 'canadian', 'unknown_line_type', 'expensive_destination', 'account_forced', 'behavioural_risk'])
|
|
.describe('Why inbound verification is required'),
|
|
});
|
|
|
|
export const PhoneSendVerificationResponse = z.union([
|
|
PhoneSendVerificationDeliveredResponse,
|
|
PhoneSendVerificationInboundChallengeResponse,
|
|
]);
|
|
|
|
export type PhoneSendVerificationResponse = z.infer<typeof PhoneSendVerificationResponse>;
|
|
|
|
export const PhoneVerifyRequest = z.object({
|
|
phone: PhoneNumberType.describe('Phone number being verified'),
|
|
code: createStringType(1, 32).describe('The verification code'),
|
|
});
|
|
|
|
export type PhoneVerifyRequest = z.infer<typeof PhoneVerifyRequest>;
|
|
|
|
export const PhoneVerifyResponse = z.object({
|
|
verified: z.literal(true).describe('Indicates the phone number was verified successfully'),
|
|
});
|
|
|
|
export type PhoneVerifyResponse = z.infer<typeof PhoneVerifyResponse>;
|
|
|
|
export const WebAuthnCredentialResponse = z.object({
|
|
id: z.string().describe('The credential ID'),
|
|
name: z.string().describe('User-assigned name for the credential'),
|
|
created_at: z.string().describe('When the credential was registered'),
|
|
last_used_at: z.string().nullable().describe('When the credential was last used'),
|
|
rp_id: z.string().describe('Relying party ID the passkey belongs to'),
|
|
});
|
|
|
|
export type WebAuthnCredentialResponse = z.infer<typeof WebAuthnCredentialResponse>;
|
|
|
|
export const WebAuthnCredentialListResponse = z.array(WebAuthnCredentialResponse);
|
|
|
|
export type WebAuthnCredentialListResponse = z.infer<typeof WebAuthnCredentialListResponse>;
|
|
|
|
export const WebAuthnChallengeResponse = z.looseObject({
|
|
challenge: z.string().describe('The WebAuthn challenge'),
|
|
});
|
|
|
|
export type WebAuthnChallengeResponse = z.infer<typeof WebAuthnChallengeResponse>;
|
|
|
|
export const WebAuthnRegisterRequest = z.object({
|
|
response: WebAuthnRegistrationResponse.describe('WebAuthn registration response'),
|
|
challenge: createStringType(1, 1024).describe('The challenge from registration options'),
|
|
name: createStringType(1, 100).describe('User-assigned name for the credential'),
|
|
});
|
|
|
|
export type WebAuthnRegisterRequest = z.infer<typeof WebAuthnRegisterRequest>;
|
|
|
|
export const WebAuthnCredentialUpdateRequest = z
|
|
.object({
|
|
name: createStringType(1, 100).describe('New name for the credential'),
|
|
})
|
|
.extend(SudoVerificationSchema.shape);
|
|
|
|
export type WebAuthnCredentialUpdateRequest = z.infer<typeof WebAuthnCredentialUpdateRequest>;
|
|
|
|
export const WebAuthnTwoFactorRequest = z
|
|
.object({
|
|
enabled: z.boolean().describe('Whether registered passkeys count as a second factor when logging in'),
|
|
})
|
|
.extend(SudoVerificationSchema.shape);
|
|
|
|
export type WebAuthnTwoFactorRequest = z.infer<typeof WebAuthnTwoFactorRequest>;
|
|
|
|
export const WebAuthnTwoFactorResponse = z.object({
|
|
user: UserPrivateResponse.describe('The updated account'),
|
|
backup_codes: z
|
|
.array(MfaBackupCodeResponse)
|
|
.nullable()
|
|
.describe('Backup codes minted by this call, or null when none were minted'),
|
|
});
|
|
|
|
export type WebAuthnTwoFactorResponse = z.infer<typeof WebAuthnTwoFactorResponse>;
|
|
|
|
export const SudoMfaMethodsResponse = z.object({
|
|
totp: z.boolean().describe('Whether TOTP is enabled'),
|
|
webauthn: z.boolean().describe('Whether the account has at least one registered WebAuthn credential'),
|
|
backup_codes: z.boolean().describe('Whether the account has at least one unconsumed backup code'),
|
|
has_mfa: z.boolean().describe('Whether the account can satisfy a sudo mode challenge'),
|
|
});
|
|
|
|
export type SudoMfaMethodsResponse = z.infer<typeof SudoMfaMethodsResponse>;
|
|
|
|
export const InboundSmsChallengeStartResponse = z.object({
|
|
challenge_code: createStringType(4, 12).describe('The numeric code the user must text to our number'),
|
|
our_number: createStringType(4, 32).describe('The Twilio number the user must text the code to (E.164)'),
|
|
expires_at: z.string().describe('ISO timestamp at which the challenge becomes invalid'),
|
|
});
|
|
|
|
export type InboundSmsChallengeStartResponse = z.infer<typeof InboundSmsChallengeStartResponse>;
|
|
|
|
export const LogoutAuthSessionsWithVerificationRequest = LogoutAuthSessionsRequest.extend(SudoVerificationSchema.shape);
|