@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.
@@ -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 { AdapterAuthContext, AdapterHttpMethod, AdapterMiddlewareOptions, AdapterRoute, AdapterSession, AdapterSessionResult, AdapterUser, AuthAdapter, AuthAdapterConfig, AuthAdapterErrorCode, AuthAdapterPluginOptions, InferAdapterConfig, } from './adapter.js';
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
@@ -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, AuthContext, AuthMiddlewareOptions, GuardDefinition, User } from './types.js';
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: AuthContext;
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: AuthContext;
53
+ auth: NativeAuthContext;
54
54
  }, TOutput>;
55
55
  optionalAuth: <TInput, TContext extends BaseContext, TOutput>() => MiddlewareFunction<TInput, TContext, TContext & {
56
56
  user?: User;
57
- auth: AuthContext;
57
+ auth: NativeAuthContext;
58
58
  }, TOutput>;
59
59
  jwt: JwtManager;
60
60
  };
@@ -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: payload,
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
- * Fastify plugin that integrates authentication with VeloxApp
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 { AuthConfig, AuthContext, TokenPair, User } from './types.js';
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): AuthContext;
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 (succinct API)
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
- * Fastify plugin that integrates authentication with VeloxApp
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
- * Creates the VeloxTS auth plugin (succinct API)
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
- let authService;
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
- // Legacy path: Direct instantiation (backward compatible)
82
- const jwt = new JwtManager(config.jwt);
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
- authService = {
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
- // Decorate server with auth service
112
- server.decorate('auth', authService);
113
- // Decorate requests with auth context (undefined initial value)
114
- server.decorateRequest('auth', undefined);
115
- server.decorateRequest('user', undefined);
116
- // Add preHandler hook to extract auth from headers (optional)
117
- if (config.autoExtract !== false) {
118
- server.addHook('preHandler', async (request) => {
119
- const authHeader = request.headers.authorization;
120
- const token = authService.jwt.extractFromHeader(authHeader);
121
- if (token) {
122
- try {
123
- const payload = authService.jwt.verifyToken(token);
124
- // Check if token is revoked
125
- if (config.isTokenRevoked && payload.jti) {
126
- const revoked = await config.isTokenRevoked(payload.jti);
127
- if (revoked) {
128
- // Token revoked - don't set auth context
129
- return;
130
- }
131
- }
132
- // Load user if loader provided
133
- let user = null;
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) {