@veloxts/auth 0.3.4 → 0.3.6

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.
Files changed (52) hide show
  1. package/README.md +425 -22
  2. package/dist/__integration__/fixtures.d.ts +41 -0
  3. package/dist/__integration__/fixtures.d.ts.map +1 -0
  4. package/dist/__integration__/fixtures.js +79 -0
  5. package/dist/__integration__/fixtures.js.map +1 -0
  6. package/dist/__integration__/setup.d.ts +26 -0
  7. package/dist/__integration__/setup.d.ts.map +1 -0
  8. package/dist/__integration__/setup.js +28 -0
  9. package/dist/__integration__/setup.js.map +1 -0
  10. package/dist/csrf.d.ts +9 -3
  11. package/dist/csrf.d.ts.map +1 -1
  12. package/dist/csrf.js +9 -3
  13. package/dist/csrf.js.map +1 -1
  14. package/dist/guards.d.ts +12 -9
  15. package/dist/guards.d.ts.map +1 -1
  16. package/dist/guards.js +17 -5
  17. package/dist/guards.js.map +1 -1
  18. package/dist/hash.d.ts +7 -1
  19. package/dist/hash.d.ts.map +1 -1
  20. package/dist/hash.js +20 -4
  21. package/dist/hash.js.map +1 -1
  22. package/dist/index.d.ts +10 -6
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +43 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/jwt.d.ts +34 -5
  27. package/dist/jwt.d.ts.map +1 -1
  28. package/dist/jwt.js +154 -28
  29. package/dist/jwt.js.map +1 -1
  30. package/dist/middleware.d.ts +18 -6
  31. package/dist/middleware.d.ts.map +1 -1
  32. package/dist/middleware.js +23 -11
  33. package/dist/middleware.js.map +1 -1
  34. package/dist/password-policy.d.ts +259 -0
  35. package/dist/password-policy.d.ts.map +1 -0
  36. package/dist/password-policy.js +529 -0
  37. package/dist/password-policy.js.map +1 -0
  38. package/dist/plugin.d.ts +25 -7
  39. package/dist/plugin.d.ts.map +1 -1
  40. package/dist/plugin.js +28 -9
  41. package/dist/plugin.js.map +1 -1
  42. package/dist/rate-limit.d.ts +231 -0
  43. package/dist/rate-limit.d.ts.map +1 -0
  44. package/dist/rate-limit.js +352 -0
  45. package/dist/rate-limit.js.map +1 -0
  46. package/dist/session.d.ts +9 -3
  47. package/dist/session.d.ts.map +1 -1
  48. package/dist/session.js +9 -3
  49. package/dist/session.js.map +1 -1
  50. package/dist/types.d.ts +11 -1
  51. package/dist/types.d.ts.map +1 -1
  52. package/package.json +30 -7
package/README.md CHANGED
@@ -9,10 +9,11 @@ Authentication and authorization system for VeloxTS Framework.
9
9
  - **Pluggable Auth Adapters** - Integrate external providers like BetterAuth, Clerk, Auth0
10
10
  - **Session Management** - Cookie-based sessions with pluggable storage backends
11
11
  - **JWT Authentication** - Stateless token-based authentication with refresh tokens
12
- - **Password Hashing** - Secure bcrypt hashing with configurable cost factors
13
- - **CSRF Protection** - Signed double-submit cookie pattern
12
+ - **Password Hashing** - Secure bcrypt/argon2 hashing with configurable cost factors
13
+ - **Password Policy** - Configurable strength requirements and breach detection
14
+ - **CSRF Protection** - Signed double-submit cookie pattern with timing-safe validation
14
15
  - **Guards and Policies** - Declarative authorization for procedures
15
- - **Rate Limiting** - Built-in request rate limiting middleware
16
+ - **Rate Limiting** - Auth-specific rate limiting with progressive backoff and lockout detection
16
17
 
17
18
  ## Table of Contents
18
19
 
@@ -23,6 +24,12 @@ Authentication and authorization system for VeloxTS Framework.
23
24
  - [JWT Authentication](#jwt-authentication)
24
25
  - [CSRF Protection](#csrf-protection)
25
26
  - [Guards and Policies](#guards-and-policies)
27
+ - [User Roles and Permissions](#user-roles-and-permissions)
28
+ - [Role-Based Guards](#role-based-guards)
29
+ - [Permission-Based Guards](#permission-based-guards)
30
+ - [Combining Guards](#combining-guards)
31
+ - [Custom Guards](#custom-guards)
32
+ - [Policies](#policies)
26
33
  - [Password Hashing](#password-hashing)
27
34
  - [Rate Limiting](#rate-limiting)
28
35
 
@@ -55,7 +62,7 @@ npm install better-auth @veloxts/auth
55
62
  #### Basic Setup
56
63
 
57
64
  ```typescript
58
- import { createVeloxApp } from '@veloxts/core';
65
+ import { veloxApp } from '@veloxts/core';
59
66
  import { createAuthAdapterPlugin, createBetterAuthAdapter } from '@veloxts/auth';
60
67
  import { betterAuth } from 'better-auth';
61
68
  import { prismaAdapter } from 'better-auth/adapters/prisma';
@@ -82,14 +89,14 @@ const betterAuthAdapter = createBetterAuthAdapter({
82
89
  });
83
90
 
84
91
  // Create the plugin
85
- const authPlugin = createAuthAdapterPlugin({
92
+ const authAdapterPlugin = createAuthAdapterPlugin({
86
93
  adapter: betterAuthAdapter,
87
94
  config: betterAuthAdapter.config,
88
95
  });
89
96
 
90
97
  // Create app and register plugin
91
- const app = createVeloxApp();
92
- await app.register(authPlugin);
98
+ const app = await veloxApp();
99
+ await app.register(authAdapterPlugin);
93
100
  ```
94
101
 
95
102
  #### Using Authentication in Procedures
@@ -342,11 +349,11 @@ Cookie-based session management with secure defaults and pluggable storage backe
342
349
  ### Quick Start
343
350
 
344
351
  ```typescript
345
- import { createSessionMiddleware, createInMemorySessionStore } from '@veloxts/auth';
352
+ import { sessionMiddleware, createInMemorySessionStore } from '@veloxts/auth';
346
353
  import { defineProcedures, procedure } from '@veloxts/router';
347
354
 
348
355
  // Create session middleware
349
- const session = createSessionMiddleware({
356
+ const session = sessionMiddleware({
350
357
  secret: process.env.SESSION_SECRET!, // Min 32 characters
351
358
  cookie: {
352
359
  secure: process.env.NODE_ENV === 'production',
@@ -420,7 +427,7 @@ const sessionManager = createSessionManager({
420
427
  ### Middleware Variants
421
428
 
422
429
  ```typescript
423
- const session = createSessionMiddleware(config);
430
+ const session = sessionMiddleware(config);
424
431
 
425
432
  // Basic session middleware - creates session for all requests
426
433
  const getPreferences = procedure
@@ -623,7 +630,7 @@ class RedisSessionStore implements SessionStore {
623
630
 
624
631
  // Use custom store
625
632
  const redisStore = new RedisSessionStore(redisClient);
626
- const session = createSessionMiddleware({
633
+ const session = sessionMiddleware({
627
634
  secret: process.env.SESSION_SECRET!,
628
635
  store: redisStore,
629
636
  });
@@ -706,16 +713,182 @@ The session implementation includes several security protections by default:
706
713
 
707
714
  ## JWT Authentication
708
715
 
709
- Coming soon in v1.1.0. For now, use session-based authentication.
716
+ Stateless token-based authentication using HMAC-SHA256 signed JWTs.
717
+
718
+ ### Quick Start
719
+
720
+ ```typescript
721
+ import { jwtManager, authMiddleware } from '@veloxts/auth';
722
+ import { defineProcedures, procedure } from '@veloxts/router';
723
+
724
+ // Create JWT manager
725
+ const jwt = jwtManager({
726
+ secret: process.env.JWT_SECRET!, // Min 64 characters
727
+ accessTokenExpiry: '15m',
728
+ refreshTokenExpiry: '7d',
729
+ issuer: 'my-app',
730
+ audience: 'my-app-users',
731
+ });
732
+
733
+ // Create auth middleware
734
+ const auth = authMiddleware({
735
+ jwt: {
736
+ secret: process.env.JWT_SECRET!,
737
+ accessTokenExpiry: '15m',
738
+ refreshTokenExpiry: '7d',
739
+ },
740
+ userLoader: async (userId) => {
741
+ return db.user.findUnique({ where: { id: userId } });
742
+ },
743
+ });
744
+
745
+ // Use in procedures
746
+ export const authProcedures = defineProcedures('auth', {
747
+ // Login - return tokens
748
+ login: procedure
749
+ .input(z.object({ email: z.string().email(), password: z.string() }))
750
+ .mutation(async ({ input }) => {
751
+ const user = await db.user.findUnique({ where: { email: input.email } });
752
+ if (!user || !await verifyPassword(input.password, user.passwordHash)) {
753
+ throw new AuthError('Invalid credentials', 401);
754
+ }
755
+ return jwt.createTokenPair(user);
756
+ }),
757
+
758
+ // Protected route
759
+ getProfile: procedure
760
+ .use(auth.requireAuth())
761
+ .query(async ({ ctx }) => {
762
+ return ctx.user; // Guaranteed to exist
763
+ }),
764
+
765
+ // Refresh tokens
766
+ refresh: procedure
767
+ .input(z.object({ refreshToken: z.string() }))
768
+ .mutation(async ({ input }) => {
769
+ return jwt.refreshTokens(input.refreshToken);
770
+ }),
771
+ });
772
+ ```
773
+
774
+ ### Configuration Options
775
+
776
+ ```typescript
777
+ import { jwtManager } from '@veloxts/auth';
778
+
779
+ const jwt = jwtManager({
780
+ // Required: Secret for signing tokens (min 64 chars)
781
+ // Generate with: openssl rand -base64 64
782
+ secret: process.env.JWT_SECRET!,
783
+
784
+ // Optional: Token expiration times
785
+ accessTokenExpiry: '15m', // Default: 15 minutes
786
+ refreshTokenExpiry: '7d', // Default: 7 days
787
+
788
+ // Optional: Token claims
789
+ issuer: 'my-app', // iss claim
790
+ audience: 'my-app-users', // aud claim
791
+ });
792
+ ```
793
+
794
+ ### Token Operations
795
+
796
+ ```typescript
797
+ // Create token pair for user
798
+ const tokens = jwt.createTokenPair(user);
799
+ // Returns: { accessToken, refreshToken, expiresIn, tokenType }
800
+
801
+ // Add custom claims (cannot override reserved claims)
802
+ const tokens = jwt.createTokenPair(user, {
803
+ role: 'admin',
804
+ permissions: ['read', 'write'],
805
+ });
806
+
807
+ // Verify access token
808
+ const payload = jwt.verifyToken(accessToken);
809
+ // Returns: { sub, email, iat, exp, type, jti, ... }
810
+
811
+ // Refresh tokens using refresh token
812
+ const newTokens = jwt.refreshTokens(refreshToken);
813
+
814
+ // Extract token from Authorization header
815
+ const token = jwt.extractFromHeader(request.headers.authorization);
816
+ ```
817
+
818
+ ### Token Revocation
819
+
820
+ For security-critical applications, implement token revocation:
821
+
822
+ ```typescript
823
+ import { createInMemoryTokenStore } from '@veloxts/auth';
824
+
825
+ // Development/testing (NOT for production!)
826
+ const tokenStore = createInMemoryTokenStore();
827
+
828
+ // Configure auth to check revocation
829
+ const auth = authMiddleware({
830
+ jwt: { secret: process.env.JWT_SECRET! },
831
+ isTokenRevoked: tokenStore.isRevoked,
832
+ });
833
+
834
+ // Revoke on logout
835
+ const logout = procedure
836
+ .use(auth.requireAuth())
837
+ .mutation(async ({ ctx }) => {
838
+ if (ctx.auth.token?.jti) {
839
+ tokenStore.revoke(ctx.auth.token.jti);
840
+ }
841
+ return { success: true };
842
+ });
843
+ ```
844
+
845
+ For production, use Redis or database-backed storage instead of the in-memory store.
846
+
847
+ ### Auth Middleware Options
848
+
849
+ ```typescript
850
+ const auth = authMiddleware(config);
851
+
852
+ // Require authentication (throws 401 if no valid token)
853
+ const getProfile = procedure
854
+ .use(auth.requireAuth())
855
+ .query(({ ctx }) => ctx.user);
856
+
857
+ // Optional authentication (user may be undefined)
858
+ const getPosts = procedure
859
+ .use(auth.optionalAuth())
860
+ .query(({ ctx }) => {
861
+ if (ctx.user) {
862
+ return getPrivatePosts(ctx.user.id);
863
+ }
864
+ return getPublicPosts();
865
+ });
866
+
867
+ // With guards (after authentication)
868
+ const adminOnly = procedure
869
+ .use(auth.middleware({ guards: [hasRole('admin')] }))
870
+ .query(({ ctx }) => getAdminData());
871
+ ```
872
+
873
+ ### Security Features
874
+
875
+ The JWT implementation includes several security protections:
876
+
877
+ - **HS256 algorithm enforcement** - Rejects `none`, RS256, and other algorithms to prevent confusion attacks
878
+ - **Timing-safe signature verification** - Prevents timing attacks on token validation
879
+ - **Secret entropy validation** - Requires at least 64 characters with 16+ unique characters
880
+ - **Reserved claim protection** - Prevents overriding `sub`, `exp`, `iat`, etc. via custom claims
881
+ - **Token expiration** - Access and refresh tokens have separate expiration times
882
+ - **Not-before claim support** - Optionally delay token validity
710
883
 
711
884
  ## CSRF Protection
712
885
 
713
886
  CSRF protection is already implemented using the signed double-submit cookie pattern with timing-safe comparison and entropy validation.
714
887
 
715
888
  ```typescript
716
- import { createCsrfMiddleware } from '@veloxts/auth';
889
+ import { csrfMiddleware } from '@veloxts/auth';
717
890
 
718
- const csrf = createCsrfMiddleware({
891
+ const csrf = csrfMiddleware({
719
892
  secret: process.env.CSRF_SECRET!,
720
893
  });
721
894
 
@@ -733,18 +906,129 @@ See the CSRF documentation for complete details on configuration and usage.
733
906
 
734
907
  Guards and policies provide declarative authorization for procedures.
735
908
 
909
+ ### User Roles and Permissions
910
+
911
+ Users can have multiple roles and permissions:
912
+
913
+ ```typescript
914
+ import type { User } from '@veloxts/auth';
915
+
916
+ // User with multiple roles
917
+ const user: User = {
918
+ id: '1',
919
+ email: 'admin@example.com',
920
+ roles: ['admin', 'editor'], // Multiple roles
921
+ permissions: ['posts.read', 'posts.write', 'users.manage'],
922
+ };
923
+ ```
924
+
925
+ ### Role-Based Guards
926
+
927
+ The `hasRole` guard checks if the user has ANY of the specified roles:
928
+
736
929
  ```typescript
737
- import { authenticated, hasRole, definePolicy } from '@veloxts/auth';
930
+ import { hasRole, hasPermission, allOf, anyOf } from '@veloxts/auth';
738
931
 
739
- // Use built-in guards
932
+ // Require a single role
740
933
  const adminOnly = procedure
741
- .use(session.requireAuth())
742
- .use(guard(hasRole('admin')))
934
+ .use(auth.middleware({ guards: [hasRole('admin')] }))
743
935
  .query(async ({ ctx }) => {
744
- // Only admins can access
936
+ // Only users with 'admin' role can access
745
937
  });
746
938
 
747
- // Define custom policies
939
+ // Require ANY of multiple roles (OR logic)
940
+ const staffAccess = procedure
941
+ .use(auth.middleware({ guards: [hasRole(['admin', 'moderator', 'editor'])] }))
942
+ .query(async ({ ctx }) => {
943
+ // Users with 'admin', 'moderator', OR 'editor' role can access
944
+ });
945
+
946
+ // User with roles: ['editor', 'reviewer'] passes hasRole(['admin', 'editor'])
947
+ // because they have the 'editor' role
948
+ ```
949
+
950
+ ### Permission-Based Guards
951
+
952
+ ```typescript
953
+ // Require ALL specified permissions (AND logic)
954
+ const canManagePosts = procedure
955
+ .use(auth.middleware({ guards: [hasPermission(['posts.read', 'posts.write'])] }))
956
+ .query(async ({ ctx }) => {
957
+ // User must have BOTH permissions
958
+ });
959
+
960
+ // Require ANY of the permissions (OR logic)
961
+ const canViewPosts = procedure
962
+ .use(auth.middleware({ guards: [hasAnyPermission(['posts.read', 'posts.admin'])] }))
963
+ .query(async ({ ctx }) => {
964
+ // User needs at least one of these permissions
965
+ });
966
+ ```
967
+
968
+ ### Combining Guards
969
+
970
+ ```typescript
971
+ // Require BOTH role AND permission (AND logic)
972
+ const adminWithPermission = procedure
973
+ .use(auth.middleware({
974
+ guards: [hasRole('admin'), hasPermission('users.delete')]
975
+ }))
976
+ .mutation(async ({ ctx }) => {
977
+ // Must be admin AND have users.delete permission
978
+ });
979
+
980
+ // Using allOf for explicit AND
981
+ const strictAccess = procedure
982
+ .use(auth.middleware({
983
+ guards: [allOf([hasRole('admin'), hasPermission('sensitive.access')])]
984
+ }))
985
+ .query(async ({ ctx }) => {
986
+ // Both conditions must pass
987
+ });
988
+
989
+ // Using anyOf for explicit OR
990
+ const flexibleAccess = procedure
991
+ .use(auth.middleware({
992
+ guards: [anyOf([hasRole('admin'), hasPermission('special.access')])]
993
+ }))
994
+ .query(async ({ ctx }) => {
995
+ // Either condition can pass
996
+ });
997
+ ```
998
+
999
+ ### Custom Guards
1000
+
1001
+ ```typescript
1002
+ import { guard, defineGuard } from '@veloxts/auth';
1003
+
1004
+ // Simple custom guard
1005
+ const isVerifiedEmail = guard('isVerifiedEmail', (ctx) => {
1006
+ return ctx.user?.emailVerified === true;
1007
+ });
1008
+
1009
+ // Guard with configuration
1010
+ const isPremiumUser = defineGuard({
1011
+ name: 'isPremiumUser',
1012
+ check: (ctx) => ctx.user?.subscription === 'premium',
1013
+ message: 'Premium subscription required',
1014
+ statusCode: 403,
1015
+ });
1016
+
1017
+ // Use in procedures
1018
+ const premiumContent = procedure
1019
+ .use(auth.middleware({ guards: [isPremiumUser] }))
1020
+ .query(async ({ ctx }) => {
1021
+ return getPremiumContent();
1022
+ });
1023
+ ```
1024
+
1025
+ ### Policies
1026
+
1027
+ Define resource-specific authorization logic:
1028
+
1029
+ ```typescript
1030
+ import { definePolicy } from '@veloxts/auth';
1031
+
748
1032
  const postPolicy = definePolicy<{ postId: string }>('post', {
749
1033
  view: async (user, { postId }) => {
750
1034
  // Anyone can view public posts
@@ -752,7 +1036,13 @@ const postPolicy = definePolicy<{ postId: string }>('post', {
752
1036
  },
753
1037
  edit: async (user, { postId }) => {
754
1038
  const post = await db.post.findUnique({ where: { id: postId } });
755
- return post?.authorId === user.id;
1039
+ // Only author or admin can edit
1040
+ return post?.authorId === user.id || user.roles?.includes('admin');
1041
+ },
1042
+ delete: async (user, { postId }) => {
1043
+ const post = await db.post.findUnique({ where: { id: postId } });
1044
+ // Only admin can delete
1045
+ return user.roles?.includes('admin') ?? false;
756
1046
  },
757
1047
  });
758
1048
  ```
@@ -773,7 +1063,120 @@ const valid = await verifyPassword('user-password', hash);
773
1063
 
774
1064
  ## Rate Limiting
775
1065
 
776
- Coming soon.
1066
+ Protect your endpoints from abuse with request rate limiting.
1067
+
1068
+ ### Quick Start
1069
+
1070
+ ```typescript
1071
+ import { rateLimitMiddleware } from '@veloxts/auth';
1072
+ import { defineProcedures, procedure } from '@veloxts/router';
1073
+
1074
+ // Create rate limit middleware
1075
+ const rateLimit = rateLimitMiddleware({
1076
+ max: 100, // Maximum requests per window
1077
+ windowMs: 60000, // Window size in milliseconds (1 minute)
1078
+ });
1079
+
1080
+ // Stricter limit for auth endpoints
1081
+ const authRateLimit = rateLimitMiddleware({
1082
+ max: 5,
1083
+ windowMs: 60000,
1084
+ message: 'Too many login attempts, please try again later',
1085
+ });
1086
+
1087
+ export const authProcedures = defineProcedures('auth', {
1088
+ // Protected with stricter rate limit
1089
+ login: procedure
1090
+ .use(authRateLimit)
1091
+ .input(LoginSchema)
1092
+ .mutation(async ({ input }) => {
1093
+ // Login logic
1094
+ }),
1095
+
1096
+ // Normal rate limit
1097
+ getProfile: procedure
1098
+ .use(rateLimit)
1099
+ .use(auth.requireAuth())
1100
+ .query(({ ctx }) => ctx.user),
1101
+ });
1102
+ ```
1103
+
1104
+ ### Configuration Options
1105
+
1106
+ ```typescript
1107
+ const rateLimit = rateLimitMiddleware({
1108
+ // Maximum requests allowed in window
1109
+ max: 100, // Default: 100
1110
+
1111
+ // Time window in milliseconds
1112
+ windowMs: 60000, // Default: 60000 (1 minute)
1113
+
1114
+ // Custom key generator (default: request IP)
1115
+ keyGenerator: (ctx) => {
1116
+ // Rate limit by user ID if authenticated
1117
+ return ctx.user?.id ?? ctx.request.ip ?? 'anonymous';
1118
+ },
1119
+
1120
+ // Custom error message
1121
+ message: 'Rate limit exceeded',
1122
+ });
1123
+ ```
1124
+
1125
+ ### Response Headers
1126
+
1127
+ Rate limit info is included in response headers:
1128
+
1129
+ ```
1130
+ X-RateLimit-Limit: 100 # Max requests allowed
1131
+ X-RateLimit-Remaining: 95 # Remaining requests in window
1132
+ X-RateLimit-Reset: 1234567890 # Unix timestamp when window resets
1133
+ ```
1134
+
1135
+ ### Production Considerations
1136
+
1137
+ The built-in rate limiter uses in-memory storage, which:
1138
+ - Does **not** persist across server restarts
1139
+ - Does **not** work across multiple server instances
1140
+
1141
+ For production, implement a custom middleware using Redis:
1142
+
1143
+ ```typescript
1144
+ import { Redis } from 'ioredis';
1145
+ import type { MiddlewareFunction } from '@veloxts/router';
1146
+ import { AuthError } from '@veloxts/auth';
1147
+
1148
+ const redis = new Redis();
1149
+
1150
+ function redisRateLimitMiddleware(options: {
1151
+ max: number;
1152
+ windowMs: number;
1153
+ prefix?: string;
1154
+ }): MiddlewareFunction {
1155
+ const { max, windowMs, prefix = 'ratelimit:' } = options;
1156
+
1157
+ return async ({ ctx, next }) => {
1158
+ const key = `${prefix}${ctx.request.ip}`;
1159
+ const current = await redis.incr(key);
1160
+
1161
+ if (current === 1) {
1162
+ await redis.pexpire(key, windowMs);
1163
+ }
1164
+
1165
+ const ttl = await redis.pttl(key);
1166
+ const remaining = Math.max(0, max - current);
1167
+
1168
+ ctx.reply.header('X-RateLimit-Limit', String(max));
1169
+ ctx.reply.header('X-RateLimit-Remaining', String(remaining));
1170
+ ctx.reply.header('X-RateLimit-Reset', String(Math.ceil((Date.now() + ttl) / 1000)));
1171
+
1172
+ if (current > max) {
1173
+ throw new AuthError('Too many requests', 429, 'RATE_LIMIT_EXCEEDED');
1174
+ }
1175
+
1176
+ return next();
1177
+ };
1178
+ }
1179
+ ```
777
1180
 
778
1181
  ## Related Packages
779
1182
 
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Integration test fixtures and helpers
3
+ * @module __integration__/fixtures
4
+ */
5
+ import { TEST_SECRETS } from '@veloxts/testing';
6
+ import type { User } from '../types.js';
7
+ export { TEST_SECRETS };
8
+ export declare const TEST_USERS: Record<string, User>;
9
+ /**
10
+ * Mock user loader for tests
11
+ */
12
+ export declare function testUserLoader(userId: string): Promise<User | null>;
13
+ /**
14
+ * Auth config with user loader (for tests that use TEST_USERS)
15
+ */
16
+ export declare function createTestAuthConfig(): {
17
+ jwt: {
18
+ secret: "test-access-secret-key-for-integration-tests-must-be-64-characters-long-at-minimum-for-hmac";
19
+ refreshSecret: "test-refresh-secret-key-for-integration-tests-must-be-64-characters-long-at-minimum-for-hmac";
20
+ accessTokenExpiry: string;
21
+ refreshTokenExpiry: string;
22
+ issuer: string;
23
+ audience: string;
24
+ };
25
+ userLoader: typeof testUserLoader;
26
+ };
27
+ /**
28
+ * Auth config without user loader (for tests with ad-hoc users)
29
+ * User info comes directly from token claims
30
+ */
31
+ export declare function createTestAuthConfigNoLoader(): {
32
+ jwt: {
33
+ secret: "test-access-secret-key-for-integration-tests-must-be-64-characters-long-at-minimum-for-hmac";
34
+ refreshSecret: "test-refresh-secret-key-for-integration-tests-must-be-64-characters-long-at-minimum-for-hmac";
35
+ accessTokenExpiry: string;
36
+ refreshTokenExpiry: string;
37
+ issuer: string;
38
+ audience: string;
39
+ };
40
+ };
41
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/__integration__/fixtures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC,OAAO,EAAE,YAAY,EAAE,CAAC;AAMxB,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAqB3C,CAAC;AAMF;;GAEG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAGzE;AAMD;;GAEG;AACH,wBAAgB,oBAAoB;;;;;;;;;;EAYnC;AAED;;;GAGG;AACH,wBAAgB,4BAA4B;;;;;;;;;EAY3C"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Integration test fixtures and helpers
3
+ * @module __integration__/fixtures
4
+ */
5
+ import { TEST_SECRETS } from '@veloxts/testing';
6
+ // Re-export shared test secrets
7
+ export { TEST_SECRETS };
8
+ // ============================================================================
9
+ // Test Users
10
+ // ============================================================================
11
+ export const TEST_USERS = {
12
+ admin: {
13
+ id: 'user-admin-123',
14
+ email: 'admin@example.com',
15
+ roles: ['admin'],
16
+ },
17
+ user: {
18
+ id: 'user-regular-456',
19
+ email: 'user@example.com',
20
+ roles: ['user'],
21
+ },
22
+ guest: {
23
+ id: 'user-guest-789',
24
+ email: 'guest@example.com',
25
+ roles: [], // Empty roles - tests guard failure
26
+ },
27
+ multiRole: {
28
+ id: 'user-multi-101',
29
+ email: 'multi@example.com',
30
+ roles: ['user', 'editor', 'moderator'],
31
+ },
32
+ };
33
+ // ============================================================================
34
+ // User Loader
35
+ // ============================================================================
36
+ /**
37
+ * Mock user loader for tests
38
+ */
39
+ export async function testUserLoader(userId) {
40
+ const user = Object.values(TEST_USERS).find((u) => u.id === userId);
41
+ return user ?? null;
42
+ }
43
+ // ============================================================================
44
+ // Auth Config Factory
45
+ // ============================================================================
46
+ /**
47
+ * Auth config with user loader (for tests that use TEST_USERS)
48
+ */
49
+ export function createTestAuthConfig() {
50
+ return {
51
+ jwt: {
52
+ secret: TEST_SECRETS.access,
53
+ refreshSecret: TEST_SECRETS.refresh,
54
+ accessTokenExpiry: '15m',
55
+ refreshTokenExpiry: '7d',
56
+ issuer: 'velox-test',
57
+ audience: 'velox-test-app',
58
+ },
59
+ userLoader: testUserLoader,
60
+ };
61
+ }
62
+ /**
63
+ * Auth config without user loader (for tests with ad-hoc users)
64
+ * User info comes directly from token claims
65
+ */
66
+ export function createTestAuthConfigNoLoader() {
67
+ return {
68
+ jwt: {
69
+ secret: TEST_SECRETS.access,
70
+ refreshSecret: TEST_SECRETS.refresh,
71
+ accessTokenExpiry: '15m',
72
+ refreshTokenExpiry: '7d',
73
+ issuer: 'velox-test',
74
+ audience: 'velox-test-app',
75
+ },
76
+ // No userLoader - user info comes from token
77
+ };
78
+ }
79
+ //# sourceMappingURL=fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../src/__integration__/fixtures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIhD,gCAAgC;AAChC,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E,MAAM,CAAC,MAAM,UAAU,GAAyB;IAC9C,KAAK,EAAE;QACL,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,CAAC,OAAO,CAAC;KACjB;IACD,IAAI,EAAE;QACJ,EAAE,EAAE,kBAAkB;QACtB,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,CAAC,MAAM,CAAC;KAChB;IACD,KAAK,EAAE;QACL,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,EAAE,EAAE,oCAAoC;KAChD;IACD,SAAS,EAAE;QACT,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,CAAC;KACvC;CACF,CAAC;AAEF,+EAA+E;AAC/E,cAAc;AACd,+EAA+E;AAE/E;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACpE,OAAO,IAAI,IAAI,IAAI,CAAC;AACtB,CAAC;AAED,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO;QACL,GAAG,EAAE;YACH,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,aAAa,EAAE,YAAY,CAAC,OAAO;YACnC,iBAAiB,EAAE,KAAK;YACxB,kBAAkB,EAAE,IAAI;YACxB,MAAM,EAAE,YAAY;YACpB,QAAQ,EAAE,gBAAgB;SAC3B;QACD,UAAU,EAAE,cAAc;KAC3B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B;IAC1C,OAAO;QACL,GAAG,EAAE;YACH,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,aAAa,EAAE,YAAY,CAAC,OAAO;YACnC,iBAAiB,EAAE,KAAK;YACxB,kBAAkB,EAAE,IAAI;YACxB,MAAM,EAAE,YAAY;YACpB,QAAQ,EAAE,gBAAgB;SAC3B;QACD,6CAA6C;KAC9C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Integration test setup utilities
3
+ * @module __integration__/setup
4
+ */
5
+ import type { FastifyInstance } from 'fastify';
6
+ import { type AuthPluginOptions } from '../plugin.js';
7
+ export { authHeader } from '@veloxts/testing';
8
+ /**
9
+ * Options for creating a test server
10
+ */
11
+ export interface TestServerOptions {
12
+ /** Auth plugin options (defaults to test config) */
13
+ authOptions?: AuthPluginOptions;
14
+ /** Skip auth plugin registration */
15
+ skipAuth?: boolean;
16
+ }
17
+ /**
18
+ * Creates a Fastify server configured for integration testing
19
+ *
20
+ * This sets up:
21
+ * - Request context decoration (mimics VeloxApp behavior)
22
+ * - Auth plugin with test configuration
23
+ * - Logging disabled for cleaner test output
24
+ */
25
+ export declare function createTestServer(options?: TestServerOptions): Promise<FastifyInstance>;
26
+ //# sourceMappingURL=setup.d.ts.map