@veloxts/auth 0.3.4 → 0.3.5

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