@@ -23,20 +23,57 @@ import type { PermissionScope } from "./types/permissions-types";
2323import type { Storage } from "./types/storage-types" ;
2424import { logger } from "./utils/logger" ;
2525
26+ /**
27+ * Result of verifying an API key
28+ */
2629export type VerifyResult = {
30+ /** Whether the key is valid */
2731 valid : boolean ;
32+ /** The API key record if valid */
2833 record ?: ApiKeyRecord ;
34+ /** Error message if invalid */
2935 error ?: string ;
3036} ;
3137
38+ /**
39+ * Options for verifying API keys
40+ */
3241export type VerifyOptions = {
42+ /** Skip cache lookup (always query storage) */
3343 skipCache ?: boolean ;
44+ /** Override header names to look for */
3445 headerNames ?: string [ ] ;
46+ /** Override extractBearer behavior */
3547 extractBearer ?: boolean ;
3648 /** Skip updating lastUsedAt timestamp (useful when autoTrackUsage is enabled) */
3749 skipTracking ?: boolean ;
3850} ;
3951
52+ /**
53+ * API Key Manager for creating, verifying, and managing API keys
54+ *
55+ * @example
56+ * ```typescript
57+ * const keys = createKeys({
58+ * prefix: "sk_live_",
59+ * storage: "redis",
60+ * redis: redisClient
61+ * });
62+ *
63+ * // Create a key
64+ * const { key, record } = await keys.create({
65+ * ownerId: "user_123",
66+ * name: "Production Key",
67+ * scopes: ["read", "write"]
68+ * });
69+ *
70+ * // Verify a key
71+ * const result = await keys.verify(key);
72+ * if (result.valid) {
73+ * console.log("Key belongs to:", result.record?.metadata.ownerId);
74+ * }
75+ * ```
76+ */
4077export class ApiKeyManager {
4178 private readonly config : Config ;
4279 private readonly storage : Storage ;
@@ -124,6 +161,21 @@ export class ApiKeyManager {
124161 } ) ;
125162 }
126163
164+ /**
165+ * Extract API key from HTTP headers
166+ *
167+ * @param headers - HTTP headers object or Headers instance
168+ * @param options - Optional extraction options
169+ * @returns The extracted API key or null if not found
170+ *
171+ * @example
172+ * ```typescript
173+ * const key = keys.extractKey(req.headers);
174+ * if (key) {
175+ * console.log("Found key:", key);
176+ * }
177+ * ```
178+ */
127179 extractKey (
128180 headers : Record < string , string | undefined > | Headers ,
129181 options ?: KeyExtractionOptions
@@ -136,6 +188,20 @@ export class ApiKeyManager {
136188 return extractKeyFromHeaders ( headers , mergedOptions ) ;
137189 }
138190
191+ /**
192+ * Check if an API key is present in HTTP headers
193+ *
194+ * @param headers - HTTP headers object or Headers instance
195+ * @param options - Optional extraction options
196+ * @returns True if an API key is found in headers
197+ *
198+ * @example
199+ * ```typescript
200+ * if (keys.hasKey(req.headers)) {
201+ * // API key is present
202+ * }
203+ * ```
204+ */
139205 hasKey (
140206 headers : Record < string , string | undefined > | Headers ,
141207 options ?: KeyExtractionOptions
@@ -148,6 +214,28 @@ export class ApiKeyManager {
148214 return hasApiKey ( headers , mergedOptions ) ;
149215 }
150216
217+ /**
218+ * Verify an API key from a string or HTTP headers
219+ *
220+ * @param keyOrHeader - The API key string or HTTP headers object
221+ * @param options - Verification options
222+ * @returns Verification result with validity status and record
223+ *
224+ * @example
225+ * ```typescript
226+ * // Verify from string
227+ * const result = await keys.verify("sk_live_abc123...");
228+ *
229+ * // Verify from headers
230+ * const result = await keys.verify(req.headers);
231+ *
232+ * if (result.valid) {
233+ * console.log("Owner:", result.record?.metadata.ownerId);
234+ * } else {
235+ * console.log("Error:", result.error);
236+ * }
237+ * ```
238+ */
151239 async verify (
152240 keyOrHeader : string | Record < string , string | undefined > | Headers ,
153241 options : VerifyOptions = { }
@@ -264,6 +352,27 @@ export class ApiKeyManager {
264352 return { valid : true , record } ;
265353 }
266354
355+ /**
356+ * Create a new API key
357+ *
358+ * @param metadata - Metadata for the API key (ownerId is required)
359+ * @returns The generated key string and the stored record
360+ *
361+ * @example
362+ * ```typescript
363+ * const { key, record } = await keys.create({
364+ * ownerId: "user_123",
365+ * name: "Production Key",
366+ * description: "API key for production access",
367+ * scopes: ["read", "write"],
368+ * expiresAt: "2025-12-31T00:00:00.000Z",
369+ * tags: ["production", "api"]
370+ * });
371+ *
372+ * console.log("New key:", key);
373+ * console.log("Key ID:", record.id);
374+ * ```
375+ */
267376 async create (
268377 metadata : Partial < ApiKeyMetadata >
269378 ) : Promise < { key : string ; record : ApiKeyRecord } > {
@@ -456,6 +565,27 @@ export class ApiKeyManager {
456565 return isExpired ( record . metadata . expiresAt ) ;
457566 }
458567
568+ /**
569+ * Check if an API key has a specific scope
570+ *
571+ * @param record - The API key record
572+ * @param scope - Required scope to check
573+ * @param options - Optional scope check options (e.g., resource filtering)
574+ * @returns True if the key has the required scope
575+ *
576+ * @example
577+ * ```typescript
578+ * const record = await storage.findById("key_id");
579+ * if (keys.hasScope(record, "read")) {
580+ * // Key has read permission
581+ * }
582+ *
583+ * // Check for resource-specific scope
584+ * if (keys.hasScope(record, "write", { resource: "project:123" })) {
585+ * // Key can write to project 123
586+ * }
587+ * ```
588+ */
459589 hasScope (
460590 record : ApiKeyRecord ,
461591 scope : PermissionScope ,
@@ -469,6 +599,21 @@ export class ApiKeyManager {
469599 ) ;
470600 }
471601
602+ /**
603+ * Check if an API key has any of the required scopes
604+ *
605+ * @param record - The API key record
606+ * @param requiredScopes - Array of scopes to check
607+ * @param options - Optional scope check options
608+ * @returns True if the key has at least one of the required scopes
609+ *
610+ * @example
611+ * ```typescript
612+ * if (keys.hasAnyScope(record, ["read", "write"])) {
613+ * // Key has read OR write permission
614+ * }
615+ * ```
616+ */
472617 hasAnyScope (
473618 record : ApiKeyRecord ,
474619 requiredScopes : PermissionScope [ ] ,
@@ -482,6 +627,21 @@ export class ApiKeyManager {
482627 ) ;
483628 }
484629
630+ /**
631+ * Check if an API key has all required scopes
632+ *
633+ * @param record - The API key record
634+ * @param requiredScopes - Array of scopes to check
635+ * @param options - Optional scope check options
636+ * @returns True if the key has all required scopes
637+ *
638+ * @example
639+ * ```typescript
640+ * if (keys.hasAllScopes(record, ["read", "write"])) {
641+ * // Key has read AND write permissions
642+ * }
643+ * ```
644+ */
485645 hasAllScopes (
486646 record : ApiKeyRecord ,
487647 requiredScopes : PermissionScope [ ] ,
@@ -563,6 +723,32 @@ export class ApiKeyManager {
563723 }
564724}
565725
726+ /**
727+ * Create an API key manager instance
728+ *
729+ * @param config - Configuration options for key generation and storage
730+ * @returns An ApiKeyManager instance for creating and verifying keys
731+ *
732+ * @example
733+ * ```typescript
734+ * // Simple in-memory setup
735+ * const keys = createKeys({ prefix: "sk_" });
736+ *
737+ * // Redis setup with caching
738+ * const keys = createKeys({
739+ * prefix: "sk_live_",
740+ * storage: "redis",
741+ * redis: redisClient,
742+ * cache: true,
743+ * cacheTtl: 300
744+ * });
745+ *
746+ * // Custom storage adapter
747+ * const keys = createKeys({
748+ * storage: myCustomStorage
749+ * });
750+ * ```
751+ */
566752export function createKeys ( config : ConfigInput = { } ) : ApiKeyManager {
567753 return new ApiKeyManager ( config ) ;
568754}
0 commit comments