@stonecrop/casl-middleware 0.30.0 → 0.32.0

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 (38) hide show
  1. package/dist/casl-middleware.d.ts +15 -0
  2. package/dist/casl-middleware.js +3328 -2650
  3. package/dist/casl-middleware.js.map +1 -1
  4. package/dist/tsdoc-metadata.json +1 -1
  5. package/package.json +23 -22
  6. package/dist/casl-middleware.tsbuildinfo +0 -1
  7. package/dist/src/index.d.ts +0 -6
  8. package/dist/src/index.d.ts.map +0 -1
  9. package/dist/src/index.js +0 -3
  10. package/dist/src/middleware/ability.d.ts +0 -55
  11. package/dist/src/middleware/ability.d.ts.map +0 -1
  12. package/dist/src/middleware/ability.js +0 -139
  13. package/dist/src/middleware/graphql.d.ts +0 -11
  14. package/dist/src/middleware/graphql.d.ts.map +0 -1
  15. package/dist/src/middleware/graphql.js +0 -120
  16. package/dist/src/middleware/introspection.d.ts +0 -71
  17. package/dist/src/middleware/introspection.d.ts.map +0 -1
  18. package/dist/src/middleware/introspection.js +0 -169
  19. package/dist/src/middleware/jwt.d.ts +0 -114
  20. package/dist/src/middleware/jwt.d.ts.map +0 -1
  21. package/dist/src/middleware/jwt.js +0 -302
  22. package/dist/src/middleware/postgraphile.d.ts +0 -7
  23. package/dist/src/middleware/postgraphile.d.ts.map +0 -1
  24. package/dist/src/middleware/postgraphile.js +0 -79
  25. package/dist/src/middleware/yoga.d.ts +0 -15
  26. package/dist/src/middleware/yoga.d.ts.map +0 -1
  27. package/dist/src/middleware/yoga.js +0 -30
  28. package/dist/src/types/index.d.ts +0 -114
  29. package/dist/src/types/index.d.ts.map +0 -1
  30. package/dist/src/types/index.js +0 -0
  31. package/src/index.ts +0 -15
  32. package/src/middleware/ability.ts +0 -197
  33. package/src/middleware/graphql.ts +0 -157
  34. package/src/middleware/introspection.ts +0 -258
  35. package/src/middleware/jwt.ts +0 -405
  36. package/src/middleware/postgraphile.ts +0 -89
  37. package/src/middleware/yoga.ts +0 -36
  38. package/src/types/index.ts +0 -133
@@ -1,169 +0,0 @@
1
- import { GraphQLError } from 'graphql';
2
- /**
3
- * Middleware to restrict GraphQL introspection based on user permissions
4
- */
5
- export const createIntrospectionMiddleware = (config = {}) => {
6
- const { enabled = process.env.NODE_ENV !== 'production', allowedRoles, allowAnonymous = false, customCheck, typePermissions = {}, fieldPermissions = {}, } = config;
7
- return async (resolve, root, args, context, info) => {
8
- // Check if this is an introspection query
9
- const isIntrospection = info.fieldName === '__schema' ||
10
- info.fieldName === '__type' ||
11
- info.parentType?.name === '__Schema' ||
12
- info.parentType?.name === '__Type';
13
- if (!isIntrospection) {
14
- // Not an introspection query, continue normally
15
- return resolve(root, args, context, info);
16
- }
17
- // Check if introspection is enabled
18
- if (!enabled) {
19
- throw new GraphQLError('Introspection is disabled');
20
- }
21
- // Custom check function
22
- if (customCheck) {
23
- const allowed = await customCheck(context);
24
- if (!allowed) {
25
- throw new GraphQLError('Introspection not allowed');
26
- }
27
- }
28
- // Check authentication
29
- if (!context.user && !allowAnonymous) {
30
- throw new GraphQLError('Authentication required for introspection');
31
- }
32
- // Check role-based access
33
- if (allowedRoles && allowedRoles.length > 0) {
34
- const userRoles = context.user?.roles || [];
35
- const hasAllowedRole = allowedRoles.some(role => userRoles.includes(role));
36
- if (!hasAllowedRole) {
37
- throw new GraphQLError('Insufficient permissions for introspection');
38
- }
39
- }
40
- // Check CASL-based permissions
41
- if (context.ability) {
42
- // Check if user can read schema
43
- if (!context.ability.can('read', '__Schema')) {
44
- throw new GraphQLError('Permission denied for schema introspection');
45
- }
46
- }
47
- // Get the result
48
- let result = await resolve(root, args, context, info);
49
- // Filter the result based on permissions
50
- if (result && (typePermissions || fieldPermissions)) {
51
- result = filterIntrospectionResult(result, context, {
52
- typePermissions,
53
- fieldPermissions,
54
- });
55
- }
56
- return result;
57
- };
58
- };
59
- /**
60
- * Filter introspection results based on user permissions
61
- */
62
- function filterIntrospectionResult(result, context, config) {
63
- if (!context.ability)
64
- return result;
65
- // Filter __schema result
66
- if (result && result.types) {
67
- result.types = result.types.filter((type) => {
68
- // Check if user has permission to see this type
69
- const permission = config.typePermissions?.[type.name];
70
- if (permission) {
71
- return context.ability.can(permission.action, permission.subject);
72
- }
73
- return true; // Show types without specific permissions
74
- });
75
- // Filter fields within types
76
- result.types.forEach((type) => {
77
- if (type.fields) {
78
- type.fields = type.fields.filter((field) => {
79
- const fieldKey = `${type.name}.${field.name}`;
80
- const permission = config.fieldPermissions?.[fieldKey];
81
- if (permission) {
82
- return context.ability.can(permission.action, permission.subject);
83
- }
84
- return true;
85
- });
86
- }
87
- });
88
- }
89
- // Filter __type result
90
- if (result && result.fields) {
91
- const typeName = result.name;
92
- result.fields = result.fields.filter((field) => {
93
- const fieldKey = `${typeName}.${field.name}`;
94
- const permission = config.fieldPermissions?.[fieldKey];
95
- if (permission) {
96
- return context.ability.can(permission.action, permission.subject);
97
- }
98
- return true;
99
- });
100
- }
101
- return result;
102
- }
103
- /**
104
- * Postgraphile plugin for introspection control
105
- */
106
- export const createPostgraphileIntrospectionPlugin = (config) => {
107
- return {
108
- name: 'IntrospectionControlPlugin',
109
- version: '1.0.0',
110
- // Disable introspection in GraphiQL based on config
111
- grafast: {
112
- hooks: {
113
- GraphQLSchema(schema) {
114
- if (!config.enabled) {
115
- // Remove introspection from schema
116
- // This is a simplified approach - real implementation would be more complex
117
- console.warn('Introspection control in Postgraphile requires custom implementation');
118
- }
119
- return schema;
120
- },
121
- },
122
- },
123
- };
124
- };
125
- /**
126
- * Utility to create ability rules for introspection
127
- */
128
- export const createIntrospectionAbilityRules = (user) => {
129
- const rules = [];
130
- if (!user) {
131
- // Anonymous users cannot introspect
132
- return rules;
133
- }
134
- const roles = user.roles || [];
135
- // Admins can introspect everything
136
- if (roles.includes('admin')) {
137
- rules.push({ action: 'read', subject: '__Schema' });
138
- rules.push({ action: 'read', subject: '__Type' });
139
- return rules;
140
- }
141
- // Developers can introspect
142
- if (roles.includes('developer')) {
143
- rules.push({ action: 'read', subject: '__Schema' });
144
- rules.push({ action: 'read', subject: '__Type' });
145
- return rules;
146
- }
147
- // Regular users get limited introspection
148
- if (roles.includes('user')) {
149
- // They can see the schema but not all types
150
- rules.push({ action: 'read', subject: '__Schema' });
151
- // Specific types they can see would be added here
152
- }
153
- return rules;
154
- };
155
- /**
156
- * Example: Combine introspection with CASL middleware
157
- */
158
- export const createSecureGraphQLMiddleware = (options) => {
159
- const middlewares = [];
160
- // Add introspection control
161
- if (options.introspection) {
162
- middlewares.push(createIntrospectionMiddleware(options.introspection));
163
- }
164
- // Combine all middlewares
165
- return (resolve, root, args, context, info) => {
166
- const chain = middlewares.reduceRight((next, middleware) => () => middleware(next, root, args, context, info), () => resolve(root, args, context, info));
167
- return chain();
168
- };
169
- };
@@ -1,114 +0,0 @@
1
- import jwt from 'jsonwebtoken';
2
- import type { Context, User } from '../types';
3
- export interface JWTConfig {
4
- enabled?: boolean;
5
- secret?: string;
6
- publicKey?: string;
7
- algorithms?: jwt.Algorithm[];
8
- issuer?: string;
9
- audience?: string;
10
- extractUser?: (payload: any) => User | undefined;
11
- headerName?: string;
12
- tokenPrefix?: string;
13
- optional?: boolean;
14
- maxAge?: string;
15
- }
16
- export interface JWTPayload extends jwt.JwtPayload {
17
- sub?: string;
18
- roles?: string[];
19
- permissions?: Array<{
20
- action: string;
21
- subject: string;
22
- conditions?: any;
23
- }>;
24
- [key: string]: any;
25
- }
26
- /**
27
- * JWT middleware factory for GraphQL servers
28
- *
29
- * @example
30
- * ```typescript
31
- * // In Nuxt Yoga
32
- * export default defineNuxtConfig({
33
- * yoga: {
34
- * middleware: [
35
- * createJWTMiddleware({
36
- * enabled: true,
37
- * secret: process.env.JWT_SECRET,
38
- * optional: true // Don't fail if no token
39
- * })
40
- * ]
41
- * }
42
- * })
43
- *
44
- * // In Postgraphile
45
- * const jwtPlugin = createPostgraphileJWTPlugin({
46
- * secret: process.env.JWT_SECRET,
47
- * extractUser: (payload) => ({
48
- * id: payload.user_id,
49
- * roles: payload.user_roles
50
- * })
51
- * })
52
- * ```
53
- */
54
- export declare const createJWTMiddleware: (config?: JWTConfig) => (context: Context, next: () => Promise<any>) => Promise<any>;
55
- /**
56
- * Create a JWT token with user data
57
- */
58
- export declare const createJWT: (user: User, config: {
59
- secret: string;
60
- expiresIn?: jwt.SignOptions["expiresIn"];
61
- issuer?: string;
62
- audience?: string;
63
- additionalClaims?: Record<string, any>;
64
- }) => string;
65
- /**
66
- * Integration with CASL ability builder
67
- */
68
- export declare const createJWTAbilityBuilder: (_config?: JWTConfig) => (user?: User) => Promise<import("./ability").AppAbility>;
69
- /**
70
- * Postgraphile-specific JWT plugin
71
- */
72
- export declare const createPostgraphileJWTPlugin: (config: JWTConfig) => {
73
- name: string;
74
- version: string;
75
- grafast: {
76
- hooks: {
77
- context(ctx: any, _build: any): Promise<any>;
78
- };
79
- };
80
- };
81
- /**
82
- * Express/Koa middleware for REST endpoints
83
- */
84
- export declare const createHTTPJWTMiddleware: (config: JWTConfig) => (req: any, res: any, next: any) => Promise<void>;
85
- /**
86
- * Refresh token utilities
87
- */
88
- export declare const refreshTokenUtils: {
89
- /**
90
- * Create access and refresh tokens
91
- */
92
- createTokenPair: (user: User, config: {
93
- accessSecret: string;
94
- refreshSecret: string;
95
- accessExpiresIn?: jwt.SignOptions["expiresIn"];
96
- refreshExpiresIn?: jwt.SignOptions["expiresIn"];
97
- }) => {
98
- accessToken: string;
99
- refreshToken: string;
100
- };
101
- /**
102
- * Verify refresh token and create new access token
103
- */
104
- refreshAccessToken: (refreshToken: string, config: {
105
- accessSecret: string;
106
- refreshSecret: string;
107
- getUserById: (id: string) => Promise<User | null>;
108
- accessExpiresIn?: jwt.SignOptions["expiresIn"];
109
- }) => Promise<{
110
- accessToken: string;
111
- user: User;
112
- }>;
113
- };
114
- //# sourceMappingURL=jwt.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/middleware/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAA;AAE9B,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAG7C,MAAM,WAAW,SAAS;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,GAAG,CAAC,SAAS,EAAE,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GAAG,SAAS,CAAA;IAChD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,UAAW,SAAQ,GAAG,CAAC,UAAU;IACjD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,WAAW,CAAC,EAAE,KAAK,CAAC;QACnB,MAAM,EAAE,MAAM,CAAA;QACd,OAAO,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,GAAG,CAAA;KAChB,CAAC,CAAA;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAClB;AAeD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAQ,SAAc,MAoB3C,SAAS,OAAO,EAAE,MAAM,MAAM,OAAO,CAAC,GAAG,CAAC,iBAwExD,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,GACrB,MAAM,IAAI,EACV,QAAQ;IACP,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;IACxC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACtC,KACC,MAiBF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,UAAS,SAAc,MAChD,OAAO,IAAI,4CAsBzB,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,2BAA2B,GAAI,QAAQ,SAAS;;;;;yBAQtC,GAAG,UAAU,GAAG;;;CAwBtC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,SAAS,MAI1C,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,kBA4B3C,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;IAC7B;;OAEG;4BAEI,IAAI,UACF;QACP,YAAY,EAAE,MAAM,CAAA;QACpB,aAAa,EAAE,MAAM,CAAA;QACrB,eAAe,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;QAC9C,gBAAgB,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;KAC/C;;;;IAkCF;;OAEG;uCAEY,MAAM,UACZ;QACP,YAAY,EAAE,MAAM,CAAA;QACpB,aAAa,EAAE,MAAM,CAAA;QACrB,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;QACjD,eAAe,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;KAC9C;;;;CAgDF,CAAA"}
@@ -1,302 +0,0 @@
1
- import jwt from 'jsonwebtoken';
2
- import { AbilityBuilder, PureAbility } from '@casl/ability';
3
- import { defaultAbilityBuilder } from './ability';
4
- /**
5
- * Default user extractor from JWT payload
6
- */
7
- const defaultUserExtractor = (payload) => {
8
- if (!payload.sub)
9
- return undefined;
10
- return {
11
- id: payload.sub,
12
- roles: payload.roles || [],
13
- ...payload, // Include any additional claims
14
- };
15
- };
16
- /**
17
- * JWT middleware factory for GraphQL servers
18
- *
19
- * @example
20
- * ```typescript
21
- * // In Nuxt Yoga
22
- * export default defineNuxtConfig({
23
- * yoga: {
24
- * middleware: [
25
- * createJWTMiddleware({
26
- * enabled: true,
27
- * secret: process.env.JWT_SECRET,
28
- * optional: true // Don't fail if no token
29
- * })
30
- * ]
31
- * }
32
- * })
33
- *
34
- * // In Postgraphile
35
- * const jwtPlugin = createPostgraphileJWTPlugin({
36
- * secret: process.env.JWT_SECRET,
37
- * extractUser: (payload) => ({
38
- * id: payload.user_id,
39
- * roles: payload.user_roles
40
- * })
41
- * })
42
- * ```
43
- */
44
- export const createJWTMiddleware = (config = {}) => {
45
- const { enabled = true, secret, publicKey, algorithms = ['HS256'], issuer, audience, headerName = 'authorization', tokenPrefix = 'Bearer ', optional = false, extractUser = defaultUserExtractor, maxAge, } = config;
46
- // Validate configuration
47
- if (enabled && !secret && !publicKey) {
48
- throw new Error('JWT middleware requires either secret or publicKey');
49
- }
50
- return async (context, next) => {
51
- // Skip if JWT is disabled
52
- if (!enabled) {
53
- return next();
54
- }
55
- try {
56
- // Extract token from request headers
57
- const authHeader = context.req?.headers?.get?.(headerName) ||
58
- context.request?.headers?.get?.(headerName) ||
59
- context.headers?.[headerName];
60
- if (!authHeader) {
61
- if (optional) {
62
- return next();
63
- }
64
- throw new Error('No authorization header found');
65
- }
66
- // Remove token prefix
67
- const token = authHeader.startsWith(tokenPrefix) ? authHeader.slice(tokenPrefix.length) : authHeader;
68
- // Prepare verification options
69
- const verifyOptions = {
70
- algorithms,
71
- ...(issuer && { issuer }),
72
- ...(audience && { audience }),
73
- ...(maxAge && { maxAge }),
74
- };
75
- // Verify and decode token
76
- const secretOrPublicKey = publicKey || secret;
77
- const decoded = jwt.verify(token, secretOrPublicKey, verifyOptions);
78
- if (typeof decoded === 'string' || decoded == null) {
79
- throw new Error('Invalid JWT payload: expected object');
80
- }
81
- const payload = decoded;
82
- // Extract user from payload
83
- const user = extractUser(payload);
84
- if (user) {
85
- context.user = user;
86
- // Store the raw payload for potential use
87
- context.jwtPayload = payload;
88
- }
89
- // Continue to next middleware
90
- return next();
91
- }
92
- catch (error) {
93
- if (optional) {
94
- // Log error in development
95
- if (process.env.NODE_ENV === 'development') {
96
- console.warn('JWT verification failed (optional):', error.message);
97
- }
98
- // Continue without user if optional
99
- return next();
100
- }
101
- // Re-throw with more specific error messages
102
- if (error.name === 'TokenExpiredError') {
103
- throw new Error('Token has expired', { cause: error });
104
- }
105
- else if (error.name === 'JsonWebTokenError') {
106
- throw new Error('Invalid token', { cause: error });
107
- }
108
- else if (error.name === 'NotBeforeError') {
109
- throw new Error('Token not active yet', { cause: error });
110
- }
111
- throw error;
112
- }
113
- };
114
- };
115
- /**
116
- * Create a JWT token with user data
117
- */
118
- export const createJWT = (user, config) => {
119
- const { secret, expiresIn = '1h', issuer, audience, additionalClaims = {} } = config;
120
- const payload = {
121
- sub: user.id,
122
- roles: user.roles || [],
123
- ...additionalClaims,
124
- };
125
- const signOptions = {};
126
- // Add optional fields only if they exist
127
- if (issuer !== undefined)
128
- signOptions.issuer = issuer;
129
- if (audience !== undefined)
130
- signOptions.audience = audience;
131
- if (expiresIn !== undefined)
132
- signOptions.expiresIn = expiresIn;
133
- return jwt.sign(payload, secret, signOptions);
134
- };
135
- /**
136
- * Integration with CASL ability builder
137
- */
138
- export const createJWTAbilityBuilder = (_config = {}) => {
139
- return async (user) => {
140
- // If user has direct permissions in JWT, use those
141
- const jwtPermissions = user?.permissions;
142
- if (jwtPermissions && Array.isArray(jwtPermissions)) {
143
- // Build ability from JWT permissions
144
- const { can, cannot, build } = new AbilityBuilder(PureAbility);
145
- jwtPermissions.forEach((permission) => {
146
- if (permission.inverted) {
147
- cannot(permission.action, permission.subject, permission.conditions);
148
- }
149
- else {
150
- can(permission.action, permission.subject, permission.conditions);
151
- }
152
- });
153
- return build();
154
- }
155
- // Fall back to role-based abilities
156
- return defaultAbilityBuilder(user);
157
- };
158
- };
159
- /**
160
- * Postgraphile-specific JWT plugin
161
- */
162
- export const createPostgraphileJWTPlugin = (config) => {
163
- return {
164
- name: 'JWTAuthPlugin',
165
- version: '1.0.0',
166
- // Hook into Postgraphile's context building
167
- grafast: {
168
- hooks: {
169
- async context(ctx, _build) {
170
- const middleware = createJWTMiddleware(config);
171
- // Create a simple context object that the middleware can work with
172
- const context = {
173
- req: ctx.req,
174
- headers: ctx.req?.headers,
175
- user: undefined,
176
- jwtPayload: undefined,
177
- };
178
- // Run the JWT middleware
179
- await middleware(context, async () => { });
180
- // Add user to Postgraphile context
181
- if (context.user) {
182
- return { ...ctx, user: context.user, jwtPayload: context.jwtPayload };
183
- }
184
- return ctx;
185
- },
186
- },
187
- },
188
- };
189
- };
190
- /**
191
- * Express/Koa middleware for REST endpoints
192
- */
193
- export const createHTTPJWTMiddleware = (config) => {
194
- const jwtMiddleware = createJWTMiddleware(config);
195
- // Express middleware
196
- return async (req, res, next) => {
197
- const context = {
198
- req: {
199
- headers: {
200
- get: (name) => req.headers[name],
201
- },
202
- },
203
- headers: req.headers,
204
- user: undefined,
205
- jwtPayload: undefined,
206
- };
207
- try {
208
- await jwtMiddleware(context, async () => { });
209
- req.user = context.user;
210
- req.jwtPayload = context.jwtPayload;
211
- next();
212
- }
213
- catch (error) {
214
- if (config.optional) {
215
- next();
216
- }
217
- else {
218
- res.status(401).json({
219
- error: error.message,
220
- code: 'UNAUTHORIZED',
221
- });
222
- }
223
- }
224
- };
225
- };
226
- /**
227
- * Refresh token utilities
228
- */
229
- export const refreshTokenUtils = {
230
- /**
231
- * Create access and refresh tokens
232
- */
233
- createTokenPair: (user, config) => {
234
- const { accessSecret, refreshSecret, accessExpiresIn = '15m', refreshExpiresIn = '7d' } = config;
235
- const accessPayload = {
236
- sub: user.id,
237
- roles: user.roles,
238
- type: 'access',
239
- };
240
- const refreshPayload = {
241
- sub: user.id,
242
- type: 'refresh',
243
- };
244
- // Create access token with proper options
245
- const accessOptions = {};
246
- if (accessExpiresIn) {
247
- accessOptions.expiresIn = accessExpiresIn;
248
- }
249
- const accessToken = jwt.sign(accessPayload, accessSecret, accessOptions);
250
- // Create refresh token with proper options
251
- const refreshOptions = {};
252
- if (refreshExpiresIn) {
253
- refreshOptions.expiresIn = refreshExpiresIn;
254
- }
255
- const refreshToken = jwt.sign(refreshPayload, refreshSecret, refreshOptions);
256
- return { accessToken, refreshToken };
257
- },
258
- /**
259
- * Verify refresh token and create new access token
260
- */
261
- refreshAccessToken: async (refreshToken, config) => {
262
- const { accessSecret, refreshSecret, getUserById, accessExpiresIn = '15m' } = config;
263
- try {
264
- // Verify refresh token
265
- const decoded = jwt.verify(refreshToken, refreshSecret);
266
- if (typeof decoded === 'string' || decoded == null) {
267
- throw new Error('Invalid refresh token payload: expected object');
268
- }
269
- const payload = decoded;
270
- if (payload.type !== 'refresh') {
271
- throw new Error('Invalid token type');
272
- }
273
- // Get fresh user data
274
- if (!payload.sub) {
275
- throw new Error('Invalid refresh token payload: missing subject');
276
- }
277
- const user = await getUserById(payload.sub);
278
- if (!user) {
279
- throw new Error('User not found');
280
- }
281
- // Create new access token
282
- const accessPayload = {
283
- sub: user.id,
284
- roles: user.roles,
285
- type: 'access',
286
- };
287
- // Create access token with proper options
288
- const accessOptions = {};
289
- if (accessExpiresIn) {
290
- accessOptions.expiresIn = accessExpiresIn;
291
- }
292
- const accessToken = jwt.sign(accessPayload, accessSecret, accessOptions);
293
- return { accessToken, user };
294
- }
295
- catch (error) {
296
- if (error.name === 'TokenExpiredError') {
297
- throw new Error('Refresh token expired', { cause: error });
298
- }
299
- throw error;
300
- }
301
- },
302
- };
@@ -1,7 +0,0 @@
1
- import { GraphileConfig } from 'postgraphile/graphile-build';
2
- /**
3
- * PostGraphile plugin for CASL authorization
4
- * @public
5
- */
6
- export declare const pglCaslPlugin: GraphileConfig.Plugin;
7
- //# sourceMappingURL=postgraphile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"postgraphile.d.ts","sourceRoot":"","sources":["../../../src/middleware/postgraphile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAI5D;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,cAAc,CAAC,MA8EzC,CAAA"}
@@ -1,79 +0,0 @@
1
- import { extendSchema, gql } from 'postgraphile/utils';
2
- import { createAbility } from './ability';
3
- /**
4
- * PostGraphile plugin for CASL authorization
5
- * @public
6
- */
7
- export const pglCaslPlugin = extendSchema(build => {
8
- const { grafast: { constant, object, sideEffect }, } = build;
9
- return {
10
- typeDefs: gql `
11
- input CreateAbilityInput {
12
- userId: String!
13
- roles: [String!]
14
- }
15
-
16
- type AbilityResponse {
17
- success: Boolean!
18
- ability: JSON
19
- message: String
20
- }
21
-
22
- type SecretData {
23
- id: String!
24
- content: String!
25
- }
26
-
27
- extend type Query {
28
- getSecretData: SecretData
29
- }
30
-
31
- extend type Mutation {
32
- createAbility(input: CreateAbilityInput!): AbilityResponse!
33
- }
34
- `,
35
- objects: {
36
- Query: {
37
- plans: {
38
- async getSecretData() {
39
- // TODO: This should be protected by CASL
40
- // const $ability = context<Context>().get('ability')
41
- // if (!$ability.can('read', 'SecretData')) {
42
- // throw new Error('Access denied')
43
- // }
44
- return object({
45
- id: constant('123'),
46
- content: constant('This is protected content'),
47
- });
48
- },
49
- },
50
- },
51
- Mutation: {
52
- plans: {
53
- async createAbility(_plan, fieldArgs) {
54
- const $userId = fieldArgs.getRaw().input.userId;
55
- const $roles = fieldArgs.getRaw().input.roles;
56
- return sideEffect([$userId, $roles], async ([userId, roles]) => {
57
- // Make this async
58
- try {
59
- const ability = await createAbility({ id: userId, roles }); // Await here
60
- return {
61
- success: true,
62
- ability: ability.rules,
63
- message: 'Ability created successfully',
64
- };
65
- }
66
- catch (error) {
67
- return {
68
- success: false,
69
- ability: null,
70
- message: error instanceof Error ? error.message : 'Unknown error occurred',
71
- };
72
- }
73
- });
74
- },
75
- },
76
- },
77
- },
78
- };
79
- });
@@ -1,15 +0,0 @@
1
- import type { Plugin } from 'graphql-yoga';
2
- import type { Context, MiddlewareOptions } from '../types';
3
- export declare const yogaCaslPlugin: Plugin<Context>;
4
- /**
5
- * Create a GraphQL Yoga plugin for CASL authorization
6
- * Note: This is a placeholder for future implementation
7
- *
8
- * @param options - CASL middleware configuration options
9
- * @returns Yoga plugin
10
- * @public
11
- */
12
- export declare const createYogaPlugin: (_options?: MiddlewareOptions) => {
13
- onExecute: (_: any) => Promise<void>;
14
- };
15
- //# sourceMappingURL=yoga.d.ts.map