@uploadista/server 0.0.3
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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-check.log +34 -0
- package/LICENSE +21 -0
- package/README.md +503 -0
- package/dist/auth/cache.d.ts +87 -0
- package/dist/auth/cache.d.ts.map +1 -0
- package/dist/auth/cache.js +121 -0
- package/dist/auth/cache.test.d.ts +2 -0
- package/dist/auth/cache.test.d.ts.map +1 -0
- package/dist/auth/cache.test.js +209 -0
- package/dist/auth/get-auth-credentials.d.ts +73 -0
- package/dist/auth/get-auth-credentials.d.ts.map +1 -0
- package/dist/auth/get-auth-credentials.js +55 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/jwt/index.d.ts +38 -0
- package/dist/auth/jwt/index.d.ts.map +1 -0
- package/dist/auth/jwt/index.js +36 -0
- package/dist/auth/jwt/types.d.ts +77 -0
- package/dist/auth/jwt/types.d.ts.map +1 -0
- package/dist/auth/jwt/types.js +1 -0
- package/dist/auth/jwt/validate.d.ts +58 -0
- package/dist/auth/jwt/validate.d.ts.map +1 -0
- package/dist/auth/jwt/validate.js +226 -0
- package/dist/auth/jwt/validate.test.d.ts +2 -0
- package/dist/auth/jwt/validate.test.d.ts.map +1 -0
- package/dist/auth/jwt/validate.test.js +492 -0
- package/dist/auth/service.d.ts +63 -0
- package/dist/auth/service.d.ts.map +1 -0
- package/dist/auth/service.js +43 -0
- package/dist/auth/service.test.d.ts +2 -0
- package/dist/auth/service.test.d.ts.map +1 -0
- package/dist/auth/service.test.js +195 -0
- package/dist/auth/types.d.ts +38 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +1 -0
- package/dist/cache.d.ts +87 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +121 -0
- package/dist/cache.test.d.ts +2 -0
- package/dist/cache.test.d.ts.map +1 -0
- package/dist/cache.test.js +209 -0
- package/dist/cloudflare-config.d.ts +72 -0
- package/dist/cloudflare-config.d.ts.map +1 -0
- package/dist/cloudflare-config.js +67 -0
- package/dist/error-types.d.ts +138 -0
- package/dist/error-types.d.ts.map +1 -0
- package/dist/error-types.js +155 -0
- package/dist/hono-adapter.d.ts +48 -0
- package/dist/hono-adapter.d.ts.map +1 -0
- package/dist/hono-adapter.js +58 -0
- package/dist/http-utils.d.ts +148 -0
- package/dist/http-utils.d.ts.map +1 -0
- package/dist/http-utils.js +233 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/layer-utils.d.ts +121 -0
- package/dist/layer-utils.d.ts.map +1 -0
- package/dist/layer-utils.js +80 -0
- package/dist/metrics/service.d.ts +26 -0
- package/dist/metrics/service.d.ts.map +1 -0
- package/dist/metrics/service.js +20 -0
- package/dist/plugins-typing.d.ts +11 -0
- package/dist/plugins-typing.d.ts.map +1 -0
- package/dist/plugins-typing.js +1 -0
- package/dist/service.d.ts +63 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +43 -0
- package/dist/service.test.d.ts +2 -0
- package/dist/service.test.d.ts.map +1 -0
- package/dist/service.test.js +195 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +47 -0
- package/src/auth/get-auth-credentials.ts +97 -0
- package/src/auth/index.ts +1 -0
- package/src/cache.test.ts +306 -0
- package/src/cache.ts +204 -0
- package/src/error-types.ts +172 -0
- package/src/http-utils.ts +264 -0
- package/src/index.ts +8 -0
- package/src/layer-utils.ts +184 -0
- package/src/plugins-typing.ts +57 -0
- package/src/service.test.ts +275 -0
- package/src/service.ts +78 -0
- package/src/types.ts +40 -0
- package/tsconfig.json +13 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AuthContext } from "../types";
|
|
2
|
+
import type { JwtValidationConfig, JwtValidationResult } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Validates a JWT token and returns the validation result with claims.
|
|
5
|
+
*
|
|
6
|
+
* This function verifies:
|
|
7
|
+
* - Token signature using the provided secret/public key
|
|
8
|
+
* - Token expiry (with clock tolerance)
|
|
9
|
+
* - Issuer claim (if configured)
|
|
10
|
+
* - Audience claim (if configured)
|
|
11
|
+
* - Required subject (sub) claim presence
|
|
12
|
+
*
|
|
13
|
+
* @param token - The JWT token string to validate
|
|
14
|
+
* @param config - Validation configuration (secret, issuer, audience, etc.)
|
|
15
|
+
* @returns JwtValidationResult with success status and claims or error
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const result = await validateJwtToken(token, {
|
|
20
|
+
* secret: 'my-secret-key',
|
|
21
|
+
* issuer: 'https://auth.example.com',
|
|
22
|
+
* audience: 'uploadista-api',
|
|
23
|
+
* clockTolerance: 60,
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* if (result.success) {
|
|
27
|
+
* console.log('User ID:', result.userId);
|
|
28
|
+
* console.log('Claims:', result.claims);
|
|
29
|
+
* } else {
|
|
30
|
+
* console.error('Validation failed:', result.error);
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateJwtToken(token: string, config: JwtValidationConfig): Promise<JwtValidationResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Extracts AuthContext from a validated JWT token.
|
|
37
|
+
* This is a convenience function that combines validation and extraction.
|
|
38
|
+
*
|
|
39
|
+
* @param token - The JWT token string to validate and extract from
|
|
40
|
+
* @param config - Validation configuration
|
|
41
|
+
* @returns AuthContext if validation succeeds, null otherwise
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const authContext = await extractAuthContextFromJwt(token, {
|
|
46
|
+
* secret: process.env.JWT_SECRET,
|
|
47
|
+
* issuer: 'https://auth.example.com',
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* if (authContext) {
|
|
51
|
+
* console.log('Authenticated user:', authContext.userId);
|
|
52
|
+
* } else {
|
|
53
|
+
* console.log('Invalid token');
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function extractAuthContextFromJwt(token: string, config: JwtValidationConfig): Promise<AuthContext | null>;
|
|
58
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/auth/jwt/validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAmF9B;AA6ED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAmC7B"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { jwtVerify } from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* Validates a JWT token and returns the validation result with claims.
|
|
4
|
+
*
|
|
5
|
+
* This function verifies:
|
|
6
|
+
* - Token signature using the provided secret/public key
|
|
7
|
+
* - Token expiry (with clock tolerance)
|
|
8
|
+
* - Issuer claim (if configured)
|
|
9
|
+
* - Audience claim (if configured)
|
|
10
|
+
* - Required subject (sub) claim presence
|
|
11
|
+
*
|
|
12
|
+
* @param token - The JWT token string to validate
|
|
13
|
+
* @param config - Validation configuration (secret, issuer, audience, etc.)
|
|
14
|
+
* @returns JwtValidationResult with success status and claims or error
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const result = await validateJwtToken(token, {
|
|
19
|
+
* secret: 'my-secret-key',
|
|
20
|
+
* issuer: 'https://auth.example.com',
|
|
21
|
+
* audience: 'uploadista-api',
|
|
22
|
+
* clockTolerance: 60,
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* if (result.success) {
|
|
26
|
+
* console.log('User ID:', result.userId);
|
|
27
|
+
* console.log('Claims:', result.claims);
|
|
28
|
+
* } else {
|
|
29
|
+
* console.error('Validation failed:', result.error);
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export async function validateJwtToken(token, config) {
|
|
34
|
+
try {
|
|
35
|
+
// Prepare secret/key for validation
|
|
36
|
+
let secret;
|
|
37
|
+
if (typeof config.secret === "string") {
|
|
38
|
+
secret = new TextEncoder().encode(config.secret);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
secret = config.secret;
|
|
42
|
+
}
|
|
43
|
+
// Prepare verification options
|
|
44
|
+
const options = {
|
|
45
|
+
clockTolerance: config.clockTolerance ?? 60, // Default 1 minute tolerance
|
|
46
|
+
};
|
|
47
|
+
if (config.issuer) {
|
|
48
|
+
options.issuer = config.issuer;
|
|
49
|
+
}
|
|
50
|
+
if (config.audience) {
|
|
51
|
+
options.audience = config.audience;
|
|
52
|
+
}
|
|
53
|
+
if (config.algorithms && config.algorithms.length > 0) {
|
|
54
|
+
options.algorithms = config.algorithms;
|
|
55
|
+
}
|
|
56
|
+
// Verify the JWT
|
|
57
|
+
let verifyResult;
|
|
58
|
+
try {
|
|
59
|
+
verifyResult = await jwtVerify(token, secret, options);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return handleJwtVerifyError(error, config);
|
|
63
|
+
}
|
|
64
|
+
const { payload, protectedHeader } = verifyResult;
|
|
65
|
+
// Validate algorithm if specified
|
|
66
|
+
if (config.algorithms &&
|
|
67
|
+
config.algorithms.length > 0 &&
|
|
68
|
+
!config.algorithms.includes(protectedHeader.alg)) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
error: {
|
|
72
|
+
type: "INVALID_ALGORITHM",
|
|
73
|
+
message: `Invalid algorithm: expected one of [${config.algorithms.join(", ")}], got ${protectedHeader.alg}`,
|
|
74
|
+
expected: config.algorithms,
|
|
75
|
+
actual: protectedHeader.alg,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Extract userId from sub claim
|
|
80
|
+
const userId = payload.sub;
|
|
81
|
+
if (!userId) {
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
error: {
|
|
85
|
+
type: "MISSING_SUBJECT",
|
|
86
|
+
message: "Token is missing required 'sub' claim. Cannot extract user ID.",
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Return success with claims
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
claims: payload,
|
|
94
|
+
userId,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Catch-all for unexpected errors
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: {
|
|
102
|
+
type: "INVALID_TOKEN",
|
|
103
|
+
message: error instanceof Error ? error.message : "Unknown error occurred",
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Handles errors from jose's jwtVerify and converts them to our error format
|
|
110
|
+
*/
|
|
111
|
+
function handleJwtVerifyError(error, config) {
|
|
112
|
+
if (!(error instanceof Error)) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: {
|
|
116
|
+
type: "INVALID_TOKEN",
|
|
117
|
+
message: "Unknown validation error occurred",
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const message = error.message.toLowerCase();
|
|
122
|
+
// Check for specific error types - order matters!
|
|
123
|
+
// Check issuer/audience before expired since "exp" might match "expected"
|
|
124
|
+
if (message.includes("issuer") || message.includes('"iss"') || message.includes("'iss'")) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: {
|
|
128
|
+
type: "INVALID_ISSUER",
|
|
129
|
+
message: "Token issuer does not match expected value",
|
|
130
|
+
expected: config.issuer ?? "",
|
|
131
|
+
actual: "unknown",
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (message.includes("audience") || message.includes('"aud"') || message.includes("'aud'")) {
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
error: {
|
|
139
|
+
type: "INVALID_AUDIENCE",
|
|
140
|
+
message: "Token audience does not match expected value",
|
|
141
|
+
expected: config.audience ?? "",
|
|
142
|
+
actual: "unknown",
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (message.includes("expired") || (message.includes("exp") && !message.includes("unexpected"))) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: {
|
|
150
|
+
type: "EXPIRED",
|
|
151
|
+
message: "Token has expired",
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (message.includes("signature")) {
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
error: {
|
|
159
|
+
type: "INVALID_SIGNATURE",
|
|
160
|
+
message: "Invalid token signature",
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// Default to invalid token error
|
|
165
|
+
return {
|
|
166
|
+
success: false,
|
|
167
|
+
error: {
|
|
168
|
+
type: "INVALID_TOKEN",
|
|
169
|
+
message: error.message,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Extracts AuthContext from a validated JWT token.
|
|
175
|
+
* This is a convenience function that combines validation and extraction.
|
|
176
|
+
*
|
|
177
|
+
* @param token - The JWT token string to validate and extract from
|
|
178
|
+
* @param config - Validation configuration
|
|
179
|
+
* @returns AuthContext if validation succeeds, null otherwise
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const authContext = await extractAuthContextFromJwt(token, {
|
|
184
|
+
* secret: process.env.JWT_SECRET,
|
|
185
|
+
* issuer: 'https://auth.example.com',
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* if (authContext) {
|
|
189
|
+
* console.log('Authenticated user:', authContext.userId);
|
|
190
|
+
* } else {
|
|
191
|
+
* console.log('Invalid token');
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export async function extractAuthContextFromJwt(token, config) {
|
|
196
|
+
const result = await validateJwtToken(token, config);
|
|
197
|
+
if (!result.success) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
// Extract permissions from claims (if present)
|
|
201
|
+
const permissions = Array.isArray(result.claims.permissions)
|
|
202
|
+
? result.claims.permissions
|
|
203
|
+
: undefined;
|
|
204
|
+
// Extract metadata (all claims except standard JWT claims)
|
|
205
|
+
const standardClaims = new Set([
|
|
206
|
+
"iss",
|
|
207
|
+
"sub",
|
|
208
|
+
"aud",
|
|
209
|
+
"exp",
|
|
210
|
+
"nbf",
|
|
211
|
+
"iat",
|
|
212
|
+
"jti",
|
|
213
|
+
"permissions",
|
|
214
|
+
]);
|
|
215
|
+
const metadata = {};
|
|
216
|
+
for (const [key, value] of Object.entries(result.claims)) {
|
|
217
|
+
if (!standardClaims.has(key)) {
|
|
218
|
+
metadata[key] = value;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
userId: result.userId,
|
|
223
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
224
|
+
permissions,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.test.d.ts","sourceRoot":"","sources":["../../../src/auth/jwt/validate.test.ts"],"names":[],"mappings":""}
|