@veloxts/auth 0.3.3 → 0.3.4
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/README.md +755 -30
- package/dist/adapter.d.ts +710 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +581 -0
- package/dist/adapter.js.map +1 -0
- package/dist/adapters/better-auth.d.ts +271 -0
- package/dist/adapters/better-auth.d.ts.map +1 -0
- package/dist/adapters/better-auth.js +341 -0
- package/dist/adapters/better-auth.js.map +1 -0
- package/dist/adapters/index.d.ts +28 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/csrf.d.ts +294 -0
- package/dist/csrf.d.ts.map +1 -0
- package/dist/csrf.js +396 -0
- package/dist/csrf.js.map +1 -0
- package/dist/guards.d.ts +139 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/guards.js +247 -0
- package/dist/guards.js.map +1 -0
- package/dist/hash.d.ts +85 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +220 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +25 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -36
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts +128 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +363 -0
- package/dist/jwt.js.map +1 -0
- package/dist/middleware.d.ts +87 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +241 -0
- package/dist/middleware.js.map +1 -0
- package/dist/plugin.d.ts +107 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +174 -0
- package/dist/plugin.js.map +1 -0
- package/dist/policies.d.ts +137 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/policies.js +240 -0
- package/dist/policies.js.map +1 -0
- package/dist/session.d.ts +494 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +795 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +251 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -7
package/dist/jwt.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT token utilities for @veloxts/auth
|
|
3
|
+
* @module auth/jwt
|
|
4
|
+
*/
|
|
5
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Constants
|
|
8
|
+
// ============================================================================
|
|
9
|
+
const DEFAULT_ACCESS_EXPIRY = '15m';
|
|
10
|
+
const DEFAULT_REFRESH_EXPIRY = '7d';
|
|
11
|
+
/**
|
|
12
|
+
* Minimum JWT secret length (64 characters = 512 bits)
|
|
13
|
+
* HS256 requires at least 256 bits, but we require 512 for extra security margin
|
|
14
|
+
*/
|
|
15
|
+
const MIN_SECRET_LENGTH = 64;
|
|
16
|
+
/**
|
|
17
|
+
* Minimum unique characters in secret for entropy validation
|
|
18
|
+
*/
|
|
19
|
+
const MIN_SECRET_ENTROPY_CHARS = 16;
|
|
20
|
+
/**
|
|
21
|
+
* Reserved JWT claims that cannot be overridden via additionalClaims
|
|
22
|
+
*/
|
|
23
|
+
const RESERVED_JWT_CLAIMS = new Set([
|
|
24
|
+
'sub',
|
|
25
|
+
'iss',
|
|
26
|
+
'aud',
|
|
27
|
+
'exp',
|
|
28
|
+
'iat',
|
|
29
|
+
'jti',
|
|
30
|
+
'nbf',
|
|
31
|
+
'type',
|
|
32
|
+
'email',
|
|
33
|
+
]);
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// JWT Implementation
|
|
36
|
+
// ============================================================================
|
|
37
|
+
/**
|
|
38
|
+
* Parses time string to seconds
|
|
39
|
+
* Supports: '15m', '1h', '7d', '30d', etc.
|
|
40
|
+
*/
|
|
41
|
+
export function parseTimeToSeconds(time) {
|
|
42
|
+
const match = time.match(/^(\d+)([smhd])$/);
|
|
43
|
+
if (!match) {
|
|
44
|
+
throw new Error(`Invalid time format: ${time}. Use format like '15m', '1h', '7d'`);
|
|
45
|
+
}
|
|
46
|
+
const value = parseInt(match[1], 10);
|
|
47
|
+
const unit = match[2];
|
|
48
|
+
switch (unit) {
|
|
49
|
+
case 's':
|
|
50
|
+
return value;
|
|
51
|
+
case 'm':
|
|
52
|
+
return value * 60;
|
|
53
|
+
case 'h':
|
|
54
|
+
return value * 60 * 60;
|
|
55
|
+
case 'd':
|
|
56
|
+
return value * 60 * 60 * 24;
|
|
57
|
+
default:
|
|
58
|
+
throw new Error(`Unknown time unit: ${unit}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Base64url encode
|
|
63
|
+
*/
|
|
64
|
+
function base64urlEncode(data) {
|
|
65
|
+
const base64 = Buffer.from(data).toString('base64');
|
|
66
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Base64url decode
|
|
70
|
+
*/
|
|
71
|
+
function base64urlDecode(data) {
|
|
72
|
+
const base64 = data.replace(/-/g, '+').replace(/_/g, '/');
|
|
73
|
+
const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
|
|
74
|
+
return Buffer.from(padded, 'base64').toString('utf8');
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create HMAC-SHA256 signature
|
|
78
|
+
*/
|
|
79
|
+
function createSignature(data, secret) {
|
|
80
|
+
const hmac = createHmac('sha256', secret);
|
|
81
|
+
hmac.update(data);
|
|
82
|
+
return base64urlEncode(hmac.digest());
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generate a unique token ID
|
|
86
|
+
*/
|
|
87
|
+
export function generateTokenId() {
|
|
88
|
+
return randomBytes(16).toString('hex');
|
|
89
|
+
}
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// JWT Manager Class
|
|
92
|
+
// ============================================================================
|
|
93
|
+
/**
|
|
94
|
+
* JWT token manager
|
|
95
|
+
*
|
|
96
|
+
* Handles token creation, verification, and refresh.
|
|
97
|
+
* Uses HS256 (HMAC-SHA256) algorithm.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const jwt = new JwtManager({
|
|
102
|
+
* secret: process.env.JWT_SECRET!,
|
|
103
|
+
* accessTokenExpiry: '15m',
|
|
104
|
+
* refreshTokenExpiry: '7d',
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* // Create tokens for user
|
|
108
|
+
* const tokens = jwt.createTokenPair(user);
|
|
109
|
+
*
|
|
110
|
+
* // Verify access token
|
|
111
|
+
* const payload = jwt.verifyToken(tokens.accessToken);
|
|
112
|
+
*
|
|
113
|
+
* // Refresh tokens
|
|
114
|
+
* const newTokens = jwt.refreshTokens(tokens.refreshToken);
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export class JwtManager {
|
|
118
|
+
config;
|
|
119
|
+
constructor(config) {
|
|
120
|
+
// Validate secret length (Critical Fix #1)
|
|
121
|
+
if (!config.secret || config.secret.length < MIN_SECRET_LENGTH) {
|
|
122
|
+
throw new Error(`JWT secret must be at least ${MIN_SECRET_LENGTH} characters long (512 bits). ` +
|
|
123
|
+
'Generate with: openssl rand -base64 64');
|
|
124
|
+
}
|
|
125
|
+
// Validate secret entropy - check for sufficient unique characters
|
|
126
|
+
const uniqueChars = new Set(config.secret).size;
|
|
127
|
+
if (uniqueChars < MIN_SECRET_ENTROPY_CHARS) {
|
|
128
|
+
throw new Error(`JWT secret has insufficient entropy (only ${uniqueChars} unique characters). ` +
|
|
129
|
+
'Use cryptographically random data with at least 16 unique characters.');
|
|
130
|
+
}
|
|
131
|
+
this.config = {
|
|
132
|
+
...config,
|
|
133
|
+
accessTokenExpiry: config.accessTokenExpiry ?? DEFAULT_ACCESS_EXPIRY,
|
|
134
|
+
refreshTokenExpiry: config.refreshTokenExpiry ?? DEFAULT_REFRESH_EXPIRY,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Creates a JWT token with the given payload
|
|
139
|
+
*/
|
|
140
|
+
createToken(payload, expiresIn) {
|
|
141
|
+
const now = Math.floor(Date.now() / 1000);
|
|
142
|
+
const exp = now + parseTimeToSeconds(expiresIn);
|
|
143
|
+
const fullPayload = {
|
|
144
|
+
...payload,
|
|
145
|
+
iat: now,
|
|
146
|
+
exp,
|
|
147
|
+
};
|
|
148
|
+
// Create header
|
|
149
|
+
const header = { alg: 'HS256', typ: 'JWT' };
|
|
150
|
+
const encodedHeader = base64urlEncode(JSON.stringify(header));
|
|
151
|
+
const encodedPayload = base64urlEncode(JSON.stringify(fullPayload));
|
|
152
|
+
// Create signature
|
|
153
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
154
|
+
const signature = createSignature(signatureInput, this.config.secret);
|
|
155
|
+
return `${signatureInput}.${signature}`;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Verifies a JWT token and returns the payload
|
|
159
|
+
*
|
|
160
|
+
* @throws Error if token is invalid or expired
|
|
161
|
+
*/
|
|
162
|
+
verifyToken(token) {
|
|
163
|
+
const parts = token.split('.');
|
|
164
|
+
if (parts.length !== 3) {
|
|
165
|
+
throw new Error('Invalid token format');
|
|
166
|
+
}
|
|
167
|
+
const [encodedHeader, encodedPayload, signature] = parts;
|
|
168
|
+
// Critical Fix #2: Validate algorithm BEFORE signature verification
|
|
169
|
+
// This prevents algorithm confusion attacks (CVE-2015-9235)
|
|
170
|
+
let header;
|
|
171
|
+
try {
|
|
172
|
+
header = JSON.parse(base64urlDecode(encodedHeader));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
throw new Error('Invalid token header');
|
|
176
|
+
}
|
|
177
|
+
// Only allow HS256 - reject "none", RS256, and other algorithms
|
|
178
|
+
if (header.alg !== 'HS256') {
|
|
179
|
+
throw new Error(`Invalid algorithm: ${header.alg}. Only HS256 is supported.`);
|
|
180
|
+
}
|
|
181
|
+
if (header.typ !== 'JWT') {
|
|
182
|
+
throw new Error('Invalid token type in header');
|
|
183
|
+
}
|
|
184
|
+
// Verify signature using timing-safe comparison to prevent timing attacks
|
|
185
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
186
|
+
const expectedSignature = createSignature(signatureInput, this.config.secret);
|
|
187
|
+
const sigBuffer = Buffer.from(signature, 'utf8');
|
|
188
|
+
const expectedBuffer = Buffer.from(expectedSignature, 'utf8');
|
|
189
|
+
if (sigBuffer.length !== expectedBuffer.length || !timingSafeEqual(sigBuffer, expectedBuffer)) {
|
|
190
|
+
throw new Error('Invalid token signature');
|
|
191
|
+
}
|
|
192
|
+
// Decode payload
|
|
193
|
+
let payload;
|
|
194
|
+
try {
|
|
195
|
+
const decoded = JSON.parse(base64urlDecode(encodedPayload));
|
|
196
|
+
// Validate required fields
|
|
197
|
+
if (typeof decoded.sub !== 'string' ||
|
|
198
|
+
typeof decoded.email !== 'string' ||
|
|
199
|
+
typeof decoded.iat !== 'number' ||
|
|
200
|
+
typeof decoded.exp !== 'number' ||
|
|
201
|
+
(decoded.type !== 'access' && decoded.type !== 'refresh')) {
|
|
202
|
+
throw new Error('Missing required token fields');
|
|
203
|
+
}
|
|
204
|
+
payload = decoded;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
throw new Error(error instanceof Error ? error.message : 'Invalid token payload');
|
|
208
|
+
}
|
|
209
|
+
// Check expiration
|
|
210
|
+
const now = Math.floor(Date.now() / 1000);
|
|
211
|
+
if (payload.exp < now) {
|
|
212
|
+
throw new Error('Token has expired');
|
|
213
|
+
}
|
|
214
|
+
// Check not-before claim if present (Medium Fix #10)
|
|
215
|
+
if (typeof payload.nbf === 'number' && payload.nbf > now) {
|
|
216
|
+
throw new Error('Token not yet valid');
|
|
217
|
+
}
|
|
218
|
+
// Verify issuer if configured
|
|
219
|
+
if (this.config.issuer && payload.iss !== this.config.issuer) {
|
|
220
|
+
throw new Error('Invalid token issuer');
|
|
221
|
+
}
|
|
222
|
+
// Verify audience if configured
|
|
223
|
+
if (this.config.audience && payload.aud !== this.config.audience) {
|
|
224
|
+
throw new Error('Invalid token audience');
|
|
225
|
+
}
|
|
226
|
+
return payload;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Creates an access/refresh token pair for a user
|
|
230
|
+
*
|
|
231
|
+
* @param user - The user to create tokens for
|
|
232
|
+
* @param additionalClaims - Custom claims to include (cannot override reserved claims)
|
|
233
|
+
* @throws Error if additionalClaims contains reserved JWT claims
|
|
234
|
+
*/
|
|
235
|
+
createTokenPair(user, additionalClaims) {
|
|
236
|
+
// Critical Fix #3: Validate additionalClaims don't contain reserved claims
|
|
237
|
+
if (additionalClaims) {
|
|
238
|
+
for (const key of Object.keys(additionalClaims)) {
|
|
239
|
+
if (RESERVED_JWT_CLAIMS.has(key)) {
|
|
240
|
+
throw new Error(`Cannot override reserved JWT claim: ${key}. ` +
|
|
241
|
+
`Reserved claims are: ${[...RESERVED_JWT_CLAIMS].join(', ')}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const tokenId = generateTokenId();
|
|
246
|
+
const basePayload = {
|
|
247
|
+
sub: user.id,
|
|
248
|
+
email: user.email,
|
|
249
|
+
jti: tokenId,
|
|
250
|
+
...(this.config.issuer && { iss: this.config.issuer }),
|
|
251
|
+
...(this.config.audience && { aud: this.config.audience }),
|
|
252
|
+
...additionalClaims,
|
|
253
|
+
};
|
|
254
|
+
const accessToken = this.createToken({ ...basePayload, type: 'access' }, this.config.accessTokenExpiry);
|
|
255
|
+
const refreshToken = this.createToken({ ...basePayload, type: 'refresh' }, this.config.refreshTokenExpiry);
|
|
256
|
+
return {
|
|
257
|
+
accessToken,
|
|
258
|
+
refreshToken,
|
|
259
|
+
expiresIn: parseTimeToSeconds(this.config.accessTokenExpiry),
|
|
260
|
+
tokenType: 'Bearer',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
refreshTokens(refreshToken, userLoader) {
|
|
264
|
+
const payload = this.verifyToken(refreshToken);
|
|
265
|
+
if (payload.type !== 'refresh') {
|
|
266
|
+
throw new Error('Invalid token type: expected refresh token');
|
|
267
|
+
}
|
|
268
|
+
// If userLoader provided, fetch fresh user data
|
|
269
|
+
if (userLoader) {
|
|
270
|
+
return userLoader(payload.sub).then((user) => {
|
|
271
|
+
if (!user) {
|
|
272
|
+
throw new Error('User not found');
|
|
273
|
+
}
|
|
274
|
+
return this.createTokenPair(user);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// Otherwise, create new tokens from payload data
|
|
278
|
+
const user = {
|
|
279
|
+
id: payload.sub,
|
|
280
|
+
email: payload.email,
|
|
281
|
+
};
|
|
282
|
+
return this.createTokenPair(user);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Decodes a token without verification
|
|
286
|
+
* Useful for extracting payload from expired tokens
|
|
287
|
+
*/
|
|
288
|
+
decodeToken(token) {
|
|
289
|
+
try {
|
|
290
|
+
const parts = token.split('.');
|
|
291
|
+
if (parts.length !== 3) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
return JSON.parse(base64urlDecode(parts[1]));
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Extracts token from Authorization header
|
|
302
|
+
* Supports 'Bearer <token>' format
|
|
303
|
+
*/
|
|
304
|
+
extractFromHeader(authHeader) {
|
|
305
|
+
if (!authHeader) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
const parts = authHeader.split(' ');
|
|
309
|
+
if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
return parts[1];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Creates a new JWT manager instance
|
|
317
|
+
*/
|
|
318
|
+
export function createJwtManager(config) {
|
|
319
|
+
return new JwtManager(config);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Creates an in-memory token store for development and testing
|
|
323
|
+
*
|
|
324
|
+
* ⚠️ WARNING: NOT suitable for production!
|
|
325
|
+
* - Does not persist across server restarts
|
|
326
|
+
* - Does not work across multiple server instances
|
|
327
|
+
* - No automatic cleanup of expired token IDs
|
|
328
|
+
*
|
|
329
|
+
* For production, use Redis or database-backed storage:
|
|
330
|
+
* - upstash/redis for serverless
|
|
331
|
+
* - ioredis for traditional servers
|
|
332
|
+
* - Database table for audit trail
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* ```typescript
|
|
336
|
+
* // Development/Testing
|
|
337
|
+
* const tokenStore = createInMemoryTokenStore();
|
|
338
|
+
*
|
|
339
|
+
* const authConfig: AuthConfig = {
|
|
340
|
+
* jwt: { secret: process.env.JWT_SECRET! },
|
|
341
|
+
* isTokenRevoked: tokenStore.isRevoked,
|
|
342
|
+
* };
|
|
343
|
+
*
|
|
344
|
+
* // Revoke on logout
|
|
345
|
+
* app.post('/logout', async (req) => {
|
|
346
|
+
* const tokenId = req.auth.token.jti;
|
|
347
|
+
* tokenStore.revoke(tokenId);
|
|
348
|
+
* });
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
export function createInMemoryTokenStore() {
|
|
352
|
+
const revokedTokens = new Set();
|
|
353
|
+
return {
|
|
354
|
+
revoke: (tokenId) => {
|
|
355
|
+
revokedTokens.add(tokenId);
|
|
356
|
+
},
|
|
357
|
+
isRevoked: (tokenId) => revokedTokens.has(tokenId),
|
|
358
|
+
clear: () => {
|
|
359
|
+
revokedTokens.clear();
|
|
360
|
+
},
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
//# sourceMappingURL=jwt.js.map
|
package/dist/jwt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication middleware for @veloxts/auth
|
|
3
|
+
* @module auth/middleware
|
|
4
|
+
*/
|
|
5
|
+
import type { BaseContext } from '@veloxts/core';
|
|
6
|
+
import type { MiddlewareFunction } from '@veloxts/router';
|
|
7
|
+
import { JwtManager } from './jwt.js';
|
|
8
|
+
import type { AuthConfig, AuthContext, AuthMiddlewareOptions, GuardDefinition, User } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Creates an authentication middleware for procedures
|
|
11
|
+
*
|
|
12
|
+
* This middleware:
|
|
13
|
+
* 1. Extracts JWT from Authorization header
|
|
14
|
+
* 2. Verifies the token
|
|
15
|
+
* 3. Loads user from database (if userLoader provided)
|
|
16
|
+
* 4. Adds user and auth context to ctx
|
|
17
|
+
* 5. Runs guards if specified
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const auth = createAuthMiddleware(authConfig);
|
|
22
|
+
*
|
|
23
|
+
* // Use in procedures
|
|
24
|
+
* const getProfile = procedure()
|
|
25
|
+
* .use(auth.middleware())
|
|
26
|
+
* .query(async ({ ctx }) => {
|
|
27
|
+
* return ctx.user; // Guaranteed to exist
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Optional auth (user may be undefined)
|
|
31
|
+
* const getPosts = procedure()
|
|
32
|
+
* .use(auth.middleware({ optional: true }))
|
|
33
|
+
* .query(async ({ ctx }) => {
|
|
34
|
+
* // ctx.user may be undefined
|
|
35
|
+
* return fetchPosts(ctx.user?.id);
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // With guards
|
|
39
|
+
* const adminOnly = procedure()
|
|
40
|
+
* .use(auth.middleware({ guards: [hasRole('admin')] }))
|
|
41
|
+
* .query(async ({ ctx }) => {
|
|
42
|
+
* // Only admins get here
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function createAuthMiddleware(config: AuthConfig): {
|
|
47
|
+
middleware: <TInput, TContext extends BaseContext, TOutput>(options?: AuthMiddlewareOptions) => MiddlewareFunction<TInput, TContext, TContext & {
|
|
48
|
+
user?: User;
|
|
49
|
+
auth: AuthContext;
|
|
50
|
+
}, TOutput>;
|
|
51
|
+
requireAuth: <TInput, TContext extends BaseContext, TOutput>(guards?: Array<GuardDefinition | string>) => MiddlewareFunction<TInput, TContext, TContext & {
|
|
52
|
+
user: User;
|
|
53
|
+
auth: AuthContext;
|
|
54
|
+
}, TOutput>;
|
|
55
|
+
optionalAuth: <TInput, TContext extends BaseContext, TOutput>() => MiddlewareFunction<TInput, TContext, TContext & {
|
|
56
|
+
user?: User;
|
|
57
|
+
auth: AuthContext;
|
|
58
|
+
}, TOutput>;
|
|
59
|
+
jwt: JwtManager;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Creates a rate limiting middleware
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const rateLimit = createRateLimitMiddleware({
|
|
67
|
+
* max: 100,
|
|
68
|
+
* windowMs: 60000, // 1 minute
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* const login = procedure()
|
|
72
|
+
* .use(rateLimit)
|
|
73
|
+
* .input(LoginSchema)
|
|
74
|
+
* .mutation(handler);
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function createRateLimitMiddleware<TInput, TContext extends BaseContext, TOutput>(options: {
|
|
78
|
+
max?: number;
|
|
79
|
+
windowMs?: number;
|
|
80
|
+
keyGenerator?: (ctx: TContext) => string;
|
|
81
|
+
message?: string;
|
|
82
|
+
}): MiddlewareFunction<TInput, TContext, TContext, TOutput>;
|
|
83
|
+
/**
|
|
84
|
+
* Clears rate limit store (useful for testing)
|
|
85
|
+
*/
|
|
86
|
+
export declare function clearRateLimitStore(): void;
|
|
87
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,qBAAqB,EACrB,eAAe,EAEf,IAAI,EACL,MAAM,YAAY,CAAC;AAOpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU;iBAMjC,MAAM,EAAE,QAAQ,SAAS,WAAW,EAAE,OAAO,YACtD,qBAAqB,KAC7B,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG;QAAE,IAAI,CAAC,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,EAAE,OAAO,CAAC;kBA8H1E,MAAM,EAAE,QAAQ,SAAS,WAAW,EAAE,OAAO,WACvD,KAAK,CAAC,eAAe,GAAG,MAAM,CAAC,KACvC,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,EAAE,OAAO,CAAC;mBAYxE,MAAM,EAAE,QAAQ,SAAS,WAAW,EAAE,OAAO,OAAK,kBAAkB,CACxF,MAAM,EACN,QAAQ,EACR,QAAQ,GAAG;QAAE,IAAI,CAAC,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,EAC7C,OAAO,CACR;;EAUF;AAkBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,QAAQ,SAAS,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE;IAChG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,MAAM,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAuC1D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
|