@fnd-platform/cognito-auth 1.0.0-alpha.1

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +323 -0
  3. package/lib/authorizer/handler.d.ts +33 -0
  4. package/lib/authorizer/handler.d.ts.map +1 -0
  5. package/lib/authorizer/handler.js +106 -0
  6. package/lib/authorizer/handler.js.map +1 -0
  7. package/lib/authorizer/index.d.ts +7 -0
  8. package/lib/authorizer/index.d.ts.map +1 -0
  9. package/lib/authorizer/index.js +16 -0
  10. package/lib/authorizer/index.js.map +1 -0
  11. package/lib/client/auth-client.d.ts +131 -0
  12. package/lib/client/auth-client.d.ts.map +1 -0
  13. package/lib/client/auth-client.js +270 -0
  14. package/lib/client/auth-client.js.map +1 -0
  15. package/lib/client/errors.d.ts +67 -0
  16. package/lib/client/errors.d.ts.map +1 -0
  17. package/lib/client/errors.js +90 -0
  18. package/lib/client/errors.js.map +1 -0
  19. package/lib/client/index.d.ts +8 -0
  20. package/lib/client/index.d.ts.map +1 -0
  21. package/lib/client/index.js +29 -0
  22. package/lib/client/index.js.map +1 -0
  23. package/lib/cognito-construct.d.ts +113 -0
  24. package/lib/cognito-construct.d.ts.map +1 -0
  25. package/lib/cognito-construct.js +211 -0
  26. package/lib/cognito-construct.js.map +1 -0
  27. package/lib/index.d.ts +30 -0
  28. package/lib/index.d.ts.map +1 -0
  29. package/lib/index.js +59 -0
  30. package/lib/index.js.map +1 -0
  31. package/lib/jwt.d.ts +89 -0
  32. package/lib/jwt.d.ts.map +1 -0
  33. package/lib/jwt.js +117 -0
  34. package/lib/jwt.js.map +1 -0
  35. package/lib/middleware/auth.d.ts +59 -0
  36. package/lib/middleware/auth.d.ts.map +1 -0
  37. package/lib/middleware/auth.js +148 -0
  38. package/lib/middleware/auth.js.map +1 -0
  39. package/lib/middleware/index.d.ts +12 -0
  40. package/lib/middleware/index.d.ts.map +1 -0
  41. package/lib/middleware/index.js +16 -0
  42. package/lib/middleware/index.js.map +1 -0
  43. package/lib/remix/admin.server.d.ts +105 -0
  44. package/lib/remix/admin.server.d.ts.map +1 -0
  45. package/lib/remix/admin.server.js +146 -0
  46. package/lib/remix/admin.server.js.map +1 -0
  47. package/lib/remix/index.d.ts +17 -0
  48. package/lib/remix/index.d.ts.map +1 -0
  49. package/lib/remix/index.js +95 -0
  50. package/lib/remix/index.js.map +1 -0
  51. package/lib/remix/session.server.d.ts +177 -0
  52. package/lib/remix/session.server.d.ts.map +1 -0
  53. package/lib/remix/session.server.js +287 -0
  54. package/lib/remix/session.server.js.map +1 -0
  55. package/lib/types.d.ts +161 -0
  56. package/lib/types.d.ts.map +1 -0
  57. package/lib/types.js +8 -0
  58. package/lib/types.js.map +1 -0
  59. package/lib/utils/index.d.ts +12 -0
  60. package/lib/utils/index.d.ts.map +1 -0
  61. package/lib/utils/index.js +22 -0
  62. package/lib/utils/index.js.map +1 -0
  63. package/lib/utils/token-refresh.d.ts +62 -0
  64. package/lib/utils/token-refresh.d.ts.map +1 -0
  65. package/lib/utils/token-refresh.js +84 -0
  66. package/lib/utils/token-refresh.js.map +1 -0
  67. package/package.json +70 -0
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /**
3
+ * Cognito authentication middleware with real JWT validation.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.withCognitoAuth = withCognitoAuth;
9
+ const jwt_js_1 = require("../jwt.js");
10
+ /**
11
+ * Creates an unauthorized response.
12
+ *
13
+ * @param message - Error message
14
+ * @returns API Gateway response with 401 status
15
+ */
16
+ function unauthorized(message) {
17
+ return {
18
+ statusCode: 401,
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ 'Access-Control-Allow-Origin': '*',
22
+ },
23
+ body: JSON.stringify({
24
+ success: false,
25
+ error: {
26
+ code: 'UNAUTHORIZED',
27
+ message,
28
+ },
29
+ }),
30
+ };
31
+ }
32
+ /**
33
+ * Creates a forbidden response.
34
+ *
35
+ * @param message - Error message
36
+ * @returns API Gateway response with 403 status
37
+ */
38
+ function forbidden(message) {
39
+ return {
40
+ statusCode: 403,
41
+ headers: {
42
+ 'Content-Type': 'application/json',
43
+ 'Access-Control-Allow-Origin': '*',
44
+ },
45
+ body: JSON.stringify({
46
+ success: false,
47
+ error: {
48
+ code: 'FORBIDDEN',
49
+ message,
50
+ },
51
+ }),
52
+ };
53
+ }
54
+ /**
55
+ * Extracts bearer token from Authorization header.
56
+ *
57
+ * @param event - API Gateway event
58
+ * @returns Token string or null if not found
59
+ */
60
+ function extractBearerToken(event) {
61
+ const authHeader = event.headers?.Authorization ?? event.headers?.authorization;
62
+ if (!authHeader?.startsWith('Bearer ')) {
63
+ return null;
64
+ }
65
+ return authHeader.slice(7);
66
+ }
67
+ /**
68
+ * Middleware that validates JWT tokens from Cognito.
69
+ *
70
+ * Unlike the API package's withAuth (which expects API Gateway to validate),
71
+ * this middleware performs actual JWT verification using aws-jwt-verify.
72
+ *
73
+ * @param options - Authentication configuration
74
+ * @returns Middleware that validates tokens and adds auth info to event
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // Basic auth - validates token
79
+ * const handler = withCognitoAuth()(async (event) => {
80
+ * const userId = event.auth.userId;
81
+ * return { statusCode: 200, body: JSON.stringify({ userId }) };
82
+ * });
83
+ *
84
+ * // With role requirement
85
+ * const adminHandler = withCognitoAuth({ roles: ['admin'] })(async (event) => {
86
+ * return { statusCode: 200, body: 'Admin access granted' };
87
+ * });
88
+ *
89
+ * // Skip auth for certain paths
90
+ * const handler = withCognitoAuth({ skipPaths: ['/health'] })(async (event) => {
91
+ * return { statusCode: 200, body: 'OK' };
92
+ * });
93
+ * ```
94
+ */
95
+ function withCognitoAuth(options = {}) {
96
+ const { userPoolId = process.env.COGNITO_USER_POOL_ID, clientId = process.env.COGNITO_CLIENT_ID, roles, skipPaths = [], tokenUse = 'access', } = options;
97
+ return (handler) => async (event, context) => {
98
+ // Skip auth for specified paths
99
+ if (skipPaths.some((path) => event.path?.includes(path))) {
100
+ // Pass through without auth info for skipped paths
101
+ return handler(event, context);
102
+ }
103
+ // Validate configuration
104
+ if (!userPoolId || !clientId) {
105
+ console.error('Missing COGNITO_USER_POOL_ID or COGNITO_CLIENT_ID');
106
+ return unauthorized('Authentication not configured');
107
+ }
108
+ // Extract token
109
+ const token = extractBearerToken(event);
110
+ if (!token) {
111
+ return unauthorized('Missing or invalid Authorization header');
112
+ }
113
+ try {
114
+ // Verify token
115
+ const authResult = await (0, jwt_js_1.verifyAndExtract)(token, {
116
+ userPoolId,
117
+ clientId,
118
+ tokenUse,
119
+ });
120
+ // Check roles if specified
121
+ if (roles && roles.length > 0) {
122
+ const hasRole = roles.some((role) => authResult.groups.includes(role));
123
+ if (!hasRole) {
124
+ return forbidden(`Required role: ${roles.join(' or ')}`);
125
+ }
126
+ }
127
+ // Add auth info to event
128
+ const authenticatedEvent = {
129
+ ...event,
130
+ auth: authResult,
131
+ };
132
+ return handler(authenticatedEvent, context);
133
+ }
134
+ catch (error) {
135
+ console.error('Token verification failed:', error);
136
+ // Provide specific error messages for common cases
137
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
138
+ if (errorMessage.includes('expired')) {
139
+ return unauthorized('Token has expired');
140
+ }
141
+ if (errorMessage.includes('invalid')) {
142
+ return unauthorized('Invalid token');
143
+ }
144
+ return unauthorized('Token verification failed');
145
+ }
146
+ };
147
+ }
148
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAuHH,0CAoEC;AAxLD,sCAA6C;AA0B7C;;;;;GAKG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO;QACL,UAAU,EAAE,GAAG;QACf,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,6BAA6B,EAAE,GAAG;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,cAAc;gBACpB,OAAO;aACR;SACF,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,SAAS,CAAC,OAAe;IAChC,OAAO;QACL,UAAU,EAAE,GAAG;QACf,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,6BAA6B,EAAE,GAAG;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,WAAW;gBACjB,OAAO;aACR;SACF,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,KAA2B;IACrD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,EAAE,aAAa,IAAI,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC;IAEhF,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAgB,eAAe,CAC7B,UAA8B,EAAE;IAEhC,MAAM,EACJ,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAC7C,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EACxC,KAAK,EACL,SAAS,GAAG,EAAE,EACd,QAAQ,GAAG,QAAQ,GACpB,GAAG,OAAO,CAAC;IAEZ,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC3C,gCAAgC;QAChC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACzD,mDAAmD;YACnD,OAAO,OAAO,CAAC,KAAkC,EAAE,OAAO,CAAC,CAAC;QAC9D,CAAC;QAED,yBAAyB;QACzB,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;YACnE,OAAO,YAAY,CAAC,+BAA+B,CAAC,CAAC;QACvD,CAAC;QAED,gBAAgB;QAChB,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,yCAAyC,CAAC,CAAC;QACjE,CAAC;QAED,IAAI,CAAC;YACH,eAAe;YACf,MAAM,UAAU,GAAG,MAAM,IAAA,yBAAgB,EAAC,KAAK,EAAE;gBAC/C,UAAU;gBACV,QAAQ;gBACR,QAAQ;aACT,CAAC,CAAC;YAEH,2BAA2B;YAC3B,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;gBACvE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,SAAS,CAAC,kBAAkB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;YAED,yBAAyB;YACzB,MAAM,kBAAkB,GAA8B;gBACpD,GAAG,KAAK;gBACR,IAAI,EAAE,UAAU;aACjB,CAAC;YAEF,OAAO,OAAO,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YAEnD,mDAAmD;YACnD,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YAC9E,IAAI,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrC,OAAO,YAAY,CAAC,mBAAmB,CAAC,CAAC;YAC3C,CAAC;YACD,IAAI,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrC,OAAO,YAAY,CAAC,eAAe,CAAC,CAAC;YACvC,CAAC;YAED,OAAO,YAAY,CAAC,2BAA2B,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Middleware exports.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export {
7
+ withCognitoAuth,
8
+ type CognitoAuthenticatedEvent,
9
+ type Middleware,
10
+ type MiddlewareHandler,
11
+ } from './auth.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,KAAK,yBAAyB,EAC9B,KAAK,UAAU,EACf,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+ /**
3
+ * Middleware exports.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ Object.defineProperty(exports, '__esModule', { value: true });
8
+ exports.withCognitoAuth = void 0;
9
+ var auth_js_1 = require('./auth.js');
10
+ Object.defineProperty(exports, 'withCognitoAuth', {
11
+ enumerable: true,
12
+ get: function () {
13
+ return auth_js_1.withCognitoAuth;
14
+ },
15
+ });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,qCAKmB;AAJjB,0GAAA,eAAe,OAAA"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Admin and role-based access utilities for Remix.
3
+ *
4
+ * Provides utilities for requiring specific roles in Remix loaders and actions.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ import type { SessionStorage } from '@remix-run/node';
9
+ /**
10
+ * Requires admin role for accessing a route.
11
+ *
12
+ * First checks if the user is authenticated, then verifies they have
13
+ * the 'admin' group in their Cognito session.
14
+ *
15
+ * @param request - Remix request object
16
+ * @param storage - Optional custom session storage
17
+ * @returns User ID if authorized
18
+ * @throws Redirect to /login if not authenticated
19
+ * @throws 403 JSON response if not admin
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * export async function loader({ request }: LoaderFunctionArgs) {
24
+ * const userId = await requireAdmin(request);
25
+ * // User is admin, load admin data
26
+ * return json({ adminData: await getAdminData() });
27
+ * }
28
+ * ```
29
+ */
30
+ export declare function requireAdmin(request: Request, storage?: SessionStorage): Promise<string>;
31
+ /**
32
+ * Requires any of the specified roles for accessing a route.
33
+ *
34
+ * First checks if the user is authenticated, then verifies they have
35
+ * at least one of the required roles.
36
+ *
37
+ * @param request - Remix request object
38
+ * @param roles - Array of required roles (user must have at least one)
39
+ * @param storage - Optional custom session storage
40
+ * @returns User ID if authorized
41
+ * @throws Redirect to /login if not authenticated
42
+ * @throws 403 JSON response if missing required role
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * export async function loader({ request }: LoaderFunctionArgs) {
47
+ * // Require admin or editor role
48
+ * const userId = await requireRole(request, ['admin', 'editor']);
49
+ * return json({ content: await getContent() });
50
+ * }
51
+ * ```
52
+ */
53
+ export declare function requireRole(
54
+ request: Request,
55
+ roles: string[],
56
+ storage?: SessionStorage
57
+ ): Promise<string>;
58
+ /**
59
+ * Checks if the user has a specific role.
60
+ *
61
+ * Does not throw errors or redirect. Returns false for unauthenticated users.
62
+ *
63
+ * @param request - Remix request object
64
+ * @param role - Role to check
65
+ * @param storage - Optional custom session storage
66
+ * @returns True if user has role, false otherwise
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * export async function loader({ request }: LoaderFunctionArgs) {
71
+ * const isAdmin = await hasRole(request, 'admin');
72
+ * const canEdit = await hasRole(request, 'editor');
73
+ * return json({ isAdmin, canEdit });
74
+ * }
75
+ * ```
76
+ */
77
+ export declare function hasRole(
78
+ request: Request,
79
+ role: string,
80
+ storage?: SessionStorage
81
+ ): Promise<boolean>;
82
+ /**
83
+ * Checks if the user has any of the specified roles.
84
+ *
85
+ * Does not throw errors or redirect. Returns false for unauthenticated users.
86
+ *
87
+ * @param request - Remix request object
88
+ * @param roles - Roles to check
89
+ * @param storage - Optional custom session storage
90
+ * @returns True if user has any role, false otherwise
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * export async function loader({ request }: LoaderFunctionArgs) {
95
+ * const canManageContent = await hasAnyRole(request, ['admin', 'editor']);
96
+ * return json({ canManageContent });
97
+ * }
98
+ * ```
99
+ */
100
+ export declare function hasAnyRole(
101
+ request: Request,
102
+ roles: string[],
103
+ storage?: SessionStorage
104
+ ): Promise<boolean>;
105
+ //# sourceMappingURL=admin.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.server.d.ts","sourceRoot":"","sources":["../../src/remix/admin.server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAE9F;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAwBjB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,OAAO,CAAC,CAclB"}
@@ -0,0 +1,146 @@
1
+ 'use strict';
2
+ /**
3
+ * Admin and role-based access utilities for Remix.
4
+ *
5
+ * Provides utilities for requiring specific roles in Remix loaders and actions.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ Object.defineProperty(exports, '__esModule', { value: true });
10
+ exports.requireAdmin = requireAdmin;
11
+ exports.requireRole = requireRole;
12
+ exports.hasRole = hasRole;
13
+ exports.hasAnyRole = hasAnyRole;
14
+ const node_1 = require('@remix-run/node');
15
+ const session_server_js_1 = require('./session.server.js');
16
+ /**
17
+ * Requires admin role for accessing a route.
18
+ *
19
+ * First checks if the user is authenticated, then verifies they have
20
+ * the 'admin' group in their Cognito session.
21
+ *
22
+ * @param request - Remix request object
23
+ * @param storage - Optional custom session storage
24
+ * @returns User ID if authorized
25
+ * @throws Redirect to /login if not authenticated
26
+ * @throws 403 JSON response if not admin
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * export async function loader({ request }: LoaderFunctionArgs) {
31
+ * const userId = await requireAdmin(request);
32
+ * // User is admin, load admin data
33
+ * return json({ adminData: await getAdminData() });
34
+ * }
35
+ * ```
36
+ */
37
+ async function requireAdmin(request, storage) {
38
+ return requireRole(request, ['admin'], storage);
39
+ }
40
+ /**
41
+ * Requires any of the specified roles for accessing a route.
42
+ *
43
+ * First checks if the user is authenticated, then verifies they have
44
+ * at least one of the required roles.
45
+ *
46
+ * @param request - Remix request object
47
+ * @param roles - Array of required roles (user must have at least one)
48
+ * @param storage - Optional custom session storage
49
+ * @returns User ID if authorized
50
+ * @throws Redirect to /login if not authenticated
51
+ * @throws 403 JSON response if missing required role
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * export async function loader({ request }: LoaderFunctionArgs) {
56
+ * // Require admin or editor role
57
+ * const userId = await requireRole(request, ['admin', 'editor']);
58
+ * return json({ content: await getContent() });
59
+ * }
60
+ * ```
61
+ */
62
+ async function requireRole(request, roles, storage) {
63
+ // First ensure user is authenticated
64
+ const userId = await (0, session_server_js_1.requireAuth)(request, '/login', storage);
65
+ // Get user's groups from session
66
+ const session = await (0, session_server_js_1.getSession)(request, storage);
67
+ const userGroups = session.get('groups') ?? [];
68
+ // Check if user has at least one required role
69
+ const hasRequiredRole = roles.some((role) => userGroups.includes(role));
70
+ if (!hasRequiredRole) {
71
+ throw (0, node_1.json)(
72
+ {
73
+ error: {
74
+ code: 'FORBIDDEN',
75
+ message: `Required role: ${roles.join(' or ')}`,
76
+ },
77
+ },
78
+ { status: 403 }
79
+ );
80
+ }
81
+ return userId;
82
+ }
83
+ /**
84
+ * Checks if the user has a specific role.
85
+ *
86
+ * Does not throw errors or redirect. Returns false for unauthenticated users.
87
+ *
88
+ * @param request - Remix request object
89
+ * @param role - Role to check
90
+ * @param storage - Optional custom session storage
91
+ * @returns True if user has role, false otherwise
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * export async function loader({ request }: LoaderFunctionArgs) {
96
+ * const isAdmin = await hasRole(request, 'admin');
97
+ * const canEdit = await hasRole(request, 'editor');
98
+ * return json({ isAdmin, canEdit });
99
+ * }
100
+ * ```
101
+ */
102
+ async function hasRole(request, role, storage) {
103
+ try {
104
+ const session = await (0, session_server_js_1.getSession)(request, storage);
105
+ const userId = session.get('userId');
106
+ if (!userId) {
107
+ return false;
108
+ }
109
+ const userGroups = session.get('groups') ?? [];
110
+ return userGroups.includes(role);
111
+ } catch {
112
+ return false;
113
+ }
114
+ }
115
+ /**
116
+ * Checks if the user has any of the specified roles.
117
+ *
118
+ * Does not throw errors or redirect. Returns false for unauthenticated users.
119
+ *
120
+ * @param request - Remix request object
121
+ * @param roles - Roles to check
122
+ * @param storage - Optional custom session storage
123
+ * @returns True if user has any role, false otherwise
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * export async function loader({ request }: LoaderFunctionArgs) {
128
+ * const canManageContent = await hasAnyRole(request, ['admin', 'editor']);
129
+ * return json({ canManageContent });
130
+ * }
131
+ * ```
132
+ */
133
+ async function hasAnyRole(request, roles, storage) {
134
+ try {
135
+ const session = await (0, session_server_js_1.getSession)(request, storage);
136
+ const userId = session.get('userId');
137
+ if (!userId) {
138
+ return false;
139
+ }
140
+ const userGroups = session.get('groups') ?? [];
141
+ return roles.some((role) => userGroups.includes(role));
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
146
+ //# sourceMappingURL=admin.server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.server.js","sourceRoot":"","sources":["../../src/remix/admin.server.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AA2BH,oCAEC;AAwBD,kCA4BC;AAqBD,0BAkBC;AAoBD,gCAkBC;AA5JD,0CAAuC;AAEvC,2DAA8D;AAE9D;;;;;;;;;;;;;;;;;;;;GAoBG;AACI,KAAK,UAAU,YAAY,CAAC,OAAgB,EAAE,OAAwB;IAC3E,OAAO,WAAW,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACI,KAAK,UAAU,WAAW,CAC/B,OAAgB,EAChB,KAAe,EACf,OAAwB;IAExB,qCAAqC;IACrC,MAAM,MAAM,GAAG,MAAM,IAAA,+BAAW,EAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE7D,iCAAiC;IACjC,MAAM,OAAO,GAAG,MAAM,IAAA,8BAAU,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,UAAU,GAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAc,IAAI,EAAE,CAAC;IAE7D,+CAA+C;IAC/C,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAExE,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAA,WAAI,EACR;YACE,KAAK,EAAE;gBACL,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,kBAAkB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;aAChD;SACF,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACI,KAAK,UAAU,OAAO,CAC3B,OAAgB,EAChB,IAAY,EACZ,OAAwB;IAExB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,IAAA,8BAAU,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAuB,CAAC;QAE3D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,UAAU,GAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAc,IAAI,EAAE,CAAC;QAC7D,OAAO,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,UAAU,CAC9B,OAAgB,EAChB,KAAe,EACf,OAAwB;IAExB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,IAAA,8BAAU,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAuB,CAAC;QAE3D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,UAAU,GAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAc,IAAI,EAAE,CAAC;QAC7D,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Remix authentication utilities exports.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export {
7
+ createSessionStorage,
8
+ getSession,
9
+ createUserSession,
10
+ requireAuth,
11
+ getOptionalUser,
12
+ getUserSession,
13
+ logout,
14
+ resetDefaultStorage,
15
+ } from './session.server.js';
16
+ export { requireAdmin, requireRole, hasRole, hasAnyRole } from './admin.server.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/remix/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,cAAc,EACd,MAAM,EACN,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+ /**
3
+ * Remix authentication utilities exports.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ Object.defineProperty(exports, '__esModule', { value: true });
8
+ exports.hasAnyRole =
9
+ exports.hasRole =
10
+ exports.requireRole =
11
+ exports.requireAdmin =
12
+ exports.resetDefaultStorage =
13
+ exports.logout =
14
+ exports.getUserSession =
15
+ exports.getOptionalUser =
16
+ exports.requireAuth =
17
+ exports.createUserSession =
18
+ exports.getSession =
19
+ exports.createSessionStorage =
20
+ void 0;
21
+ var session_server_js_1 = require('./session.server.js');
22
+ Object.defineProperty(exports, 'createSessionStorage', {
23
+ enumerable: true,
24
+ get: function () {
25
+ return session_server_js_1.createSessionStorage;
26
+ },
27
+ });
28
+ Object.defineProperty(exports, 'getSession', {
29
+ enumerable: true,
30
+ get: function () {
31
+ return session_server_js_1.getSession;
32
+ },
33
+ });
34
+ Object.defineProperty(exports, 'createUserSession', {
35
+ enumerable: true,
36
+ get: function () {
37
+ return session_server_js_1.createUserSession;
38
+ },
39
+ });
40
+ Object.defineProperty(exports, 'requireAuth', {
41
+ enumerable: true,
42
+ get: function () {
43
+ return session_server_js_1.requireAuth;
44
+ },
45
+ });
46
+ Object.defineProperty(exports, 'getOptionalUser', {
47
+ enumerable: true,
48
+ get: function () {
49
+ return session_server_js_1.getOptionalUser;
50
+ },
51
+ });
52
+ Object.defineProperty(exports, 'getUserSession', {
53
+ enumerable: true,
54
+ get: function () {
55
+ return session_server_js_1.getUserSession;
56
+ },
57
+ });
58
+ Object.defineProperty(exports, 'logout', {
59
+ enumerable: true,
60
+ get: function () {
61
+ return session_server_js_1.logout;
62
+ },
63
+ });
64
+ Object.defineProperty(exports, 'resetDefaultStorage', {
65
+ enumerable: true,
66
+ get: function () {
67
+ return session_server_js_1.resetDefaultStorage;
68
+ },
69
+ });
70
+ var admin_server_js_1 = require('./admin.server.js');
71
+ Object.defineProperty(exports, 'requireAdmin', {
72
+ enumerable: true,
73
+ get: function () {
74
+ return admin_server_js_1.requireAdmin;
75
+ },
76
+ });
77
+ Object.defineProperty(exports, 'requireRole', {
78
+ enumerable: true,
79
+ get: function () {
80
+ return admin_server_js_1.requireRole;
81
+ },
82
+ });
83
+ Object.defineProperty(exports, 'hasRole', {
84
+ enumerable: true,
85
+ get: function () {
86
+ return admin_server_js_1.hasRole;
87
+ },
88
+ });
89
+ Object.defineProperty(exports, 'hasAnyRole', {
90
+ enumerable: true,
91
+ get: function () {
92
+ return admin_server_js_1.hasAnyRole;
93
+ },
94
+ });
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/remix/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,yDAS6B;AAR3B,yHAAA,oBAAoB,OAAA;AACpB,+GAAA,UAAU,OAAA;AACV,sHAAA,iBAAiB,OAAA;AACjB,gHAAA,WAAW,OAAA;AACX,oHAAA,eAAe,OAAA;AACf,mHAAA,cAAc,OAAA;AACd,2GAAA,MAAM,OAAA;AACN,wHAAA,mBAAmB,OAAA;AAGrB,qDAAmF;AAA1E,+GAAA,YAAY,OAAA;AAAE,8GAAA,WAAW,OAAA;AAAE,0GAAA,OAAO,OAAA;AAAE,6GAAA,UAAU,OAAA"}