@veloxts/auth 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +755 -30
- package/dist/adapter.d.ts +710 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +581 -0
- package/dist/adapter.js.map +1 -0
- package/dist/adapters/better-auth.d.ts +271 -0
- package/dist/adapters/better-auth.d.ts.map +1 -0
- package/dist/adapters/better-auth.js +341 -0
- package/dist/adapters/better-auth.js.map +1 -0
- package/dist/adapters/index.d.ts +28 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/csrf.d.ts +294 -0
- package/dist/csrf.d.ts.map +1 -0
- package/dist/csrf.js +396 -0
- package/dist/csrf.js.map +1 -0
- package/dist/guards.d.ts +139 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/guards.js +247 -0
- package/dist/guards.js.map +1 -0
- package/dist/hash.d.ts +85 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +220 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +25 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -36
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts +128 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +363 -0
- package/dist/jwt.js.map +1 -0
- package/dist/middleware.d.ts +87 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +241 -0
- package/dist/middleware.js.map +1 -0
- package/dist/plugin.d.ts +107 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +174 -0
- package/dist/plugin.js.map +1 -0
- package/dist/policies.d.ts +137 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/policies.js +240 -0
- package/dist/policies.js.map +1 -0
- package/dist/session.d.ts +494 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +795 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +251 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -7
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication middleware for @veloxts/auth
|
|
3
|
+
* @module auth/middleware
|
|
4
|
+
*/
|
|
5
|
+
import { executeGuards } from './guards.js';
|
|
6
|
+
import { JwtManager } from './jwt.js';
|
|
7
|
+
import { AuthError } from './types.js';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Auth Middleware Factory
|
|
10
|
+
// ============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Creates an authentication middleware for procedures
|
|
13
|
+
*
|
|
14
|
+
* This middleware:
|
|
15
|
+
* 1. Extracts JWT from Authorization header
|
|
16
|
+
* 2. Verifies the token
|
|
17
|
+
* 3. Loads user from database (if userLoader provided)
|
|
18
|
+
* 4. Adds user and auth context to ctx
|
|
19
|
+
* 5. Runs guards if specified
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const auth = createAuthMiddleware(authConfig);
|
|
24
|
+
*
|
|
25
|
+
* // Use in procedures
|
|
26
|
+
* const getProfile = procedure()
|
|
27
|
+
* .use(auth.middleware())
|
|
28
|
+
* .query(async ({ ctx }) => {
|
|
29
|
+
* return ctx.user; // Guaranteed to exist
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Optional auth (user may be undefined)
|
|
33
|
+
* const getPosts = procedure()
|
|
34
|
+
* .use(auth.middleware({ optional: true }))
|
|
35
|
+
* .query(async ({ ctx }) => {
|
|
36
|
+
* // ctx.user may be undefined
|
|
37
|
+
* return fetchPosts(ctx.user?.id);
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* // With guards
|
|
41
|
+
* const adminOnly = procedure()
|
|
42
|
+
* .use(auth.middleware({ guards: [hasRole('admin')] }))
|
|
43
|
+
* .query(async ({ ctx }) => {
|
|
44
|
+
* // Only admins get here
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function createAuthMiddleware(config) {
|
|
49
|
+
const jwt = new JwtManager(config.jwt);
|
|
50
|
+
/**
|
|
51
|
+
* Creates the actual middleware function
|
|
52
|
+
*/
|
|
53
|
+
function middleware(options = {}) {
|
|
54
|
+
return async ({ ctx, next }) => {
|
|
55
|
+
const request = ctx.request;
|
|
56
|
+
// Extract token from header
|
|
57
|
+
const authHeader = request.headers.authorization;
|
|
58
|
+
const token = jwt.extractFromHeader(authHeader);
|
|
59
|
+
// No token handling
|
|
60
|
+
if (!token) {
|
|
61
|
+
if (options.optional) {
|
|
62
|
+
// Optional auth - continue without user
|
|
63
|
+
const authContext = {
|
|
64
|
+
user: undefined,
|
|
65
|
+
token: undefined,
|
|
66
|
+
isAuthenticated: false,
|
|
67
|
+
};
|
|
68
|
+
return next({
|
|
69
|
+
ctx: {
|
|
70
|
+
...ctx,
|
|
71
|
+
auth: authContext,
|
|
72
|
+
user: undefined,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Required auth - reject
|
|
77
|
+
throw new AuthError('Authorization header required', 401);
|
|
78
|
+
}
|
|
79
|
+
// Verify token
|
|
80
|
+
let payload;
|
|
81
|
+
try {
|
|
82
|
+
payload = jwt.verifyToken(token);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (options.optional) {
|
|
86
|
+
// Invalid token with optional auth - continue without user
|
|
87
|
+
const authContext = {
|
|
88
|
+
user: undefined,
|
|
89
|
+
token: undefined,
|
|
90
|
+
isAuthenticated: false,
|
|
91
|
+
};
|
|
92
|
+
return next({
|
|
93
|
+
ctx: {
|
|
94
|
+
...ctx,
|
|
95
|
+
auth: authContext,
|
|
96
|
+
user: undefined,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
throw new AuthError(error instanceof Error ? error.message : 'Invalid token', 401);
|
|
101
|
+
}
|
|
102
|
+
// Check if token is revoked
|
|
103
|
+
if (config.isTokenRevoked && payload.jti) {
|
|
104
|
+
const revoked = await config.isTokenRevoked(payload.jti);
|
|
105
|
+
if (revoked) {
|
|
106
|
+
throw new AuthError('Token has been revoked', 401, 'TOKEN_REVOKED');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Load user from database
|
|
110
|
+
let user = null;
|
|
111
|
+
if (config.userLoader) {
|
|
112
|
+
user = await config.userLoader(payload.sub);
|
|
113
|
+
if (!user && !options.optional) {
|
|
114
|
+
throw new AuthError('User not found', 401, 'USER_NOT_FOUND');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// No user loader - create minimal user from token
|
|
119
|
+
user = {
|
|
120
|
+
id: payload.sub,
|
|
121
|
+
email: payload.email,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// Create auth context
|
|
125
|
+
const authContext = {
|
|
126
|
+
user: user ?? undefined,
|
|
127
|
+
token: payload,
|
|
128
|
+
isAuthenticated: !!user,
|
|
129
|
+
};
|
|
130
|
+
// Build extended context
|
|
131
|
+
const extendedCtx = {
|
|
132
|
+
...ctx,
|
|
133
|
+
auth: authContext,
|
|
134
|
+
user: user ?? undefined,
|
|
135
|
+
};
|
|
136
|
+
// Run guards if specified
|
|
137
|
+
if (options.guards && options.guards.length > 0) {
|
|
138
|
+
// Validate that all guards are GuardDefinition objects (strings are not supported)
|
|
139
|
+
const guardDefs = options.guards.map((g) => {
|
|
140
|
+
if (typeof g === 'string') {
|
|
141
|
+
throw new AuthError(`String guard references are not supported. Use a GuardDefinition object instead of "${g}"`, 500, 'INVALID_GUARD');
|
|
142
|
+
}
|
|
143
|
+
return g;
|
|
144
|
+
});
|
|
145
|
+
const result = await executeGuards(guardDefs, extendedCtx, request, ctx.reply);
|
|
146
|
+
if (!result.passed) {
|
|
147
|
+
throw new AuthError(result.message ?? `Guard failed: ${result.failedGuard}`, result.statusCode ?? 403, 'GUARD_FAILED');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Continue to next middleware/handler
|
|
151
|
+
return next({ ctx: extendedCtx });
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Shorthand for required authentication
|
|
156
|
+
*/
|
|
157
|
+
function requireAuth(guards) {
|
|
158
|
+
return middleware({ optional: false, guards });
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Shorthand for optional authentication
|
|
162
|
+
*/
|
|
163
|
+
function optionalAuth() {
|
|
164
|
+
return middleware({ optional: true });
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
middleware,
|
|
168
|
+
requireAuth,
|
|
169
|
+
optionalAuth,
|
|
170
|
+
jwt,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// Error Helpers
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// AuthError is now imported from types.ts
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// Rate Limiting Middleware
|
|
179
|
+
// ============================================================================
|
|
180
|
+
/**
|
|
181
|
+
* Simple in-memory rate limiter
|
|
182
|
+
* For production, use Redis-based rate limiting
|
|
183
|
+
*/
|
|
184
|
+
const rateLimitStore = new Map();
|
|
185
|
+
/**
|
|
186
|
+
* Creates a rate limiting middleware
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```typescript
|
|
190
|
+
* const rateLimit = createRateLimitMiddleware({
|
|
191
|
+
* max: 100,
|
|
192
|
+
* windowMs: 60000, // 1 minute
|
|
193
|
+
* });
|
|
194
|
+
*
|
|
195
|
+
* const login = procedure()
|
|
196
|
+
* .use(rateLimit)
|
|
197
|
+
* .input(LoginSchema)
|
|
198
|
+
* .mutation(handler);
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
export function createRateLimitMiddleware(options) {
|
|
202
|
+
const max = options.max ?? 100;
|
|
203
|
+
const windowMs = options.windowMs ?? 60000;
|
|
204
|
+
const keyGenerator = options.keyGenerator ?? ((ctx) => ctx.request.ip ?? 'unknown');
|
|
205
|
+
const message = options.message ?? 'Too many requests, please try again later';
|
|
206
|
+
return async ({ ctx, next }) => {
|
|
207
|
+
const key = keyGenerator(ctx);
|
|
208
|
+
const now = Date.now();
|
|
209
|
+
let record = rateLimitStore.get(key);
|
|
210
|
+
// Clean up expired record
|
|
211
|
+
if (record && record.resetAt <= now) {
|
|
212
|
+
rateLimitStore.delete(key);
|
|
213
|
+
record = undefined;
|
|
214
|
+
}
|
|
215
|
+
if (!record) {
|
|
216
|
+
// First request in window
|
|
217
|
+
record = { count: 1, resetAt: now + windowMs };
|
|
218
|
+
rateLimitStore.set(key, record);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// Increment count
|
|
222
|
+
record.count++;
|
|
223
|
+
}
|
|
224
|
+
// Check limit
|
|
225
|
+
if (record.count > max) {
|
|
226
|
+
throw new AuthError(message, 429, 'RATE_LIMIT_EXCEEDED');
|
|
227
|
+
}
|
|
228
|
+
// Add rate limit headers
|
|
229
|
+
ctx.reply.header('X-RateLimit-Limit', String(max));
|
|
230
|
+
ctx.reply.header('X-RateLimit-Remaining', String(Math.max(0, max - record.count)));
|
|
231
|
+
ctx.reply.header('X-RateLimit-Reset', String(Math.ceil(record.resetAt / 1000)));
|
|
232
|
+
return next();
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Clears rate limit store (useful for testing)
|
|
237
|
+
*/
|
|
238
|
+
export function clearRateLimitStore() {
|
|
239
|
+
rateLimitStore.clear();
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAStC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAkB;IACrD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAEvC;;OAEG;IACH,SAAS,UAAU,CACjB,UAAiC,EAAE;QAEnC,OAAO,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE;YAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;YAE5B,4BAA4B;YAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;YACjD,MAAM,KAAK,GAAG,GAAG,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAEhD,oBAAoB;YACpB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACrB,wCAAwC;oBACxC,MAAM,WAAW,GAAgB;wBAC/B,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE,SAAS;wBAChB,eAAe,EAAE,KAAK;qBACvB,CAAC;oBAEF,OAAO,IAAI,CAAC;wBACV,GAAG,EAAE;4BACH,GAAG,GAAG;4BACN,IAAI,EAAE,WAAW;4BACjB,IAAI,EAAE,SAAS;yBAChB;qBACF,CAAC,CAAC;gBACL,CAAC;gBAED,yBAAyB;gBACzB,MAAM,IAAI,SAAS,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;YAC5D,CAAC;YAED,eAAe;YACf,IAAI,OAAqB,CAAC;YAC1B,IAAI,CAAC;gBACH,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACrB,2DAA2D;oBAC3D,MAAM,WAAW,GAAgB;wBAC/B,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE,SAAS;wBAChB,eAAe,EAAE,KAAK;qBACvB,CAAC;oBAEF,OAAO,IAAI,CAAC;wBACV,GAAG,EAAE;4BACH,GAAG,GAAG;4BACN,IAAI,EAAE,WAAW;4BACjB,IAAI,EAAE,SAAS;yBAChB;qBACF,CAAC,CAAC;gBACL,CAAC;gBAED,MAAM,IAAI,SAAS,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;YACrF,CAAC;YAED,4BAA4B;YAC5B,IAAI,MAAM,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;gBACzC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACzD,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,IAAI,SAAS,CAAC,wBAAwB,EAAE,GAAG,EAAE,eAAe,CAAC,CAAC;gBACtE,CAAC;YACH,CAAC;YAED,0BAA0B;YAC1B,IAAI,IAAI,GAAgB,IAAI,CAAC;YAC7B,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACtB,IAAI,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC5C,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;oBAC/B,MAAM,IAAI,SAAS,CAAC,gBAAgB,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC;gBAC/D,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,kDAAkD;gBAClD,IAAI,GAAG;oBACL,EAAE,EAAE,OAAO,CAAC,GAAG;oBACf,KAAK,EAAE,OAAO,CAAC,KAAK;iBACrB,CAAC;YACJ,CAAC;YAED,sBAAsB;YACtB,MAAM,WAAW,GAAgB;gBAC/B,IAAI,EAAE,IAAI,IAAI,SAAS;gBACvB,KAAK,EAAE,OAAO;gBACd,eAAe,EAAE,CAAC,CAAC,IAAI;aACxB,CAAC;YAEF,yBAAyB;YACzB,MAAM,WAAW,GAAG;gBAClB,GAAG,GAAG;gBACN,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,IAAI,IAAI,SAAS;aACxB,CAAC;YAEF,0BAA0B;YAC1B,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChD,mFAAmF;gBACnF,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBACzC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;wBAC1B,MAAM,IAAI,SAAS,CACjB,uFAAuF,CAAC,GAAG,EAC3F,GAAG,EACH,eAAe,CAChB,CAAC;oBACJ,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC,CAA0C,CAAC;gBAE5C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;gBAE/E,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;oBACnB,MAAM,IAAI,SAAS,CACjB,MAAM,CAAC,OAAO,IAAI,iBAAiB,MAAM,CAAC,WAAW,EAAE,EACvD,MAAM,CAAC,UAAU,IAAI,GAAG,EACxB,cAAc,CACf,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,sCAAsC;YACtC,OAAO,IAAI,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;QACpC,CAAC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,SAAS,WAAW,CAClB,MAAwC;QAExC,OAAO,UAAU,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAK5C,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,SAAS,YAAY;QAMnB,OAAO,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,OAAO;QACL,UAAU;QACV,WAAW;QACX,YAAY;QACZ,GAAG;KACJ,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,0CAA0C;AAE1C,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,EAA8C,CAAC;AAE7E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,yBAAyB,CAAgD,OAKxF;IACC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;IAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,SAAS,CAAC,CAAC;IACpF,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,2CAA2C,CAAC;IAE/E,OAAO,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,IAAI,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAErC,0BAA0B;QAC1B,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;YACpC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,GAAG,SAAS,CAAC;QACrB,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,0BAA0B;YAC1B,MAAM,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAC;YAC/C,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,kBAAkB;YAClB,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QAED,cAAc;QACd,IAAI,MAAM,CAAC,KAAK,GAAG,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,qBAAqB,CAAC,CAAC;QAC3D,CAAC;QAED,yBAAyB;QACzB,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACnF,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEhF,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VeloxTS Auth Plugin
|
|
3
|
+
* Fastify plugin that integrates authentication with VeloxApp
|
|
4
|
+
* @module auth/plugin
|
|
5
|
+
*/
|
|
6
|
+
import type { VeloxPlugin } from '@veloxts/core';
|
|
7
|
+
import { PasswordHasher } from './hash.js';
|
|
8
|
+
import { JwtManager } from './jwt.js';
|
|
9
|
+
import { createAuthMiddleware } from './middleware.js';
|
|
10
|
+
import type { AuthConfig, AuthContext, TokenPair, User } from './types.js';
|
|
11
|
+
/** Auth package version */
|
|
12
|
+
export declare const AUTH_VERSION: string;
|
|
13
|
+
/**
|
|
14
|
+
* Options for the auth plugin
|
|
15
|
+
*/
|
|
16
|
+
export interface AuthPluginOptions extends AuthConfig {
|
|
17
|
+
/**
|
|
18
|
+
* Enable debug logging
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
debug?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Auth service instance attached to Fastify
|
|
25
|
+
* Provides authentication utilities for the application
|
|
26
|
+
*/
|
|
27
|
+
export interface AuthService {
|
|
28
|
+
/**
|
|
29
|
+
* JWT manager for token operations
|
|
30
|
+
*/
|
|
31
|
+
jwt: JwtManager;
|
|
32
|
+
/**
|
|
33
|
+
* Password hasher for secure password storage
|
|
34
|
+
*/
|
|
35
|
+
hasher: PasswordHasher;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a token pair for a user
|
|
38
|
+
*/
|
|
39
|
+
createTokens(user: User, additionalClaims?: Record<string, unknown>): TokenPair;
|
|
40
|
+
/**
|
|
41
|
+
* Verifies an access token and returns the auth context
|
|
42
|
+
*/
|
|
43
|
+
verifyToken(token: string): AuthContext;
|
|
44
|
+
/**
|
|
45
|
+
* Refreshes tokens using a refresh token
|
|
46
|
+
*/
|
|
47
|
+
refreshTokens(refreshToken: string): Promise<TokenPair> | TokenPair;
|
|
48
|
+
/**
|
|
49
|
+
* Gets the auth middleware factory
|
|
50
|
+
*/
|
|
51
|
+
middleware: ReturnType<typeof createAuthMiddleware>;
|
|
52
|
+
}
|
|
53
|
+
declare module 'fastify' {
|
|
54
|
+
interface FastifyInstance {
|
|
55
|
+
auth: AuthService;
|
|
56
|
+
}
|
|
57
|
+
interface FastifyRequest {
|
|
58
|
+
auth?: AuthContext;
|
|
59
|
+
user?: User;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Creates the VeloxTS auth plugin
|
|
64
|
+
*
|
|
65
|
+
* This plugin provides:
|
|
66
|
+
* - JWT token management (access + refresh tokens)
|
|
67
|
+
* - Password hashing (bcrypt/argon2)
|
|
68
|
+
* - Request decorations for auth context
|
|
69
|
+
* - Auth middleware factory for procedures
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* import { createAuthPlugin } from '@veloxts/auth';
|
|
74
|
+
*
|
|
75
|
+
* const authPlugin = createAuthPlugin({
|
|
76
|
+
* jwt: {
|
|
77
|
+
* secret: process.env.JWT_SECRET!,
|
|
78
|
+
* accessTokenExpiry: '15m',
|
|
79
|
+
* refreshTokenExpiry: '7d',
|
|
80
|
+
* },
|
|
81
|
+
* hash: {
|
|
82
|
+
* algorithm: 'bcrypt',
|
|
83
|
+
* bcryptRounds: 12,
|
|
84
|
+
* },
|
|
85
|
+
* userLoader: async (userId) => {
|
|
86
|
+
* return db.user.findUnique({ where: { id: userId } });
|
|
87
|
+
* },
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* // Register with VeloxApp
|
|
91
|
+
* app.register(authPlugin);
|
|
92
|
+
*
|
|
93
|
+
* // Use in procedures
|
|
94
|
+
* const { middleware, requireAuth } = app.auth.middleware;
|
|
95
|
+
*
|
|
96
|
+
* const getProfile = procedure()
|
|
97
|
+
* .use(requireAuth())
|
|
98
|
+
* .query(async ({ ctx }) => ctx.user);
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function createAuthPlugin(options: AuthPluginOptions): VeloxPlugin<AuthPluginOptions>;
|
|
102
|
+
/**
|
|
103
|
+
* Default auth plugin with minimal configuration
|
|
104
|
+
* Requires JWT_SECRET environment variable
|
|
105
|
+
*/
|
|
106
|
+
export declare function authPlugin(): VeloxPlugin<AuthPluginOptions>;
|
|
107
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAM3E,2BAA2B;AAC3B,eAAO,MAAM,YAAY,EAAE,MAA+C,CAAC;AAM3E;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,cAAc,CAAC;IAEvB;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAEhF;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IAExC;;OAEG;IACH,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;IAEpE;;OAEG;IACH,UAAU,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;CACrD;AAMD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,IAAI,EAAE,WAAW,CAAC;KACnB;IAED,UAAU,cAAc;QACtB,IAAI,CAAC,EAAE,WAAW,CAAC;QACnB,IAAI,CAAC,EAAE,IAAI,CAAC;KACb;CACF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAqH3F;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,WAAW,CAAC,iBAAiB,CAAC,CAY3D"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VeloxTS Auth Plugin
|
|
3
|
+
* Fastify plugin that integrates authentication with VeloxApp
|
|
4
|
+
* @module auth/plugin
|
|
5
|
+
*/
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import { PasswordHasher } from './hash.js';
|
|
8
|
+
import { JwtManager } from './jwt.js';
|
|
9
|
+
import { createAuthMiddleware } from './middleware.js';
|
|
10
|
+
// Read version from package.json dynamically
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const packageJson = require('../package.json');
|
|
13
|
+
/** Auth package version */
|
|
14
|
+
export const AUTH_VERSION = packageJson.version ?? '0.0.0-unknown';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Auth Plugin
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Creates the VeloxTS auth plugin
|
|
20
|
+
*
|
|
21
|
+
* This plugin provides:
|
|
22
|
+
* - JWT token management (access + refresh tokens)
|
|
23
|
+
* - Password hashing (bcrypt/argon2)
|
|
24
|
+
* - Request decorations for auth context
|
|
25
|
+
* - Auth middleware factory for procedures
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { createAuthPlugin } from '@veloxts/auth';
|
|
30
|
+
*
|
|
31
|
+
* const authPlugin = createAuthPlugin({
|
|
32
|
+
* jwt: {
|
|
33
|
+
* secret: process.env.JWT_SECRET!,
|
|
34
|
+
* accessTokenExpiry: '15m',
|
|
35
|
+
* refreshTokenExpiry: '7d',
|
|
36
|
+
* },
|
|
37
|
+
* hash: {
|
|
38
|
+
* algorithm: 'bcrypt',
|
|
39
|
+
* bcryptRounds: 12,
|
|
40
|
+
* },
|
|
41
|
+
* userLoader: async (userId) => {
|
|
42
|
+
* return db.user.findUnique({ where: { id: userId } });
|
|
43
|
+
* },
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* // Register with VeloxApp
|
|
47
|
+
* app.register(authPlugin);
|
|
48
|
+
*
|
|
49
|
+
* // Use in procedures
|
|
50
|
+
* const { middleware, requireAuth } = app.auth.middleware;
|
|
51
|
+
*
|
|
52
|
+
* const getProfile = procedure()
|
|
53
|
+
* .use(requireAuth())
|
|
54
|
+
* .query(async ({ ctx }) => ctx.user);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function createAuthPlugin(options) {
|
|
58
|
+
return {
|
|
59
|
+
name: '@veloxts/auth',
|
|
60
|
+
version: AUTH_VERSION,
|
|
61
|
+
dependencies: ['@veloxts/core'],
|
|
62
|
+
async register(server, _opts) {
|
|
63
|
+
const config = { ...options, ..._opts };
|
|
64
|
+
const { debug = false } = config;
|
|
65
|
+
if (debug) {
|
|
66
|
+
server.log.info('Registering @veloxts/auth plugin');
|
|
67
|
+
}
|
|
68
|
+
// Create instances
|
|
69
|
+
const jwt = new JwtManager(config.jwt);
|
|
70
|
+
const hasher = new PasswordHasher(config.hash);
|
|
71
|
+
const authMiddleware = createAuthMiddleware(config);
|
|
72
|
+
// Create auth service
|
|
73
|
+
const authService = {
|
|
74
|
+
jwt,
|
|
75
|
+
hasher,
|
|
76
|
+
createTokens(user, additionalClaims) {
|
|
77
|
+
return jwt.createTokenPair(user, additionalClaims);
|
|
78
|
+
},
|
|
79
|
+
verifyToken(token) {
|
|
80
|
+
const payload = jwt.verifyToken(token);
|
|
81
|
+
return {
|
|
82
|
+
user: {
|
|
83
|
+
id: payload.sub,
|
|
84
|
+
email: payload.email,
|
|
85
|
+
},
|
|
86
|
+
token: payload,
|
|
87
|
+
isAuthenticated: true,
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
refreshTokens(refreshToken) {
|
|
91
|
+
if (config.userLoader) {
|
|
92
|
+
return jwt.refreshTokens(refreshToken, config.userLoader);
|
|
93
|
+
}
|
|
94
|
+
return jwt.refreshTokens(refreshToken);
|
|
95
|
+
},
|
|
96
|
+
middleware: authMiddleware,
|
|
97
|
+
};
|
|
98
|
+
// Decorate server with auth service
|
|
99
|
+
server.decorate('auth', authService);
|
|
100
|
+
// Decorate requests with auth context (undefined initial value)
|
|
101
|
+
server.decorateRequest('auth', undefined);
|
|
102
|
+
server.decorateRequest('user', undefined);
|
|
103
|
+
// Add preHandler hook to extract auth from headers (optional)
|
|
104
|
+
if (config.autoExtract !== false) {
|
|
105
|
+
server.addHook('preHandler', async (request) => {
|
|
106
|
+
const authHeader = request.headers.authorization;
|
|
107
|
+
const token = jwt.extractFromHeader(authHeader);
|
|
108
|
+
if (token) {
|
|
109
|
+
try {
|
|
110
|
+
const payload = jwt.verifyToken(token);
|
|
111
|
+
// Check if token is revoked
|
|
112
|
+
if (config.isTokenRevoked && payload.jti) {
|
|
113
|
+
const revoked = await config.isTokenRevoked(payload.jti);
|
|
114
|
+
if (revoked) {
|
|
115
|
+
// Token revoked - don't set auth context
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Load user if loader provided
|
|
120
|
+
let user = null;
|
|
121
|
+
if (config.userLoader) {
|
|
122
|
+
user = await config.userLoader(payload.sub);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
user = {
|
|
126
|
+
id: payload.sub,
|
|
127
|
+
email: payload.email,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (user) {
|
|
131
|
+
request.auth = {
|
|
132
|
+
user,
|
|
133
|
+
token: payload,
|
|
134
|
+
isAuthenticated: true,
|
|
135
|
+
};
|
|
136
|
+
request.user = user;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Invalid token - silently ignore (optional auth)
|
|
141
|
+
if (debug) {
|
|
142
|
+
server.log.debug('Invalid auth token in request');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// Add shutdown hook for cleanup
|
|
149
|
+
server.addHook('onClose', async () => {
|
|
150
|
+
if (debug) {
|
|
151
|
+
server.log.info('Shutting down @veloxts/auth plugin');
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
if (debug) {
|
|
155
|
+
server.log.info('@veloxts/auth plugin registered successfully');
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Default auth plugin with minimal configuration
|
|
162
|
+
* Requires JWT_SECRET environment variable
|
|
163
|
+
*/
|
|
164
|
+
export function authPlugin() {
|
|
165
|
+
const secret = process.env.JWT_SECRET;
|
|
166
|
+
if (!secret) {
|
|
167
|
+
throw new Error('JWT_SECRET environment variable is required for auth plugin. ' +
|
|
168
|
+
'Set it to a secure random string of at least 32 characters.');
|
|
169
|
+
}
|
|
170
|
+
return createAuthPlugin({
|
|
171
|
+
jwt: { secret },
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|