@stacksjs/bun-router 0.0.5 → 0.0.6
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.
- package/dist/auth.d.ts +134 -0
- package/dist/cache/lru-cache.d.ts +139 -0
- package/dist/cache/middleware-memoization.d.ts +151 -0
- package/dist/cache/route-cache-warmer.d.ts +161 -0
- package/dist/cache/sqlite-cache.d.ts +209 -0
- package/dist/cache/streaming-cache.d.ts +132 -0
- package/dist/cli/colors.d.ts +15 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/middleware.d.ts +31 -0
- package/dist/cli/openapi.d.ts +17 -0
- package/dist/cli/router.d.ts +15 -0
- package/dist/cli/routes.d.ts +29 -0
- package/dist/cli/utils.d.ts +74 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/container/container.d.ts +273 -0
- package/dist/container/contextual-binding.d.ts +240 -0
- package/dist/container/decorators.d.ts +141 -0
- package/dist/container/service-provider.d.ts +285 -0
- package/dist/development/hot-reload.d.ts +187 -0
- package/dist/development/index.d.ts +167 -0
- package/dist/development/performance-profiler.d.ts +217 -0
- package/dist/development/route-debugger.d.ts +154 -0
- package/dist/development/route-inspector.d.ts +211 -0
- package/dist/development/typescript-utilities.d.ts +209 -0
- package/dist/docs.d.ts +11 -0
- package/dist/errors/circuit-breaker.d.ts +195 -0
- package/dist/errors/error-handler.d.ts +88 -0
- package/dist/errors/error-reporting.d.ts +144 -0
- package/dist/errors/exceptions.d.ts +184 -0
- package/dist/errors/graceful-degradation.d.ts +154 -0
- package/dist/errors/index.d.ts +9 -0
- package/dist/errors/router-errors.d.ts +110 -0
- package/dist/file-serving/static-files.d.ts +143 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +319 -205
- package/dist/middleware/auth.d.ts +63 -0
- package/dist/middleware/content_security_policy.d.ts +53 -0
- package/dist/middleware/cors.d.ts +5 -0
- package/dist/middleware/csrf.d.ts +7 -0
- package/dist/middleware/ddos_protection.d.ts +39 -0
- package/dist/middleware/file_security.d.ts +26 -0
- package/dist/middleware/file_upload.d.ts +38 -0
- package/dist/middleware/helmet.d.ts +54 -0
- package/dist/middleware/index.d.ts +44 -0
- package/dist/middleware/input_validation.d.ts +45 -0
- package/dist/middleware/json_body.d.ts +4 -0
- package/dist/middleware/performance_alerting.d.ts +87 -0
- package/dist/middleware/performance_dashboard.d.ts +87 -0
- package/dist/middleware/performance_monitor.d.ts +209 -0
- package/dist/middleware/pipeline.d.ts +131 -0
- package/dist/middleware/rate_limit.d.ts +36 -0
- package/dist/middleware/request_id.d.ts +4 -0
- package/dist/middleware/request_signing.d.ts +153 -0
- package/dist/middleware/request_tracer.d.ts +72 -0
- package/dist/middleware/response_cache.d.ts +97 -0
- package/dist/middleware/security.d.ts +69 -0
- package/dist/middleware/security_suite.d.ts +46 -0
- package/dist/middleware/session.d.ts +23 -0
- package/dist/model-binding/index.d.ts +2 -0
- package/dist/model-binding/model-middleware.d.ts +118 -0
- package/dist/model-binding/model-registry.d.ts +164 -0
- package/dist/model-binding.d.ts +185 -0
- package/dist/model-resolver-factory.d.ts +31 -0
- package/dist/observability/correlation.d.ts +187 -0
- package/dist/observability/health-checks.d.ts +186 -0
- package/dist/observability/index.d.ts +60 -0
- package/dist/observability/integration.d.ts +147 -0
- package/dist/observability/metrics.d.ts +184 -0
- package/dist/observability/tracing.d.ts +187 -0
- package/dist/optimization/bun-utilities.d.ts +224 -0
- package/dist/query-builder-integration.d.ts +36 -0
- package/dist/request/context.d.ts +34 -0
- package/dist/request/enhanced-request.d.ts +213 -0
- package/dist/request/macros.d.ts +300 -0
- package/dist/response/macros.d.ts +245 -0
- package/dist/response/response-factory.d.ts +133 -0
- package/dist/router/api-routes.d.ts +5 -0
- package/dist/router/file-based-routing.d.ts +118 -0
- package/dist/router/file-streaming.d.ts +5 -0
- package/dist/router/fluent-router.d.ts +315 -0
- package/dist/router/fluent-routing.d.ts +271 -0
- package/dist/router/group-organization.d.ts +5 -0
- package/dist/router/handler-resolver.d.ts +24 -0
- package/dist/router/http-methods.d.ts +10 -0
- package/dist/router/index.d.ts +113 -0
- package/dist/router/middleware-groups.d.ts +94 -0
- package/dist/router/middleware-integration.d.ts +112 -0
- package/dist/router/middleware.d.ts +5 -0
- package/dist/router/model-binding.d.ts +5 -0
- package/dist/router/optimized-route-matching.d.ts +6 -0
- package/dist/router/route-building.d.ts +5 -0
- package/dist/router/route-compiler.d.ts +153 -0
- package/dist/router/route-matching.d.ts +5 -0
- package/dist/router/route-trie.d.ts +114 -0
- package/dist/router/router.d.ts +276 -0
- package/dist/router/server.d.ts +5 -0
- package/dist/router/validation-integration.d.ts +170 -0
- package/dist/router/view-rendering.d.ts +5 -0
- package/dist/router/websocket.d.ts +5 -0
- package/dist/routing/route-caching.d.ts +129 -0
- package/dist/routing/route-throttling.d.ts +150 -0
- package/dist/routing/subdomain-routing.d.ts +206 -0
- package/dist/session/database-store.d.ts +51 -0
- package/dist/session/file-store.d.ts +22 -0
- package/dist/session/index.d.ts +106 -0
- package/dist/session/memory-store.d.ts +21 -0
- package/dist/session/redis-store.d.ts +31 -0
- package/dist/streaming/index.d.ts +3 -0
- package/dist/streaming/sse-handler.d.ts +138 -0
- package/dist/streaming/stream-handler.d.ts +114 -0
- package/dist/testing/auth-testing.d.ts +156 -0
- package/dist/testing/file-upload-testing.d.ts +190 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/middleware-testing.d.ts +138 -0
- package/dist/testing/model-binding-testing.d.ts +186 -0
- package/dist/testing/performance-testing.d.ts +235 -0
- package/dist/testing/test-client.d.ts +117 -0
- package/dist/testing/test-request.d.ts +85 -0
- package/dist/testing/test-response.d.ts +90 -0
- package/dist/testing/types.d.ts +207 -0
- package/dist/testing/websocket-testing.d.ts +227 -0
- package/dist/types/controller-types.d.ts +208 -0
- package/dist/types/core.d.ts +543 -0
- package/dist/types/middleware-types.d.ts +227 -0
- package/dist/types/request-response-augmentation.d.ts +261 -0
- package/dist/types/route-inference.d.ts +168 -0
- package/dist/types.d.ts +1776 -0
- package/dist/url.d.ts +56 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/query-preservation.d.ts +48 -0
- package/dist/utils.d.ts +69 -0
- package/dist/validation/validator.d.ts +140 -0
- package/dist/websocket/clustering.d.ts +185 -0
- package/package.json +3 -2
- package/src/auth.ts +469 -0
- package/src/cache/lru-cache.ts +457 -0
- package/src/cache/middleware-memoization.ts +531 -0
- package/src/cache/route-cache-warmer.ts +486 -0
- package/src/cache/sqlite-cache.ts +783 -0
- package/src/cache/streaming-cache.ts +572 -0
- package/src/cli/colors.ts +29 -0
- package/src/cli/index.ts +287 -0
- package/src/cli/middleware.ts +291 -0
- package/src/cli/openapi.ts +407 -0
- package/src/cli/router.ts +188 -0
- package/src/cli/routes.ts +265 -0
- package/src/cli/utils.ts +531 -0
- package/src/cli.ts +5 -0
- package/src/config.ts +322 -0
- package/src/container/container.ts +740 -0
- package/src/container/contextual-binding.ts +603 -0
- package/src/container/decorators.ts +359 -0
- package/src/container/service-provider.ts +596 -0
- package/src/development/hot-reload.ts +673 -0
- package/src/development/index.ts +499 -0
- package/src/development/performance-profiler.ts +717 -0
- package/src/development/route-debugger.ts +527 -0
- package/src/development/route-inspector.ts +749 -0
- package/src/development/typescript-utilities.ts +678 -0
- package/src/docs.ts +397 -0
- package/src/errors/circuit-breaker.ts +732 -0
- package/src/errors/error-handler.ts +569 -0
- package/src/errors/error-reporting.ts +672 -0
- package/src/errors/exceptions.ts +536 -0
- package/src/errors/graceful-degradation.ts +621 -0
- package/src/errors/index.ts +21 -0
- package/src/errors/router-errors.ts +632 -0
- package/src/file-serving/static-files.ts +581 -0
- package/src/index.ts +14 -0
- package/src/middleware/auth.ts +219 -0
- package/src/middleware/content_security_policy.ts +215 -0
- package/src/middleware/cors.ts +74 -0
- package/src/middleware/csrf.ts +108 -0
- package/src/middleware/ddos_protection.ts +255 -0
- package/src/middleware/file_security.ts +191 -0
- package/src/middleware/file_upload.ts +264 -0
- package/src/middleware/helmet.ts +268 -0
- package/src/middleware/index.ts +117 -0
- package/src/middleware/input_validation.ts +449 -0
- package/src/middleware/json_body.ts +37 -0
- package/src/middleware/performance_alerting.ts +538 -0
- package/src/middleware/performance_dashboard.ts +661 -0
- package/src/middleware/performance_monitor.ts +943 -0
- package/src/middleware/pipeline.ts +489 -0
- package/src/middleware/rate_limit.ts +245 -0
- package/src/middleware/request_id.ts +36 -0
- package/src/middleware/request_signing.ts +636 -0
- package/src/middleware/request_tracer.ts +636 -0
- package/src/middleware/response_cache.ts +743 -0
- package/src/middleware/security.ts +482 -0
- package/src/middleware/security_suite.ts +257 -0
- package/src/middleware/session.ts +91 -0
- package/src/model-binding/index.ts +18 -0
- package/src/model-binding/model-middleware.ts +425 -0
- package/src/model-binding/model-registry.ts +550 -0
- package/src/model-binding.ts +370 -0
- package/src/model-resolver-factory.ts +106 -0
- package/src/observability/correlation.ts +691 -0
- package/src/observability/health-checks.ts +729 -0
- package/src/observability/index.ts +184 -0
- package/src/observability/integration.ts +548 -0
- package/src/observability/metrics.ts +753 -0
- package/src/observability/tracing.ts +638 -0
- package/src/optimization/bun-utilities.ts +778 -0
- package/src/query-builder-integration.ts +137 -0
- package/src/request/context.ts +62 -0
- package/src/request/enhanced-request.ts +857 -0
- package/src/request/macros.ts +688 -0
- package/src/response/macros.ts +612 -0
- package/src/response/response-factory.ts +588 -0
- package/src/router/api-routes.ts +243 -0
- package/src/router/file-based-routing.ts +670 -0
- package/src/router/file-streaming.ts +372 -0
- package/src/router/fluent-router.ts +927 -0
- package/src/router/fluent-routing.ts +797 -0
- package/src/router/group-organization.ts +213 -0
- package/src/router/handler-resolver.ts +291 -0
- package/src/router/http-methods.ts +377 -0
- package/src/router/index.ts +185 -0
- package/src/router/middleware-groups.ts +222 -0
- package/src/router/middleware-integration.ts +399 -0
- package/src/router/middleware.ts +231 -0
- package/src/router/model-binding.ts +215 -0
- package/src/router/optimized-route-matching.ts +253 -0
- package/src/router/route-building.ts +212 -0
- package/src/router/route-compiler.ts +702 -0
- package/src/router/route-matching.ts +349 -0
- package/src/router/route-trie.ts +464 -0
- package/src/router/router.ts +1508 -0
- package/src/router/server.ts +406 -0
- package/src/router/validation-integration.ts +443 -0
- package/src/router/view-rendering.ts +233 -0
- package/src/router/websocket.ts +100 -0
- package/src/routing/route-caching.ts +402 -0
- package/src/routing/route-throttling.ts +469 -0
- package/src/routing/subdomain-routing.ts +492 -0
- package/src/session/database-store.ts +109 -0
- package/src/session/file-store.ts +148 -0
- package/src/session/index.ts +244 -0
- package/src/session/memory-store.ts +88 -0
- package/src/session/redis-store.ts +93 -0
- package/src/streaming/index.ts +17 -0
- package/src/streaming/sse-handler.ts +482 -0
- package/src/streaming/stream-handler.ts +552 -0
- package/src/testing/auth-testing.ts +446 -0
- package/src/testing/file-upload-testing.ts +543 -0
- package/src/testing/index.ts +10 -0
- package/src/testing/middleware-testing.ts +322 -0
- package/src/testing/model-binding-testing.ts +645 -0
- package/src/testing/performance-testing.ts +738 -0
- package/src/testing/test-client.ts +310 -0
- package/src/testing/test-request.ts +315 -0
- package/src/testing/test-response.ts +332 -0
- package/src/testing/types.ts +224 -0
- package/src/testing/websocket-testing.ts +590 -0
- package/src/types/controller-types.ts +385 -0
- package/src/types/core.ts +683 -0
- package/src/types/middleware-types.ts +419 -0
- package/src/types/request-response-augmentation.ts +489 -0
- package/src/types/route-inference.ts +357 -0
- package/src/types.ts +2049 -0
- package/src/url.ts +126 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/query-preservation.ts +201 -0
- package/src/utils.ts +327 -0
- package/src/validation/validator.ts +685 -0
- package/src/websocket/clustering.ts +762 -0
package/src/auth.ts
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import type { EnhancedRequest, JwtPayload } from './types'
|
|
2
|
+
|
|
3
|
+
// Types
|
|
4
|
+
export interface JwtVerifyOptions {
|
|
5
|
+
issuer?: string
|
|
6
|
+
audience?: string
|
|
7
|
+
subject?: string
|
|
8
|
+
algorithms?: string[]
|
|
9
|
+
expiresIn?: string | number
|
|
10
|
+
notBefore?: string | number
|
|
11
|
+
ignoreExpiration?: boolean
|
|
12
|
+
allowInvalidAsymmetricKeyTypes?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface JwtSignOptions {
|
|
16
|
+
algorithm?: 'HS256' | 'HS384' | 'HS512' | 'RS256' | 'RS384' | 'RS512' | 'ES256' | 'ES384' | 'ES512' | 'PS256' | 'PS384' | 'PS512' | 'none'
|
|
17
|
+
expiresIn?: string | number
|
|
18
|
+
notBefore?: string | number
|
|
19
|
+
audience?: string | string[]
|
|
20
|
+
issuer?: string
|
|
21
|
+
subject?: string
|
|
22
|
+
keyid?: string
|
|
23
|
+
jwtid?: string
|
|
24
|
+
noTimestamp?: boolean
|
|
25
|
+
header?: Record<string, any>
|
|
26
|
+
encoding?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ApiKeyOptions {
|
|
30
|
+
source?: 'header' | 'query' | 'cookie'
|
|
31
|
+
keyName?: string
|
|
32
|
+
keyPrefix?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface OAuth2Config {
|
|
36
|
+
clientId: string
|
|
37
|
+
clientSecret: string
|
|
38
|
+
authorizeUrl: string
|
|
39
|
+
tokenUrl: string
|
|
40
|
+
redirectUri: string
|
|
41
|
+
scope?: string
|
|
42
|
+
state?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Re-export JwtPayload from types for backwards compatibility
|
|
46
|
+
export type { JwtPayload }
|
|
47
|
+
|
|
48
|
+
// Base64URL encoding/decoding functions
|
|
49
|
+
function base64UrlEncode(str: string): string {
|
|
50
|
+
return btoa(str)
|
|
51
|
+
.replace(/\+/g, '-')
|
|
52
|
+
.replace(/\//g, '_')
|
|
53
|
+
.replace(/=+$/, '')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function base64UrlDecode(str: string): string {
|
|
57
|
+
str = str.replace(/-/g, '+').replace(/_/g, '/')
|
|
58
|
+
while (str.length % 4) {
|
|
59
|
+
str += '='
|
|
60
|
+
}
|
|
61
|
+
return atob(str)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Simple JWT implementation for signing and verifying tokens
|
|
66
|
+
*/
|
|
67
|
+
export class JWT {
|
|
68
|
+
private secret: string
|
|
69
|
+
|
|
70
|
+
constructor(secret: string) {
|
|
71
|
+
this.secret = secret
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sign payload into a JWT token
|
|
76
|
+
*/
|
|
77
|
+
sign(payload: Record<string, any>, options: JwtSignOptions = {}): string {
|
|
78
|
+
// Default header
|
|
79
|
+
const header = {
|
|
80
|
+
alg: options.algorithm || 'HS256',
|
|
81
|
+
typ: 'JWT',
|
|
82
|
+
...options.header,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Prepare payload with registered claims
|
|
86
|
+
const now = Math.floor(Date.now() / 1000)
|
|
87
|
+
|
|
88
|
+
const jwtPayload: JwtPayload = {
|
|
89
|
+
...payload,
|
|
90
|
+
iat: now,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Add registered claims from options
|
|
94
|
+
if (options.expiresIn) {
|
|
95
|
+
const expiresIn = typeof options.expiresIn === 'string'
|
|
96
|
+
? this.parseTimespan(options.expiresIn)
|
|
97
|
+
: options.expiresIn
|
|
98
|
+
|
|
99
|
+
jwtPayload.exp = now + expiresIn
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (options.notBefore) {
|
|
103
|
+
const notBefore = typeof options.notBefore === 'string'
|
|
104
|
+
? this.parseTimespan(options.notBefore)
|
|
105
|
+
: options.notBefore
|
|
106
|
+
|
|
107
|
+
jwtPayload.nbf = now + notBefore
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (options.issuer)
|
|
111
|
+
jwtPayload.iss = options.issuer
|
|
112
|
+
if (options.subject)
|
|
113
|
+
jwtPayload.sub = options.subject
|
|
114
|
+
if (options.audience)
|
|
115
|
+
jwtPayload.aud = options.audience
|
|
116
|
+
if (options.jwtid)
|
|
117
|
+
jwtPayload.jti = options.jwtid
|
|
118
|
+
|
|
119
|
+
// Encode header and payload
|
|
120
|
+
const encodedHeader = base64UrlEncode(JSON.stringify(header))
|
|
121
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(jwtPayload))
|
|
122
|
+
|
|
123
|
+
// Create signature - Note: In a production environment, you'd use Bun's crypto APIs
|
|
124
|
+
// or a proper JWT library. This is a simplified implementation.
|
|
125
|
+
const data = `${encodedHeader}.${encodedPayload}`
|
|
126
|
+
const signature = this.createSignature(data, this.secret, header.alg)
|
|
127
|
+
|
|
128
|
+
// Return the JWT
|
|
129
|
+
return `${data}.${signature}`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Verify a JWT token and return the decoded payload
|
|
134
|
+
*/
|
|
135
|
+
verify(token: string, options: JwtVerifyOptions = {}): Record<string, any> | null {
|
|
136
|
+
try {
|
|
137
|
+
const parts = token.split('.')
|
|
138
|
+
|
|
139
|
+
if (parts.length !== 3) {
|
|
140
|
+
throw new Error('Token structure invalid')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const [encodedHeader, encodedPayload, signature] = parts
|
|
144
|
+
|
|
145
|
+
// Decode header and payload
|
|
146
|
+
const header = JSON.parse(base64UrlDecode(encodedHeader))
|
|
147
|
+
const payload = JSON.parse(base64UrlDecode(encodedPayload))
|
|
148
|
+
|
|
149
|
+
// Verify algorithm if specified
|
|
150
|
+
if (options.algorithms && !options.algorithms.includes(header.alg)) {
|
|
151
|
+
throw new Error(`Algorithm ${header.alg} not allowed`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Verify signature - Note: In a production environment, you'd use Bun's crypto APIs
|
|
155
|
+
// or a proper JWT library. This is a simplified implementation.
|
|
156
|
+
const data = `${encodedHeader}.${encodedPayload}`
|
|
157
|
+
const expectedSignature = this.createSignature(data, this.secret, header.alg)
|
|
158
|
+
|
|
159
|
+
if (signature !== expectedSignature) {
|
|
160
|
+
throw new Error('Invalid signature')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Verify time-based claims
|
|
164
|
+
const now = Math.floor(Date.now() / 1000)
|
|
165
|
+
|
|
166
|
+
if (payload.exp && !options.ignoreExpiration && now >= payload.exp) {
|
|
167
|
+
throw new Error('Token expired')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (payload.nbf && now < payload.nbf) {
|
|
171
|
+
throw new Error('Token not active yet')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (payload.iat && payload.iat > now) {
|
|
175
|
+
throw new Error('Token used before issued')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Verify issuer
|
|
179
|
+
if (options.issuer && payload.iss !== options.issuer) {
|
|
180
|
+
throw new Error('Invalid issuer')
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Verify audience
|
|
184
|
+
if (options.audience && payload.aud !== options.audience) {
|
|
185
|
+
throw new Error('Invalid audience')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Verify subject
|
|
189
|
+
if (options.subject && payload.sub !== options.subject) {
|
|
190
|
+
throw new Error('Invalid subject')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return payload
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Decode a JWT token without verifying the signature
|
|
202
|
+
*/
|
|
203
|
+
decode(token: string): { header: Record<string, any>, payload: Record<string, any> } | null {
|
|
204
|
+
try {
|
|
205
|
+
const parts = token.split('.')
|
|
206
|
+
|
|
207
|
+
if (parts.length !== 3) {
|
|
208
|
+
return null
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const [encodedHeader, encodedPayload] = parts
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
header: JSON.parse(base64UrlDecode(encodedHeader)),
|
|
215
|
+
payload: JSON.parse(base64UrlDecode(encodedPayload)),
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Simple signature creation - Note: This is NOT secure for production
|
|
225
|
+
* In a real app, use Bun's crypto functionality or a dedicated JWT library
|
|
226
|
+
*/
|
|
227
|
+
private createSignature(data: string, secret: string, algorithm: string): string {
|
|
228
|
+
// This is a placeholder. In a real implementation, you'd use
|
|
229
|
+
// proper HMAC or RSA signing based on the algorithm.
|
|
230
|
+
// For demonstration purposes only!
|
|
231
|
+
return base64UrlEncode(JSON.stringify({ data, secret: secret.slice(0, 5), alg: algorithm }))
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Parse timespan string to seconds
|
|
236
|
+
*/
|
|
237
|
+
private parseTimespan(timespan: string): number {
|
|
238
|
+
const match = timespan.match(/^(\d+)\s*([smhd])$/i)
|
|
239
|
+
|
|
240
|
+
if (!match) {
|
|
241
|
+
return Number.parseInt(timespan, 10) || 0
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const value = Number.parseInt(match[1], 10)
|
|
245
|
+
const unit = match[2].toLowerCase()
|
|
246
|
+
|
|
247
|
+
switch (unit) {
|
|
248
|
+
case 's': return value
|
|
249
|
+
case 'm': return value * 60
|
|
250
|
+
case 'h': return value * 60 * 60
|
|
251
|
+
case 'd': return value * 24 * 60 * 60
|
|
252
|
+
default: return value
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* API Key manager to generate, validate and handle API keys
|
|
259
|
+
*/
|
|
260
|
+
export class ApiKeyManager {
|
|
261
|
+
private keys: Map<string, { owner: string, scopes: string[], expiresAt?: Date }>
|
|
262
|
+
private options: ApiKeyOptions
|
|
263
|
+
|
|
264
|
+
constructor(options: ApiKeyOptions = {}) {
|
|
265
|
+
this.keys = new Map()
|
|
266
|
+
this.options = {
|
|
267
|
+
source: 'header',
|
|
268
|
+
keyName: 'X-API-Key',
|
|
269
|
+
keyPrefix: 'bun_',
|
|
270
|
+
...options,
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Generate a new API key
|
|
276
|
+
*/
|
|
277
|
+
generateKey(owner: string, scopes: string[] = [], expiresIn?: number): string {
|
|
278
|
+
// In a real implementation, use a secure random generator
|
|
279
|
+
const key = `${this.options.keyPrefix}${this.randomString(32)}`
|
|
280
|
+
|
|
281
|
+
// Store key information
|
|
282
|
+
this.keys.set(key, {
|
|
283
|
+
owner,
|
|
284
|
+
scopes,
|
|
285
|
+
expiresAt: expiresIn ? new Date(Date.now() + expiresIn * 1000) : undefined,
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
return key
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Validate an API key
|
|
293
|
+
*/
|
|
294
|
+
validateKey(key: string, requiredScopes: string[] = []): boolean {
|
|
295
|
+
const keyInfo = this.keys.get(key)
|
|
296
|
+
|
|
297
|
+
if (!keyInfo) {
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Check expiration
|
|
302
|
+
if (keyInfo.expiresAt && keyInfo.expiresAt < new Date()) {
|
|
303
|
+
return false
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Check scopes
|
|
307
|
+
if (requiredScopes.length > 0) {
|
|
308
|
+
return requiredScopes.every(scope => keyInfo.scopes.includes(scope))
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return true
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Get key information
|
|
316
|
+
*/
|
|
317
|
+
getKeyInfo(key: string): { owner: string, scopes: string[], expiresAt?: Date } | null {
|
|
318
|
+
return this.keys.get(key) || null
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Revoke an API key
|
|
323
|
+
*/
|
|
324
|
+
revokeKey(key: string): boolean {
|
|
325
|
+
return this.keys.delete(key)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Extract API key from request based on configured source
|
|
330
|
+
*/
|
|
331
|
+
extractFromRequest(req: EnhancedRequest): string | null {
|
|
332
|
+
const { source, keyName } = this.options
|
|
333
|
+
|
|
334
|
+
switch (source) {
|
|
335
|
+
case 'header':
|
|
336
|
+
return req.headers.get(keyName || 'X-API-Key') || null
|
|
337
|
+
case 'query':
|
|
338
|
+
return new URL(req.url).searchParams.get(keyName || 'api_key') || null
|
|
339
|
+
case 'cookie':
|
|
340
|
+
// Handle both Map-like cookies (with .get()) and plain object cookies
|
|
341
|
+
if (req.cookies) {
|
|
342
|
+
const key = keyName || 'api_key'
|
|
343
|
+
if (typeof req.cookies.get === 'function') {
|
|
344
|
+
return req.cookies.get(key) || null
|
|
345
|
+
}
|
|
346
|
+
return (req.cookies as unknown as Record<string, string>)[key] || null
|
|
347
|
+
}
|
|
348
|
+
return null
|
|
349
|
+
default:
|
|
350
|
+
return null
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Generate a random string for keys
|
|
356
|
+
*/
|
|
357
|
+
private randomString(length: number): string {
|
|
358
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
359
|
+
let result = ''
|
|
360
|
+
|
|
361
|
+
// This is a simple implementation - for production, use crypto.getRandomValues()
|
|
362
|
+
for (let i = 0; i < length; i++) {
|
|
363
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return result
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* OAuth2 helper for authorization code flow
|
|
372
|
+
*/
|
|
373
|
+
export class OAuth2Helper {
|
|
374
|
+
private config: OAuth2Config
|
|
375
|
+
|
|
376
|
+
constructor(config: OAuth2Config) {
|
|
377
|
+
this.config = config
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Generate authorization URL for redirect
|
|
382
|
+
*/
|
|
383
|
+
getAuthorizationUrl(additionalParams: Record<string, string> = {}): string {
|
|
384
|
+
const params = new URLSearchParams({
|
|
385
|
+
client_id: this.config.clientId,
|
|
386
|
+
redirect_uri: this.config.redirectUri,
|
|
387
|
+
response_type: 'code',
|
|
388
|
+
...additionalParams,
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
if (this.config.scope) {
|
|
392
|
+
params.set('scope', this.config.scope)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (this.config.state) {
|
|
396
|
+
params.set('state', this.config.state)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return `${this.config.authorizeUrl}?${params.toString()}`
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Exchange authorization code for access token
|
|
404
|
+
*/
|
|
405
|
+
async exchangeCodeForToken(code: string): Promise<Record<string, any>> {
|
|
406
|
+
const params = new URLSearchParams({
|
|
407
|
+
grant_type: 'authorization_code',
|
|
408
|
+
code,
|
|
409
|
+
redirect_uri: this.config.redirectUri,
|
|
410
|
+
client_id: this.config.clientId,
|
|
411
|
+
client_secret: this.config.clientSecret,
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
const response = await fetch(this.config.tokenUrl, {
|
|
415
|
+
method: 'POST',
|
|
416
|
+
headers: {
|
|
417
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
418
|
+
},
|
|
419
|
+
body: params.toString(),
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
if (!response.ok) {
|
|
423
|
+
throw new Error(`OAuth token exchange failed: ${response.status} ${response.statusText}`)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return await response.json() as Record<string, any>
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Refresh an access token using a refresh token
|
|
431
|
+
*/
|
|
432
|
+
async refreshToken(refreshToken: string): Promise<Record<string, any>> {
|
|
433
|
+
const params = new URLSearchParams({
|
|
434
|
+
grant_type: 'refresh_token',
|
|
435
|
+
refresh_token: refreshToken,
|
|
436
|
+
client_id: this.config.clientId,
|
|
437
|
+
client_secret: this.config.clientSecret,
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
const response = await fetch(this.config.tokenUrl, {
|
|
441
|
+
method: 'POST',
|
|
442
|
+
headers: {
|
|
443
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
444
|
+
},
|
|
445
|
+
body: params.toString(),
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
if (!response.ok) {
|
|
449
|
+
throw new Error(`OAuth token refresh failed: ${response.status} ${response.statusText}`)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return await response.json() as Record<string, any>
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Authentication helpers namespace
|
|
458
|
+
*/
|
|
459
|
+
const Auth: {
|
|
460
|
+
JWT: typeof JWT
|
|
461
|
+
ApiKeyManager: typeof ApiKeyManager
|
|
462
|
+
OAuth2Helper: typeof OAuth2Helper
|
|
463
|
+
} = {
|
|
464
|
+
JWT,
|
|
465
|
+
ApiKeyManager,
|
|
466
|
+
OAuth2Helper,
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export default Auth
|