@geekmidas/auth 0.0.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +343 -214
  2. package/dist/{cacheTokenStorage-Ca05vf3T.mjs → cacheTokenStorage-16NE8Cd9.mjs} +3 -3
  3. package/dist/cacheTokenStorage-16NE8Cd9.mjs.map +1 -0
  4. package/dist/{cacheTokenStorage-BzBRJfd-.d.mts → cacheTokenStorage-B7sOoysu.d.cts} +8 -6
  5. package/dist/cacheTokenStorage-B7sOoysu.d.cts.map +1 -0
  6. package/dist/{cacheTokenStorage-CvptztkW.cjs → cacheTokenStorage-CEpMniTi.cjs} +3 -3
  7. package/dist/cacheTokenStorage-CEpMniTi.cjs.map +1 -0
  8. package/dist/{cacheTokenStorage-yNUpDD8W.d.cts → cacheTokenStorage-_tq4Kmhc.d.mts} +8 -6
  9. package/dist/cacheTokenStorage-_tq4Kmhc.d.mts.map +1 -0
  10. package/dist/cacheTokenStorage.cjs +1 -1
  11. package/dist/cacheTokenStorage.d.cts +1 -1
  12. package/dist/cacheTokenStorage.d.mts +1 -1
  13. package/dist/cacheTokenStorage.mjs +1 -1
  14. package/dist/{client-DaiUm0UZ.cjs → client-BPl_mMvb.cjs} +14 -8
  15. package/dist/client-BPl_mMvb.cjs.map +1 -0
  16. package/dist/{client-DjNZS6vZ.mjs → client-eidYNJSH.mjs} +14 -8
  17. package/dist/client-eidYNJSH.mjs.map +1 -0
  18. package/dist/client.cjs +2 -2
  19. package/dist/client.d.cts +1 -1
  20. package/dist/client.d.mts +1 -1
  21. package/dist/client.mjs +2 -2
  22. package/dist/hono/jwt.cjs +1 -1
  23. package/dist/hono/jwt.cjs.map +1 -1
  24. package/dist/hono/jwt.d.cts +2 -1
  25. package/dist/hono/jwt.d.cts.map +1 -0
  26. package/dist/hono/jwt.d.mts +2 -1
  27. package/dist/hono/jwt.d.mts.map +1 -0
  28. package/dist/hono/jwt.mjs +1 -1
  29. package/dist/hono/jwt.mjs.map +1 -1
  30. package/dist/hono/oidc.cjs +1 -1
  31. package/dist/hono/oidc.cjs.map +1 -1
  32. package/dist/hono/oidc.d.cts +3 -2
  33. package/dist/hono/oidc.d.cts.map +1 -0
  34. package/dist/hono/oidc.d.mts +3 -2
  35. package/dist/hono/oidc.d.mts.map +1 -0
  36. package/dist/hono/oidc.mjs +1 -1
  37. package/dist/hono/oidc.mjs.map +1 -1
  38. package/dist/index.cjs +3 -3
  39. package/dist/index.d.cts +2 -2
  40. package/dist/index.d.mts +2 -2
  41. package/dist/index.mjs +3 -3
  42. package/dist/{jwt-DF9kFbmh.d.cts → jwt-3eIuY2dg.d.cts} +2 -1
  43. package/dist/jwt-3eIuY2dg.d.cts.map +1 -0
  44. package/dist/jwt-Bo_eACEi.cjs.map +1 -1
  45. package/dist/{jwt-DH8CJ2xC.d.mts → jwt-BqLlRkw-.d.mts} +2 -1
  46. package/dist/jwt-BqLlRkw-.d.mts.map +1 -0
  47. package/dist/jwt-D88cPJyT.mjs.map +1 -1
  48. package/dist/jwt.d.cts +1 -1
  49. package/dist/jwt.d.mts +1 -1
  50. package/dist/lambda/jwt.cjs +2 -2
  51. package/dist/lambda/jwt.cjs.map +1 -1
  52. package/dist/lambda/jwt.d.cts +2 -1
  53. package/dist/lambda/jwt.d.cts.map +1 -0
  54. package/dist/lambda/jwt.d.mts +2 -1
  55. package/dist/lambda/jwt.d.mts.map +1 -0
  56. package/dist/lambda/jwt.mjs +2 -2
  57. package/dist/lambda/jwt.mjs.map +1 -1
  58. package/dist/lambda/oidc.cjs +2 -2
  59. package/dist/lambda/oidc.cjs.map +1 -1
  60. package/dist/lambda/oidc.d.cts +3 -2
  61. package/dist/lambda/oidc.d.cts.map +1 -0
  62. package/dist/lambda/oidc.d.mts +3 -2
  63. package/dist/lambda/oidc.d.mts.map +1 -0
  64. package/dist/lambda/oidc.mjs +2 -2
  65. package/dist/lambda/oidc.mjs.map +1 -1
  66. package/dist/oidc-C0KyYaAK.cjs.map +1 -1
  67. package/dist/{oidc-CKOGLxjV.d.mts → oidc-ChA17wLB.d.cts} +3 -2
  68. package/dist/oidc-ChA17wLB.d.cts.map +1 -0
  69. package/dist/{oidc-DDLIB5gb.d.cts → oidc-DTRgBBl6.d.mts} +3 -2
  70. package/dist/oidc-DTRgBBl6.d.mts.map +1 -0
  71. package/dist/oidc-KA30gMlx.mjs.map +1 -1
  72. package/dist/oidc.d.cts +2 -2
  73. package/dist/oidc.d.mts +2 -2
  74. package/dist/server.cjs +2 -2
  75. package/dist/server.d.cts +2 -2
  76. package/dist/server.d.mts +2 -2
  77. package/dist/server.mjs +2 -2
  78. package/dist/{tokenManager--Q93eNYr.d.mts → tokenManager-CL3yE06D.d.cts} +2 -1
  79. package/dist/tokenManager-CL3yE06D.d.cts.map +1 -0
  80. package/dist/{tokenManager-BVXxfHRa.mjs → tokenManager-Ci2Tgxp8.mjs} +5 -3
  81. package/dist/tokenManager-Ci2Tgxp8.mjs.map +1 -0
  82. package/dist/{tokenManager-EPmwgai2.cjs → tokenManager-CvEMgxOp.cjs} +5 -3
  83. package/dist/tokenManager-CvEMgxOp.cjs.map +1 -0
  84. package/dist/{tokenManager-H6-lWEah.d.cts → tokenManager-RrwjL0y-.d.mts} +2 -1
  85. package/dist/tokenManager-RrwjL0y-.d.mts.map +1 -0
  86. package/dist/tokenManager.cjs +1 -1
  87. package/dist/tokenManager.d.cts +1 -1
  88. package/dist/tokenManager.d.mts +1 -1
  89. package/dist/tokenManager.mjs +1 -1
  90. package/package.json +10 -5
  91. package/src/__tests__/cacheTokenStorage.spec.ts +148 -148
  92. package/src/__tests__/client.spec.ts +339 -339
  93. package/src/__tests__/hono-jwt.spec.ts +219 -219
  94. package/src/__tests__/hono-oidc.spec.ts +560 -560
  95. package/src/__tests__/jwt.spec.ts +150 -127
  96. package/src/__tests__/lambda-jwt.spec.ts +205 -205
  97. package/src/__tests__/lambda-oidc.spec.ts +580 -579
  98. package/src/__tests__/oidc.spec.ts +365 -365
  99. package/src/__tests__/tokenManager.spec.ts +254 -254
  100. package/src/cacheTokenStorage.ts +32 -32
  101. package/src/client.ts +199 -190
  102. package/src/hono/jwt.ts +111 -111
  103. package/src/hono/oidc.ts +131 -131
  104. package/src/index.ts +2 -2
  105. package/src/jwt.ts +57 -57
  106. package/src/lambda/jwt.ts +170 -170
  107. package/src/lambda/oidc.ts +165 -165
  108. package/src/oidc.ts +121 -121
  109. package/src/server.ts +6 -7
  110. package/src/tokenManager.ts +89 -87
  111. package/tsconfig.json +10 -0
  112. package/dist/cacheTokenStorage-Ca05vf3T.mjs.map +0 -1
  113. package/dist/cacheTokenStorage-CvptztkW.cjs.map +0 -1
  114. package/dist/client-DaiUm0UZ.cjs.map +0 -1
  115. package/dist/client-DjNZS6vZ.mjs.map +0 -1
  116. package/dist/tokenManager-BVXxfHRa.mjs.map +0 -1
  117. package/dist/tokenManager-EPmwgai2.cjs.map +0 -1
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  # @geekmidas/auth
2
2
 
3
- A comprehensive authentication library providing JWT token management, secure storage, and client-side token handling for TypeScript applications.
3
+ A comprehensive authentication library providing JWT and OIDC token verification, Hono middleware, AWS Lambda authorizers, and client-side token management for TypeScript applications.
4
4
 
5
5
  ## Features
6
6
 
7
- - **JWT Token Management**: Generate, verify, and refresh JWT access and refresh tokens
7
+ - **JWT Verification**: Verify tokens using secrets or JWKS endpoints
8
+ - **OIDC Support**: Auto-discovery from `.well-known/openid-configuration`
9
+ - **Hono Middleware**: Ready-to-use middleware for Hono applications
10
+ - **Lambda Authorizers**: TOKEN and REQUEST authorizers for AWS API Gateway
11
+ - **Token Management**: Client-side token storage and automatic refresh
12
+ - **Type-Safe**: Full TypeScript support with generic claims types
8
13
  - **Multiple Storage Options**: LocalStorage, Memory, and Cache-based token storage
9
- - **Automatic Token Refresh**: Seamless token refresh with expiration handling
10
- - **Type-Safe**: Full TypeScript support with complete type inference
11
- - **Framework Agnostic**: Works with any JavaScript framework or vanilla JS
12
- - **OpenAuth Integration**: Built on top of @openauthjs/openauth for standards compliance
13
14
 
14
15
  ## Installation
15
16
 
@@ -21,26 +22,274 @@ pnpm add @geekmidas/auth
21
22
 
22
23
  The package provides multiple entry points for different use cases:
23
24
 
24
- - `@geekmidas/auth` - Main client-side exports
25
+ - `@geekmidas/auth/jwt` - JWT verification
26
+ - `@geekmidas/auth/oidc` - OIDC verification with auto-discovery
27
+ - `@geekmidas/auth/hono/jwt` - Hono JWT middleware
28
+ - `@geekmidas/auth/hono/oidc` - Hono OIDC middleware
29
+ - `@geekmidas/auth/lambda/jwt` - Lambda JWT authorizer
30
+ - `@geekmidas/auth/lambda/oidc` - Lambda OIDC authorizer
25
31
  - `@geekmidas/auth/client` - Client-side token management
26
32
  - `@geekmidas/auth/server` - Server-side token management
27
33
 
28
- ## Client-Side Usage
34
+ ## JWT Verification
35
+
36
+ ### Basic Usage
37
+
38
+ ```typescript
39
+ import { JwtVerifier } from '@geekmidas/auth/jwt';
40
+ import { EnvironmentParser } from '@geekmidas/envkit';
41
+
42
+ // Parse environment configuration
43
+ const env = new EnvironmentParser(process.env)
44
+ .create((get) => ({
45
+ jwt: {
46
+ secret: get('JWT_SECRET').string(),
47
+ issuer: get('JWT_ISSUER').string().optional(),
48
+ audience: get('JWT_AUDIENCE').string().optional(),
49
+ },
50
+ }))
51
+ .parse();
52
+
53
+ // With secret (HS256)
54
+ const verifier = new JwtVerifier({
55
+ secret: env.jwt.secret,
56
+ issuer: env.jwt.issuer,
57
+ audience: env.jwt.audience,
58
+ });
59
+
60
+ const claims = await verifier.verify(token);
61
+ console.log('User:', claims.sub);
62
+
63
+ // Returns null instead of throwing for invalid tokens
64
+ const claimsOrNull = await verifier.verifyOrNull(token);
65
+ ```
66
+
67
+ ### With JWKS (RS256, ES256, etc.)
68
+
69
+ ```typescript
70
+ import { JwtVerifier } from '@geekmidas/auth/jwt';
71
+
72
+ const verifier = new JwtVerifier({
73
+ jwksUri: 'https://auth.example.com/.well-known/jwks.json',
74
+ issuer: 'https://auth.example.com',
75
+ audience: 'my-api',
76
+ });
77
+
78
+ const claims = await verifier.verify(token);
79
+ ```
80
+
81
+ ### Custom Claims Type
82
+
83
+ ```typescript
84
+ interface MyClaims {
85
+ sub: string;
86
+ role: string;
87
+ permissions: string[];
88
+ }
89
+
90
+ const verifier = new JwtVerifier<MyClaims>({
91
+ secret: env.jwt.secret, // From envkit parser
92
+ });
93
+
94
+ const claims = await verifier.verify(token);
95
+ console.log('Role:', claims.role); // Typed!
96
+ ```
97
+
98
+ ### Decode Without Verification
99
+
100
+ ```typescript
101
+ import { decodeJwt } from '@geekmidas/auth/jwt';
102
+
103
+ // Decode token without verification (useful for debugging)
104
+ const claims = decodeJwt(token);
105
+ ```
106
+
107
+ ## OIDC Verification
108
+
109
+ OIDC verifier automatically discovers JWKS URI and other configuration from the issuer's `.well-known/openid-configuration` endpoint.
110
+
111
+ ```typescript
112
+ import { OidcVerifier } from '@geekmidas/auth/oidc';
113
+
114
+ const verifier = new OidcVerifier({
115
+ issuer: 'https://auth.example.com',
116
+ audience: 'my-client-id',
117
+ });
118
+
119
+ // Verify token (auto-discovers JWKS)
120
+ const claims = await verifier.verify(token);
121
+
122
+ // Fetch user info from userinfo endpoint
123
+ const userInfo = await verifier.fetchUserInfo(token);
124
+ console.log('Email:', userInfo?.email);
125
+
126
+ // Get discovery document
127
+ const discovery = await verifier.getDiscovery();
128
+ console.log('Token endpoint:', discovery.token_endpoint);
129
+ ```
130
+
131
+ ## Hono Middleware
132
+
133
+ ### JWT Middleware
134
+
135
+ ```typescript
136
+ import { Hono } from 'hono';
137
+ import { JwtMiddleware } from '@geekmidas/auth/hono/jwt';
138
+
139
+ const app = new Hono();
140
+
141
+ const jwt = new JwtMiddleware({
142
+ config: {
143
+ secret: env.jwt.secret,
144
+ issuer: env.jwt.issuer,
145
+ },
146
+ contextKey: 'jwtClaims', // Where to store claims in context
147
+ });
148
+
149
+ // Protected routes
150
+ app.use('/api/*', jwt.handler());
151
+
152
+ app.get('/api/profile', (c) => {
153
+ const claims = c.get('jwtClaims');
154
+ return c.json({ userId: claims.sub });
155
+ });
156
+
157
+ // Optional authentication (doesn't fail if no token)
158
+ app.use('/public/*', jwt.optional());
159
+
160
+ app.get('/public/posts', (c) => {
161
+ const claims = c.get('jwtClaims'); // May be undefined
162
+ return c.json({ authenticated: !!claims });
163
+ });
164
+ ```
165
+
166
+ ### OIDC Middleware
167
+
168
+ ```typescript
169
+ import { OidcMiddleware } from '@geekmidas/auth/hono/oidc';
170
+
171
+ const oidc = new OidcMiddleware({
172
+ config: {
173
+ issuer: 'https://auth.example.com',
174
+ audience: 'my-client-id',
175
+ },
176
+ fetchUserInfo: true, // Also fetch user info
177
+ });
178
+
179
+ app.use('/api/*', oidc.handler());
180
+
181
+ app.get('/api/profile', (c) => {
182
+ const claims = c.get('oidcClaims');
183
+ const userInfo = c.get('oidcUserInfo');
184
+ return c.json({ sub: claims.sub, email: userInfo?.email });
185
+ });
186
+ ```
187
+
188
+ ### Custom Error Handling
189
+
190
+ ```typescript
191
+ const jwt = new JwtMiddleware({
192
+ config: { secret: env.jwt.secret },
193
+ onError: (c, error) => {
194
+ console.error('Auth error:', error.message);
195
+ return c.json({ error: 'Authentication failed' }, 401);
196
+ },
197
+ });
198
+ ```
199
+
200
+ ### Token Extraction Options
201
+
202
+ ```typescript
203
+ const jwt = new JwtMiddleware({
204
+ config: { secret: env.jwt.secret },
205
+ extraction: {
206
+ headerName: 'x-auth-token', // Custom header (default: 'authorization')
207
+ tokenPrefix: 'Token ', // Custom prefix (default: 'Bearer ')
208
+ cookieName: 'auth_token', // Also check cookies
209
+ },
210
+ });
211
+ ```
212
+
213
+ ## Lambda Authorizers
214
+
215
+ ### JWT Authorizer
216
+
217
+ ```typescript
218
+ import { JwtAuthorizer } from '@geekmidas/auth/lambda/jwt';
219
+
220
+ const authorizer = new JwtAuthorizer({
221
+ config: {
222
+ secret: env.jwt.secret,
223
+ issuer: env.jwt.issuer,
224
+ },
225
+ // Extract principal ID from claims
226
+ getPrincipalId: (claims) => claims.sub ?? 'unknown',
227
+ // Add claims to request context
228
+ getContext: (claims) => ({
229
+ userId: claims.sub!,
230
+ role: claims.role,
231
+ }),
232
+ // Custom authorization logic
233
+ authorize: async (claims) => {
234
+ return claims.role === 'admin';
235
+ },
236
+ });
237
+
238
+ // For TOKEN authorizers (API Gateway v1)
239
+ export const tokenHandler = authorizer.tokenHandler();
240
+
241
+ // For REQUEST authorizers (API Gateway v1/v2)
242
+ export const requestHandler = authorizer.requestHandler();
243
+ ```
244
+
245
+ ### OIDC Authorizer
246
+
247
+ ```typescript
248
+ import { OidcAuthorizer } from '@geekmidas/auth/lambda/oidc';
249
+
250
+ const authorizer = new OidcAuthorizer({
251
+ config: {
252
+ issuer: 'https://auth.example.com',
253
+ audience: 'my-api',
254
+ },
255
+ getContext: (claims) => ({
256
+ userId: claims.sub!,
257
+ email: claims.email,
258
+ }),
259
+ });
260
+
261
+ export const handler = authorizer.requestHandler();
262
+ ```
263
+
264
+ ### Token Extraction for REQUEST Authorizers
265
+
266
+ ```typescript
267
+ const authorizer = new JwtAuthorizer({
268
+ config: { secret: env.jwt.secret },
269
+ extraction: {
270
+ headerName: 'authorization',
271
+ tokenPrefix: 'Bearer ',
272
+ cookieName: 'auth_token', // Also check cookies
273
+ },
274
+ // Use specific resource ARN vs wildcard
275
+ wildcardResource: true, // Default: true (enables caching)
276
+ });
277
+ ```
278
+
279
+ ## Client-Side Token Management
29
280
 
30
281
  ### Basic Token Client
31
282
 
32
283
  ```typescript
33
284
  import { TokenClient, LocalStorageTokenStorage } from '@geekmidas/auth/client';
34
285
 
35
- // Create a token client with localStorage
36
286
  const client = new TokenClient({
37
287
  storage: new LocalStorageTokenStorage(),
38
288
  refreshEndpoint: '/api/auth/refresh',
39
289
  onTokenRefresh: (tokens) => {
40
- console.log('Tokens refreshed:', tokens);
290
+ console.log('Tokens refreshed');
41
291
  },
42
292
  onTokenExpired: () => {
43
- console.log('Tokens expired, redirect to login');
44
293
  window.location.href = '/login';
45
294
  },
46
295
  });
@@ -85,65 +334,32 @@ import { CacheTokenStorage } from '@geekmidas/auth/client';
85
334
  import { InMemoryCache } from '@geekmidas/cache/memory';
86
335
 
87
336
  const cache = new InMemoryCache<string>();
88
- const storage = new CacheTokenStorage(
89
- cache,
90
- 'access_token', // Access token key
91
- 'refresh_token' // Refresh token key
92
- );
337
+ const storage = new CacheTokenStorage(cache);
93
338
 
94
339
  // Supports TTL for automatic expiration
95
340
  await storage.setAccessToken('token', 3600); // 1 hour TTL
96
341
  ```
97
342
 
98
- ### Token Validation
99
-
100
- ```typescript
101
- // Check if a token is expired
102
- const isExpired = client.isTokenExpired(token);
103
-
104
- // Get token expiration date
105
- const expiration = client.getTokenExpiration(token);
106
- console.log('Token expires at:', expiration);
107
-
108
- // Get a valid token (refreshes automatically if needed)
109
- const validToken = await client.getValidAccessToken();
110
- ```
111
-
112
- ### HTTP Client Integration
113
-
114
- ```typescript
115
- import { TokenClient } from '@geekmidas/auth/client';
116
-
117
- class ApiClient {
118
- constructor(private tokenClient: TokenClient) {}
119
-
120
- async makeRequest(url: string, options: RequestInit = {}) {
121
- const headers = await this.tokenClient.createValidAuthHeaders();
122
-
123
- return fetch(url, {
124
- ...options,
125
- headers: {
126
- ...options.headers,
127
- ...headers,
128
- },
129
- });
130
- }
131
- }
132
-
133
- const apiClient = new ApiClient(tokenClient);
134
- const response = await apiClient.makeRequest('/api/users');
135
- ```
136
-
137
- ## Server-Side Usage
343
+ ## Server-Side Token Management
138
344
 
139
345
  ### Token Manager
140
346
 
141
347
  ```typescript
142
348
  import { TokenManager } from '@geekmidas/auth/server';
349
+ import { EnvironmentParser } from '@geekmidas/envkit';
350
+
351
+ const env = new EnvironmentParser(process.env)
352
+ .create((get) => ({
353
+ auth: {
354
+ accessTokenSecret: get('ACCESS_TOKEN_SECRET').string(),
355
+ refreshTokenSecret: get('REFRESH_TOKEN_SECRET').string(),
356
+ },
357
+ }))
358
+ .parse();
143
359
 
144
360
  const tokenManager = new TokenManager({
145
- accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
146
- refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
361
+ accessTokenSecret: env.auth.accessTokenSecret,
362
+ refreshTokenSecret: env.auth.refreshTokenSecret,
147
363
  accessTokenExpiresIn: '15m',
148
364
  refreshTokenExpiresIn: '7d',
149
365
  });
@@ -156,207 +372,120 @@ const tokens = tokenManager.generateTokenPair({
156
372
  });
157
373
 
158
374
  // Verify tokens
159
- try {
160
- const payload = tokenManager.verifyAccessToken(accessToken);
161
- console.log('User ID:', payload.userId);
162
- } catch (error) {
163
- console.error('Invalid token:', error.message);
164
- }
375
+ const payload = tokenManager.verifyAccessToken(accessToken);
165
376
 
166
377
  // Refresh access token
167
- try {
168
- const newAccessToken = tokenManager.refreshAccessToken(refreshToken);
169
- } catch (error) {
170
- console.error('Refresh failed:', error.message);
171
- }
378
+ const newAccessToken = tokenManager.refreshAccessToken(refreshToken);
172
379
  ```
173
380
 
174
- ### Token Validation Middleware
381
+ ## API Reference
175
382
 
176
- ```typescript
177
- import { TokenManager } from '@geekmidas/auth/server';
383
+ ### JwtVerifier
178
384
 
179
- const tokenManager = new TokenManager({
180
- accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
181
- refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
182
- });
385
+ ```typescript
386
+ class JwtVerifier<TClaims extends JwtClaims = JwtClaims> {
387
+ constructor(config: JwtConfig);
388
+ verify(token: string): Promise<TClaims>;
389
+ verifyOrNull(token: string): Promise<TClaims | null>;
390
+ clearCache(): void;
391
+ }
183
392
 
184
- function authenticateToken(req: Request, res: Response, next: NextFunction) {
185
- const authHeader = req.headers.authorization;
186
- const token = authHeader && authHeader.split(' ')[1];
187
-
188
- if (!token) {
189
- return res.status(401).json({ error: 'Access token required' });
190
- }
191
-
192
- try {
193
- const payload = tokenManager.verifyAccessToken(token);
194
- req.user = payload;
195
- next();
196
- } catch (error) {
197
- return res.status(403).json({ error: 'Invalid or expired token' });
198
- }
393
+ interface JwtConfig {
394
+ secret?: string; // For HS256
395
+ jwksUri?: string; // For RS256, ES256, etc.
396
+ issuer?: string; // Expected issuer
397
+ audience?: string; // Expected audience
398
+ algorithms?: string[]; // Allowed algorithms
199
399
  }
200
400
  ```
201
401
 
202
- ## API Reference
203
-
204
- ### TokenClient
205
-
206
- The main client for managing tokens on the client-side.
207
-
208
- #### Constructor Options
402
+ ### OidcVerifier
209
403
 
210
404
  ```typescript
211
- interface TokenClientOptions {
212
- storage?: TokenStorage; // Token storage implementation
213
- refreshEndpoint?: string; // API endpoint for token refresh
214
- onTokenRefresh?: (tokens: { // Callback on successful refresh
215
- accessToken: string;
216
- refreshToken?: string;
217
- }) => void;
218
- onTokenExpired?: () => void; // Callback when tokens expire
405
+ class OidcVerifier<TClaims, TUserInfo> {
406
+ constructor(config: OidcConfig);
407
+ verify(token: string): Promise<TClaims>;
408
+ verifyOrNull(token: string): Promise<TClaims | null>;
409
+ fetchUserInfo(token: string): Promise<TUserInfo | null>;
410
+ getDiscovery(): Promise<OidcDiscovery>;
411
+ clearCache(): void;
219
412
  }
220
- ```
221
-
222
- #### Methods
223
-
224
- - `getAccessToken(): Promise<string | null>` - Get stored access token
225
- - `getRefreshToken(): Promise<string | null>` - Get stored refresh token
226
- - `setTokens(accessToken, refreshToken?, accessTtl?, refreshTtl?): Promise<void>` - Store tokens
227
- - `clearTokens(): Promise<void>` - Clear all stored tokens
228
- - `isTokenExpired(token: string): boolean` - Check if token is expired
229
- - `getTokenExpiration(token: string): Date | null` - Get token expiration date
230
- - `refreshTokens(): Promise<boolean>` - Refresh tokens via API
231
- - `getValidAccessToken(): Promise<string | null>` - Get valid token (auto-refresh)
232
- - `createAuthHeaders(): Promise<Record<string, string>>` - Create auth headers
233
- - `createValidAuthHeaders(): Promise<Record<string, string>>` - Create auth headers with valid token
234
413
 
235
- ### TokenStorage Interface
236
-
237
- ```typescript
238
- interface TokenStorage {
239
- getAccessToken(): Promise<string | null> | string | null;
240
- setAccessToken(token: string, ttl?: number): Promise<void> | void;
241
- getRefreshToken(): Promise<string | null> | string | null;
242
- setRefreshToken(token: string, ttl?: number): Promise<void> | void;
243
- clearTokens(): Promise<void> | void;
414
+ interface OidcConfig {
415
+ issuer: string; // OIDC issuer URL
416
+ audience?: string; // Expected audience
417
+ algorithms?: string[]; // Allowed algorithms
244
418
  }
245
419
  ```
246
420
 
247
- ### TokenManager
248
-
249
- Server-side JWT token management.
250
-
251
- #### Constructor Options
421
+ ### JwtMiddleware / OidcMiddleware
252
422
 
253
423
  ```typescript
254
- interface TokenManagerOptions {
255
- accessTokenSecret: string; // Secret for signing access tokens
256
- refreshTokenSecret: string; // Secret for signing refresh tokens
257
- accessTokenExpiresIn?: string; // Access token expiration (default: '15m')
258
- refreshTokenExpiresIn?: string; // Refresh token expiration (default: '7d')
424
+ class JwtMiddleware<TClaims> {
425
+ constructor(options: JwtMiddlewareOptions<TClaims>);
426
+ handler(): MiddlewareHandler; // Required auth
427
+ optional(): MiddlewareHandler; // Optional auth
259
428
  }
260
- ```
261
429
 
262
- #### Methods
263
-
264
- - `generateTokenPair(payload: TokenPayload): TokenPair` - Generate access and refresh tokens
265
- - `verifyAccessToken(token: string): DecodedToken` - Verify and decode access token
266
- - `verifyRefreshToken(token: string): DecodedToken` - Verify and decode refresh token
267
- - `refreshAccessToken(refreshToken: string): string` - Generate new access token from refresh token
268
- - `decodeToken(token: string): DecodedToken | null` - Decode token without verification
269
- - `isTokenExpired(token: string): boolean` - Check if token is expired
270
- - `getTokenExpiration(token: string): Date | null` - Get token expiration date
430
+ interface JwtMiddlewareOptions<TClaims> {
431
+ config: JwtConfig;
432
+ extraction?: TokenExtractionOptions;
433
+ contextKey?: string;
434
+ onError?: (c: Context, error: Error) => Response;
435
+ transformClaims?: (claims: JwtClaims) => TClaims;
436
+ }
437
+ ```
271
438
 
272
- ### Types
439
+ ### JwtAuthorizer / OidcAuthorizer
273
440
 
274
441
  ```typescript
275
- interface TokenPayload {
276
- userId: string;
277
- email?: string;
278
- [key: string]: any;
279
- }
280
-
281
- interface TokenPair {
282
- accessToken: string;
283
- refreshToken: string;
442
+ class JwtAuthorizer<TClaims> {
443
+ constructor(options: JwtAuthorizerOptions<TClaims>);
444
+ tokenHandler(): Handler; // TOKEN authorizer
445
+ requestHandler(): Handler; // REQUEST authorizer
284
446
  }
285
447
 
286
- interface DecodedToken extends TokenPayload {
287
- iat: number; // Issued at
288
- exp: number; // Expiration time
448
+ interface JwtAuthorizerOptions<TClaims> {
449
+ config: JwtConfig;
450
+ extraction?: TokenExtractionOptions;
451
+ wildcardResource?: boolean;
452
+ getPrincipalId?: (claims: TClaims) => string;
453
+ getContext?: (claims: TClaims) => Record<string, string | number | boolean>;
454
+ authorize?: (claims: TClaims) => boolean | Promise<boolean>;
289
455
  }
290
456
  ```
291
457
 
292
- ## Error Handling
293
-
294
- The library provides descriptive error messages for common scenarios:
458
+ ### TokenExtractionOptions
295
459
 
296
460
  ```typescript
297
- try {
298
- const payload = tokenManager.verifyAccessToken(token);
299
- } catch (error) {
300
- if (error.message.includes('Invalid access token')) {
301
- // Handle invalid token
302
- } else if (error.message.includes('expired')) {
303
- // Handle expired token
304
- }
461
+ interface TokenExtractionOptions {
462
+ headerName?: string; // Default: 'authorization'
463
+ tokenPrefix?: string; // Default: 'Bearer '
464
+ cookieName?: string; // Optional cookie fallback
305
465
  }
306
466
  ```
307
467
 
308
468
  ## Security Best Practices
309
469
 
310
- 1. **Use Strong Secrets**: Ensure your JWT secrets are cryptographically secure and different for access and refresh tokens.
470
+ 1. **Use Strong Secrets**: Ensure your JWT secrets are cryptographically secure.
311
471
 
312
472
  2. **Short Access Token Expiration**: Keep access tokens short-lived (15 minutes recommended).
313
473
 
314
- 3. **Secure Token Storage**: Use appropriate storage based on your environment:
315
- - Browser: LocalStorage or secure cookies
316
- - Server: Memory or secure cache
317
- - Mobile: Secure keychain/keystore
318
-
319
- 4. **HTTPS Only**: Always use HTTPS in production to prevent token interception.
320
-
321
- 5. **Token Rotation**: Implement refresh token rotation for enhanced security.
322
-
323
- ## Testing
324
-
325
- The package includes comprehensive test utilities:
326
-
327
- ```typescript
328
- import { MemoryTokenStorage, TokenClient } from '@geekmidas/auth/client';
329
-
330
- // Use memory storage for tests
331
- const storage = new MemoryTokenStorage();
332
- const client = new TokenClient({ storage });
333
-
334
- // Mock refresh endpoint with MSW
335
- import { http, HttpResponse } from 'msw';
336
-
337
- const server = setupServer(
338
- http.post('/auth/refresh', () => {
339
- return HttpResponse.json({
340
- accessToken: 'new-access-token',
341
- refreshToken: 'new-refresh-token',
342
- });
343
- }),
344
- );
345
- ```
474
+ 3. **Use JWKS for Production**: Prefer JWKS over shared secrets for better key rotation.
346
475
 
347
- ## Migration Guide
476
+ 4. **Validate Claims**: Always validate issuer and audience claims.
348
477
 
349
- ### From v0.0.x
478
+ 5. **HTTPS Only**: Always use HTTPS in production to prevent token interception.
350
479
 
351
- The auth package is currently in initial development. Breaking changes may occur between minor versions until v1.0.0.
480
+ 6. **Token Rotation**: Implement refresh token rotation for enhanced security.
352
481
 
353
482
  ## Dependencies
354
483
 
355
- - `@openauthjs/openauth` - OpenAuth integration
356
- - `@geekmidas/cache` - Cache storage support
357
- - `jsonwebtoken` - JWT token operations
358
- - `@types/ms` - Time duration types
484
+ - `jose` - JWT/JWS/JWE operations
485
+ - `@geekmidas/cache` - Cache storage support (optional)
486
+ - `hono` - Hono framework (peer dependency for middleware)
487
+ - `@types/aws-lambda` - AWS Lambda types (peer dependency for authorizers)
359
488
 
360
489
  ## License
361
490
 
362
- MIT License - see the LICENSE file for details.
491
+ MIT License - see the LICENSE file for details.
@@ -10,14 +10,14 @@ var CacheTokenStorage = class {
10
10
  }
11
11
  async getAccessToken() {
12
12
  const token = await this.cache.get(this.accessTokenKey);
13
- return token || null;
13
+ return token ?? null;
14
14
  }
15
15
  async setAccessToken(token, ttl) {
16
16
  await this.cache.set(this.accessTokenKey, token, ttl);
17
17
  }
18
18
  async getRefreshToken() {
19
19
  const token = await this.cache.get(this.refreshTokenKey);
20
- return token || null;
20
+ return token ?? null;
21
21
  }
22
22
  async setRefreshToken(token, ttl) {
23
23
  await this.cache.set(this.refreshTokenKey, token, ttl);
@@ -29,4 +29,4 @@ var CacheTokenStorage = class {
29
29
 
30
30
  //#endregion
31
31
  export { CacheTokenStorage };
32
- //# sourceMappingURL=cacheTokenStorage-Ca05vf3T.mjs.map
32
+ //# sourceMappingURL=cacheTokenStorage-16NE8Cd9.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cacheTokenStorage-16NE8Cd9.mjs","names":["cache: Cache","accessTokenKey: string","refreshTokenKey: string","token: string","ttl?: number"],"sources":["../src/cacheTokenStorage.ts"],"sourcesContent":["import type { Cache } from '@geekmidas/cache';\nimport type { TokenStorage } from './client.js';\n\nexport class CacheTokenStorage implements TokenStorage {\n\tprivate cache: Cache;\n\tprivate accessTokenKey: string;\n\tprivate refreshTokenKey: string;\n\n\tconstructor(\n\t\tcache: Cache,\n\t\taccessTokenKey: string = 'access_token',\n\t\trefreshTokenKey: string = 'refresh_token',\n\t) {\n\t\tthis.cache = cache;\n\t\tthis.accessTokenKey = accessTokenKey;\n\t\tthis.refreshTokenKey = refreshTokenKey;\n\t}\n\n\tasync getAccessToken(): Promise<string | null> {\n\t\tconst token = await this.cache.get<string>(this.accessTokenKey);\n\t\treturn token ?? null;\n\t}\n\n\tasync setAccessToken(token: string, ttl?: number): Promise<void> {\n\t\tawait this.cache.set(this.accessTokenKey, token, ttl);\n\t}\n\n\tasync getRefreshToken(): Promise<string | null> {\n\t\tconst token = await this.cache.get<string>(this.refreshTokenKey);\n\t\treturn token ?? null;\n\t}\n\n\tasync setRefreshToken(token: string, ttl?: number): Promise<void> {\n\t\tawait this.cache.set(this.refreshTokenKey, token, ttl);\n\t}\n\n\tasync clearTokens(): Promise<void> {\n\t\tawait Promise.all([\n\t\t\tthis.cache.delete(this.accessTokenKey),\n\t\t\tthis.cache.delete(this.refreshTokenKey),\n\t\t]);\n\t}\n}\n"],"mappings":";AAGA,IAAa,oBAAb,MAAuD;CACtD,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YACCA,OACAC,iBAAyB,gBACzBC,kBAA0B,iBACzB;AACD,OAAK,QAAQ;AACb,OAAK,iBAAiB;AACtB,OAAK,kBAAkB;CACvB;CAED,MAAM,iBAAyC;EAC9C,MAAM,QAAQ,MAAM,KAAK,MAAM,IAAY,KAAK,eAAe;AAC/D,SAAO,SAAS;CAChB;CAED,MAAM,eAAeC,OAAeC,KAA6B;AAChE,QAAM,KAAK,MAAM,IAAI,KAAK,gBAAgB,OAAO,IAAI;CACrD;CAED,MAAM,kBAA0C;EAC/C,MAAM,QAAQ,MAAM,KAAK,MAAM,IAAY,KAAK,gBAAgB;AAChE,SAAO,SAAS;CAChB;CAED,MAAM,gBAAgBD,OAAeC,KAA6B;AACjE,QAAM,KAAK,MAAM,IAAI,KAAK,iBAAiB,OAAO,IAAI;CACtD;CAED,MAAM,cAA6B;AAClC,QAAM,QAAQ,IAAI,CACjB,KAAK,MAAM,OAAO,KAAK,eAAe,EACtC,KAAK,MAAM,OAAO,KAAK,gBAAgB,AACvC,EAAC;CACF;AACD"}