@stackwright-pro/auth 0.2.0-alpha.13 → 0.2.0-alpha.15

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.
@@ -0,0 +1,334 @@
1
+ "use client";
2
+ import { createContext, useContext, useMemo } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var AuthContext = createContext(null);
6
+ function useAuth() {
7
+ const context = useContext(AuthContext);
8
+ if (!context) {
9
+ throw new Error("useAuth must be used within AuthProvider");
10
+ }
11
+ return context;
12
+ }
13
+ function useRequireAuth() {
14
+ const auth = useAuth();
15
+ if (!auth.isAuthenticated) {
16
+ console.warn("useRequireAuth: User is not authenticated");
17
+ return null;
18
+ }
19
+ return auth;
20
+ }
21
+
22
+ // src/rbac/rbac-engine.ts
23
+ var RBACEngine = class {
24
+ config;
25
+ rolePermissions;
26
+ constructor(config) {
27
+ this.config = config;
28
+ this.rolePermissions = this.buildRolePermissionMap();
29
+ }
30
+ /**
31
+ * Build internal map of role → permissions for fast lookups
32
+ */
33
+ buildRolePermissionMap() {
34
+ const map = /* @__PURE__ */ new Map();
35
+ for (const role of this.config.roles) {
36
+ map.set(role.name, new Set(role.permissions || []));
37
+ }
38
+ return map;
39
+ }
40
+ /**
41
+ * Check if user has a specific role
42
+ */
43
+ hasRole(user, role) {
44
+ return user.roles.includes(role);
45
+ }
46
+ /**
47
+ * Check if user has any of the specified roles
48
+ */
49
+ hasAnyRole(user, roles) {
50
+ return roles.some((role) => this.hasRole(user, role));
51
+ }
52
+ /**
53
+ * Check if user has all of the specified roles
54
+ */
55
+ hasAllRoles(user, roles) {
56
+ return roles.every((role) => this.hasRole(user, role));
57
+ }
58
+ /**
59
+ * Check if user has a specific permission
60
+ * Permissions are checked both:
61
+ * 1. Directly in user.permissions array
62
+ * 2. Through role-based permissions from config
63
+ */
64
+ hasPermission(user, permission) {
65
+ if (user.permissions?.includes(permission)) {
66
+ return true;
67
+ }
68
+ for (const role of user.roles) {
69
+ const permissions = this.rolePermissions.get(role);
70
+ if (permissions?.has(permission)) {
71
+ return true;
72
+ }
73
+ if (permissions) {
74
+ for (const p of permissions) {
75
+ if (p.endsWith(":*")) {
76
+ const prefix = p.slice(0, -1);
77
+ if (permission.startsWith(prefix)) {
78
+ return true;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ return false;
85
+ }
86
+ /**
87
+ * Check if user has any of the specified permissions
88
+ */
89
+ hasAnyPermission(user, permissions) {
90
+ return permissions.some((permission) => this.hasPermission(user, permission));
91
+ }
92
+ /**
93
+ * Check if user has all of the specified permissions
94
+ */
95
+ hasAllPermissions(user, permissions) {
96
+ return permissions.every((permission) => this.hasPermission(user, permission));
97
+ }
98
+ /**
99
+ * Check if route is public (no auth required)
100
+ */
101
+ isPublicRoute(path) {
102
+ if (!this.config.public_routes) {
103
+ return false;
104
+ }
105
+ return this.config.public_routes.some((publicPath) => {
106
+ return this.matchPath(path, publicPath);
107
+ });
108
+ }
109
+ /**
110
+ * Check if user can access a route based on protected_routes config
111
+ */
112
+ canAccessRoute(user, path) {
113
+ if (this.isPublicRoute(path)) {
114
+ return true;
115
+ }
116
+ if (!user) {
117
+ return false;
118
+ }
119
+ if (!this.config.protected_routes || this.config.protected_routes.length === 0) {
120
+ return true;
121
+ }
122
+ const matchingRoute = this.config.protected_routes.find((route) => {
123
+ return this.matchPath(path, route.path);
124
+ });
125
+ if (!matchingRoute) {
126
+ return true;
127
+ }
128
+ return this.hasAnyRole(user, matchingRoute.roles);
129
+ }
130
+ /**
131
+ * Check if user can access a component based on component auth config
132
+ */
133
+ canAccessComponent(user, authConfig) {
134
+ if (!authConfig) {
135
+ return true;
136
+ }
137
+ if (!user) {
138
+ return false;
139
+ }
140
+ if (authConfig.required_roles && authConfig.required_roles.length > 0) {
141
+ if (!this.hasAnyRole(user, authConfig.required_roles)) {
142
+ return false;
143
+ }
144
+ }
145
+ if (authConfig.required_permissions && authConfig.required_permissions.length > 0) {
146
+ if (!this.hasAllPermissions(user, authConfig.required_permissions)) {
147
+ return false;
148
+ }
149
+ }
150
+ return true;
151
+ }
152
+ // Match a path against a pattern with wildcard support
153
+ matchPath(path, pattern) {
154
+ if (path === pattern) {
155
+ return true;
156
+ }
157
+ if (!pattern.includes("*")) {
158
+ return false;
159
+ }
160
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
161
+ const regex = new RegExp(`^${escaped}$`);
162
+ return regex.test(path);
163
+ }
164
+ };
165
+ function AuthProvider({
166
+ user,
167
+ session,
168
+ rbacConfig,
169
+ isLoading = false,
170
+ children
171
+ }) {
172
+ const rbac = useMemo(() => new RBACEngine(rbacConfig), [rbacConfig]);
173
+ const value = useMemo(
174
+ () => ({
175
+ user,
176
+ session,
177
+ isAuthenticated: user !== null,
178
+ isLoading,
179
+ hasRole: (role) => {
180
+ if (!user) return false;
181
+ return rbac.hasRole(user, role);
182
+ },
183
+ hasPermission: (permission) => {
184
+ if (!user) return false;
185
+ return rbac.hasPermission(user, permission);
186
+ },
187
+ hasAnyRole: (roles) => {
188
+ if (!user) return false;
189
+ return rbac.hasAnyRole(user, roles);
190
+ },
191
+ hasAllPermissions: (permissions) => {
192
+ if (!user) return false;
193
+ return rbac.hasAllPermissions(user, permissions);
194
+ }
195
+ }),
196
+ [user, session, isLoading, rbac]
197
+ );
198
+ return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
199
+ }
200
+ var FallbackComponents = {
201
+ /**
202
+ * Hide component (render nothing)
203
+ */
204
+ hide: () => null,
205
+ /**
206
+ * Show placeholder message
207
+ */
208
+ placeholder: ({ className }) => /* @__PURE__ */ jsx(
209
+ "div",
210
+ {
211
+ className: className || "auth-placeholder",
212
+ style: {
213
+ padding: "1rem",
214
+ border: "1px dashed #ccc",
215
+ borderRadius: "4px",
216
+ color: "#666",
217
+ fontStyle: "italic",
218
+ textAlign: "center"
219
+ },
220
+ children: "Content requires authorization"
221
+ }
222
+ ),
223
+ /**
224
+ * Show custom message
225
+ */
226
+ message: ({ message, className }) => /* @__PURE__ */ jsx(
227
+ "div",
228
+ {
229
+ className: className || "auth-message",
230
+ style: {
231
+ padding: "1rem",
232
+ border: "1px solid #f0ad4e",
233
+ borderRadius: "4px",
234
+ backgroundColor: "#fcf8e3",
235
+ color: "#8a6d3b"
236
+ },
237
+ children: message || "Unauthorized"
238
+ }
239
+ )
240
+ };
241
+ function withAuth(Component, authConfig) {
242
+ if (!authConfig) {
243
+ return Component;
244
+ }
245
+ const WrappedComponent = (props) => {
246
+ const auth = useAuth();
247
+ if (authConfig.required_roles && authConfig.required_roles.length > 0) {
248
+ if (!auth.hasAnyRole(authConfig.required_roles)) {
249
+ return renderFallback(authConfig);
250
+ }
251
+ }
252
+ if (authConfig.required_permissions && authConfig.required_permissions.length > 0) {
253
+ if (!auth.hasAllPermissions(authConfig.required_permissions)) {
254
+ return renderFallback(authConfig);
255
+ }
256
+ }
257
+ return /* @__PURE__ */ jsx(Component, { ...props });
258
+ };
259
+ const componentName = Component.displayName || Component.name || "Component";
260
+ WrappedComponent.displayName = `withAuth(${componentName})`;
261
+ return WrappedComponent;
262
+ }
263
+ function renderFallback(authConfig) {
264
+ const fallbackType = authConfig.fallback || "hide";
265
+ switch (fallbackType) {
266
+ case "hide":
267
+ return FallbackComponents.hide();
268
+ case "placeholder":
269
+ return FallbackComponents.placeholder({});
270
+ case "message":
271
+ return FallbackComponents.message({
272
+ message: authConfig.fallback_message
273
+ });
274
+ default:
275
+ return null;
276
+ }
277
+ }
278
+ function withAuthFallback(Component, authConfig, FallbackComponent) {
279
+ const WrappedComponent = (props) => {
280
+ const auth = useAuth();
281
+ const isAuthorized = checkAuthorization(auth, authConfig);
282
+ if (!isAuthorized) {
283
+ return /* @__PURE__ */ jsx(FallbackComponent, {});
284
+ }
285
+ return /* @__PURE__ */ jsx(Component, { ...props });
286
+ };
287
+ const componentName = Component.displayName || Component.name || "Component";
288
+ WrappedComponent.displayName = `withAuthFallback(${componentName})`;
289
+ return WrappedComponent;
290
+ }
291
+ function checkAuthorization(auth, authConfig) {
292
+ if (authConfig.required_roles && authConfig.required_roles.length > 0) {
293
+ if (!auth.hasAnyRole(authConfig.required_roles)) {
294
+ return false;
295
+ }
296
+ }
297
+ if (authConfig.required_permissions && authConfig.required_permissions.length > 0) {
298
+ if (!auth.hasAllPermissions(authConfig.required_permissions)) {
299
+ return false;
300
+ }
301
+ }
302
+ return true;
303
+ }
304
+
305
+ // src/registration.ts
306
+ var authDecoratorRegistry = {
307
+ decorator: null
308
+ };
309
+ function registerAuthDecorator() {
310
+ authDecoratorRegistry.decorator = withAuth;
311
+ if (typeof window !== "undefined" && window.__STACKWRIGHT_DEBUG__) {
312
+ console.log("\u{1F510} Auth decorator registered");
313
+ }
314
+ }
315
+ function getAuthDecorator() {
316
+ return authDecoratorRegistry.decorator;
317
+ }
318
+ function maybeWrapWithAuth(Component, authConfig) {
319
+ const decorator = getAuthDecorator();
320
+ if (!decorator || !authConfig) {
321
+ return Component;
322
+ }
323
+ return decorator(Component, authConfig);
324
+ }
325
+ function hasAuthConfig(item) {
326
+ if (!item || typeof item !== "object") {
327
+ return false;
328
+ }
329
+ return "auth" in item;
330
+ }
331
+
332
+ export { AuthContext, AuthProvider, getAuthDecorator, hasAuthConfig, maybeWrapWithAuth, registerAuthDecorator, useAuth, useRequireAuth, withAuth, withAuthFallback };
333
+ //# sourceMappingURL=client.mjs.map
334
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context/AuthContext.tsx","../src/rbac/rbac-engine.ts","../src/context/AuthProvider.tsx","../src/decorators/withAuth.tsx","../src/registration.ts"],"names":["jsx"],"mappings":"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","file":"client.mjs","sourcesContent":["import { createContext, useContext } from 'react';\nimport type { AuthUser, AuthSession } from '../types/index.js';\n\n/**\n * Auth context value provided to component tree\n */\nexport interface AuthContextValue {\n /**\n * Currently authenticated user (null if not authenticated)\n */\n user: AuthUser | null;\n\n /**\n * Current session (null if not authenticated)\n */\n session: AuthSession | null;\n\n /**\n * Whether user is authenticated\n */\n isAuthenticated: boolean;\n\n /**\n * Whether auth is still loading\n */\n isLoading: boolean;\n\n /**\n * Check if user has a specific role\n */\n hasRole: (role: string) => boolean;\n\n /**\n * Check if user has a specific permission\n */\n hasPermission: (permission: string) => boolean;\n\n /**\n * Check if user has any of the specified roles\n */\n hasAnyRole: (roles: string[]) => boolean;\n\n /**\n * Check if user has all of the specified permissions\n */\n hasAllPermissions: (permissions: string[]) => boolean;\n}\n\n/**\n * Auth context - provides authentication state to components\n */\nexport const AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Hook to access auth context\n * Throws error if used outside AuthProvider\n */\nexport function useAuth(): AuthContextValue {\n const context = useContext(AuthContext);\n\n if (!context) {\n throw new Error('useAuth must be used within AuthProvider');\n }\n\n return context;\n}\n\n/**\n * Hook to require authentication\n * Returns null and logs warning if not authenticated\n */\nexport function useRequireAuth(): AuthContextValue | null {\n const auth = useAuth();\n\n if (!auth.isAuthenticated) {\n console.warn('useRequireAuth: User is not authenticated');\n return null;\n }\n\n return auth;\n}\n","import type { AuthUser, RBACConfig, ComponentAuthConfig } from '../types/index.js';\n\n/**\n * RBAC Engine for checking roles and permissions\n */\nexport class RBACEngine {\n private config: RBACConfig;\n private rolePermissions: Map<string, Set<string>>;\n\n constructor(config: RBACConfig) {\n this.config = config;\n this.rolePermissions = this.buildRolePermissionMap();\n }\n\n /**\n * Build internal map of role → permissions for fast lookups\n */\n private buildRolePermissionMap(): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n\n for (const role of this.config.roles) {\n map.set(role.name, new Set(role.permissions || []));\n }\n\n return map;\n }\n\n /**\n * Check if user has a specific role\n */\n hasRole(user: AuthUser, role: string): boolean {\n return user.roles.includes(role);\n }\n\n /**\n * Check if user has any of the specified roles\n */\n hasAnyRole(user: AuthUser, roles: string[]): boolean {\n return roles.some((role) => this.hasRole(user, role));\n }\n\n /**\n * Check if user has all of the specified roles\n */\n hasAllRoles(user: AuthUser, roles: string[]): boolean {\n return roles.every((role) => this.hasRole(user, role));\n }\n\n /**\n * Check if user has a specific permission\n * Permissions are checked both:\n * 1. Directly in user.permissions array\n * 2. Through role-based permissions from config\n */\n hasPermission(user: AuthUser, permission: string): boolean {\n // Check direct permissions\n if (user.permissions?.includes(permission)) {\n return true;\n }\n\n // Check role-based permissions\n for (const role of user.roles) {\n const permissions = this.rolePermissions.get(role);\n if (permissions?.has(permission)) {\n return true;\n }\n\n // Check wildcard permissions (e.g., admin:* grants admin:read, admin:write)\n if (permissions) {\n for (const p of permissions) {\n if (p.endsWith(':*')) {\n const prefix = p.slice(0, -1); // Remove the *\n if (permission.startsWith(prefix)) {\n return true;\n }\n }\n }\n }\n }\n\n return false;\n }\n\n /**\n * Check if user has any of the specified permissions\n */\n hasAnyPermission(user: AuthUser, permissions: string[]): boolean {\n return permissions.some((permission) => this.hasPermission(user, permission));\n }\n\n /**\n * Check if user has all of the specified permissions\n */\n hasAllPermissions(user: AuthUser, permissions: string[]): boolean {\n return permissions.every((permission) => this.hasPermission(user, permission));\n }\n\n /**\n * Check if route is public (no auth required)\n */\n isPublicRoute(path: string): boolean {\n if (!this.config.public_routes) {\n return false;\n }\n\n return this.config.public_routes.some((publicPath) => {\n return this.matchPath(path, publicPath);\n });\n }\n\n /**\n * Check if user can access a route based on protected_routes config\n */\n canAccessRoute(user: AuthUser | null, path: string): boolean {\n // Check if route is public\n if (this.isPublicRoute(path)) {\n return true;\n }\n\n // No user = can't access protected routes\n if (!user) {\n return false;\n }\n\n // If no protected routes configured, allow by default\n if (!this.config.protected_routes || this.config.protected_routes.length === 0) {\n return true;\n }\n\n // Find matching protected route\n const matchingRoute = this.config.protected_routes.find((route) => {\n return this.matchPath(path, route.path);\n });\n\n // If no matching protected route, allow (route not explicitly protected)\n if (!matchingRoute) {\n return true;\n }\n\n // Check if user has required role for this route\n return this.hasAnyRole(user, matchingRoute.roles);\n }\n\n /**\n * Check if user can access a component based on component auth config\n */\n canAccessComponent(user: AuthUser | null, authConfig: ComponentAuthConfig | undefined): boolean {\n // No auth config = public component\n if (!authConfig) {\n return true;\n }\n\n // No user = can't access protected component\n if (!user) {\n return false;\n }\n\n // Check required roles\n if (authConfig.required_roles && authConfig.required_roles.length > 0) {\n if (!this.hasAnyRole(user, authConfig.required_roles)) {\n return false;\n }\n }\n\n // Check required permissions\n if (authConfig.required_permissions && authConfig.required_permissions.length > 0) {\n if (!this.hasAllPermissions(user, authConfig.required_permissions)) {\n return false;\n }\n }\n\n return true;\n }\n\n // Match a path against a pattern with wildcard support\n private matchPath(path: string, pattern: string): boolean {\n // Exact match\n if (path === pattern) {\n return true;\n }\n\n // No wildcards in pattern — only exact match applies\n if (!pattern.includes('*')) {\n return false;\n }\n\n // Escape ALL regex metacharacters, then convert glob * to .*\n // This prevents ReDoS from crafted route patterns in YAML config\n const escaped = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex specials\n .replace(/\\*/g, '.*'); // Convert glob * to .*\n\n const regex = new RegExp(`^${escaped}$`);\n return regex.test(path);\n }\n}\n","import React, { useMemo, type ReactNode, type ReactElement } from 'react';\nimport { AuthContext, type AuthContextValue } from './AuthContext.js';\nimport { RBACEngine } from '../rbac/rbac-engine.js';\nimport type { AuthUser, AuthSession, RBACConfig } from '../types/index.js';\n\nexport interface AuthProviderProps {\n /**\n * Current authenticated user (null if not authenticated)\n */\n user: AuthUser | null;\n\n /**\n * Current session (null if not authenticated)\n */\n session: AuthSession | null;\n\n /**\n * RBAC configuration for role/permission checking\n */\n rbacConfig: RBACConfig;\n\n /**\n * Whether auth is still loading\n */\n isLoading?: boolean;\n\n /**\n * Child components\n */\n children: ReactNode;\n}\n\n/**\n * AuthProvider - Provides authentication state to component tree\n *\n * @example\n * ```tsx\n * <AuthProvider user={user} session={session} rbacConfig={config}>\n * <App />\n * </AuthProvider>\n * ```\n */\nexport function AuthProvider({\n user,\n session,\n rbacConfig,\n isLoading = false,\n children,\n}: AuthProviderProps): ReactElement {\n // Create RBAC engine for role/permission checking\n const rbac = useMemo(() => new RBACEngine(rbacConfig), [rbacConfig]);\n\n // Build context value with helper functions\n const value: AuthContextValue = useMemo(\n () => ({\n user,\n session,\n isAuthenticated: user !== null,\n isLoading,\n\n hasRole: (role: string) => {\n if (!user) return false;\n return rbac.hasRole(user, role);\n },\n\n hasPermission: (permission: string) => {\n if (!user) return false;\n return rbac.hasPermission(user, permission);\n },\n\n hasAnyRole: (roles: string[]) => {\n if (!user) return false;\n return rbac.hasAnyRole(user, roles);\n },\n\n hasAllPermissions: (permissions: string[]) => {\n if (!user) return false;\n return rbac.hasAllPermissions(user, permissions);\n },\n }),\n [user, session, isLoading, rbac]\n );\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n","import React from 'react';\nimport { useAuth } from '../context/AuthContext.js';\nimport type { ComponentAuthConfig } from '../types/index.js';\n\n/**\n * Props that any component wrapped with withAuth will receive\n */\nexport interface ComponentProps {\n id?: string;\n [key: string]: any;\n}\n\n/**\n * Fallback component props\n */\ninterface FallbackProps {\n message?: string;\n className?: string;\n}\n\n/**\n * Default fallback components for unauthorized access\n */\nconst FallbackComponents = {\n /**\n * Hide component (render nothing)\n */\n hide: () => null,\n\n /**\n * Show placeholder message\n */\n placeholder: ({ className }: FallbackProps) => (\n <div\n className={className || 'auth-placeholder'}\n style={{\n padding: '1rem',\n border: '1px dashed #ccc',\n borderRadius: '4px',\n color: '#666',\n fontStyle: 'italic',\n textAlign: 'center',\n }}\n >\n Content requires authorization\n </div>\n ),\n\n /**\n * Show custom message\n */\n message: ({ message, className }: FallbackProps) => (\n <div\n className={className || 'auth-message'}\n style={{\n padding: '1rem',\n border: '1px solid #f0ad4e',\n borderRadius: '4px',\n backgroundColor: '#fcf8e3',\n color: '#8a6d3b',\n }}\n >\n {message || 'Unauthorized'}\n </div>\n ),\n};\n\n/**\n * Higher-order component that wraps a component with authentication checks\n *\n * @example\n * ```tsx\n * const ProtectedButton = withAuth(Button, {\n * required_roles: ['ADMIN'],\n * fallback: 'message',\n * fallback_message: 'Only admins can see this button'\n * });\n * ```\n *\n * @param Component - The component to wrap\n * @param authConfig - Authentication requirements from YAML\n * @returns Wrapped component with auth enforcement\n */\nexport function withAuth<P extends ComponentProps>(\n Component: React.ComponentType<P>,\n authConfig?: ComponentAuthConfig\n): React.ComponentType<P> {\n // If no auth config, return component unchanged\n if (!authConfig) {\n return Component;\n }\n\n // Create wrapped component with display name for debugging\n const WrappedComponent = (props: P) => {\n const auth = useAuth();\n\n // Check role requirements\n if (authConfig.required_roles && authConfig.required_roles.length > 0) {\n if (!auth.hasAnyRole(authConfig.required_roles)) {\n return renderFallback(authConfig);\n }\n }\n\n // Check permission requirements (must have ALL permissions)\n if (authConfig.required_permissions && authConfig.required_permissions.length > 0) {\n if (!auth.hasAllPermissions(authConfig.required_permissions)) {\n return renderFallback(authConfig);\n }\n }\n\n // User authorized - render component\n return <Component {...props} />;\n };\n\n // Set display name for React DevTools\n const componentName = Component.displayName || Component.name || 'Component';\n WrappedComponent.displayName = `withAuth(${componentName})`;\n\n return WrappedComponent;\n}\n\n/**\n * Render fallback component based on configuration\n */\nfunction renderFallback(authConfig: ComponentAuthConfig): React.ReactElement | null {\n const fallbackType = authConfig.fallback || 'hide';\n\n switch (fallbackType) {\n case 'hide':\n return FallbackComponents.hide();\n\n case 'placeholder':\n return FallbackComponents.placeholder({});\n\n case 'message':\n return FallbackComponents.message({\n message: authConfig.fallback_message,\n });\n\n default:\n return null;\n }\n}\n\n/**\n * Custom fallback component (for advanced use cases)\n */\nexport function withAuthFallback<P extends ComponentProps>(\n Component: React.ComponentType<P>,\n authConfig: ComponentAuthConfig,\n FallbackComponent: React.ComponentType<any>\n): React.ComponentType<P> {\n const WrappedComponent = (props: P) => {\n const auth = useAuth();\n\n // Check authorization\n const isAuthorized = checkAuthorization(auth, authConfig);\n\n if (!isAuthorized) {\n return <FallbackComponent />;\n }\n\n return <Component {...props} />;\n };\n\n const componentName = Component.displayName || Component.name || 'Component';\n WrappedComponent.displayName = `withAuthFallback(${componentName})`;\n\n return WrappedComponent;\n}\n\n/**\n * Check if user is authorized based on auth config\n */\nfunction checkAuthorization(\n auth: ReturnType<typeof useAuth>,\n authConfig: ComponentAuthConfig\n): boolean {\n // Check roles\n if (authConfig.required_roles && authConfig.required_roles.length > 0) {\n if (!auth.hasAnyRole(authConfig.required_roles)) {\n return false;\n }\n }\n\n // Check permissions\n if (authConfig.required_permissions && authConfig.required_permissions.length > 0) {\n if (!auth.hasAllPermissions(authConfig.required_permissions)) {\n return false;\n }\n }\n\n return true;\n}\n","import { withAuth } from './decorators/withAuth.js';\nimport type { ComponentAuthConfig } from './types/index.js';\nimport type React from 'react';\n\n/**\n * Global registry for auth decorator function\n * This allows OSS packages to optionally use auth without depending on it\n *\n * Design principle: No hard dependencies!\n * - OSS core never imports from pro packages\n * - Pro package registers itself at runtime\n * - User app is the glue that connects them\n */\nconst authDecoratorRegistry: {\n decorator: typeof withAuth | null;\n} = {\n decorator: null,\n};\n\n/**\n * Register the auth decorator for use by content renderers\n *\n * This should be called once in your app's initialization (e.g., _app.tsx)\n * to enable auth-aware component rendering.\n *\n * @example\n * ```tsx\n * // In pages/_app.tsx\n * import { registerAuthDecorator } from '@stackwright-pro/auth';\n *\n * registerAuthDecorator();\n *\n * function MyApp({ Component, pageProps }: AppProps) {\n * return (\n * <AuthProvider {...authProps}>\n * <Component {...pageProps} />\n * </AuthProvider>\n * );\n * }\n * ```\n */\nexport function registerAuthDecorator(): void {\n authDecoratorRegistry.decorator = withAuth;\n\n // Debug logging (only in browser with debug flag)\n if (typeof window !== 'undefined' && (window as any).__STACKWRIGHT_DEBUG__) {\n console.log('🔐 Auth decorator registered');\n }\n}\n\n/**\n * Get the registered auth decorator (for use by content renderers)\n *\n * Returns null if auth is not registered, allowing graceful degradation.\n * This is the function that OSS core would call to check if auth is available.\n *\n * @returns The withAuth decorator function, or null if not registered\n */\nexport function getAuthDecorator(): typeof withAuth | null {\n return authDecoratorRegistry.decorator;\n}\n\n/**\n * Wrap a component with auth if decorator is registered and config exists\n *\n * This is a safe wrapper that OSS packages can use without depending on auth.\n * If auth is not registered, returns the original component unchanged.\n * If auth config is missing/undefined, returns the original component unchanged.\n *\n * @example\n * ```tsx\n * // In content renderer (can live in OSS core!)\\n * function renderContentItem(item: ContentItem) {\n * const Component = getComponentFromRegistry(item.type);\n *\n * // Apply auth if available\n * const WrappedComponent = maybeWrapWithAuth(Component, item.auth);\n *\n * return <WrappedComponent {...item} />;\n * }\n * ```\n *\n * @param Component - Component to wrap\n * @param authConfig - Auth configuration from YAML (optional)\n * @returns Wrapped component if auth is registered, original component otherwise\n */\nexport function maybeWrapWithAuth<P extends { id?: string; [key: string]: any }>(\n Component: React.ComponentType<P>,\n authConfig?: ComponentAuthConfig\n): React.ComponentType<P> {\n const decorator = getAuthDecorator();\n\n // No decorator registered or no auth config = return unwrapped\n // This ensures graceful degradation when auth isn't installed\n if (!decorator || !authConfig) {\n return Component;\n }\n\n // Wrap with auth\n return decorator(Component, authConfig);\n}\n\n/**\n * Type guard to check if content item has auth config\n *\n * Useful for conditionally applying auth in renderers without\n * needing to import auth types.\n *\n * @example\n * ```tsx\n * if (hasAuthConfig(item)) {\n * // TypeScript knows item.auth exists\n * WrappedComponent = maybeWrapWithAuth(Component, item.auth);\n * }\n * ```\n */\nexport function hasAuthConfig(item: any): item is { auth: ComponentAuthConfig } {\n // Explicitly return boolean to handle null/undefined properly\n if (!item || typeof item !== 'object') {\n return false;\n }\n return 'auth' in item;\n}\n"]}
package/dist/index.d.mts CHANGED
@@ -1,8 +1,6 @@
1
- import { AuthProvider as AuthProvider$1, PKIConfig, AuthContext as AuthContext$1, AuthUser, AuthSession, OIDCConfig, RBACConfig, ComponentAuthConfig, CertRevocationConfig } from '@stackwright-pro/types';
1
+ import { AuthProvider, PKIConfig, AuthContext, AuthUser, AuthSession, OIDCConfig, RBACConfig, ComponentAuthConfig, CertRevocationConfig } from '@stackwright-pro/types';
2
2
  export { AuthConfig, AuthSession, AuthUser, CertRevocationConfig, ComponentAuthConfig, OIDCConfig, PKIConfig, RBACConfig, authConfigSchema, authSessionSchema, authUserSchema, componentAuthSchema, oidcConfigSchema, pkiConfigSchema, rbacConfigSchema } from '@stackwright-pro/types';
3
3
  import { X509Certificate } from '@peculiar/x509';
4
- import * as React from 'react';
5
- import React__default, { ReactNode, ReactElement } from 'react';
6
4
 
7
5
  /**
8
6
  * Authentication Audit Logging
@@ -107,12 +105,12 @@ declare function createAuditEvent(type: AuditEventType, outcome: 'success' | 'fa
107
105
  * - Pluggable audit logging for SIEM integration
108
106
  */
109
107
 
110
- declare class PKIProvider implements AuthProvider$1 {
108
+ declare class PKIProvider implements AuthProvider {
111
109
  private config;
112
110
  private auditLogger?;
113
111
  private revocationChecker;
114
112
  constructor(config: PKIConfig, auditLogger?: AuditLogger);
115
- authenticate(context: AuthContext$1): Promise<AuthUser | null>;
113
+ authenticate(context: AuthContext): Promise<AuthUser | null>;
116
114
  validate(session: AuthSession): Promise<boolean>;
117
115
  /**
118
116
  * Extract roles from certificate based on organizational units
@@ -160,7 +158,7 @@ interface AuthorizationRequest {
160
158
  * - Session refresh
161
159
  * - Provider-specific quirks (Keycloak, Cognito, etc.)
162
160
  */
163
- declare class OIDCProvider implements AuthProvider$1 {
161
+ declare class OIDCProvider implements AuthProvider {
164
162
  private config;
165
163
  private metadata;
166
164
  private auditLogger?;
@@ -190,7 +188,7 @@ declare class OIDCProvider implements AuthProvider$1 {
190
188
  * @returns Authenticated user or null if no code present
191
189
  * @throws Error if state mismatch (CSRF) or token exchange/validation fails
192
190
  */
193
- authenticate(context: AuthContext$1): Promise<AuthUser | null>;
191
+ authenticate(context: AuthContext): Promise<AuthUser | null>;
194
192
  /**
195
193
  * Validate session (check if token is still valid)
196
194
  *
@@ -771,92 +769,6 @@ declare class RBACEngine {
771
769
  private matchPath;
772
770
  }
773
771
 
774
- /**
775
- * Auth context value provided to component tree
776
- */
777
- interface AuthContextValue {
778
- /**
779
- * Currently authenticated user (null if not authenticated)
780
- */
781
- user: AuthUser | null;
782
- /**
783
- * Current session (null if not authenticated)
784
- */
785
- session: AuthSession | null;
786
- /**
787
- * Whether user is authenticated
788
- */
789
- isAuthenticated: boolean;
790
- /**
791
- * Whether auth is still loading
792
- */
793
- isLoading: boolean;
794
- /**
795
- * Check if user has a specific role
796
- */
797
- hasRole: (role: string) => boolean;
798
- /**
799
- * Check if user has a specific permission
800
- */
801
- hasPermission: (permission: string) => boolean;
802
- /**
803
- * Check if user has any of the specified roles
804
- */
805
- hasAnyRole: (roles: string[]) => boolean;
806
- /**
807
- * Check if user has all of the specified permissions
808
- */
809
- hasAllPermissions: (permissions: string[]) => boolean;
810
- }
811
- /**
812
- * Auth context - provides authentication state to components
813
- */
814
- declare const AuthContext: React.Context<AuthContextValue | null>;
815
- /**
816
- * Hook to access auth context
817
- * Throws error if used outside AuthProvider
818
- */
819
- declare function useAuth(): AuthContextValue;
820
- /**
821
- * Hook to require authentication
822
- * Returns null and logs warning if not authenticated
823
- */
824
- declare function useRequireAuth(): AuthContextValue | null;
825
-
826
- interface AuthProviderProps {
827
- /**
828
- * Current authenticated user (null if not authenticated)
829
- */
830
- user: AuthUser | null;
831
- /**
832
- * Current session (null if not authenticated)
833
- */
834
- session: AuthSession | null;
835
- /**
836
- * RBAC configuration for role/permission checking
837
- */
838
- rbacConfig: RBACConfig;
839
- /**
840
- * Whether auth is still loading
841
- */
842
- isLoading?: boolean;
843
- /**
844
- * Child components
845
- */
846
- children: ReactNode;
847
- }
848
- /**
849
- * AuthProvider - Provides authentication state to component tree
850
- *
851
- * @example
852
- * ```tsx
853
- * <AuthProvider user={user} session={session} rbacConfig={config}>
854
- * <App />
855
- * </AuthProvider>
856
- * ```
857
- */
858
- declare function AuthProvider({ user, session, rbacConfig, isLoading, children, }: AuthProviderProps): ReactElement;
859
-
860
772
  /**
861
773
  * HMAC Header Signing for PKI Gateway Authentication
862
774
  *
@@ -1072,110 +984,4 @@ declare function createDoDCACConfig(overrides?: Partial<PKIConfig>): PKIConfig;
1072
984
  */
1073
985
  declare function createDoDCACDevConfig(): PKIConfig;
1074
986
 
1075
- /**
1076
- * Props that any component wrapped with withAuth will receive
1077
- */
1078
- interface ComponentProps {
1079
- id?: string;
1080
- [key: string]: any;
1081
- }
1082
- /**
1083
- * Higher-order component that wraps a component with authentication checks
1084
- *
1085
- * @example
1086
- * ```tsx
1087
- * const ProtectedButton = withAuth(Button, {
1088
- * required_roles: ['ADMIN'],
1089
- * fallback: 'message',
1090
- * fallback_message: 'Only admins can see this button'
1091
- * });
1092
- * ```
1093
- *
1094
- * @param Component - The component to wrap
1095
- * @param authConfig - Authentication requirements from YAML
1096
- * @returns Wrapped component with auth enforcement
1097
- */
1098
- declare function withAuth<P extends ComponentProps>(Component: React__default.ComponentType<P>, authConfig?: ComponentAuthConfig): React__default.ComponentType<P>;
1099
- /**
1100
- * Custom fallback component (for advanced use cases)
1101
- */
1102
- declare function withAuthFallback<P extends ComponentProps>(Component: React__default.ComponentType<P>, authConfig: ComponentAuthConfig, FallbackComponent: React__default.ComponentType<any>): React__default.ComponentType<P>;
1103
-
1104
- /**
1105
- * Register the auth decorator for use by content renderers
1106
- *
1107
- * This should be called once in your app's initialization (e.g., _app.tsx)
1108
- * to enable auth-aware component rendering.
1109
- *
1110
- * @example
1111
- * ```tsx
1112
- * // In pages/_app.tsx
1113
- * import { registerAuthDecorator } from '@stackwright-pro/auth';
1114
- *
1115
- * registerAuthDecorator();
1116
- *
1117
- * function MyApp({ Component, pageProps }: AppProps) {
1118
- * return (
1119
- * <AuthProvider {...authProps}>
1120
- * <Component {...pageProps} />
1121
- * </AuthProvider>
1122
- * );
1123
- * }
1124
- * ```
1125
- */
1126
- declare function registerAuthDecorator(): void;
1127
- /**
1128
- * Get the registered auth decorator (for use by content renderers)
1129
- *
1130
- * Returns null if auth is not registered, allowing graceful degradation.
1131
- * This is the function that OSS core would call to check if auth is available.
1132
- *
1133
- * @returns The withAuth decorator function, or null if not registered
1134
- */
1135
- declare function getAuthDecorator(): typeof withAuth | null;
1136
- /**
1137
- * Wrap a component with auth if decorator is registered and config exists
1138
- *
1139
- * This is a safe wrapper that OSS packages can use without depending on auth.
1140
- * If auth is not registered, returns the original component unchanged.
1141
- * If auth config is missing/undefined, returns the original component unchanged.
1142
- *
1143
- * @example
1144
- * ```tsx
1145
- * // In content renderer (can live in OSS core!)\n * function renderContentItem(item: ContentItem) {
1146
- * const Component = getComponentFromRegistry(item.type);
1147
- *
1148
- * // Apply auth if available
1149
- * const WrappedComponent = maybeWrapWithAuth(Component, item.auth);
1150
- *
1151
- * return <WrappedComponent {...item} />;
1152
- * }
1153
- * ```
1154
- *
1155
- * @param Component - Component to wrap
1156
- * @param authConfig - Auth configuration from YAML (optional)
1157
- * @returns Wrapped component if auth is registered, original component otherwise
1158
- */
1159
- declare function maybeWrapWithAuth<P extends {
1160
- id?: string;
1161
- [key: string]: any;
1162
- }>(Component: React__default.ComponentType<P>, authConfig?: ComponentAuthConfig): React__default.ComponentType<P>;
1163
- /**
1164
- * Type guard to check if content item has auth config
1165
- *
1166
- * Useful for conditionally applying auth in renderers without
1167
- * needing to import auth types.
1168
- *
1169
- * @example
1170
- * ```tsx
1171
- * if (hasAuthConfig(item)) {
1172
- * // TypeScript knows item.auth exists
1173
- * WrappedComponent = maybeWrapWithAuth(Component, item.auth);
1174
- * }
1175
- * ```
1176
- */
1177
- declare function hasAuthConfig(item: any): item is {
1178
- auth: ComponentAuthConfig;
1179
- };
1180
-
1181
- export { type AuditEvent, AuditEventType, type AuditLogger, AuthContext, type AuthContextValue, AuthProvider, type AuthProviderProps, type AuthorizationRequest, type BuildAuthorizationUrlOptions, CRLRevocationChecker, type CertRevocationChecker, type ComponentProps, CompositeAuditLogger, CompositeRevocationChecker, ConsoleAuditLogger, type CookieOptions, DOD_CAC_PROFILE, type HeaderSigningConfig, InMemoryRevocationStore, KeycloakAdapter, NoopAuditLogger, OCSPRevocationChecker, type OIDCMetadata, OIDCProvider, PKIProvider, type ParsedCertificate, RBACEngine, RevocationCache, type RevocationInput, type RevocationStatus, type RevocationStore, SessionManager, type SessionManagerConfig, SkipRevocationChecker, type TokenSet, buildAuthorizationUrl, clearCookie, createAuditEvent, createDoDCACConfig, createDoDCACDevConfig, createRevocationChecker, decryptToken, deriveEncryptionKey, discoverOIDC, encryptToken, exchangeCodeForTokens, extractEDIPI, generateCodeChallenge, generateCodeVerifier, generateJti, generateNonce, generateState, getAuthDecorator, hasAuthConfig, maybeWrapWithAuth, normalizeSerial, parseCertFromHeaders, parseCertificate, parseCookies, refreshAccessToken, registerAuthDecorator, serializeCookie, signCertHeaders, useAuth, useRequireAuth, validateDoDCAC, validateIdToken, verifyCertHeaders, verifyState, withAuth, withAuthFallback };
987
+ export { type AuditEvent, AuditEventType, type AuditLogger, type AuthorizationRequest, type BuildAuthorizationUrlOptions, CRLRevocationChecker, type CertRevocationChecker, CompositeAuditLogger, CompositeRevocationChecker, ConsoleAuditLogger, type CookieOptions, DOD_CAC_PROFILE, type HeaderSigningConfig, InMemoryRevocationStore, KeycloakAdapter, NoopAuditLogger, OCSPRevocationChecker, type OIDCMetadata, OIDCProvider, PKIProvider, type ParsedCertificate, RBACEngine, RevocationCache, type RevocationInput, type RevocationStatus, type RevocationStore, SessionManager, type SessionManagerConfig, SkipRevocationChecker, type TokenSet, buildAuthorizationUrl, clearCookie, createAuditEvent, createDoDCACConfig, createDoDCACDevConfig, createRevocationChecker, decryptToken, deriveEncryptionKey, discoverOIDC, encryptToken, exchangeCodeForTokens, extractEDIPI, generateCodeChallenge, generateCodeVerifier, generateJti, generateNonce, generateState, normalizeSerial, parseCertFromHeaders, parseCertificate, parseCookies, refreshAccessToken, serializeCookie, signCertHeaders, validateDoDCAC, validateIdToken, verifyCertHeaders, verifyState };