@veloxts/auth 0.3.3 → 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 (66) hide show
  1. package/README.md +1157 -30
  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/adapter.d.ts +710 -0
  11. package/dist/adapter.d.ts.map +1 -0
  12. package/dist/adapter.js +581 -0
  13. package/dist/adapter.js.map +1 -0
  14. package/dist/adapters/better-auth.d.ts +271 -0
  15. package/dist/adapters/better-auth.d.ts.map +1 -0
  16. package/dist/adapters/better-auth.js +341 -0
  17. package/dist/adapters/better-auth.js.map +1 -0
  18. package/dist/adapters/index.d.ts +28 -0
  19. package/dist/adapters/index.d.ts.map +1 -0
  20. package/dist/adapters/index.js +28 -0
  21. package/dist/adapters/index.js.map +1 -0
  22. package/dist/csrf.d.ts +300 -0
  23. package/dist/csrf.d.ts.map +1 -0
  24. package/dist/csrf.js +402 -0
  25. package/dist/csrf.js.map +1 -0
  26. package/dist/guards.d.ts +142 -0
  27. package/dist/guards.d.ts.map +1 -0
  28. package/dist/guards.js +259 -0
  29. package/dist/guards.js.map +1 -0
  30. package/dist/hash.d.ts +91 -0
  31. package/dist/hash.d.ts.map +1 -0
  32. package/dist/hash.js +236 -0
  33. package/dist/hash.js.map +1 -0
  34. package/dist/index.d.ts +27 -32
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +94 -36
  37. package/dist/index.js.map +1 -1
  38. package/dist/jwt.d.ts +157 -0
  39. package/dist/jwt.d.ts.map +1 -0
  40. package/dist/jwt.js +489 -0
  41. package/dist/jwt.js.map +1 -0
  42. package/dist/middleware.d.ts +99 -0
  43. package/dist/middleware.d.ts.map +1 -0
  44. package/dist/middleware.js +253 -0
  45. package/dist/middleware.js.map +1 -0
  46. package/dist/plugin.d.ts +125 -0
  47. package/dist/plugin.d.ts.map +1 -0
  48. package/dist/plugin.js +193 -0
  49. package/dist/plugin.js.map +1 -0
  50. package/dist/policies.d.ts +137 -0
  51. package/dist/policies.d.ts.map +1 -0
  52. package/dist/policies.js +240 -0
  53. package/dist/policies.js.map +1 -0
  54. package/dist/rate-limit.d.ts +231 -0
  55. package/dist/rate-limit.d.ts.map +1 -0
  56. package/dist/rate-limit.js +352 -0
  57. package/dist/rate-limit.js.map +1 -0
  58. package/dist/session.d.ts +500 -0
  59. package/dist/session.d.ts.map +1 -0
  60. package/dist/session.js +801 -0
  61. package/dist/session.js.map +1 -0
  62. package/dist/types.d.ts +261 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +33 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +61 -7
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Resource-level authorization policies for @veloxts/auth
3
+ * @module auth/policies
4
+ */
5
+ import type { PolicyAction, PolicyDefinition, User } from './types.js';
6
+ /**
7
+ * Registers a policy for a resource type
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * registerPolicy('Post', {
12
+ * view: (user, post) => true, // Anyone can view
13
+ * update: (user, post) => user.id === post.authorId,
14
+ * delete: (user, post) => user.id === post.authorId || user.role === 'admin',
15
+ * });
16
+ * ```
17
+ */
18
+ export declare function registerPolicy<TUser = User, TResource = unknown>(resourceName: string, policy: PolicyDefinition<TUser, TResource>): void;
19
+ /**
20
+ * Gets a registered policy by resource name
21
+ */
22
+ export declare function getPolicy(resourceName: string): PolicyDefinition | undefined;
23
+ /**
24
+ * Clears all registered policies (useful for testing)
25
+ */
26
+ export declare function clearPolicies(): void;
27
+ /**
28
+ * Defines a policy for a resource type
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const PostPolicy = definePolicy<User, Post>({
33
+ * view: () => true,
34
+ * create: (user) => user.emailVerified,
35
+ * update: (user, post) => user.id === post.authorId,
36
+ * delete: (user, post) => user.id === post.authorId || user.role === 'admin',
37
+ * publish: (user, post) => user.role === 'editor' && user.id === post.authorId,
38
+ * });
39
+ *
40
+ * // Register it
41
+ * registerPolicy('Post', PostPolicy);
42
+ * ```
43
+ */
44
+ export declare function definePolicy<TUser = User, TResource = unknown>(actions: PolicyDefinition<TUser, TResource>): PolicyDefinition<TUser, TResource>;
45
+ /**
46
+ * Checks if a user can perform an action on a resource
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const canEdit = await can(user, 'update', 'Post', post);
51
+ * if (!canEdit) {
52
+ * throw new ForbiddenError('Cannot edit this post');
53
+ * }
54
+ * ```
55
+ */
56
+ export declare function can<TResource = unknown>(user: User | null | undefined, action: string, resourceName: string, resource?: TResource): Promise<boolean>;
57
+ /**
58
+ * Checks if a user cannot perform an action (inverse of can)
59
+ */
60
+ export declare function cannot<TResource = unknown>(user: User | null | undefined, action: string, resourceName: string, resource?: TResource): Promise<boolean>;
61
+ /**
62
+ * Throws an error if user cannot perform action
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * await authorize(ctx.user, 'delete', 'Post', post);
67
+ * // If we get here, user is authorized
68
+ * await db.post.delete({ where: { id: post.id } });
69
+ * ```
70
+ */
71
+ export declare function authorize<TResource = unknown>(user: User | null | undefined, action: string, resourceName: string, resource?: TResource): Promise<void>;
72
+ /**
73
+ * Fluent policy builder for complex policies
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const CommentPolicy = createPolicyBuilder<User, Comment>()
78
+ * .allow('view', () => true)
79
+ * .allow('create', (user) => user.emailVerified)
80
+ * .allow('update', (user, comment) => user.id === comment.authorId)
81
+ * .allow('delete', (user, comment) =>
82
+ * user.id === comment.authorId || user.role === 'admin'
83
+ * )
84
+ * .build();
85
+ * ```
86
+ */
87
+ export declare function createPolicyBuilder<TUser = User, TResource = unknown>(): PolicyBuilder<TUser, TResource>;
88
+ declare class PolicyBuilder<TUser = User, TResource = unknown> {
89
+ private actions;
90
+ /**
91
+ * Allows an action based on the check function
92
+ */
93
+ allow(action: string, check: PolicyAction<TUser, TResource>): this;
94
+ /**
95
+ * Denies an action (always returns false)
96
+ */
97
+ deny(action: string): this;
98
+ /**
99
+ * Allows action only for resource owner
100
+ * Assumes resource has a userId or authorId field
101
+ */
102
+ allowOwner(action: string, ownerField?: keyof TResource): this;
103
+ /**
104
+ * Allows action for owner OR users with specific role
105
+ */
106
+ allowOwnerOr(action: string, roles: string[], ownerField?: keyof TResource): this;
107
+ /**
108
+ * Builds the final policy definition
109
+ */
110
+ build(): PolicyDefinition<TUser, TResource>;
111
+ /**
112
+ * Builds and registers the policy
113
+ */
114
+ register(resourceName: string): PolicyDefinition<TUser, TResource>;
115
+ }
116
+ /**
117
+ * Creates a policy that allows all actions for admins
118
+ * and owner-only for regular users
119
+ */
120
+ export declare function createOwnerOrAdminPolicy<TResource extends {
121
+ userId?: string;
122
+ authorId?: string;
123
+ }>(ownerField?: 'userId' | 'authorId'): PolicyDefinition<User & {
124
+ role?: string;
125
+ }, TResource>;
126
+ /**
127
+ * Creates a read-only policy (only view allowed)
128
+ */
129
+ export declare function createReadOnlyPolicy<TResource>(): PolicyDefinition<User, TResource>;
130
+ /**
131
+ * Creates a policy for admin-only resources
132
+ */
133
+ export declare function createAdminOnlyPolicy<TResource>(): PolicyDefinition<User & {
134
+ role?: string;
135
+ }, TResource>;
136
+ export {};
137
+ //# sourceMappingURL=policies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Resource-level authorization policies for @veloxts/auth
3
+ * @module auth/policies
4
+ */
5
+ // ============================================================================
6
+ // Policy Registry
7
+ // ============================================================================
8
+ /**
9
+ * Global policy registry
10
+ * Maps resource names to their policy definitions
11
+ */
12
+ const policyRegistry = new Map();
13
+ /**
14
+ * Registers a policy for a resource type
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * registerPolicy('Post', {
19
+ * view: (user, post) => true, // Anyone can view
20
+ * update: (user, post) => user.id === post.authorId,
21
+ * delete: (user, post) => user.id === post.authorId || user.role === 'admin',
22
+ * });
23
+ * ```
24
+ */
25
+ export function registerPolicy(resourceName, policy) {
26
+ policyRegistry.set(resourceName, policy);
27
+ }
28
+ /**
29
+ * Gets a registered policy by resource name
30
+ */
31
+ export function getPolicy(resourceName) {
32
+ return policyRegistry.get(resourceName);
33
+ }
34
+ /**
35
+ * Clears all registered policies (useful for testing)
36
+ */
37
+ export function clearPolicies() {
38
+ policyRegistry.clear();
39
+ }
40
+ // ============================================================================
41
+ // Policy Definition Helper
42
+ // ============================================================================
43
+ /**
44
+ * Defines a policy for a resource type
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const PostPolicy = definePolicy<User, Post>({
49
+ * view: () => true,
50
+ * create: (user) => user.emailVerified,
51
+ * update: (user, post) => user.id === post.authorId,
52
+ * delete: (user, post) => user.id === post.authorId || user.role === 'admin',
53
+ * publish: (user, post) => user.role === 'editor' && user.id === post.authorId,
54
+ * });
55
+ *
56
+ * // Register it
57
+ * registerPolicy('Post', PostPolicy);
58
+ * ```
59
+ */
60
+ export function definePolicy(actions) {
61
+ return actions;
62
+ }
63
+ // ============================================================================
64
+ // Authorization Checks
65
+ // ============================================================================
66
+ /**
67
+ * Checks if a user can perform an action on a resource
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const canEdit = await can(user, 'update', 'Post', post);
72
+ * if (!canEdit) {
73
+ * throw new ForbiddenError('Cannot edit this post');
74
+ * }
75
+ * ```
76
+ */
77
+ export async function can(user, action, resourceName, resource) {
78
+ // No user = no permission
79
+ if (!user) {
80
+ return false;
81
+ }
82
+ const policy = policyRegistry.get(resourceName);
83
+ if (!policy) {
84
+ // No policy registered = deny by default
85
+ console.warn(`No policy registered for resource: ${resourceName}`);
86
+ return false;
87
+ }
88
+ const actionHandler = policy[action];
89
+ if (!actionHandler) {
90
+ // Action not defined = deny by default
91
+ return false;
92
+ }
93
+ return actionHandler(user, resource);
94
+ }
95
+ /**
96
+ * Checks if a user cannot perform an action (inverse of can)
97
+ */
98
+ export async function cannot(user, action, resourceName, resource) {
99
+ return !(await can(user, action, resourceName, resource));
100
+ }
101
+ /**
102
+ * Throws an error if user cannot perform action
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * await authorize(ctx.user, 'delete', 'Post', post);
107
+ * // If we get here, user is authorized
108
+ * await db.post.delete({ where: { id: post.id } });
109
+ * ```
110
+ */
111
+ export async function authorize(user, action, resourceName, resource) {
112
+ const allowed = await can(user, action, resourceName, resource);
113
+ if (!allowed) {
114
+ const error = new Error(`Unauthorized: cannot ${action} ${resourceName}${resource ? ` (id: ${resource.id ?? 'unknown'})` : ''}`);
115
+ error.statusCode = 403;
116
+ throw error;
117
+ }
118
+ }
119
+ // ============================================================================
120
+ // Policy Builder
121
+ // ============================================================================
122
+ /**
123
+ * Fluent policy builder for complex policies
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const CommentPolicy = createPolicyBuilder<User, Comment>()
128
+ * .allow('view', () => true)
129
+ * .allow('create', (user) => user.emailVerified)
130
+ * .allow('update', (user, comment) => user.id === comment.authorId)
131
+ * .allow('delete', (user, comment) =>
132
+ * user.id === comment.authorId || user.role === 'admin'
133
+ * )
134
+ * .build();
135
+ * ```
136
+ */
137
+ export function createPolicyBuilder() {
138
+ return new PolicyBuilder();
139
+ }
140
+ class PolicyBuilder {
141
+ actions = {};
142
+ /**
143
+ * Allows an action based on the check function
144
+ */
145
+ allow(action, check) {
146
+ this.actions[action] = check;
147
+ return this;
148
+ }
149
+ /**
150
+ * Denies an action (always returns false)
151
+ */
152
+ deny(action) {
153
+ this.actions[action] = () => false;
154
+ return this;
155
+ }
156
+ /**
157
+ * Allows action only for resource owner
158
+ * Assumes resource has a userId or authorId field
159
+ */
160
+ allowOwner(action, ownerField = 'userId') {
161
+ this.actions[action] = (user, resource) => {
162
+ const ownerId = resource?.[ownerField];
163
+ return ownerId === user.id;
164
+ };
165
+ return this;
166
+ }
167
+ /**
168
+ * Allows action for owner OR users with specific role
169
+ */
170
+ allowOwnerOr(action, roles, ownerField = 'userId') {
171
+ this.actions[action] = (user, resource) => {
172
+ const ownerId = resource?.[ownerField];
173
+ const userRole = user.role;
174
+ const isOwner = ownerId === user.id;
175
+ const hasRole = typeof userRole === 'string' && roles.includes(userRole);
176
+ return isOwner || hasRole;
177
+ };
178
+ return this;
179
+ }
180
+ /**
181
+ * Builds the final policy definition
182
+ */
183
+ build() {
184
+ return { ...this.actions };
185
+ }
186
+ /**
187
+ * Builds and registers the policy
188
+ */
189
+ register(resourceName) {
190
+ const policy = this.build();
191
+ registerPolicy(resourceName, policy);
192
+ return policy;
193
+ }
194
+ }
195
+ // ============================================================================
196
+ // Common Policy Patterns
197
+ // ============================================================================
198
+ /**
199
+ * Creates a policy that allows all actions for admins
200
+ * and owner-only for regular users
201
+ */
202
+ export function createOwnerOrAdminPolicy(ownerField = 'userId') {
203
+ const isOwnerOrAdmin = (user, resource) => {
204
+ if (user.role === 'admin') {
205
+ return true;
206
+ }
207
+ const ownerId = resource[ownerField];
208
+ return ownerId === user.id;
209
+ };
210
+ return {
211
+ view: () => true,
212
+ create: () => true,
213
+ update: isOwnerOrAdmin,
214
+ delete: isOwnerOrAdmin,
215
+ };
216
+ }
217
+ /**
218
+ * Creates a read-only policy (only view allowed)
219
+ */
220
+ export function createReadOnlyPolicy() {
221
+ return {
222
+ view: () => true,
223
+ create: () => false,
224
+ update: () => false,
225
+ delete: () => false,
226
+ };
227
+ }
228
+ /**
229
+ * Creates a policy for admin-only resources
230
+ */
231
+ export function createAdminOnlyPolicy() {
232
+ const isAdmin = (user) => user.role === 'admin';
233
+ return {
234
+ view: isAdmin,
235
+ create: isAdmin,
236
+ update: isAdmin,
237
+ delete: isAdmin,
238
+ };
239
+ }
240
+ //# sourceMappingURL=policies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Authentication-specific rate limiting
3
+ *
4
+ * Provides specialized rate limiters for authentication endpoints with:
5
+ * - Per-email+IP tracking (prevents brute force on specific accounts)
6
+ * - Account lockout detection
7
+ * - Separate limits for login, register, and password reset
8
+ * - Progressive backoff support
9
+ *
10
+ * @module auth/rate-limit
11
+ */
12
+ import type { BaseContext } from '@veloxts/core';
13
+ import type { MiddlewareFunction } from '@veloxts/router';
14
+ /**
15
+ * Configuration for auth rate limiting
16
+ */
17
+ export interface AuthRateLimitConfig {
18
+ /**
19
+ * Maximum attempts before lockout
20
+ * @default 5
21
+ */
22
+ maxAttempts?: number;
23
+ /**
24
+ * Window duration in milliseconds
25
+ * @default 900000 (15 minutes)
26
+ */
27
+ windowMs?: number;
28
+ /**
29
+ * Lockout duration in milliseconds after max attempts exceeded
30
+ * @default 900000 (15 minutes)
31
+ */
32
+ lockoutDurationMs?: number;
33
+ /**
34
+ * Custom key generator for rate limiting
35
+ * Default uses IP + identifier (email)
36
+ */
37
+ keyGenerator?: (ctx: BaseContext, identifier?: string) => string;
38
+ /**
39
+ * Error message when rate limited
40
+ * @default 'Too many attempts. Please try again later.'
41
+ */
42
+ message?: string;
43
+ /**
44
+ * Enable progressive backoff (double lockout on repeated violations)
45
+ * @default false
46
+ */
47
+ progressiveBackoff?: boolean;
48
+ }
49
+ /**
50
+ * Configuration for the auth rate limiter factory
51
+ */
52
+ export interface AuthRateLimiterConfig {
53
+ /**
54
+ * Rate limit for login attempts
55
+ * @default { maxAttempts: 5, windowMs: 900000 }
56
+ */
57
+ login?: AuthRateLimitConfig;
58
+ /**
59
+ * Rate limit for registration attempts
60
+ * @default { maxAttempts: 3, windowMs: 3600000 }
61
+ */
62
+ register?: AuthRateLimitConfig;
63
+ /**
64
+ * Rate limit for password reset requests
65
+ * @default { maxAttempts: 3, windowMs: 3600000 }
66
+ */
67
+ passwordReset?: AuthRateLimitConfig;
68
+ /**
69
+ * Rate limit for token refresh
70
+ * @default { maxAttempts: 10, windowMs: 60000 }
71
+ */
72
+ refresh?: AuthRateLimitConfig;
73
+ }
74
+ /**
75
+ * Stop cleanup interval (for testing)
76
+ */
77
+ export declare function stopAuthRateLimitCleanup(): void;
78
+ /**
79
+ * Clear all rate limit entries (for testing)
80
+ */
81
+ export declare function clearAuthRateLimitStore(): void;
82
+ /**
83
+ * Creates an authentication rate limiter
84
+ *
85
+ * This factory returns rate limit middlewares configured for different
86
+ * auth operations with sensible defaults.
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const authRateLimiter = createAuthRateLimiter({
91
+ * login: { maxAttempts: 5, windowMs: 15 * 60 * 1000 },
92
+ * register: { maxAttempts: 3, windowMs: 60 * 60 * 1000 },
93
+ * });
94
+ *
95
+ * // Apply to procedures
96
+ * const login = procedure()
97
+ * .use(authRateLimiter.login(ctx => ctx.input.email))
98
+ * .mutation(loginHandler);
99
+ *
100
+ * const register = procedure()
101
+ * .use(authRateLimiter.register())
102
+ * .mutation(registerHandler);
103
+ * ```
104
+ */
105
+ export declare function createAuthRateLimiter(config?: AuthRateLimiterConfig): {
106
+ /**
107
+ * Rate limiter for login attempts
108
+ *
109
+ * @param identifierFn - Function to extract identifier (email) from context
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * login: procedure()
114
+ * .use(authRateLimiter.login((ctx) => (ctx.input as { email: string }).email))
115
+ * .input(LoginSchema)
116
+ * .mutation(handler)
117
+ * ```
118
+ */
119
+ login: <TInput, TContext extends BaseContext, TOutput>(identifierFn?: (ctx: TContext & {
120
+ input?: unknown;
121
+ }) => string) => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
122
+ /**
123
+ * Rate limiter for registration attempts
124
+ * Uses IP-only by default (no identifier needed)
125
+ */
126
+ register: <TInput, TContext extends BaseContext, TOutput>() => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
127
+ /**
128
+ * Rate limiter for password reset requests
129
+ *
130
+ * @param identifierFn - Optional function to extract identifier (email)
131
+ */
132
+ passwordReset: <TInput, TContext extends BaseContext, TOutput>(identifierFn?: (ctx: TContext & {
133
+ input?: unknown;
134
+ }) => string) => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
135
+ /**
136
+ * Rate limiter for token refresh
137
+ */
138
+ refresh: <TInput, TContext extends BaseContext, TOutput>() => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
139
+ /**
140
+ * Record a failed attempt (call after authentication fails)
141
+ *
142
+ * This allows tracking failures even when rate limit hasn't been hit,
143
+ * enabling account lockout after X failed passwords.
144
+ *
145
+ * @param key - Rate limit key (usually IP:email or IP)
146
+ * @param operation - Operation type for key namespacing
147
+ */
148
+ recordFailure: (key: string, operation: "login" | "register" | "password-reset") => void;
149
+ /**
150
+ * Reset rate limit for a key (call after successful auth)
151
+ */
152
+ resetLimit: (key: string, operation: "login" | "register" | "password-reset") => void;
153
+ /**
154
+ * Check if a key is currently locked out
155
+ */
156
+ isLockedOut: (key: string, operation: "login" | "register" | "password-reset") => boolean;
157
+ /**
158
+ * Get remaining attempts for a key
159
+ */
160
+ getRemainingAttempts: (key: string, operation: "login" | "register" | "password-reset" | "refresh") => number;
161
+ };
162
+ /**
163
+ * Pre-configured auth rate limiter with sensible defaults
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * import { authRateLimiter } from '@veloxts/auth';
168
+ *
169
+ * const login = procedure()
170
+ * .use(authRateLimiter.login((ctx) => ctx.input.email))
171
+ * .mutation(handler);
172
+ * ```
173
+ */
174
+ export declare const authRateLimiter: {
175
+ /**
176
+ * Rate limiter for login attempts
177
+ *
178
+ * @param identifierFn - Function to extract identifier (email) from context
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * login: procedure()
183
+ * .use(authRateLimiter.login((ctx) => (ctx.input as { email: string }).email))
184
+ * .input(LoginSchema)
185
+ * .mutation(handler)
186
+ * ```
187
+ */
188
+ login: <TInput, TContext extends BaseContext, TOutput>(identifierFn?: (ctx: TContext & {
189
+ input?: unknown;
190
+ }) => string) => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
191
+ /**
192
+ * Rate limiter for registration attempts
193
+ * Uses IP-only by default (no identifier needed)
194
+ */
195
+ register: <TInput, TContext extends BaseContext, TOutput>() => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
196
+ /**
197
+ * Rate limiter for password reset requests
198
+ *
199
+ * @param identifierFn - Optional function to extract identifier (email)
200
+ */
201
+ passwordReset: <TInput, TContext extends BaseContext, TOutput>(identifierFn?: (ctx: TContext & {
202
+ input?: unknown;
203
+ }) => string) => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
204
+ /**
205
+ * Rate limiter for token refresh
206
+ */
207
+ refresh: <TInput, TContext extends BaseContext, TOutput>() => MiddlewareFunction<TInput, TContext, TContext, TOutput>;
208
+ /**
209
+ * Record a failed attempt (call after authentication fails)
210
+ *
211
+ * This allows tracking failures even when rate limit hasn't been hit,
212
+ * enabling account lockout after X failed passwords.
213
+ *
214
+ * @param key - Rate limit key (usually IP:email or IP)
215
+ * @param operation - Operation type for key namespacing
216
+ */
217
+ recordFailure: (key: string, operation: "login" | "register" | "password-reset") => void;
218
+ /**
219
+ * Reset rate limit for a key (call after successful auth)
220
+ */
221
+ resetLimit: (key: string, operation: "login" | "register" | "password-reset") => void;
222
+ /**
223
+ * Check if a key is currently locked out
224
+ */
225
+ isLockedOut: (key: string, operation: "login" | "register" | "password-reset") => boolean;
226
+ /**
227
+ * Get remaining attempts for a key
228
+ */
229
+ getRemainingAttempts: (key: string, operation: "login" | "register" | "password-reset" | "refresh") => number;
230
+ };
231
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}