UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

1 lines • 32.5 kB
{"version":3,"file":"routes.mjs","names":["deviceCode","verificationUrl: URL"],"sources":["../../../src/plugins/device-authorization/routes.ts"],"sourcesContent":["import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"better-call\";\nimport * as z from \"zod\";\nimport { getSessionFromCtx } from \"../../api/routes/session\";\nimport { generateRandomString } from \"../../crypto\";\nimport { ms } from \"../../utils/time\";\nimport type { DeviceAuthorizationOptions } from \".\";\nimport { DEVICE_AUTHORIZATION_ERROR_CODES } from \"./error-codes\";\nimport type { DeviceCode } from \"./schema\";\n\n/* cspell:disable-next-line */\nconst defaultCharset = \"ABCDEFGHJKLMNPQRSTUVWXYZ23456789\";\n\nconst deviceCodeBodySchema = z.object({\n\tclient_id: z.string().meta({\n\t\tdescription: \"The client ID of the application\",\n\t}),\n\tscope: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Space-separated list of scopes\",\n\t\t})\n\t\t.optional(),\n});\n\nconst deviceCodeErrorSchema = z.object({\n\terror: z.enum([\"invalid_request\", \"invalid_client\"]).meta({\n\t\tdescription: \"Error code\",\n\t}),\n\terror_description: z.string().meta({\n\t\tdescription: \"Detailed error description\",\n\t}),\n});\n\nexport const deviceCode = (opts: DeviceAuthorizationOptions) => {\n\tconst generateDeviceCode = async () => {\n\t\tif (opts.generateDeviceCode) {\n\t\t\treturn opts.generateDeviceCode();\n\t\t}\n\t\treturn defaultGenerateDeviceCode(opts.deviceCodeLength);\n\t};\n\n\tconst generateUserCode = async () => {\n\t\tif (opts.generateUserCode) {\n\t\t\treturn opts.generateUserCode();\n\t\t}\n\t\treturn defaultGenerateUserCode(opts.userCodeLength);\n\t};\n\treturn createAuthEndpoint(\n\t\t\"/device/code\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: deviceCodeBodySchema,\n\t\t\terror: deviceCodeErrorSchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: `Request a device and user code\n\nFollow [rfc8628#section-3.2](https://datatracker.ietf.org/doc/html/rfc8628#section-3.2)`,\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tdevice_code: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The device verification code\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tuser_code: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The user code to display\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tverification_uri: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"The URL for user verification. Defaults to /device if not configured.\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tverification_uri_complete: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"The complete URL with user code as query parameter.\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\texpires_in: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"number\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Lifetime in seconds of the device code\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tinterval: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"number\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Minimum polling interval in seconds\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t400: {\n\t\t\t\t\t\t\tdescription: \"Error response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"invalid_request\", \"invalid_client\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\terror_description: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (opts.validateClient) {\n\t\t\t\tconst isValid = await opts.validateClient(ctx.body.client_id);\n\t\t\t\tif (!isValid) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\terror_description: \"Invalid client ID\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (opts.onDeviceAuthRequest) {\n\t\t\t\tawait opts.onDeviceAuthRequest(ctx.body.client_id, ctx.body.scope);\n\t\t\t}\n\n\t\t\tconst deviceCode = await generateDeviceCode();\n\t\t\tconst userCode = await generateUserCode();\n\t\t\tconst expiresIn = ms(opts.expiresIn);\n\t\t\tconst expiresAt = new Date(Date.now() + expiresIn);\n\n\t\t\tawait ctx.context.adapter.create({\n\t\t\t\tmodel: \"deviceCode\",\n\t\t\t\tdata: {\n\t\t\t\t\tdeviceCode,\n\t\t\t\t\tuserCode,\n\t\t\t\t\texpiresAt,\n\t\t\t\t\tstatus: \"pending\",\n\t\t\t\t\tpollingInterval: ms(opts.interval),\n\t\t\t\t\tclientId: ctx.body.client_id,\n\t\t\t\t\tscope: ctx.body.scope,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst { verificationUri, verificationUriComplete } =\n\t\t\t\tbuildVerificationUris(\n\t\t\t\t\topts.verificationUri,\n\t\t\t\t\tctx.context.baseURL,\n\t\t\t\t\tuserCode,\n\t\t\t\t);\n\n\t\t\treturn ctx.json(\n\t\t\t\t{\n\t\t\t\t\tdevice_code: deviceCode,\n\t\t\t\t\tuser_code: userCode,\n\t\t\t\t\tverification_uri: verificationUri,\n\t\t\t\t\tverification_uri_complete: verificationUriComplete,\n\t\t\t\t\texpires_in: Math.floor(expiresIn / 1000),\n\t\t\t\t\tinterval: Math.floor(ms(opts.interval) / 1000),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Cache-Control\": \"no-store\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n};\n\nconst deviceTokenBodySchema = z.object({\n\tgrant_type: z.literal(\"urn:ietf:params:oauth:grant-type:device_code\").meta({\n\t\tdescription: \"The grant type for device flow\",\n\t}),\n\tdevice_code: z.string().meta({\n\t\tdescription: \"The device verification code\",\n\t}),\n\tclient_id: z.string().meta({\n\t\tdescription: \"The client ID of the application\",\n\t}),\n});\n\nconst deviceTokenErrorSchema = z.object({\n\terror: z\n\t\t.enum([\n\t\t\t\"authorization_pending\",\n\t\t\t\"slow_down\",\n\t\t\t\"expired_token\",\n\t\t\t\"access_denied\",\n\t\t\t\"invalid_request\",\n\t\t\t\"invalid_grant\",\n\t\t])\n\t\t.meta({\n\t\t\tdescription: \"Error code\",\n\t\t}),\n\terror_description: z.string().meta({\n\t\tdescription: \"Detailed error description\",\n\t}),\n});\n\nexport const deviceToken = (opts: DeviceAuthorizationOptions) =>\n\tcreateAuthEndpoint(\n\t\t\"/device/token\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: deviceTokenBodySchema,\n\t\t\terror: deviceTokenErrorSchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: `Exchange device code for access token\n\nFollow [rfc8628#section-3.4](https://datatracker.ietf.org/doc/html/rfc8628#section-3.4)`,\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tsession: {\n\t\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/Session\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/User\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t400: {\n\t\t\t\t\t\t\tdescription: \"Error response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\tenum: [\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"authorization_pending\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"slow_down\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"expired_token\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"access_denied\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"invalid_request\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"invalid_grant\",\n\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\terror_description: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst { device_code, client_id } = ctx.body;\n\n\t\t\tif (opts.validateClient) {\n\t\t\t\tconst isValid = await opts.validateClient(client_id);\n\t\t\t\tif (!isValid) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\t\terror_description: \"Invalid client ID\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst deviceCodeRecord = await ctx.context.adapter.findOne<{\n\t\t\t\tid: string;\n\t\t\t\tdeviceCode: string;\n\t\t\t\tuserCode: string;\n\t\t\t\tuserId?: string | undefined;\n\t\t\t\texpiresAt: Date;\n\t\t\t\tstatus: string;\n\t\t\t\tlastPolledAt?: Date | undefined;\n\t\t\t\tpollingInterval?: number | undefined;\n\t\t\t\tclientId?: string | undefined;\n\t\t\t\tscope?: string | undefined;\n\t\t\t}>({\n\t\t\t\tmodel: \"deviceCode\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"deviceCode\",\n\t\t\t\t\t\tvalue: device_code,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (!deviceCodeRecord) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\terror_description:\n\t\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.INVALID_DEVICE_CODE,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tdeviceCodeRecord.clientId &&\n\t\t\t\tdeviceCodeRecord.clientId !== client_id\n\t\t\t) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\terror_description: \"Client ID mismatch\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Check for rate limiting\n\t\t\tif (deviceCodeRecord.lastPolledAt && deviceCodeRecord.pollingInterval) {\n\t\t\t\tconst timeSinceLastPoll =\n\t\t\t\t\tDate.now() - new Date(deviceCodeRecord.lastPolledAt).getTime();\n\t\t\t\tconst minInterval = deviceCodeRecord.pollingInterval;\n\n\t\t\t\tif (timeSinceLastPoll < minInterval) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\terror: \"slow_down\",\n\t\t\t\t\t\terror_description:\n\t\t\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.POLLING_TOO_FREQUENTLY,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update last polled time\n\t\t\tawait ctx.context.adapter.update({\n\t\t\t\tmodel: \"deviceCode\",\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\tvalue: deviceCodeRecord.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tupdate: {\n\t\t\t\t\tlastPolledAt: new Date(),\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (deviceCodeRecord.expiresAt < new Date()) {\n\t\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\t\tmodel: \"deviceCode\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: deviceCodeRecord.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\terror: \"expired_token\",\n\t\t\t\t\terror_description:\n\t\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_DEVICE_CODE,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (deviceCodeRecord.status === \"pending\") {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\terror: \"authorization_pending\",\n\t\t\t\t\terror_description:\n\t\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.AUTHORIZATION_PENDING,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (deviceCodeRecord.status === \"denied\") {\n\t\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\t\tmodel: \"deviceCode\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: deviceCodeRecord.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\terror: \"access_denied\",\n\t\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.ACCESS_DENIED,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (deviceCodeRecord.status === \"approved\" && deviceCodeRecord.userId) {\n\t\t\t\tconst user = await ctx.context.internalAdapter.findUserById(\n\t\t\t\t\tdeviceCodeRecord.userId,\n\t\t\t\t);\n\n\t\t\t\tif (!user) {\n\t\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\t\terror: \"server_error\",\n\t\t\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.USER_NOT_FOUND,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst session = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\tuser.id,\n\t\t\t\t);\n\n\t\t\t\tif (!session) {\n\t\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\t\terror: \"server_error\",\n\t\t\t\t\t\terror_description:\n\t\t\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Set new session context for hooks and plugins\n\t\t\t\t// (matches setSessionCookie logic)\n\t\t\t\tctx.context.setNewSession({\n\t\t\t\t\tsession,\n\t\t\t\t\tuser,\n\t\t\t\t});\n\n\t\t\t\t// If secondary storage is enabled, store the session data in the secondary storage\n\t\t\t\t// (matches setSessionCookie logic)\n\t\t\t\tif (ctx.context.options.secondaryStorage) {\n\t\t\t\t\tawait ctx.context.secondaryStorage?.set(\n\t\t\t\t\t\tsession.token,\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tuser,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tMath.floor(\n\t\t\t\t\t\t\t(new Date(session.expiresAt).getTime() - Date.now()) / 1000,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Delete the device code after successful authorization\n\t\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\t\tmodel: \"deviceCode\",\n\t\t\t\t\twhere: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfield: \"id\",\n\t\t\t\t\t\t\tvalue: deviceCodeRecord.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\t// Return OAuth 2.0 compliant token response\n\t\t\t\treturn ctx.json(\n\t\t\t\t\t{\n\t\t\t\t\t\taccess_token: session.token,\n\t\t\t\t\t\ttoken_type: \"Bearer\",\n\t\t\t\t\t\texpires_in: Math.floor(\n\t\t\t\t\t\t\t(new Date(session.expiresAt).getTime() - Date.now()) / 1000,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tscope: deviceCodeRecord.scope || \"\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Cache-Control\": \"no-store\",\n\t\t\t\t\t\t\tPragma: \"no-cache\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\terror: \"server_error\",\n\t\t\t\terror_description:\n\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.INVALID_DEVICE_CODE_STATUS,\n\t\t\t});\n\t\t},\n\t);\n\nexport const deviceVerify = createAuthEndpoint(\n\t\"/device\",\n\t{\n\t\tmethod: \"GET\",\n\t\tquery: z.object({\n\t\t\tuser_code: z.string().meta({\n\t\t\t\tdescription: \"The user code to verify\",\n\t\t\t}),\n\t\t}),\n\t\terror: z.object({\n\t\t\terror: z.enum([\"invalid_request\"]).meta({\n\t\t\t\tdescription: \"Error code\",\n\t\t\t}),\n\t\t\terror_description: z.string().meta({\n\t\t\t\tdescription: \"Detailed error description\",\n\t\t\t}),\n\t\t}),\n\t\tmetadata: {\n\t\t\topenapi: {\n\t\t\t\tdescription: \"Verify user code and get device authorization status\",\n\t\t\t\tresponses: {\n\t\t\t\t\t200: {\n\t\t\t\t\t\tdescription: \"Device authorization status\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tuser_code: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The user code to verify\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\tenum: [\"pending\", \"approved\", \"denied\"],\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"Current status of the device authorization\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tasync (ctx) => {\n\t\tconst { user_code } = ctx.query;\n\t\tconst cleanUserCode = user_code.replace(/-/g, \"\");\n\n\t\tconst deviceCodeRecord = await ctx.context.adapter.findOne<DeviceCode>({\n\t\t\tmodel: \"deviceCode\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"userCode\",\n\t\t\t\t\tvalue: cleanUserCode,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tif (!deviceCodeRecord) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"invalid_request\",\n\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE,\n\t\t\t});\n\t\t}\n\n\t\tif (deviceCodeRecord.expiresAt < new Date()) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"expired_token\",\n\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE,\n\t\t\t});\n\t\t}\n\n\t\treturn ctx.json({\n\t\t\tuser_code: user_code,\n\t\t\tstatus: deviceCodeRecord.status,\n\t\t});\n\t},\n);\n\nexport const deviceApprove = createAuthEndpoint(\n\t\"/device/approve\",\n\t{\n\t\tmethod: \"POST\",\n\t\tbody: z.object({\n\t\t\tuserCode: z.string().meta({\n\t\t\t\tdescription: \"The user code to approve\",\n\t\t\t}),\n\t\t}),\n\t\terror: z.object({\n\t\t\terror: z\n\t\t\t\t.enum([\n\t\t\t\t\t\"invalid_request\",\n\t\t\t\t\t\"expired_token\",\n\t\t\t\t\t\"device_code_already_processed\",\n\t\t\t\t])\n\t\t\t\t.meta({\n\t\t\t\t\tdescription: \"Error code\",\n\t\t\t\t}),\n\t\t\terror_description: z.string().meta({\n\t\t\t\tdescription: \"Detailed error description\",\n\t\t\t}),\n\t\t}),\n\t\trequireHeaders: true,\n\t\tmetadata: {\n\t\t\topenapi: {\n\t\t\t\tdescription: \"Approve device authorization\",\n\t\t\t\tresponses: {\n\t\t\t\t\t200: {\n\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsuccess: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tasync (ctx) => {\n\t\tconst session = await getSessionFromCtx(ctx);\n\t\tif (!session) {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\terror: \"unauthorized\",\n\t\t\t\terror_description:\n\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.AUTHENTICATION_REQUIRED,\n\t\t\t});\n\t\t}\n\n\t\tconst { userCode } = ctx.body;\n\t\tconst cleanUserCode = userCode.replace(/-/g, \"\");\n\n\t\tconst deviceCodeRecord = await ctx.context.adapter.findOne<DeviceCode>({\n\t\t\tmodel: \"deviceCode\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"userCode\",\n\t\t\t\t\tvalue: cleanUserCode,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tif (!deviceCodeRecord) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"invalid_request\",\n\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE,\n\t\t\t});\n\t\t}\n\n\t\tif (deviceCodeRecord.expiresAt < new Date()) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"expired_token\",\n\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE,\n\t\t\t});\n\t\t}\n\n\t\tif (deviceCodeRecord.status !== \"pending\") {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"invalid_request\",\n\t\t\t\terror_description:\n\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.DEVICE_CODE_ALREADY_PROCESSED,\n\t\t\t});\n\t\t}\n\n\t\t// Update device code with approved status and user ID\n\t\tawait ctx.context.adapter.update({\n\t\t\tmodel: \"deviceCode\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"id\",\n\t\t\t\t\tvalue: deviceCodeRecord.id,\n\t\t\t\t},\n\t\t\t],\n\t\t\tupdate: {\n\t\t\t\tstatus: \"approved\",\n\t\t\t\tuserId: session.user.id,\n\t\t\t},\n\t\t});\n\n\t\treturn ctx.json({\n\t\t\tsuccess: true,\n\t\t});\n\t},\n);\n\nexport const deviceDeny = createAuthEndpoint(\n\t\"/device/deny\",\n\t{\n\t\tmethod: \"POST\",\n\t\tbody: z.object({\n\t\t\tuserCode: z.string().meta({\n\t\t\t\tdescription: \"The user code to deny\",\n\t\t\t}),\n\t\t}),\n\t\terror: z.object({\n\t\t\terror: z.enum([\"invalid_request\", \"expired_token\"]).meta({\n\t\t\t\tdescription: \"Error code\",\n\t\t\t}),\n\t\t\terror_description: z.string().meta({\n\t\t\t\tdescription: \"Detailed error description\",\n\t\t\t}),\n\t\t}),\n\t\tmetadata: {\n\t\t\topenapi: {\n\t\t\t\tdescription: \"Deny device authorization\",\n\t\t\t\tresponses: {\n\t\t\t\t\t200: {\n\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsuccess: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tasync (ctx) => {\n\t\tconst { userCode } = ctx.body;\n\t\tconst cleanUserCode = userCode.replace(/-/g, \"\");\n\n\t\tconst deviceCodeRecord = await ctx.context.adapter.findOne<DeviceCode>({\n\t\t\tmodel: \"deviceCode\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"userCode\",\n\t\t\t\t\tvalue: cleanUserCode,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tif (!deviceCodeRecord) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"invalid_request\",\n\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.INVALID_USER_CODE,\n\t\t\t});\n\t\t}\n\n\t\tif (deviceCodeRecord.expiresAt < new Date()) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"expired_token\",\n\t\t\t\terror_description: DEVICE_AUTHORIZATION_ERROR_CODES.EXPIRED_USER_CODE,\n\t\t\t});\n\t\t}\n\n\t\tif (deviceCodeRecord.status !== \"pending\") {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\terror: \"invalid_request\",\n\t\t\t\terror_description:\n\t\t\t\t\tDEVICE_AUTHORIZATION_ERROR_CODES.DEVICE_CODE_ALREADY_PROCESSED,\n\t\t\t});\n\t\t}\n\n\t\t// Update device code with denied status\n\t\tawait ctx.context.adapter.update({\n\t\t\tmodel: \"deviceCode\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"id\",\n\t\t\t\t\tvalue: deviceCodeRecord.id,\n\t\t\t\t},\n\t\t\t],\n\t\t\tupdate: {\n\t\t\t\tstatus: \"denied\",\n\t\t\t},\n\t\t});\n\n\t\treturn ctx.json({\n\t\t\tsuccess: true,\n\t\t});\n\t},\n);\n\n/**\n * @internal\n */\nconst buildVerificationUris = (\n\tverificationUri: string | undefined,\n\tbaseURL: string,\n\tuserCode: string,\n): {\n\tverificationUri: string;\n\tverificationUriComplete: string;\n} => {\n\tconst uri = verificationUri || \"/device\";\n\n\tlet verificationUrl: URL;\n\ttry {\n\t\tverificationUrl = new URL(uri);\n\t} catch {\n\t\tverificationUrl = new URL(uri, baseURL);\n\t}\n\n\tconst verificationUriCompleteUrl = new URL(verificationUrl);\n\tverificationUriCompleteUrl.searchParams.set(\"user_code\", userCode);\n\n\tconst verificationUriString = verificationUrl.toString();\n\tconst verificationUriCompleteString = verificationUriCompleteUrl.toString();\n\n\treturn {\n\t\tverificationUri: verificationUriString,\n\t\tverificationUriComplete: verificationUriCompleteString,\n\t};\n};\n\n/**\n * @internal\n */\nconst defaultGenerateDeviceCode = (length: number) => {\n\treturn generateRandomString(length, \"a-z\", \"A-Z\", \"0-9\");\n};\n\n/**\n * @internal\n */\nconst defaultGenerateUserCode = (length: number) => {\n\tconst chars = new Uint8Array(length);\n\treturn Array.from(crypto.getRandomValues(chars))\n\t\t.map((byte) => defaultCharset[byte % defaultCharset.length])\n\t\t.join(\"\");\n};\n"],"mappings":"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"}