@veloxts/auth 0.6.68 → 0.6.70
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/CHANGELOG.md +87 -0
- package/dist/adapter.d.ts +10 -4
- package/dist/adapter.js +15 -9
- package/dist/adapters/index.d.ts +26 -6
- package/dist/adapters/index.js +25 -7
- package/dist/adapters/jwt-adapter.d.ts +261 -0
- package/dist/adapters/jwt-adapter.js +360 -0
- package/dist/decoration.d.ts +88 -0
- package/dist/decoration.js +112 -0
- package/dist/guards-narrowing.d.ts +123 -0
- package/dist/guards-narrowing.js +80 -0
- package/dist/index.d.ts +11 -8
- package/dist/index.js +8 -1
- package/dist/middleware.d.ts +4 -4
- package/dist/middleware.js +7 -1
- package/dist/plugin.d.ts +70 -5
- package/dist/plugin.js +172 -62
- package/dist/providers.js +3 -1
- package/dist/token-store.d.ts +105 -0
- package/dist/token-store.js +159 -0
- package/dist/types.d.ts +70 -33
- package/package.json +5 -5
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrowing Guards (Experimental)
|
|
3
|
+
*
|
|
4
|
+
* These guards provide TypeScript type narrowing after they pass.
|
|
5
|
+
* When using `guardNarrow(authenticatedNarrow)`, the context type
|
|
6
|
+
* is narrowed to guarantee `ctx.user` is non-null.
|
|
7
|
+
*
|
|
8
|
+
* EXPERIMENTAL: This API may change. The current recommended approach
|
|
9
|
+
* is to use middleware for context type extension.
|
|
10
|
+
*
|
|
11
|
+
* @module auth/guards-narrowing
|
|
12
|
+
*/
|
|
13
|
+
import { authenticated, hasRole as hasRoleBase } from './guards.js';
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Narrowing Guards
|
|
16
|
+
// ============================================================================
|
|
17
|
+
/**
|
|
18
|
+
* Authenticated guard with type narrowing.
|
|
19
|
+
*
|
|
20
|
+
* When used with `guardNarrow()`, narrows `ctx.user` from `User | undefined`
|
|
21
|
+
* to `User`, eliminating the need for null checks in the handler.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { authenticatedNarrow } from '@veloxts/auth';
|
|
26
|
+
*
|
|
27
|
+
* // With guardNarrow (experimental):
|
|
28
|
+
* procedure()
|
|
29
|
+
* .guardNarrow(authenticatedNarrow)
|
|
30
|
+
* .query(({ ctx }) => {
|
|
31
|
+
* // ctx.user is typed as User (non-null)
|
|
32
|
+
* return { email: ctx.user.email };
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Current recommended alternative using middleware:
|
|
36
|
+
* procedure()
|
|
37
|
+
* .guard(authenticated)
|
|
38
|
+
* .use(async ({ ctx, next }) => {
|
|
39
|
+
* if (!ctx.user) throw new Error('Unreachable');
|
|
40
|
+
* return next({ ctx: { user: ctx.user } });
|
|
41
|
+
* })
|
|
42
|
+
* .query(({ ctx }) => {
|
|
43
|
+
* // ctx.user is non-null via middleware
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export const authenticatedNarrow = {
|
|
48
|
+
...authenticated,
|
|
49
|
+
// Phantom type: value is never used at runtime, only carries type info.
|
|
50
|
+
// The `undefined as unknown as T` pattern is standard for phantom types.
|
|
51
|
+
_narrows: undefined,
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Creates a role-checking guard with type narrowing.
|
|
55
|
+
*
|
|
56
|
+
* Narrows `ctx.user` to guarantee non-null with roles array.
|
|
57
|
+
*
|
|
58
|
+
* @param roles - Required role(s)
|
|
59
|
+
* @returns NarrowingGuard that guarantees user with roles
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import { hasRoleNarrow } from '@veloxts/auth';
|
|
64
|
+
*
|
|
65
|
+
* procedure()
|
|
66
|
+
* .guardNarrow(hasRoleNarrow('admin'))
|
|
67
|
+
* .mutation(({ ctx }) => {
|
|
68
|
+
* // ctx.user is typed as User (non-null)
|
|
69
|
+
* // ctx.user.roles is string[]
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function hasRoleNarrow(roles) {
|
|
74
|
+
const baseGuard = hasRoleBase(roles);
|
|
75
|
+
return {
|
|
76
|
+
...baseGuard,
|
|
77
|
+
// Phantom type: carries type info for guardNarrow() context narrowing
|
|
78
|
+
_narrows: undefined,
|
|
79
|
+
};
|
|
80
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,30 +8,33 @@
|
|
|
8
8
|
* @module @veloxts/auth
|
|
9
9
|
*/
|
|
10
10
|
export { AUTH_VERSION } from './plugin.js';
|
|
11
|
-
export type { AuthConfig, AuthContext, AuthMiddlewareOptions, GuardDefinition, GuardFunction, HashConfig, JwtConfig,
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated Use SessionConfig from session.ts for full session management
|
|
14
|
-
*/
|
|
15
|
-
LegacySessionConfig, PolicyAction, PolicyDefinition, RateLimitConfig, TokenPair, TokenPayload, User, } from './types.js';
|
|
11
|
+
export type { AdapterAuthContext, AuthConfig, AuthContext, AuthMiddlewareOptions, BaseAuthContext, GuardDefinition, GuardFunction, HashConfig, JwtConfig, NativeAuthContext, PolicyAction, PolicyDefinition, RateLimitConfig, TokenPair, TokenPayload, User, } from './types.js';
|
|
16
12
|
export { AuthError } from './types.js';
|
|
13
|
+
export { AUTH_REGISTERED, checkDoubleRegistration, decorateAuth, getRequestAuth, getRequestUser, setRequestAuth, } from './decoration.js';
|
|
17
14
|
export type { TokenStore } from './jwt.js';
|
|
18
15
|
export { createInMemoryTokenStore, generateTokenId, isValidTimespan, JwtManager, jwtManager, parseTimeToSeconds, validateTokenExpiration, } from './jwt.js';
|
|
16
|
+
export type { EnhancedTokenStore, EnhancedTokenStoreOptions } from './token-store.js';
|
|
17
|
+
export { createEnhancedTokenStore, DEFAULT_ALLOWED_ROLES, parseUserRoles, } from './token-store.js';
|
|
18
|
+
export type { AuthenticatedContext, InferNarrowedContext, NarrowingGuard, RoleNarrowedContext, } from './guards-narrowing.js';
|
|
19
|
+
export { authenticatedNarrow, hasRoleNarrow } from './guards-narrowing.js';
|
|
19
20
|
export { hashPassword, PasswordHasher, passwordHasher, verifyPassword, } from './hash.js';
|
|
20
21
|
export { allOf, anyOf, authenticated, defineGuard, emailVerified, executeGuard, executeGuards, guard, hasAnyPermission, hasPermission, hasRole, not, userCan, } from './guards.js';
|
|
21
22
|
export { authorize, can, cannot, clearPolicies, createAdminOnlyPolicy, createOwnerOrAdminPolicy, createPolicyBuilder, createReadOnlyPolicy, definePolicy, getPolicy, registerPolicy, } from './policies.js';
|
|
22
23
|
export { authMiddleware, clearRateLimitStore, rateLimitMiddleware, } from './middleware.js';
|
|
23
24
|
export type { AuthRateLimitConfig, AuthRateLimiter, AuthRateLimiterConfig } from './rate-limit.js';
|
|
24
25
|
export { authRateLimiter, clearAuthRateLimitStore, createAuthRateLimiter, stopAuthRateLimitCleanup, } from './rate-limit.js';
|
|
25
|
-
export type { AuthPluginOptions, AuthService } from './plugin.js';
|
|
26
|
-
export { authPlugin, defaultAuthPlugin, } from './plugin.js';
|
|
26
|
+
export type { AuthPluginOptions, AuthService, JwtAuthOptions } from './plugin.js';
|
|
27
|
+
export { authPlugin, defaultAuthPlugin, jwtAuth, } from './plugin.js';
|
|
27
28
|
export type { CsrfConfig, CsrfContext, CsrfCookieConfig, CsrfErrorCode, CsrfManager, CsrfMiddlewareOptions, CsrfTokenConfig, CsrfTokenData, CsrfTokenResult, CsrfValidationConfig, } from './csrf.js';
|
|
28
29
|
export { CsrfError, csrfManager, csrfMiddleware, } from './csrf.js';
|
|
29
30
|
export type { Session, SessionAuthContext, SessionConfig, SessionContext, SessionCookieConfig, SessionData, SessionExpirationConfig, SessionManager, SessionMiddlewareOptions, SessionStore, StoredSession, } from './session.js';
|
|
30
31
|
export { inMemorySessionStore, sessionManager, sessionMiddleware, } from './session.js';
|
|
31
|
-
export type {
|
|
32
|
+
export type { AdapterHttpMethod, AdapterMiddlewareContext, AdapterMiddlewareOptions, AdapterRoute, AdapterSession, AdapterSessionResult, AdapterUser, AuthAdapter, AuthAdapterConfig, AuthAdapterErrorCode, AuthAdapterPluginOptions, InferAdapterConfig, } from './adapter.js';
|
|
32
33
|
export { AuthAdapterError, BaseAuthAdapter, createAdapterAuthMiddleware, createAuthAdapterPlugin, defineAuthAdapter, isAuthAdapter, } from './adapter.js';
|
|
33
34
|
export type { BetterAuthAdapterConfig, BetterAuthApi, BetterAuthHandler, BetterAuthInstance, BetterAuthSession, BetterAuthSessionResult, BetterAuthUser, } from './adapters/better-auth.js';
|
|
34
35
|
export { BetterAuthAdapter, createBetterAuthAdapter } from './adapters/better-auth.js';
|
|
36
|
+
export type { JwtAdapterConfig } from './adapters/jwt-adapter.js';
|
|
37
|
+
export { createJwtAdapter, JwtAdapter } from './adapters/jwt-adapter.js';
|
|
35
38
|
export type { PasswordPolicyConfig, PasswordValidationResult, UserInfo, } from './password-policy.js';
|
|
36
39
|
export { checkPasswordBreach, checkPasswordStrength, isCommonPassword, PasswordPolicy, PasswordStrength, passwordPolicy, } from './password-policy.js';
|
|
37
40
|
/**
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,13 @@
|
|
|
12
12
|
// ============================================================================
|
|
13
13
|
export { AUTH_VERSION } from './plugin.js';
|
|
14
14
|
export { AuthError } from './types.js';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Decoration Utilities
|
|
17
|
+
// ============================================================================
|
|
18
|
+
export { AUTH_REGISTERED, checkDoubleRegistration, decorateAuth, getRequestAuth, getRequestUser, setRequestAuth, } from './decoration.js';
|
|
15
19
|
export { createInMemoryTokenStore, generateTokenId, isValidTimespan, JwtManager, jwtManager, parseTimeToSeconds, validateTokenExpiration, } from './jwt.js';
|
|
20
|
+
export { createEnhancedTokenStore, DEFAULT_ALLOWED_ROLES, parseUserRoles, } from './token-store.js';
|
|
21
|
+
export { authenticatedNarrow, hasRoleNarrow } from './guards-narrowing.js';
|
|
16
22
|
// ============================================================================
|
|
17
23
|
// Password Hashing
|
|
18
24
|
// ============================================================================
|
|
@@ -52,7 +58,7 @@ authRateLimiter,
|
|
|
52
58
|
clearAuthRateLimitStore,
|
|
53
59
|
// Factory
|
|
54
60
|
createAuthRateLimiter, stopAuthRateLimitCleanup, } from './rate-limit.js';
|
|
55
|
-
export { authPlugin, defaultAuthPlugin, } from './plugin.js';
|
|
61
|
+
export { authPlugin, defaultAuthPlugin, jwtAuth, } from './plugin.js';
|
|
56
62
|
export { CsrfError, csrfManager, csrfMiddleware, } from './csrf.js';
|
|
57
63
|
export { inMemorySessionStore, sessionManager, sessionMiddleware, } from './session.js';
|
|
58
64
|
export {
|
|
@@ -65,6 +71,7 @@ createAdapterAuthMiddleware, createAuthAdapterPlugin, defineAuthAdapter,
|
|
|
65
71
|
// Type guard
|
|
66
72
|
isAuthAdapter, } from './adapter.js';
|
|
67
73
|
export { BetterAuthAdapter, createBetterAuthAdapter } from './adapters/better-auth.js';
|
|
74
|
+
export { createJwtAdapter, JwtAdapter } from './adapters/jwt-adapter.js';
|
|
68
75
|
export { checkPasswordBreach, checkPasswordStrength, isCommonPassword, PasswordPolicy, PasswordStrength, passwordPolicy, } from './password-policy.js';
|
|
69
76
|
// ============================================================================
|
|
70
77
|
// Dependency Injection
|
package/dist/middleware.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { BaseContext } from '@veloxts/core';
|
|
6
6
|
import type { MiddlewareFunction } from '@veloxts/router';
|
|
7
7
|
import { JwtManager } from './jwt.js';
|
|
8
|
-
import type { AuthConfig,
|
|
8
|
+
import type { AuthConfig, AuthMiddlewareOptions, GuardDefinition, NativeAuthContext, User } from './types.js';
|
|
9
9
|
/**
|
|
10
10
|
* Creates an authentication middleware for procedures (succinct API)
|
|
11
11
|
*
|
|
@@ -46,15 +46,15 @@ import type { AuthConfig, AuthContext, AuthMiddlewareOptions, GuardDefinition, U
|
|
|
46
46
|
export declare function authMiddleware(config: AuthConfig): {
|
|
47
47
|
middleware: <TInput, TContext extends BaseContext, TOutput>(options?: AuthMiddlewareOptions) => MiddlewareFunction<TInput, TContext, TContext & {
|
|
48
48
|
user?: User;
|
|
49
|
-
auth:
|
|
49
|
+
auth: NativeAuthContext;
|
|
50
50
|
}, TOutput>;
|
|
51
51
|
requireAuth: <TInput, TContext extends BaseContext, TOutput>(guards?: Array<GuardDefinition | string>) => MiddlewareFunction<TInput, TContext, TContext & {
|
|
52
52
|
user: User;
|
|
53
|
-
auth:
|
|
53
|
+
auth: NativeAuthContext;
|
|
54
54
|
}, TOutput>;
|
|
55
55
|
optionalAuth: <TInput, TContext extends BaseContext, TOutput>() => MiddlewareFunction<TInput, TContext, TContext & {
|
|
56
56
|
user?: User;
|
|
57
|
-
auth:
|
|
57
|
+
auth: NativeAuthContext;
|
|
58
58
|
}, TOutput>;
|
|
59
59
|
jwt: JwtManager;
|
|
60
60
|
};
|
package/dist/middleware.js
CHANGED
|
@@ -61,8 +61,10 @@ export function authMiddleware(config) {
|
|
|
61
61
|
if (options.optional) {
|
|
62
62
|
// Optional auth - continue without user
|
|
63
63
|
const authContext = {
|
|
64
|
+
authMode: 'native',
|
|
64
65
|
user: undefined,
|
|
65
66
|
token: undefined,
|
|
67
|
+
payload: undefined,
|
|
66
68
|
isAuthenticated: false,
|
|
67
69
|
};
|
|
68
70
|
return next({
|
|
@@ -85,8 +87,10 @@ export function authMiddleware(config) {
|
|
|
85
87
|
if (options.optional) {
|
|
86
88
|
// Invalid token with optional auth - continue without user
|
|
87
89
|
const authContext = {
|
|
90
|
+
authMode: 'native',
|
|
88
91
|
user: undefined,
|
|
89
92
|
token: undefined,
|
|
93
|
+
payload: undefined,
|
|
90
94
|
isAuthenticated: false,
|
|
91
95
|
};
|
|
92
96
|
return next({
|
|
@@ -123,8 +127,10 @@ export function authMiddleware(config) {
|
|
|
123
127
|
}
|
|
124
128
|
// Create auth context
|
|
125
129
|
const authContext = {
|
|
130
|
+
authMode: 'native',
|
|
126
131
|
user: user ?? undefined,
|
|
127
|
-
token
|
|
132
|
+
token,
|
|
133
|
+
payload,
|
|
128
134
|
isAuthenticated: !!user,
|
|
129
135
|
};
|
|
130
136
|
// Build extended context
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* VeloxTS Auth Plugin
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Unified authentication using the adapter pattern internally.
|
|
5
|
+
* This plugin provides a convenient API while using JwtAdapter under the hood.
|
|
6
|
+
*
|
|
4
7
|
* @module auth/plugin
|
|
5
8
|
*/
|
|
6
9
|
import type { Container, VeloxPlugin } from '@veloxts/core';
|
|
10
|
+
import type { AuthAdapterPluginOptions } from './adapter.js';
|
|
11
|
+
import type { JwtAdapterConfig } from './adapters/jwt-adapter.js';
|
|
7
12
|
import { PasswordHasher } from './hash.js';
|
|
8
|
-
import { JwtManager } from './jwt.js';
|
|
13
|
+
import type { JwtManager, TokenStore } from './jwt.js';
|
|
9
14
|
import { authMiddleware } from './middleware.js';
|
|
10
|
-
import type {
|
|
15
|
+
import type { AdapterAuthContext, AuthConfig, TokenPair, User } from './types.js';
|
|
11
16
|
/** Auth package version */
|
|
12
17
|
export declare const AUTH_VERSION: string;
|
|
13
18
|
/**
|
|
@@ -59,6 +64,10 @@ export interface AuthService {
|
|
|
59
64
|
* Password hasher for secure password storage
|
|
60
65
|
*/
|
|
61
66
|
hasher: PasswordHasher;
|
|
67
|
+
/**
|
|
68
|
+
* Token store for revocation (if configured)
|
|
69
|
+
*/
|
|
70
|
+
tokenStore?: TokenStore;
|
|
62
71
|
/**
|
|
63
72
|
* Creates a token pair for a user
|
|
64
73
|
*/
|
|
@@ -66,7 +75,7 @@ export interface AuthService {
|
|
|
66
75
|
/**
|
|
67
76
|
* Verifies an access token and returns the auth context
|
|
68
77
|
*/
|
|
69
|
-
verifyToken(token: string):
|
|
78
|
+
verifyToken(token: string): AdapterAuthContext;
|
|
70
79
|
/**
|
|
71
80
|
* Refreshes tokens using a refresh token
|
|
72
81
|
*/
|
|
@@ -76,6 +85,7 @@ export interface AuthService {
|
|
|
76
85
|
*/
|
|
77
86
|
middleware: ReturnType<typeof authMiddleware>;
|
|
78
87
|
}
|
|
88
|
+
import type { AuthContext } from './types.js';
|
|
79
89
|
declare module 'fastify' {
|
|
80
90
|
interface FastifyInstance {
|
|
81
91
|
auth: AuthService;
|
|
@@ -86,7 +96,10 @@ declare module 'fastify' {
|
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
98
|
/**
|
|
89
|
-
* Creates the VeloxTS auth plugin
|
|
99
|
+
* Creates the VeloxTS auth plugin
|
|
100
|
+
*
|
|
101
|
+
* **Internally uses the JwtAdapter** for unified architecture.
|
|
102
|
+
* All authentication in VeloxTS uses the adapter pattern.
|
|
90
103
|
*
|
|
91
104
|
* This plugin provides:
|
|
92
105
|
* - JWT token management (access + refresh tokens)
|
|
@@ -142,3 +155,55 @@ export declare function authPlugin(options: AuthPluginOptions): VeloxPlugin<Auth
|
|
|
142
155
|
* ```
|
|
143
156
|
*/
|
|
144
157
|
export declare function defaultAuthPlugin(): VeloxPlugin<AuthPluginOptions>;
|
|
158
|
+
/**
|
|
159
|
+
* Options for jwtAuth convenience function
|
|
160
|
+
*
|
|
161
|
+
* Omits the 'name' field since it's auto-set to 'jwt'.
|
|
162
|
+
*/
|
|
163
|
+
export type JwtAuthOptions = Omit<JwtAdapterConfig, 'name'>;
|
|
164
|
+
/**
|
|
165
|
+
* Creates JWT auth using the adapter pattern directly
|
|
166
|
+
*
|
|
167
|
+
* This is an alternative to `authPlugin` that gives you more control over
|
|
168
|
+
* adapter-specific features like built-in routes and route prefixes.
|
|
169
|
+
*
|
|
170
|
+
* **Note:** Both `authPlugin` and `jwtAuth` now use the adapter pattern internally.
|
|
171
|
+
* Choose based on your needs:
|
|
172
|
+
*
|
|
173
|
+
* **Use `authPlugin` when:**
|
|
174
|
+
* - You need the `authMiddleware` factory for fine-grained procedure control
|
|
175
|
+
* - You're using DI container integration
|
|
176
|
+
* - You want the familiar VeloxTS auth API (`fastify.auth.createTokens()`, etc.)
|
|
177
|
+
*
|
|
178
|
+
* **Use `jwtAuth` when:**
|
|
179
|
+
* - You want built-in `/api/auth/refresh` and `/api/auth/logout` routes
|
|
180
|
+
* - You're building a pure adapter-based setup
|
|
181
|
+
* - You want direct access to adapter features
|
|
182
|
+
*
|
|
183
|
+
* @param options - JWT adapter configuration
|
|
184
|
+
* @returns VeloxPlugin ready for registration
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* import { jwtAuth } from '@veloxts/auth';
|
|
189
|
+
*
|
|
190
|
+
* // With built-in routes
|
|
191
|
+
* app.use(jwtAuth({
|
|
192
|
+
* jwt: {
|
|
193
|
+
* secret: process.env.JWT_SECRET!,
|
|
194
|
+
* accessTokenExpiry: '15m',
|
|
195
|
+
* refreshTokenExpiry: '7d',
|
|
196
|
+
* },
|
|
197
|
+
* userLoader: async (userId) => {
|
|
198
|
+
* return db.user.findUnique({ where: { id: userId } });
|
|
199
|
+
* },
|
|
200
|
+
* enableRoutes: true, // Mount /api/auth/refresh and /api/auth/logout
|
|
201
|
+
* routePrefix: '/api/auth',
|
|
202
|
+
* }));
|
|
203
|
+
*
|
|
204
|
+
* // Access JWT utilities via fastify
|
|
205
|
+
* const tokens = fastify.jwtManager!.createTokenPair(user);
|
|
206
|
+
* await fastify.tokenStore!.revoke(tokenId);
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
export declare function jwtAuth(options: JwtAuthOptions): VeloxPlugin<AuthAdapterPluginOptions<JwtAdapterConfig>>;
|
package/dist/plugin.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* VeloxTS Auth Plugin
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Unified authentication using the adapter pattern internally.
|
|
5
|
+
* This plugin provides a convenient API while using JwtAdapter under the hood.
|
|
6
|
+
*
|
|
4
7
|
* @module auth/plugin
|
|
5
8
|
*/
|
|
6
9
|
import { createRequire } from 'node:module';
|
|
10
|
+
import { createAuthAdapterPlugin } from './adapter.js';
|
|
11
|
+
import { createJwtAdapter } from './adapters/jwt-adapter.js';
|
|
12
|
+
import { checkDoubleRegistration, decorateAuth } from './decoration.js';
|
|
7
13
|
import { PasswordHasher } from './hash.js';
|
|
8
|
-
import { JwtManager } from './jwt.js';
|
|
9
14
|
import { authMiddleware } from './middleware.js';
|
|
10
15
|
import { registerAuthProviders } from './providers.js';
|
|
11
16
|
import { AUTH_SERVICE } from './tokens.js';
|
|
@@ -18,7 +23,26 @@ export const AUTH_VERSION = packageJson.version ?? '0.0.0-unknown';
|
|
|
18
23
|
// Auth Plugin
|
|
19
24
|
// ============================================================================
|
|
20
25
|
/**
|
|
21
|
-
*
|
|
26
|
+
* Wraps isTokenRevoked callback as a TokenStore interface
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
function createCallbackTokenStore(isTokenRevoked) {
|
|
30
|
+
return {
|
|
31
|
+
revoke: () => {
|
|
32
|
+
// No-op: callback-based stores don't support revocation
|
|
33
|
+
// Users must implement their own revocation mechanism
|
|
34
|
+
},
|
|
35
|
+
isRevoked: isTokenRevoked,
|
|
36
|
+
clear: () => {
|
|
37
|
+
// No-op
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates the VeloxTS auth plugin
|
|
43
|
+
*
|
|
44
|
+
* **Internally uses the JwtAdapter** for unified architecture.
|
|
45
|
+
* All authentication in VeloxTS uses the adapter pattern.
|
|
22
46
|
*
|
|
23
47
|
* This plugin provides:
|
|
24
48
|
* - JWT token management (access + refresh tokens)
|
|
@@ -60,43 +84,103 @@ export function authPlugin(options) {
|
|
|
60
84
|
return {
|
|
61
85
|
name: '@veloxts/auth',
|
|
62
86
|
version: AUTH_VERSION,
|
|
63
|
-
// No explicit dependencies - works with any Fastify instance
|
|
64
|
-
// The plugin decorates Fastify with auth functionality
|
|
65
87
|
async register(server, _opts) {
|
|
66
88
|
const config = { ...options, ..._opts };
|
|
67
89
|
const { debug = false, container } = config;
|
|
90
|
+
// Prevent double-registration of auth systems
|
|
91
|
+
checkDoubleRegistration(server, 'authPlugin');
|
|
68
92
|
if (debug) {
|
|
69
|
-
server.log.info('Registering @veloxts/auth plugin');
|
|
93
|
+
server.log.info('Registering @veloxts/auth plugin (adapter-based)');
|
|
70
94
|
}
|
|
71
|
-
|
|
95
|
+
// DI-enabled path: Use container for service resolution
|
|
72
96
|
if (container) {
|
|
73
|
-
// DI-enabled path: Register providers and resolve from container
|
|
74
97
|
if (debug) {
|
|
75
98
|
server.log.info('Using DI container for auth services');
|
|
76
99
|
}
|
|
77
100
|
registerAuthProviders(container, config);
|
|
78
|
-
authService = container.resolve(AUTH_SERVICE);
|
|
101
|
+
const authService = container.resolve(AUTH_SERVICE);
|
|
102
|
+
server.decorate('auth', authService);
|
|
103
|
+
// Still need to register the adapter plugin for session loading
|
|
104
|
+
const { adapter, config: adapterConfig } = createJwtAdapter({
|
|
105
|
+
jwt: config.jwt,
|
|
106
|
+
userLoader: config.userLoader,
|
|
107
|
+
tokenStore: config.isTokenRevoked
|
|
108
|
+
? createCallbackTokenStore(config.isTokenRevoked)
|
|
109
|
+
: undefined,
|
|
110
|
+
enableRoutes: false, // Don't mount routes when using authPlugin
|
|
111
|
+
debug,
|
|
112
|
+
});
|
|
113
|
+
// Initialize the adapter for session loading
|
|
114
|
+
await adapter.initialize(server, adapterConfig);
|
|
115
|
+
// Decorate requests with auth context
|
|
116
|
+
decorateAuth(server);
|
|
117
|
+
// Add preHandler hook for session loading
|
|
118
|
+
server.addHook('preHandler', async (request) => {
|
|
119
|
+
if (config.autoExtract === false)
|
|
120
|
+
return;
|
|
121
|
+
const session = await adapter.getSession(request);
|
|
122
|
+
if (session) {
|
|
123
|
+
const user = {
|
|
124
|
+
id: session.user.id,
|
|
125
|
+
email: session.user.email,
|
|
126
|
+
...(session.user.emailVerified !== undefined && {
|
|
127
|
+
emailVerified: session.user.emailVerified,
|
|
128
|
+
}),
|
|
129
|
+
...session.user.providerData,
|
|
130
|
+
};
|
|
131
|
+
const authContext = {
|
|
132
|
+
authMode: 'adapter',
|
|
133
|
+
isAuthenticated: true,
|
|
134
|
+
user,
|
|
135
|
+
providerId: 'jwt',
|
|
136
|
+
session: session.session.providerData,
|
|
137
|
+
};
|
|
138
|
+
request.auth = authContext;
|
|
139
|
+
request.user = user;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
79
142
|
}
|
|
80
143
|
else {
|
|
81
|
-
//
|
|
82
|
-
|
|
144
|
+
// Adapter-based path: Use JwtAdapter directly
|
|
145
|
+
// Convert isTokenRevoked callback to TokenStore if provided
|
|
146
|
+
const tokenStore = config.isTokenRevoked
|
|
147
|
+
? createCallbackTokenStore(config.isTokenRevoked)
|
|
148
|
+
: undefined;
|
|
149
|
+
// Create the JWT adapter
|
|
150
|
+
const { adapter, config: adapterConfig } = createJwtAdapter({
|
|
151
|
+
jwt: config.jwt,
|
|
152
|
+
userLoader: config.userLoader,
|
|
153
|
+
tokenStore,
|
|
154
|
+
enableRoutes: false, // authPlugin manages its own API
|
|
155
|
+
debug,
|
|
156
|
+
});
|
|
157
|
+
// Initialize adapter
|
|
158
|
+
await adapter.initialize(server, adapterConfig);
|
|
159
|
+
// Decorate requests with auth context
|
|
160
|
+
decorateAuth(server);
|
|
161
|
+
// Get JWT manager from adapter
|
|
162
|
+
const jwt = adapter.getJwtManager();
|
|
83
163
|
const hasher = new PasswordHasher(config.hash);
|
|
84
164
|
const authMw = authMiddleware(config);
|
|
85
|
-
|
|
165
|
+
// Build AuthService from adapter
|
|
166
|
+
const authService = {
|
|
86
167
|
jwt,
|
|
87
168
|
hasher,
|
|
169
|
+
tokenStore: adapter.getTokenStore(),
|
|
88
170
|
createTokens(user, additionalClaims) {
|
|
89
171
|
return jwt.createTokenPair(user, additionalClaims);
|
|
90
172
|
},
|
|
91
173
|
verifyToken(token) {
|
|
92
174
|
const payload = jwt.verifyToken(token);
|
|
93
175
|
return {
|
|
176
|
+
authMode: 'adapter',
|
|
94
177
|
user: {
|
|
95
178
|
id: payload.sub,
|
|
96
179
|
email: payload.email,
|
|
97
180
|
},
|
|
98
|
-
token: payload,
|
|
99
181
|
isAuthenticated: true,
|
|
182
|
+
providerId: 'jwt',
|
|
183
|
+
session: { token, payload },
|
|
100
184
|
};
|
|
101
185
|
},
|
|
102
186
|
refreshTokens(refreshToken) {
|
|
@@ -107,56 +191,33 @@ export function authPlugin(options) {
|
|
|
107
191
|
},
|
|
108
192
|
middleware: authMw,
|
|
109
193
|
};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (config.userLoader) {
|
|
135
|
-
user = await config.userLoader(payload.sub);
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
user = {
|
|
139
|
-
id: payload.sub,
|
|
140
|
-
email: payload.email,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
if (user) {
|
|
144
|
-
request.auth = {
|
|
145
|
-
user,
|
|
146
|
-
token: payload,
|
|
147
|
-
isAuthenticated: true,
|
|
148
|
-
};
|
|
149
|
-
request.user = user;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
// Invalid token - silently ignore (optional auth)
|
|
154
|
-
if (debug) {
|
|
155
|
-
server.log.debug('Invalid auth token in request');
|
|
156
|
-
}
|
|
194
|
+
// Decorate server with auth service
|
|
195
|
+
server.decorate('auth', authService);
|
|
196
|
+
// Add preHandler hook for session loading (using adapter)
|
|
197
|
+
if (config.autoExtract !== false) {
|
|
198
|
+
server.addHook('preHandler', async (request) => {
|
|
199
|
+
const session = await adapter.getSession(request);
|
|
200
|
+
if (session) {
|
|
201
|
+
const user = {
|
|
202
|
+
id: session.user.id,
|
|
203
|
+
email: session.user.email,
|
|
204
|
+
...(session.user.emailVerified !== undefined && {
|
|
205
|
+
emailVerified: session.user.emailVerified,
|
|
206
|
+
}),
|
|
207
|
+
...session.user.providerData,
|
|
208
|
+
};
|
|
209
|
+
const authContext = {
|
|
210
|
+
authMode: 'adapter',
|
|
211
|
+
isAuthenticated: true,
|
|
212
|
+
user,
|
|
213
|
+
providerId: 'jwt',
|
|
214
|
+
session: session.session.providerData,
|
|
215
|
+
};
|
|
216
|
+
request.auth = authContext;
|
|
217
|
+
request.user = user;
|
|
157
218
|
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
160
221
|
}
|
|
161
222
|
// Add shutdown hook for cleanup
|
|
162
223
|
server.addHook('onClose', async () => {
|
|
@@ -196,3 +257,52 @@ export function defaultAuthPlugin() {
|
|
|
196
257
|
jwt: { secret },
|
|
197
258
|
});
|
|
198
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Creates JWT auth using the adapter pattern directly
|
|
262
|
+
*
|
|
263
|
+
* This is an alternative to `authPlugin` that gives you more control over
|
|
264
|
+
* adapter-specific features like built-in routes and route prefixes.
|
|
265
|
+
*
|
|
266
|
+
* **Note:** Both `authPlugin` and `jwtAuth` now use the adapter pattern internally.
|
|
267
|
+
* Choose based on your needs:
|
|
268
|
+
*
|
|
269
|
+
* **Use `authPlugin` when:**
|
|
270
|
+
* - You need the `authMiddleware` factory for fine-grained procedure control
|
|
271
|
+
* - You're using DI container integration
|
|
272
|
+
* - You want the familiar VeloxTS auth API (`fastify.auth.createTokens()`, etc.)
|
|
273
|
+
*
|
|
274
|
+
* **Use `jwtAuth` when:**
|
|
275
|
+
* - You want built-in `/api/auth/refresh` and `/api/auth/logout` routes
|
|
276
|
+
* - You're building a pure adapter-based setup
|
|
277
|
+
* - You want direct access to adapter features
|
|
278
|
+
*
|
|
279
|
+
* @param options - JWT adapter configuration
|
|
280
|
+
* @returns VeloxPlugin ready for registration
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```typescript
|
|
284
|
+
* import { jwtAuth } from '@veloxts/auth';
|
|
285
|
+
*
|
|
286
|
+
* // With built-in routes
|
|
287
|
+
* app.use(jwtAuth({
|
|
288
|
+
* jwt: {
|
|
289
|
+
* secret: process.env.JWT_SECRET!,
|
|
290
|
+
* accessTokenExpiry: '15m',
|
|
291
|
+
* refreshTokenExpiry: '7d',
|
|
292
|
+
* },
|
|
293
|
+
* userLoader: async (userId) => {
|
|
294
|
+
* return db.user.findUnique({ where: { id: userId } });
|
|
295
|
+
* },
|
|
296
|
+
* enableRoutes: true, // Mount /api/auth/refresh and /api/auth/logout
|
|
297
|
+
* routePrefix: '/api/auth',
|
|
298
|
+
* }));
|
|
299
|
+
*
|
|
300
|
+
* // Access JWT utilities via fastify
|
|
301
|
+
* const tokens = fastify.jwtManager!.createTokenPair(user);
|
|
302
|
+
* await fastify.tokenStore!.revoke(tokenId);
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
export function jwtAuth(options) {
|
|
306
|
+
const { adapter, config } = createJwtAdapter(options);
|
|
307
|
+
return createAuthAdapterPlugin({ adapter, config });
|
|
308
|
+
}
|
package/dist/providers.js
CHANGED
|
@@ -109,12 +109,14 @@ export function authServiceProvider() {
|
|
|
109
109
|
verifyToken(token) {
|
|
110
110
|
const payload = jwt.verifyToken(token);
|
|
111
111
|
return {
|
|
112
|
+
authMode: 'adapter',
|
|
112
113
|
user: {
|
|
113
114
|
id: payload.sub,
|
|
114
115
|
email: payload.email,
|
|
115
116
|
},
|
|
116
|
-
token: payload,
|
|
117
117
|
isAuthenticated: true,
|
|
118
|
+
providerId: 'jwt',
|
|
119
|
+
session: { token, payload },
|
|
118
120
|
};
|
|
119
121
|
},
|
|
120
122
|
refreshTokens(refreshToken) {
|