@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
|
@@ -12,7 +12,7 @@ export class TokenBlacklist {
|
|
|
12
12
|
async add(tokenId: string, expiresAt: number): Promise<void> {
|
|
13
13
|
const ttl = Math.max(0, expiresAt - Date.now());
|
|
14
14
|
this.blacklistedTokens.set(tokenId, Date.now() + ttl);
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
// Auto-cleanup expired tokens
|
|
17
17
|
globalThis.setTimeout(() => {
|
|
18
18
|
this.blacklistedTokens.delete(tokenId);
|
|
@@ -26,26 +26,32 @@ export class TokenBlacklist {
|
|
|
26
26
|
async isBlacklisted(tokenId: string): Promise<boolean> {
|
|
27
27
|
const expiry = this.blacklistedTokens.get(tokenId);
|
|
28
28
|
if (!expiry) return false;
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
if (expiry < Date.now()) {
|
|
31
31
|
this.blacklistedTokens.delete(tokenId);
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
return true;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
async blacklistAllUserTokens(
|
|
38
|
+
async blacklistAllUserTokens(
|
|
39
|
+
userId: string,
|
|
40
|
+
issuedBefore: number,
|
|
41
|
+
): Promise<void> {
|
|
39
42
|
// In production, this would use Redis with a user-specific key
|
|
40
43
|
// For now, we'll use a simple approach
|
|
41
44
|
const userKey = `user:${userId}:blacklist_before`;
|
|
42
45
|
this.blacklistedTokens.set(userKey, issuedBefore);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
async isUserTokenBlacklisted(
|
|
48
|
+
async isUserTokenBlacklisted(
|
|
49
|
+
userId: string,
|
|
50
|
+
tokenIssuedAt: number,
|
|
51
|
+
): Promise<boolean> {
|
|
46
52
|
const userKey = `user:${userId}:blacklist_before`;
|
|
47
53
|
const blacklistBefore = this.blacklistedTokens.get(userKey);
|
|
48
|
-
|
|
54
|
+
|
|
49
55
|
return blacklistBefore ? tokenIssuedAt < blacklistBefore : false;
|
|
50
56
|
}
|
|
51
57
|
|
|
@@ -57,4 +63,4 @@ export class TokenBlacklist {
|
|
|
57
63
|
}
|
|
58
64
|
});
|
|
59
65
|
}
|
|
60
|
-
}
|
|
66
|
+
}
|
package/src/core/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "./jwt/jwt.manager";
|
|
2
|
+
export * from "./session/session.manager";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sign, verify, type JwtPayload, type SignOptions } from "jsonwebtoken";
|
|
2
|
-
import type { AuthTokens } from
|
|
2
|
+
import type { AuthTokens } from "@plyaz/types";
|
|
3
3
|
import { NUMERIX } from "@plyaz/config";
|
|
4
4
|
|
|
5
5
|
// Define the token payload interface
|
|
@@ -12,7 +12,6 @@ export interface TokenPayload extends JwtPayload {
|
|
|
12
12
|
// Define the StringValue type to match what jsonwebtoken expects
|
|
13
13
|
type StringValue = `${number}s` | `${number}m` | `${number}h` | `${number}d`;
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
export class JwtManager {
|
|
17
16
|
private config: {
|
|
18
17
|
privateKey: string;
|
|
@@ -24,7 +23,7 @@ export class JwtManager {
|
|
|
24
23
|
|
|
25
24
|
constructor(
|
|
26
25
|
config: typeof JwtManager.prototype.config,
|
|
27
|
-
algorithm: "RS256" | "HS256" | "ES256" | "HS512"
|
|
26
|
+
algorithm: "RS256" | "HS256" | "ES256" | "HS512",
|
|
28
27
|
) {
|
|
29
28
|
this.config = config;
|
|
30
29
|
this.algorithm = algorithm;
|
|
@@ -35,7 +34,7 @@ export class JwtManager {
|
|
|
35
34
|
// -------------------
|
|
36
35
|
private generateToken(
|
|
37
36
|
payload: Omit<TokenPayload, "iss" | "aud" | "iat" | "exp">,
|
|
38
|
-
expiresIn: string | number = "1h"
|
|
37
|
+
expiresIn: string | number = "1h",
|
|
39
38
|
): string {
|
|
40
39
|
const signOptions: SignOptions = {
|
|
41
40
|
algorithm: this.algorithm,
|
|
@@ -50,20 +49,31 @@ export class JwtManager {
|
|
|
50
49
|
generateTokens(
|
|
51
50
|
user: { id: string },
|
|
52
51
|
accessTokenExpiry: string | number = "1h",
|
|
53
|
-
refreshTokenExpiry: string | number = "7d"
|
|
52
|
+
refreshTokenExpiry: string | number = "7d",
|
|
54
53
|
): AuthTokens {
|
|
55
|
-
const accessToken = this.generateToken(
|
|
56
|
-
|
|
54
|
+
const accessToken = this.generateToken(
|
|
55
|
+
{ sub: user.id, type: "access" },
|
|
56
|
+
accessTokenExpiry,
|
|
57
|
+
);
|
|
58
|
+
const refreshToken = this.generateToken(
|
|
59
|
+
{ sub: user.id, type: "refresh" },
|
|
60
|
+
refreshTokenExpiry,
|
|
61
|
+
);
|
|
57
62
|
|
|
58
63
|
// Calculate the actual expiration time in seconds
|
|
59
64
|
let expiresIn: number;
|
|
60
|
-
if (typeof accessTokenExpiry ===
|
|
65
|
+
if (typeof accessTokenExpiry === "string") {
|
|
61
66
|
// Parse time strings like "1h", "30m", etc.
|
|
62
67
|
const match = accessTokenExpiry.match(/^(\d+)([smhd])$/);
|
|
63
68
|
if (match) {
|
|
64
69
|
const value = globalThis.parseInt(match[1], NUMERIX.TEN);
|
|
65
70
|
const unit = match[2];
|
|
66
|
-
const multipliers: Record<string, number> = {
|
|
71
|
+
const multipliers: Record<string, number> = {
|
|
72
|
+
s: 1,
|
|
73
|
+
m: 60,
|
|
74
|
+
h: 3600,
|
|
75
|
+
d: 86400,
|
|
76
|
+
};
|
|
67
77
|
expiresIn = value * multipliers[unit];
|
|
68
78
|
} else {
|
|
69
79
|
expiresIn = NUMERIX.THIRTY_SIX_HUNDERD; // Default to 1 hour
|
|
@@ -72,48 +82,63 @@ export class JwtManager {
|
|
|
72
82
|
expiresIn = accessTokenExpiry;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
return {
|
|
76
|
-
accessToken,
|
|
85
|
+
return {
|
|
86
|
+
accessToken,
|
|
77
87
|
refreshToken,
|
|
78
88
|
expiresIn,
|
|
79
|
-
tokenType: "Bearer"
|
|
89
|
+
tokenType: "Bearer",
|
|
80
90
|
};
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
// Generate access token
|
|
84
|
-
generateAccessToken(
|
|
94
|
+
generateAccessToken(
|
|
95
|
+
userId: string,
|
|
96
|
+
expiresIn: string | number = "1h",
|
|
97
|
+
): string {
|
|
85
98
|
return this.generateToken({ sub: userId, type: "access" }, expiresIn);
|
|
86
99
|
}
|
|
87
100
|
|
|
88
101
|
// Generate refresh token
|
|
89
|
-
generateRefreshToken(
|
|
102
|
+
generateRefreshToken(
|
|
103
|
+
userId: string,
|
|
104
|
+
expiresIn: string | number = "7d",
|
|
105
|
+
): string {
|
|
90
106
|
return this.generateToken({ sub: userId, type: "refresh" }, expiresIn);
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
// Generate verification token (email confirmation)
|
|
94
|
-
generateVerificationToken(
|
|
110
|
+
generateVerificationToken(
|
|
111
|
+
userId: string,
|
|
112
|
+
expiresIn: string | number = "24h",
|
|
113
|
+
): string {
|
|
95
114
|
return this.generateToken({ sub: userId, type: "verification" }, expiresIn);
|
|
96
115
|
}
|
|
97
116
|
|
|
98
117
|
// -------------------
|
|
99
118
|
// Token Verification
|
|
100
119
|
// -------------------
|
|
101
|
-
private verifyToken(
|
|
120
|
+
private verifyToken(
|
|
121
|
+
token: string,
|
|
122
|
+
tokenType: "access" | "refresh" | "verification",
|
|
123
|
+
): TokenPayload {
|
|
102
124
|
const decoded = verify(token, this.config.publicKey, {
|
|
103
125
|
algorithms: [this.algorithm],
|
|
104
126
|
issuer: this.config.issuer,
|
|
105
127
|
audience: this.config.audience,
|
|
106
128
|
});
|
|
107
129
|
|
|
108
|
-
if (typeof decoded === "string")
|
|
109
|
-
|
|
130
|
+
if (typeof decoded === "string")
|
|
131
|
+
throw new Error(`Invalid ${tokenType} token payload`);
|
|
132
|
+
|
|
110
133
|
const payload = decoded as JwtPayload;
|
|
111
|
-
|
|
134
|
+
|
|
112
135
|
// Verify the token type
|
|
113
136
|
if (payload.type !== tokenType) {
|
|
114
|
-
throw new Error(
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Invalid token type. Expected ${tokenType}, got ${payload.type}`,
|
|
139
|
+
);
|
|
115
140
|
}
|
|
116
|
-
|
|
141
|
+
|
|
117
142
|
return payload as TokenPayload;
|
|
118
143
|
}
|
|
119
144
|
|
|
@@ -128,4 +153,4 @@ export class JwtManager {
|
|
|
128
153
|
verifyVerificationToken(token: string): TokenPayload {
|
|
129
154
|
return this.verifyToken(token, "verification");
|
|
130
155
|
}
|
|
131
|
-
}
|
|
156
|
+
}
|
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Session,
|
|
3
|
+
SessionRepository,
|
|
4
|
+
SessionConfig,
|
|
5
|
+
CreateSessionData,
|
|
6
|
+
} from "@plyaz/types";
|
|
5
7
|
|
|
6
8
|
export class SessionManager {
|
|
7
9
|
constructor(
|
|
8
10
|
private sessionRepo: SessionRepository,
|
|
9
|
-
private config: SessionConfig
|
|
11
|
+
private config: SessionConfig,
|
|
10
12
|
) {}
|
|
11
13
|
|
|
12
|
-
async createSession(
|
|
14
|
+
async createSession(
|
|
15
|
+
userId: string,
|
|
16
|
+
deviceInfo: Record<string, unknown>,
|
|
17
|
+
): Promise<Session> {
|
|
13
18
|
await this.enforceSessionLimits(userId);
|
|
14
|
-
|
|
19
|
+
|
|
15
20
|
const sessionData: CreateSessionData = {
|
|
16
21
|
userId,
|
|
17
|
-
provider:
|
|
22
|
+
provider: "default",
|
|
18
23
|
expiresAt: new Date(Date.now() + this.config.sessionTTL),
|
|
19
|
-
metadata:deviceInfo,
|
|
24
|
+
metadata: deviceInfo,
|
|
20
25
|
};
|
|
21
26
|
|
|
22
27
|
return this.sessionRepo.create(sessionData);
|
|
@@ -45,12 +50,10 @@ export class SessionManager {
|
|
|
45
50
|
private async enforceSessionLimits(userId: string): Promise<void> {
|
|
46
51
|
const sessions = await this.sessionRepo.findByUserId(userId);
|
|
47
52
|
if (sessions.length >= this.config.maxConcurrentSessions) {
|
|
48
|
-
const oldestSession = sessions.sort(
|
|
49
|
-
a.lastActivityAt.getTime() - b.lastActivityAt.getTime()
|
|
53
|
+
const oldestSession = sessions.sort(
|
|
54
|
+
(a, b) => a.lastActivityAt.getTime() - b.lastActivityAt.getTime(),
|
|
50
55
|
)[0];
|
|
51
56
|
await this.sessionRepo.delete(oldestSession.id);
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
59
|
+
}
|