@plyaz/auth 1.0.0 → 1.0.2
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/commits.txt +3 -3
- package/dist/common/index.cjs +3 -1
- package/dist/common/index.cjs.map +1 -1
- package/dist/common/index.mjs +3 -1
- package/dist/common/index.mjs.map +1 -1
- package/dist/index.cjs +424 -154
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +421 -152
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/release_message.txt +28 -0
- package/src/adapters/auth-adapter-factory.ts +4 -3
- package/src/adapters/auth-adapter.mapper.ts +2 -2
- package/src/adapters/base-auth.adapter.ts +17 -9
- package/src/adapters/clerk/clerk.adapter.ts +9 -12
- package/src/adapters/custom/custom.adapter.ts +19 -10
- package/src/adapters/index.ts +0 -1
- package/src/adapters/next-auth/authOptions.ts +20 -16
- package/src/adapters/next-auth/next-auth.adapter.ts +13 -15
- package/src/api/client.ts +4 -6
- package/src/audit/audit.logger.ts +19 -10
- package/src/client/components/ProtectedRoute.tsx +15 -11
- package/src/client/hooks/useAuth.ts +23 -21
- package/src/client/hooks/useConnectedAccounts.ts +57 -45
- package/src/client/hooks/usePermissions.ts +1 -1
- package/src/client/hooks/useRBAC.ts +6 -6
- package/src/client/hooks/useSession.ts +5 -5
- package/src/client/providers/AuthProvider.tsx +23 -17
- package/src/client/store/auth.store.ts +71 -62
- package/src/client/utils/storage.ts +45 -18
- package/src/common/constants/oauth-providers.ts +10 -7
- package/src/common/errors/auth.errors.ts +4 -4
- package/src/common/errors/specific-auth-errors.ts +5 -9
- package/src/common/regex/index.ts +6 -4
- package/src/common/types/auth.types.ts +47 -38
- package/src/common/types/index.ts +12 -6
- package/src/common/utils/index.ts +15 -11
- package/src/core/blacklist/token.blacklist.ts +13 -7
- package/src/core/index.ts +2 -2
- package/src/core/jwt/jwt.manager.ts +47 -22
- package/src/core/session/session.manager.ts +17 -14
- package/src/db/repositories/connected-account.repository.ts +120 -78
- package/src/db/repositories/role.repository.ts +41 -26
- package/src/db/repositories/session.repository.ts +9 -10
- package/src/db/repositories/user.repository.ts +105 -91
- package/src/flows/index.ts +2 -2
- package/src/flows/sign-in.flow.ts +28 -14
- package/src/flows/sign-up.flow.ts +31 -20
- package/src/index.ts +36 -37
- package/src/libs/clerk.helper.ts +6 -7
- package/src/libs/supabase.helper.ts +79 -61
- package/src/libs/supabaseClient.ts +3 -3
- package/src/providers/base/auth-provider.interface.ts +13 -11
- package/src/providers/base/index.ts +1 -1
- package/src/providers/index.ts +1 -1
- package/src/providers/oauth/facebook.provider.ts +63 -39
- package/src/providers/oauth/github.provider.ts +14 -10
- package/src/providers/oauth/google.provider.ts +39 -28
- package/src/providers/oauth/index.ts +1 -1
- package/src/rbac/dynamic-roles.ts +88 -54
- package/src/rbac/index.ts +4 -4
- package/src/rbac/permission-checker.ts +147 -75
- package/src/rbac/role-hierarchy.ts +8 -8
- package/src/rbac/role.manager.ts +11 -8
- package/src/security/csrf/csrf.protection.ts +9 -7
- package/src/security/index.ts +2 -2
- package/src/security/rate-limiting/auth/auth.controller.ts +2 -4
- package/src/security/rate-limiting/auth/rate-limiting.interface.ts +26 -6
- package/src/security/rate-limiting/auth.module.ts +1 -2
- package/src/server/auth.module.ts +55 -52
- package/src/server/decorators/auth.decorator.ts +9 -11
- package/src/server/decorators/auth.decorators.ts +8 -9
- package/src/server/decorators/current-user.decorator.ts +6 -6
- package/src/server/decorators/permission.decorator.ts +17 -9
- package/src/server/guards/auth.guard.ts +21 -16
- package/src/server/guards/custom-throttler.guard.ts +4 -9
- package/src/server/guards/permissions.guard.ts +32 -23
- package/src/server/guards/roles.guard.ts +14 -12
- package/src/server/middleware/auth.middleware.ts +4 -4
- package/src/server/middleware/session.middleware.ts +4 -4
- package/src/server/services/account.service.ts +96 -48
- package/src/server/services/auth.service.ts +57 -28
- package/src/server/services/brute-force.service.ts +24 -19
- package/src/server/services/index.ts +1 -1
- package/src/server/services/rate-limiter.service.ts +9 -4
- package/src/server/services/session.service.ts +84 -48
- package/src/server/services/token.service.ts +71 -51
- package/src/session/cookie-store.ts +47 -34
- package/src/session/enhanced-session-manager.ts +69 -48
- package/src/session/index.ts +5 -5
- package/src/session/memory-store.ts +37 -30
- package/src/session/redis-store.ts +105 -72
- package/src/strategies/oauth.strategy.ts +10 -9
- package/src/strategies/traditional-auth.strategy.ts +41 -29
- package/src/tokens/index.ts +4 -4
- package/src/tokens/refresh-token-manager.ts +70 -55
- package/src/tokens/token-validator.ts +109 -53
- package/vitest.setup.d.ts +2 -2
- package/vitest.setup.ts +1 -1
|
@@ -2,30 +2,30 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @fileoverview Token validator for @plyaz/auth
|
|
4
4
|
* @module @plyaz/auth/tokens/token-validator
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @description
|
|
7
7
|
* Provides comprehensive JWT token validation including signature verification,
|
|
8
8
|
* expiration checks, blacklist validation, and issuer/audience verification.
|
|
9
9
|
* Used by guards and middleware to validate incoming authentication tokens.
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```typescript
|
|
13
13
|
* import { TokenValidator } from '@plyaz/auth';
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
15
|
* const validator = new TokenValidator({
|
|
16
16
|
* publicKey: 'your-public-key',
|
|
17
17
|
* issuer: 'plyaz.com',
|
|
18
18
|
* audience: 'plyaz-api'
|
|
19
19
|
* });
|
|
20
|
-
*
|
|
20
|
+
*
|
|
21
21
|
* const payload = await validator.validateToken(token);
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import type { JwtPayload, Algorithm } from
|
|
26
|
-
import { verify } from
|
|
27
|
-
import { TokenBlacklist } from
|
|
28
|
-
import { Buffer } from
|
|
25
|
+
import type { JwtPayload, Algorithm } from "jsonwebtoken";
|
|
26
|
+
import { verify } from "jsonwebtoken";
|
|
27
|
+
import { TokenBlacklist } from "../core/blacklist/token.blacklist";
|
|
28
|
+
import { Buffer } from "buffer";
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Token validation configuration
|
|
@@ -58,7 +58,7 @@ export interface ValidatedTokenPayload extends JwtPayload {
|
|
|
58
58
|
/** User permissions */
|
|
59
59
|
permissions?: string[];
|
|
60
60
|
/** Token type */
|
|
61
|
-
type?:
|
|
61
|
+
type?: "access" | "refresh";
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -87,14 +87,17 @@ export class TokenValidator {
|
|
|
87
87
|
|
|
88
88
|
constructor(config: TokenValidatorConfig) {
|
|
89
89
|
this.config = {
|
|
90
|
-
algorithm:
|
|
90
|
+
algorithm: "RS256",
|
|
91
91
|
clockTolerance: 30,
|
|
92
92
|
enableBlacklist: true,
|
|
93
|
-
...config
|
|
93
|
+
...config,
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
if (this.config.enableBlacklist) {
|
|
97
|
-
this.blacklist = new TokenBlacklist({
|
|
97
|
+
this.blacklist = new TokenBlacklist({
|
|
98
|
+
keyPrefix: "token:",
|
|
99
|
+
defaultTTL: 3600,
|
|
100
|
+
});
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
|
|
@@ -106,13 +109,19 @@ export class TokenValidator {
|
|
|
106
109
|
async validateToken(token: string): Promise<TokenValidationResult> {
|
|
107
110
|
try {
|
|
108
111
|
// Basic format check
|
|
109
|
-
if (!token || typeof token !==
|
|
110
|
-
return this.createErrorResult(
|
|
112
|
+
if (!token || typeof token !== "string") {
|
|
113
|
+
return this.createErrorResult(
|
|
114
|
+
"INVALID_FORMAT",
|
|
115
|
+
"Token format is invalid",
|
|
116
|
+
);
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
// Check if token is blacklisted
|
|
114
|
-
if (this.blacklist && await this.blacklist.isBlacklisted(token)) {
|
|
115
|
-
return this.createErrorResult(
|
|
120
|
+
if (this.blacklist && (await this.blacklist.isBlacklisted(token))) {
|
|
121
|
+
return this.createErrorResult(
|
|
122
|
+
"TOKEN_REVOKED",
|
|
123
|
+
"Token has been revoked",
|
|
124
|
+
);
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
// Verify JWT signature and claims
|
|
@@ -120,7 +129,7 @@ export class TokenValidator {
|
|
|
120
129
|
issuer: this.config.issuer,
|
|
121
130
|
audience: this.config.audience,
|
|
122
131
|
algorithms: [this.config.algorithm as Algorithm],
|
|
123
|
-
clockTolerance: this.config.clockTolerance
|
|
132
|
+
clockTolerance: this.config.clockTolerance,
|
|
124
133
|
}) as ValidatedTokenPayload;
|
|
125
134
|
|
|
126
135
|
// Additional payload validation
|
|
@@ -131,9 +140,8 @@ export class TokenValidator {
|
|
|
131
140
|
|
|
132
141
|
return {
|
|
133
142
|
valid: true,
|
|
134
|
-
payload
|
|
143
|
+
payload,
|
|
135
144
|
};
|
|
136
|
-
|
|
137
145
|
} catch (error) {
|
|
138
146
|
return this.handleVerificationError(error as Error);
|
|
139
147
|
}
|
|
@@ -146,11 +154,18 @@ export class TokenValidator {
|
|
|
146
154
|
*/
|
|
147
155
|
async validateAccessToken(token: string): Promise<TokenValidationResult> {
|
|
148
156
|
const result = await this.validateToken(token);
|
|
149
|
-
|
|
150
|
-
if (
|
|
151
|
-
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
result.valid &&
|
|
160
|
+
result.payload?.type &&
|
|
161
|
+
result.payload.type !== "access"
|
|
162
|
+
) {
|
|
163
|
+
return this.createErrorResult(
|
|
164
|
+
"INVALID_TOKEN_TYPE",
|
|
165
|
+
"Expected access token",
|
|
166
|
+
);
|
|
152
167
|
}
|
|
153
|
-
|
|
168
|
+
|
|
154
169
|
return result;
|
|
155
170
|
}
|
|
156
171
|
|
|
@@ -161,11 +176,18 @@ export class TokenValidator {
|
|
|
161
176
|
*/
|
|
162
177
|
async validateRefreshToken(token: string): Promise<TokenValidationResult> {
|
|
163
178
|
const result = await this.validateToken(token);
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
179
|
+
|
|
180
|
+
if (
|
|
181
|
+
result.valid &&
|
|
182
|
+
result.payload?.type &&
|
|
183
|
+
result.payload.type !== "refresh"
|
|
184
|
+
) {
|
|
185
|
+
return this.createErrorResult(
|
|
186
|
+
"INVALID_TOKEN_TYPE",
|
|
187
|
+
"Expected refresh token",
|
|
188
|
+
);
|
|
167
189
|
}
|
|
168
|
-
|
|
190
|
+
|
|
169
191
|
return result;
|
|
170
192
|
}
|
|
171
193
|
|
|
@@ -176,12 +198,12 @@ export class TokenValidator {
|
|
|
176
198
|
*/
|
|
177
199
|
extractPayload(token: string): ValidatedTokenPayload | null {
|
|
178
200
|
try {
|
|
179
|
-
const parts = token.split(
|
|
201
|
+
const parts = token.split(".");
|
|
180
202
|
if (parts.length !== 3) {
|
|
181
203
|
return null;
|
|
182
204
|
}
|
|
183
205
|
|
|
184
|
-
const payload = JSON.parse(Buffer.from(parts[1],
|
|
206
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64").toString());
|
|
185
207
|
return payload;
|
|
186
208
|
} catch {
|
|
187
209
|
return null;
|
|
@@ -195,7 +217,7 @@ export class TokenValidator {
|
|
|
195
217
|
*/
|
|
196
218
|
isTokenExpired(token: string): boolean {
|
|
197
219
|
const payload = this.extractPayload(token);
|
|
198
|
-
|
|
220
|
+
|
|
199
221
|
if (!payload?.exp) {
|
|
200
222
|
return true;
|
|
201
223
|
}
|
|
@@ -211,7 +233,7 @@ export class TokenValidator {
|
|
|
211
233
|
*/
|
|
212
234
|
getTokenExpiration(token: string): Date | null {
|
|
213
235
|
const payload = this.extractPayload(token);
|
|
214
|
-
|
|
236
|
+
|
|
215
237
|
if (!payload?.exp) {
|
|
216
238
|
return null;
|
|
217
239
|
}
|
|
@@ -226,14 +248,14 @@ export class TokenValidator {
|
|
|
226
248
|
*/
|
|
227
249
|
getTimeUntilExpiration(token: string): number | null {
|
|
228
250
|
const expiration = this.getTokenExpiration(token);
|
|
229
|
-
|
|
251
|
+
|
|
230
252
|
if (!expiration) {
|
|
231
253
|
return null;
|
|
232
254
|
}
|
|
233
255
|
|
|
234
256
|
const now = Date.now();
|
|
235
257
|
const timeUntilExpiry = expiration.getTime() - now;
|
|
236
|
-
|
|
258
|
+
|
|
237
259
|
return Math.max(0, Math.floor(timeUntilExpiry / 1000));
|
|
238
260
|
}
|
|
239
261
|
|
|
@@ -243,28 +265,42 @@ export class TokenValidator {
|
|
|
243
265
|
* @returns Error result if invalid, null if valid
|
|
244
266
|
* @private
|
|
245
267
|
*/
|
|
246
|
-
private validatePayload(
|
|
268
|
+
private validatePayload(
|
|
269
|
+
payload: ValidatedTokenPayload,
|
|
270
|
+
): TokenValidationResult | null {
|
|
247
271
|
// Check required claims
|
|
248
272
|
if (!payload.sub) {
|
|
249
|
-
return this.createErrorResult(
|
|
273
|
+
return this.createErrorResult(
|
|
274
|
+
"MISSING_SUBJECT",
|
|
275
|
+
"Token missing subject claim",
|
|
276
|
+
);
|
|
250
277
|
}
|
|
251
278
|
|
|
252
279
|
if (!payload.iat) {
|
|
253
|
-
return this.createErrorResult(
|
|
280
|
+
return this.createErrorResult(
|
|
281
|
+
"MISSING_ISSUED_AT",
|
|
282
|
+
"Token missing issued at claim",
|
|
283
|
+
);
|
|
254
284
|
}
|
|
255
285
|
|
|
256
286
|
if (!payload.exp) {
|
|
257
|
-
return this.createErrorResult(
|
|
287
|
+
return this.createErrorResult(
|
|
288
|
+
"MISSING_EXPIRATION",
|
|
289
|
+
"Token missing expiration claim",
|
|
290
|
+
);
|
|
258
291
|
}
|
|
259
292
|
|
|
260
293
|
// Validate issued at time (not in future)
|
|
261
294
|
const now = Math.floor(Date.now() / 1000);
|
|
262
295
|
if (payload.iat > now + this.config.clockTolerance) {
|
|
263
|
-
return this.createErrorResult(
|
|
296
|
+
return this.createErrorResult(
|
|
297
|
+
"TOKEN_NOT_YET_VALID",
|
|
298
|
+
"Token issued in the future",
|
|
299
|
+
);
|
|
264
300
|
}
|
|
265
301
|
|
|
266
302
|
// Additional custom validations can be added here
|
|
267
|
-
|
|
303
|
+
|
|
268
304
|
return null;
|
|
269
305
|
}
|
|
270
306
|
|
|
@@ -274,20 +310,36 @@ export class TokenValidator {
|
|
|
274
310
|
* @returns Error result
|
|
275
311
|
* @private
|
|
276
312
|
*/
|
|
277
|
-
private handleVerificationError(error
|
|
278
|
-
if (error.name ===
|
|
279
|
-
return this.createErrorResult(
|
|
313
|
+
private handleVerificationError(error: Error): TokenValidationResult {
|
|
314
|
+
if (error.name === "TokenExpiredError") {
|
|
315
|
+
return this.createErrorResult(
|
|
316
|
+
"TOKEN_EXPIRED",
|
|
317
|
+
"Token has expired",
|
|
318
|
+
error,
|
|
319
|
+
);
|
|
280
320
|
}
|
|
281
|
-
|
|
282
|
-
if (error.name ===
|
|
283
|
-
return this.createErrorResult(
|
|
321
|
+
|
|
322
|
+
if (error.name === "JsonWebTokenError") {
|
|
323
|
+
return this.createErrorResult(
|
|
324
|
+
"INVALID_SIGNATURE",
|
|
325
|
+
"Token signature is invalid",
|
|
326
|
+
error,
|
|
327
|
+
);
|
|
284
328
|
}
|
|
285
|
-
|
|
286
|
-
if (error.name ===
|
|
287
|
-
return this.createErrorResult(
|
|
329
|
+
|
|
330
|
+
if (error.name === "NotBeforeError") {
|
|
331
|
+
return this.createErrorResult(
|
|
332
|
+
"TOKEN_NOT_YET_VALID",
|
|
333
|
+
"Token not yet valid",
|
|
334
|
+
error,
|
|
335
|
+
);
|
|
288
336
|
}
|
|
289
337
|
|
|
290
|
-
return this.createErrorResult(
|
|
338
|
+
return this.createErrorResult(
|
|
339
|
+
"VALIDATION_FAILED",
|
|
340
|
+
error.message ?? "Token validation failed",
|
|
341
|
+
error,
|
|
342
|
+
);
|
|
291
343
|
}
|
|
292
344
|
|
|
293
345
|
/**
|
|
@@ -298,14 +350,18 @@ export class TokenValidator {
|
|
|
298
350
|
* @returns Error result
|
|
299
351
|
* @private
|
|
300
352
|
*/
|
|
301
|
-
private createErrorResult(
|
|
353
|
+
private createErrorResult(
|
|
354
|
+
code: string,
|
|
355
|
+
message: string,
|
|
356
|
+
details?: unknown,
|
|
357
|
+
): TokenValidationResult {
|
|
302
358
|
return {
|
|
303
359
|
valid: false,
|
|
304
360
|
error: {
|
|
305
361
|
code,
|
|
306
362
|
message,
|
|
307
|
-
details
|
|
308
|
-
}
|
|
363
|
+
details,
|
|
364
|
+
},
|
|
309
365
|
};
|
|
310
366
|
}
|
|
311
|
-
}
|
|
367
|
+
}
|
package/vitest.setup.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
//# sourceMappingURL=vitest.setup.d.ts.map
|
|
1
|
+
import "@plyaz/devtools/configs/vitest.setup";
|
|
2
|
+
//# sourceMappingURL=vitest.setup.d.ts.map
|
package/vitest.setup.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "@plyaz/devtools/configs/vitest.setup";
|