UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

1 lines • 19 kB
{"version":3,"file":"adapter.mjs","names":["userIds: string[]","apiKeys: ApiKey[]"],"sources":["../../../src/plugins/api-key/adapter.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport type { SecondaryStorage } from \"@better-auth/core/db\";\nimport type { PredefinedApiKeyOptions } from \"./routes\";\nimport type { ApiKey } from \"./types\";\n\n/**\n * Generate storage key for API key by hashed key\n */\nfunction getStorageKeyByHashedKey(hashedKey: string): string {\n\treturn `api-key:${hashedKey}`;\n}\n\n/**\n * Generate storage key for API key by ID\n */\nfunction getStorageKeyById(id: string): string {\n\treturn `api-key:by-id:${id}`;\n}\n\n/**\n * Generate storage key for user's API key list\n */\nfunction getStorageKeyByUserId(userId: string): string {\n\treturn `api-key:by-user:${userId}`;\n}\n\n/**\n * Serialize API key for storage\n */\nfunction serializeApiKey(apiKey: ApiKey): string {\n\treturn JSON.stringify({\n\t\t...apiKey,\n\t\tcreatedAt: apiKey.createdAt.toISOString(),\n\t\tupdatedAt: apiKey.updatedAt.toISOString(),\n\t\texpiresAt: apiKey.expiresAt?.toISOString() ?? null,\n\t\tlastRefillAt: apiKey.lastRefillAt?.toISOString() ?? null,\n\t\tlastRequest: apiKey.lastRequest?.toISOString() ?? null,\n\t});\n}\n\n/**\n * Deserialize API key from storage\n */\nfunction deserializeApiKey(data: unknown): ApiKey | null {\n\tif (!data || typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst parsed = JSON.parse(data);\n\t\treturn {\n\t\t\t...parsed,\n\t\t\tcreatedAt: new Date(parsed.createdAt),\n\t\t\tupdatedAt: new Date(parsed.updatedAt),\n\t\t\texpiresAt: parsed.expiresAt ? new Date(parsed.expiresAt) : null,\n\t\t\tlastRefillAt: parsed.lastRefillAt ? new Date(parsed.lastRefillAt) : null,\n\t\t\tlastRequest: parsed.lastRequest ? new Date(parsed.lastRequest) : null,\n\t\t} as ApiKey;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Get the storage instance to use (custom methods take precedence)\n */\nfunction getStorageInstance(\n\tctx: GenericEndpointContext,\n\topts: PredefinedApiKeyOptions,\n): SecondaryStorage | null {\n\tif (opts.customStorage) {\n\t\treturn opts.customStorage as SecondaryStorage;\n\t}\n\treturn ctx.context.secondaryStorage || null;\n}\n\n/**\n * Calculate TTL in seconds for an API key\n */\nfunction calculateTTL(apiKey: ApiKey): number | undefined {\n\tif (apiKey.expiresAt) {\n\t\tconst now = Date.now();\n\t\tconst expiresAt = new Date(apiKey.expiresAt).getTime();\n\t\tconst ttlSeconds = Math.floor((expiresAt - now) / 1000);\n\t\t// Only set TTL if expiration is in the future\n\t\tif (ttlSeconds > 0) {\n\t\t\treturn ttlSeconds;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Get API key from secondary storage by hashed key\n */\nasync function getApiKeyFromStorage(\n\tctx: GenericEndpointContext,\n\thashedKey: string,\n\tstorage: SecondaryStorage,\n): Promise<ApiKey | null> {\n\tconst key = getStorageKeyByHashedKey(hashedKey);\n\tconst data = await storage.get(key);\n\treturn deserializeApiKey(data);\n}\n\n/**\n * Get API key from secondary storage by ID\n */\nasync function getApiKeyByIdFromStorage(\n\tctx: GenericEndpointContext,\n\tid: string,\n\tstorage: SecondaryStorage,\n): Promise<ApiKey | null> {\n\tconst key = getStorageKeyById(id);\n\tconst data = await storage.get(key);\n\treturn deserializeApiKey(data);\n}\n\n/**\n * Store API key in secondary storage\n */\nasync function setApiKeyInStorage(\n\tctx: GenericEndpointContext,\n\tapiKey: ApiKey,\n\tstorage: SecondaryStorage,\n\tttl?: number | undefined,\n): Promise<void> {\n\tconst serialized = serializeApiKey(apiKey);\n\tconst hashedKey = apiKey.key;\n\tconst id = apiKey.id;\n\n\t// Store by hashed key (primary lookup)\n\tawait storage.set(getStorageKeyByHashedKey(hashedKey), serialized, ttl);\n\n\t// Store by ID (for ID-based lookups)\n\tawait storage.set(getStorageKeyById(id), serialized, ttl);\n\n\t// Update user's API key list\n\tconst userKey = getStorageKeyByUserId(apiKey.userId);\n\tconst userListData = await storage.get(userKey);\n\tlet userIds: string[] = [];\n\n\tif (userListData && typeof userListData === \"string\") {\n\t\ttry {\n\t\t\tuserIds = JSON.parse(userListData);\n\t\t} catch {\n\t\t\tuserIds = [];\n\t\t}\n\t} else if (Array.isArray(userListData)) {\n\t\tuserIds = userListData;\n\t}\n\n\tif (!userIds.includes(id)) {\n\t\tuserIds.push(id);\n\t\tawait storage.set(userKey, JSON.stringify(userIds));\n\t}\n}\n\n/**\n * Delete API key from secondary storage\n */\nasync function deleteApiKeyFromStorage(\n\tctx: GenericEndpointContext,\n\tapiKey: ApiKey,\n\tstorage: SecondaryStorage,\n): Promise<void> {\n\tconst hashedKey = apiKey.key;\n\tconst id = apiKey.id;\n\tconst userId = apiKey.userId;\n\n\t// Delete by hashed key\n\tawait storage.delete(getStorageKeyByHashedKey(hashedKey));\n\n\t// Delete by ID\n\tawait storage.delete(getStorageKeyById(id));\n\n\t// Update user's API key list\n\tconst userKey = getStorageKeyByUserId(userId);\n\tconst userListData = await storage.get(userKey);\n\tlet userIds: string[] = [];\n\n\tif (userListData && typeof userListData === \"string\") {\n\t\ttry {\n\t\t\tuserIds = JSON.parse(userListData);\n\t\t} catch {\n\t\t\tuserIds = [];\n\t\t}\n\t} else if (Array.isArray(userListData)) {\n\t\tuserIds = userListData;\n\t}\n\n\tconst filteredIds = userIds.filter((keyId) => keyId !== id);\n\tif (filteredIds.length === 0) {\n\t\tawait storage.delete(userKey);\n\t} else {\n\t\tawait storage.set(userKey, JSON.stringify(filteredIds));\n\t}\n}\n\n/**\n * Unified getter for API keys with support for all storage modes\n */\nexport async function getApiKey(\n\tctx: GenericEndpointContext,\n\thashedKey: string,\n\topts: PredefinedApiKeyOptions,\n): Promise<ApiKey | null> {\n\tconst storage = getStorageInstance(ctx, opts);\n\n\t// Database mode only\n\tif (opts.storage === \"database\") {\n\t\treturn await ctx.context.adapter.findOne<ApiKey>({\n\t\t\tmodel: \"apikey\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"key\",\n\t\t\t\t\tvalue: hashedKey,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\t// Secondary storage mode with fallback\n\tif (opts.storage === \"secondary-storage\" && opts.fallbackToDatabase) {\n\t\tif (storage) {\n\t\t\tconst cached = await getApiKeyFromStorage(ctx, hashedKey, storage);\n\t\t\tif (cached) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t}\n\t\tconst dbKey = await ctx.context.adapter.findOne<ApiKey>({\n\t\t\tmodel: \"apikey\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"key\",\n\t\t\t\t\tvalue: hashedKey,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tif (dbKey && storage) {\n\t\t\t// Populate secondary storage for future reads\n\t\t\tconst ttl = calculateTTL(dbKey);\n\t\t\tawait setApiKeyInStorage(ctx, dbKey, storage, ttl);\n\t\t}\n\n\t\treturn dbKey;\n\t}\n\n\t// Secondary storage mode only\n\tif (opts.storage === \"secondary-storage\") {\n\t\tif (!storage) {\n\t\t\treturn null;\n\t\t}\n\t\treturn await getApiKeyFromStorage(ctx, hashedKey, storage);\n\t}\n\n\t// Default fallback\n\treturn await ctx.context.adapter.findOne<ApiKey>({\n\t\tmodel: \"apikey\",\n\t\twhere: [\n\t\t\t{\n\t\t\t\tfield: \"key\",\n\t\t\t\tvalue: hashedKey,\n\t\t\t},\n\t\t],\n\t});\n}\n\n/**\n * Unified getter for API keys by ID\n */\nexport async function getApiKeyById(\n\tctx: GenericEndpointContext,\n\tid: string,\n\topts: PredefinedApiKeyOptions,\n): Promise<ApiKey | null> {\n\tconst storage = getStorageInstance(ctx, opts);\n\n\t// Database mode only\n\tif (opts.storage === \"database\") {\n\t\treturn await ctx.context.adapter.findOne<ApiKey>({\n\t\t\tmodel: \"apikey\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"id\",\n\t\t\t\t\tvalue: id,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\t// Secondary storage mode with fallback\n\tif (opts.storage === \"secondary-storage\" && opts.fallbackToDatabase) {\n\t\tif (storage) {\n\t\t\tconst cached = await getApiKeyByIdFromStorage(ctx, id, storage);\n\t\t\tif (cached) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t}\n\t\tconst dbKey = await ctx.context.adapter.findOne<ApiKey>({\n\t\t\tmodel: \"apikey\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"id\",\n\t\t\t\t\tvalue: id,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tif (dbKey && storage) {\n\t\t\t// Populate secondary storage for future reads\n\t\t\tconst ttl = calculateTTL(dbKey);\n\t\t\tawait setApiKeyInStorage(ctx, dbKey, storage, ttl);\n\t\t}\n\n\t\treturn dbKey;\n\t}\n\n\t// Secondary storage mode only\n\tif (opts.storage === \"secondary-storage\") {\n\t\tif (!storage) {\n\t\t\treturn null;\n\t\t}\n\t\treturn await getApiKeyByIdFromStorage(ctx, id, storage);\n\t}\n\n\t// Default fallback\n\treturn await ctx.context.adapter.findOne<ApiKey>({\n\t\tmodel: \"apikey\",\n\t\twhere: [\n\t\t\t{\n\t\t\t\tfield: \"id\",\n\t\t\t\tvalue: id,\n\t\t\t},\n\t\t],\n\t});\n}\n\n/**\n * Unified setter for API keys with support for all storage modes\n */\nexport async function setApiKey(\n\tctx: GenericEndpointContext,\n\tapiKey: ApiKey,\n\topts: PredefinedApiKeyOptions,\n): Promise<void> {\n\tconst storage = getStorageInstance(ctx, opts);\n\tconst ttl = calculateTTL(apiKey);\n\n\t// Database mode only - handled by adapter in route handlers\n\tif (opts.storage === \"database\") {\n\t\treturn;\n\t}\n\n\t// Secondary storage mode (with or without fallback)\n\tif (opts.storage === \"secondary-storage\") {\n\t\tif (!storage) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Secondary storage is required when storage mode is 'secondary-storage'\",\n\t\t\t);\n\t\t}\n\t\tawait setApiKeyInStorage(ctx, apiKey, storage, ttl);\n\t\treturn;\n\t}\n}\n\n/**\n * Unified deleter for API keys with support for all storage modes\n */\nexport async function deleteApiKey(\n\tctx: GenericEndpointContext,\n\tapiKey: ApiKey,\n\topts: PredefinedApiKeyOptions,\n): Promise<void> {\n\tconst storage = getStorageInstance(ctx, opts);\n\n\t// Database mode only - handled by adapter in route handlers\n\tif (opts.storage === \"database\") {\n\t\treturn;\n\t}\n\n\t// Secondary storage mode (with or without fallback)\n\tif (opts.storage === \"secondary-storage\") {\n\t\tif (!storage) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Secondary storage is required when storage mode is 'secondary-storage'\",\n\t\t\t);\n\t\t}\n\t\tawait deleteApiKeyFromStorage(ctx, apiKey, storage);\n\t\treturn;\n\t}\n}\n\n/**\n * List API keys for a user with support for all storage modes\n */\nexport async function listApiKeys(\n\tctx: GenericEndpointContext,\n\tuserId: string,\n\topts: PredefinedApiKeyOptions,\n): Promise<ApiKey[]> {\n\tconst storage = getStorageInstance(ctx, opts);\n\n\t// Database mode only\n\tif (opts.storage === \"database\") {\n\t\treturn await ctx.context.adapter.findMany<ApiKey>({\n\t\t\tmodel: \"apikey\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\tvalue: userId,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\t// Secondary storage mode with fallback\n\tif (opts.storage === \"secondary-storage\" && opts.fallbackToDatabase) {\n\t\tconst userKey = getStorageKeyByUserId(userId);\n\n\t\tif (storage) {\n\t\t\tconst userListData = await storage.get(userKey);\n\t\t\tlet userIds: string[] = [];\n\n\t\t\tif (userListData && typeof userListData === \"string\") {\n\t\t\t\ttry {\n\t\t\t\t\tuserIds = JSON.parse(userListData);\n\t\t\t\t} catch {\n\t\t\t\t\tuserIds = [];\n\t\t\t\t}\n\t\t\t} else if (Array.isArray(userListData)) {\n\t\t\t\tuserIds = userListData;\n\t\t\t}\n\n\t\t\tif (userIds.length > 0) {\n\t\t\t\tconst apiKeys: ApiKey[] = [];\n\t\t\t\tfor (const id of userIds) {\n\t\t\t\t\tconst apiKey = await getApiKeyByIdFromStorage(ctx, id, storage);\n\t\t\t\t\tif (apiKey) {\n\t\t\t\t\t\tapiKeys.push(apiKey);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn apiKeys;\n\t\t\t}\n\t\t}\n\t\t// Fallback to database\n\t\tconst dbKeys = await ctx.context.adapter.findMany<ApiKey>({\n\t\t\tmodel: \"apikey\",\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\tvalue: userId,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\t// Populate secondary storage with fetched keys\n\t\tif (storage && dbKeys.length > 0) {\n\t\t\tconst userIds: string[] = [];\n\t\t\tfor (const apiKey of dbKeys) {\n\t\t\t\t// Store each key in secondary storage\n\t\t\t\tconst ttl = calculateTTL(apiKey);\n\t\t\t\tawait setApiKeyInStorage(ctx, apiKey, storage, ttl);\n\t\t\t\tuserIds.push(apiKey.id);\n\t\t\t}\n\t\t\t// Update user's key list in secondary storage\n\t\t\tawait storage.set(userKey, JSON.stringify(userIds));\n\t\t}\n\n\t\treturn dbKeys;\n\t}\n\n\t// Secondary storage mode only\n\tif (opts.storage === \"secondary-storage\") {\n\t\tif (!storage) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst userKey = getStorageKeyByUserId(userId);\n\t\tconst userListData = await storage.get(userKey);\n\t\tlet userIds: string[] = [];\n\n\t\tif (userListData && typeof userListData === \"string\") {\n\t\t\ttry {\n\t\t\t\tuserIds = JSON.parse(userListData);\n\t\t\t} catch {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t} else if (Array.isArray(userListData)) {\n\t\t\tuserIds = userListData;\n\t\t} else {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst apiKeys: ApiKey[] = [];\n\t\tfor (const id of userIds) {\n\t\t\tconst apiKey = await getApiKeyByIdFromStorage(ctx, id, storage);\n\t\t\tif (apiKey) {\n\t\t\t\tapiKeys.push(apiKey);\n\t\t\t}\n\t\t}\n\n\t\treturn apiKeys;\n\t}\n\n\t// Default fallback\n\treturn await ctx.context.adapter.findMany<ApiKey>({\n\t\tmodel: \"apikey\",\n\t\twhere: [\n\t\t\t{\n\t\t\t\tfield: \"userId\",\n\t\t\t\tvalue: userId,\n\t\t\t},\n\t\t],\n\t});\n}\n"],"mappings":"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"}