UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

558 lines (557 loc) • 22.6 kB
import { TimeString, ms, sec } from "../../utils/time.mjs"; import { DeviceCode } from "./schema.mjs"; import { DeviceCodeRedemptionAuthorization, DeviceCodeRedemptionResult, redeemDeviceCode } from "./routes.mjs"; import { GenericEndpointContext, StandardSchemaV1 } from "@better-auth/core"; import { DBFieldAttribute } from "@better-auth/core/db"; import * as _$_better_auth_core_utils_error_codes0 from "@better-auth/core/utils/error-codes"; import * as _$better_call0 from "better-call"; import * as z from "zod"; //#region src/plugins/device-authorization/index.d.ts declare module "@better-auth/core" { interface BetterAuthPluginRegistry<AuthOptions, Options> { "device-authorization": { creator: typeof deviceAuthorization; }; } } declare const deviceAuthorizationOptionsSchema: z.ZodObject<{ expiresIn: z.ZodDefault<z.ZodCustom<TimeString, TimeString>>; interval: z.ZodDefault<z.ZodCustom<TimeString, TimeString>>; deviceCodeLength: z.ZodDefault<z.ZodNumber>; userCodeLength: z.ZodDefault<z.ZodNumber>; generateDeviceCode: z.ZodOptional<z.ZodCustom<() => string | Promise<string>, () => string | Promise<string>>>; generateUserCode: z.ZodOptional<z.ZodCustom<() => string | Promise<string>, () => string | Promise<string>>>; validateClient: z.ZodOptional<z.ZodCustom<(clientId: string) => boolean | Promise<boolean>, (clientId: string) => boolean | Promise<boolean>>>; onDeviceAuthRequest: z.ZodOptional<z.ZodCustom<(clientId: string, scope: string | undefined) => void | Promise<void>, (clientId: string, scope: string | undefined) => void | Promise<void>>>; verificationUri: z.ZodOptional<z.ZodString>; schema: z.ZodOptional<z.ZodCustom<{ deviceCode?: { modelName?: string | undefined; fields?: { deviceCode?: string | undefined; userCode?: string | undefined; userId?: string | undefined; expiresAt?: string | undefined; status?: string | undefined; lastPolledAt?: string | undefined; pollingInterval?: string | undefined; clientId?: string | undefined; scope?: string | undefined; } | undefined; } | undefined; }, { deviceCode?: { modelName?: string | undefined; fields?: { deviceCode?: string | undefined; userCode?: string | undefined; userId?: string | undefined; expiresAt?: string | undefined; status?: string | undefined; lastPolledAt?: string | undefined; pollingInterval?: string | undefined; clientId?: string | undefined; scope?: string | undefined; } | undefined; } | undefined; }>>; }, z.core.$strip>; type DeviceAuthorizationOptions = z.infer<typeof deviceAuthorizationOptionsSchema>; interface DeviceAuthorizationRequest { client_id?: string | undefined; user_id?: string | undefined; scope?: string | undefined; } /** The client binding and grant-owned fields produced by request authorization. */ interface DeviceAuthorizationGrantAuthorization { /** The client identifier that owns the device code. */ clientId: string; /** Additional grant-owned fields persisted with the device code. */ deviceCodeFields: Record<string, unknown>; } /** * A token grant that contributes its request state to device authorization * without expanding the standalone plugin's database or endpoint contracts. */ interface DeviceAuthorizationGrant<RequestFields extends z.ZodRawShape = z.ZodRawShape, VerificationContext extends Record<string, unknown> = Record<string, unknown>> { /** Additional request fields accepted only when this grant is configured. */ requestSchemaFields: RequestFields; /** Additional request errors introduced by the grant's protocol extensions. */ requestErrorCodes?: readonly string[]; /** Additional OpenAPI responses introduced by the grant's request protocol. */ requestOpenAPIResponses?: Record<string, Record<string, unknown>>; /** Translate validation issues raised by the grant's request fields. */ onRequestValidationError?: (issues: readonly StandardSchemaV1.Issue[]) => void; /** Database fields persisted only when this grant is configured. */ deviceCodeSchemaFields: Record<string, DBFieldAttribute>; /** Validate a request and return its client binding and fields to persist. */ authorizeRequest: (input: { ctx: GenericEndpointContext; request: DeviceAuthorizationRequest & z.infer<z.ZodObject<RequestFields>>; }) => DeviceAuthorizationGrantAuthorization | undefined | Promise<DeviceAuthorizationGrantAuthorization | undefined>; /** Refuse the standalone session-token endpoint for grant-owned codes. */ assertSessionRedemption: (input: { ctx: GenericEndpointContext; deviceCode: Record<string, unknown>; }) => void | Promise<void>; /** Add grant-owned information to the owner-only verification response. */ getVerificationContext: (deviceCode: Record<string, unknown>) => VerificationContext | undefined; /** OpenAPI properties matching `getVerificationContext()`. */ verificationOpenAPIProperties?: Record<string, Record<string, unknown>>; } type DeviceAuthorizationPluginOptions<Grant extends DeviceAuthorizationGrant | undefined = undefined> = Partial<DeviceAuthorizationOptions> & { /** Optional token grant that extends the device authorization flow. */grant?: Grant; }; declare const deviceAuthorization: <Grant extends DeviceAuthorizationGrant | undefined = undefined>(options?: DeviceAuthorizationPluginOptions<Grant>) => { id: "device-authorization"; version: string; schema: { deviceCode: { fields: { deviceCode: { type: "string"; required: true; }; userCode: { type: "string"; required: true; }; userId: { type: "string"; required: false; }; expiresAt: { type: "date"; required: true; }; status: { type: "string"; required: true; }; lastPolledAt: { type: "date"; required: false; }; pollingInterval: { type: "number"; required: false; }; clientId: { type: "string"; required: false; }; scope: { type: "string"; required: false; }; }; indexes: { fields: [string]; unique: true; }[]; }; }; endpoints: { deviceCode: _$better_call0.StrictEndpoint<"/device/code", { method: "POST"; cloneRequest: true; body: z.ZodObject<(("scope" | "user_id" | "client_id") & keyof (Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) extends never ? { client_id: z.ZodString; user_id: z.ZodOptional<z.ZodString>; scope: z.ZodOptional<z.ZodString>; } & (Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) : ({ client_id: z.ZodString; user_id: z.ZodOptional<z.ZodString>; scope: z.ZodOptional<z.ZodString>; } extends infer T_1 extends z.core.util.SomeObject ? { [K in keyof T_1 as K extends keyof (Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) ? never : K]: T_1[K] } : never) & ((Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) extends infer T_2 extends z.core.util.SomeObject ? { [K_1 in keyof T_2]: T_2[K_1] } : never)) extends infer T ? { [k in keyof T]: T[k] } : never, z.core.$strip> | z.ZodObject<(("scope" | "user_id" | "client_id") & keyof (Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) extends never ? { user_id: z.ZodOptional<z.ZodString>; scope: z.ZodOptional<z.ZodString>; client_id: z.ZodOptional<z.ZodString>; } & (Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) : ({ user_id: z.ZodOptional<z.ZodString>; scope: z.ZodOptional<z.ZodString>; client_id: z.ZodOptional<z.ZodString>; } extends infer T_4 extends z.core.util.SomeObject ? { [K_2 in keyof T_4 as K_2 extends keyof (Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) ? never : K_2]: T_4[K_2] } : never) & ((Grant extends DeviceAuthorizationGrant<infer RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> ? RequestFields : Record<never, never>) extends infer T_5 extends z.core.util.SomeObject ? { [K_1 in keyof T_5]: T_5[K_1] } : never)) extends infer T_3 ? { [k_1 in keyof T_3]: T_3[k_1] } : never, z.core.$strip>; error: z.ZodObject<{ error: z.ZodEnum<{ [k_3 in (readonly ["invalid_request", "invalid_client", "unauthorized_client", "invalid_scope", ...Grant extends { requestErrorCodes: infer ErrorCodes extends readonly string[]; } ? ErrorCodes : readonly [], "server_error"])[number]]: k_3 } extends infer T_6 ? { [k_2 in keyof T_6]: T_6[k_2] } : never>; error_description: z.ZodString; }, z.core.$strip>; onValidationError: ({ issues, message }: { message: string; issues: readonly _$better_call0.StandardSchemaV1.Issue[]; }) => never; metadata: { noStore: boolean; allowedMediaTypes: string[]; openapi: { description: string; responses: { 200: { description: string; content: { "application/json": { schema: { type: "object"; properties: { device_code: { type: string; description: string; }; user_code: { type: string; description: string; }; verification_uri: { type: string; format: string; description: string; }; verification_uri_complete: { type: string; format: string; description: string; }; expires_in: { type: string; description: string; }; interval: { type: string; description: string; }; }; }; }; }; }; 400: { description: string; content: { "application/json": { schema: { type: "object"; properties: { error: { type: string; enum: ["invalid_request", "invalid_client", "unauthorized_client", "invalid_scope", ...Grant extends { requestErrorCodes: infer ErrorCodes extends readonly string[]; } ? ErrorCodes : readonly []]; }; error_description: { type: string; }; }; }; }; }; }; 500: { description: string; content: { "application/json": { schema: { type: "object"; properties: { error: { type: string; enum: string[]; }; error_description: { type: string; }; }; }; }; }; }; }; }; }; }, { device_code: string; user_code: string; verification_uri: string; verification_uri_complete: string; expires_in: number; interval: number; }>; deviceToken: _$better_call0.StrictEndpoint<"/device/token", { method: "POST"; body: z.ZodObject<{ grant_type: z.ZodLiteral<"urn:ietf:params:oauth:grant-type:device_code">; device_code: z.ZodString; client_id: z.ZodString; }, z.core.$strip>; error: z.ZodObject<{ error: z.ZodEnum<{ invalid_request: "invalid_request"; authorization_pending: "authorization_pending"; slow_down: "slow_down"; expired_token: "expired_token"; access_denied: "access_denied"; invalid_grant: "invalid_grant"; }>; error_description: z.ZodString; }, z.core.$strip>; metadata: { noStore: boolean; openapi: { description: string; responses: { 200: { description: string; content: { "application/json": { schema: { type: "object"; properties: { session: { $ref: string; }; user: { $ref: string; }; }; }; }; }; }; 400: { description: string; content: { "application/json": { schema: { type: "object"; properties: { error: { type: string; enum: string[]; }; error_description: { type: string; }; }; }; }; }; }; }; }; }; }, { access_token: string; token_type: string; expires_in: number; scope: string; }>; deviceVerify: _$better_call0.StrictEndpoint<"/device", { method: "GET"; query: z.ZodObject<{ user_code: z.ZodString; }, z.core.$strip>; error: z.ZodObject<{ error: z.ZodEnum<{ invalid_request: "invalid_request"; }>; error_description: z.ZodString; }, z.core.$strip>; metadata: { openapi: { description: string; responses: { 200: { description: string; content: { "application/json": { schema: { type: "object"; properties: { user_code: { type: string; description: string; }; status: { type: string; enum: string[]; description: string; }; client_id: { type: string; description: string; }; scope: { type: string; description: string; }; }; }; }; }; }; }; }; }; }, { user_code: string; status: string; client_id?: string | undefined; scope?: string | undefined; } & Partial<Grant extends DeviceAuthorizationGrant<infer _RequestFields extends Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, infer VerificationContext extends Record<string, unknown>> ? VerificationContext : Record<never, never>>>; deviceApprove: _$better_call0.StrictEndpoint<"/device/approve", { method: "POST"; body: z.ZodObject<{ userCode: z.ZodString; }, z.core.$strip>; error: z.ZodObject<{ error: z.ZodEnum<{ invalid_request: "invalid_request"; expired_token: "expired_token"; access_denied: "access_denied"; device_code_already_processed: "device_code_already_processed"; unauthorized: "unauthorized"; }>; error_description: z.ZodString; }, z.core.$strip>; requireHeaders: true; metadata: { openapi: { description: string; responses: { 200: { description: string; content: { "application/json": { schema: { type: "object"; properties: { success: { type: string; }; }; }; }; }; }; }; }; }; }, { success: boolean; }>; deviceDeny: _$better_call0.StrictEndpoint<"/device/deny", { method: "POST"; body: z.ZodObject<{ userCode: z.ZodString; }, z.core.$strip>; error: z.ZodObject<{ error: z.ZodEnum<{ invalid_request: "invalid_request"; expired_token: "expired_token"; access_denied: "access_denied"; unauthorized: "unauthorized"; }>; error_description: z.ZodString; }, z.core.$strip>; requireHeaders: true; metadata: { openapi: { description: string; responses: { 200: { description: string; content: { "application/json": { schema: { type: "object"; properties: { success: { type: string; }; }; }; }; }; }; }; }; }; }, { success: boolean; }>; }; rateLimit: { pathMatcher(path: string): path is "/device"; window: number; max: number; }[]; $ERROR_CODES: { USER_NOT_FOUND: _$_better_auth_core_utils_error_codes0.RawError<"USER_NOT_FOUND">; FAILED_TO_CREATE_SESSION: _$_better_auth_core_utils_error_codes0.RawError<"FAILED_TO_CREATE_SESSION">; INVALID_DEVICE_CODE: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_DEVICE_CODE">; EXPIRED_DEVICE_CODE: _$_better_auth_core_utils_error_codes0.RawError<"EXPIRED_DEVICE_CODE">; EXPIRED_USER_CODE: _$_better_auth_core_utils_error_codes0.RawError<"EXPIRED_USER_CODE">; AUTHORIZATION_PENDING: _$_better_auth_core_utils_error_codes0.RawError<"AUTHORIZATION_PENDING">; ACCESS_DENIED: _$_better_auth_core_utils_error_codes0.RawError<"ACCESS_DENIED">; INVALID_USER_CODE: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_USER_CODE">; DEVICE_CODE_ALREADY_PROCESSED: _$_better_auth_core_utils_error_codes0.RawError<"DEVICE_CODE_ALREADY_PROCESSED">; DEVICE_CODE_NOT_CLAIMED: _$_better_auth_core_utils_error_codes0.RawError<"DEVICE_CODE_NOT_CLAIMED">; POLLING_TOO_FREQUENTLY: _$_better_auth_core_utils_error_codes0.RawError<"POLLING_TOO_FREQUENTLY">; INVALID_DEVICE_CODE_STATUS: _$_better_auth_core_utils_error_codes0.RawError<"INVALID_DEVICE_CODE_STATUS">; AUTHENTICATION_REQUIRED: _$_better_auth_core_utils_error_codes0.RawError<"AUTHENTICATION_REQUIRED">; }; options: { grant: DeviceAuthorizationGrant<Readonly<{ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>; }>, Record<string, unknown>> | undefined; expiresIn: TimeString; interval: TimeString; deviceCodeLength: number; userCodeLength: number; generateDeviceCode?: (() => string | Promise<string>) | undefined; generateUserCode?: (() => string | Promise<string>) | undefined; validateClient?: ((clientId: string) => boolean | Promise<boolean>) | undefined; onDeviceAuthRequest?: ((clientId: string, scope: string | undefined) => void | Promise<void>) | undefined; verificationUri?: string | undefined; schema?: { deviceCode?: { modelName?: string | undefined; fields?: { deviceCode?: string | undefined; userCode?: string | undefined; userId?: string | undefined; expiresAt?: string | undefined; status?: string | undefined; lastPolledAt?: string | undefined; pollingInterval?: string | undefined; clientId?: string | undefined; scope?: string | undefined; } | undefined; } | undefined; } | undefined; }; }; //#endregion export { DeviceAuthorizationGrant, DeviceAuthorizationGrantAuthorization, DeviceAuthorizationOptions, DeviceAuthorizationPluginOptions, DeviceAuthorizationRequest, type DeviceCode, type DeviceCodeRedemptionAuthorization, type DeviceCodeRedemptionResult, TimeString, deviceAuthorization, deviceAuthorizationOptionsSchema, ms, redeemDeviceCode, sec };