@oxyhq/services 5.7.5 → 5.8.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.
- package/README.md +76 -76
- package/lib/commonjs/core/index.js +177 -102
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +88 -29
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/node/createAuth.js +585 -7
- package/lib/commonjs/node/createAuth.js.map +1 -1
- package/lib/commonjs/node/index.js +38 -1
- package/lib/commonjs/node/index.js.map +1 -1
- package/lib/commonjs/ui/components/Avatar.js +15 -6
- package/lib/commonjs/ui/components/Avatar.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedItem.js +58 -13
- package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedSection.js +7 -1
- package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
- package/lib/commonjs/ui/components/Header.js +322 -0
- package/lib/commonjs/ui/components/Header.js.map +1 -0
- package/lib/commonjs/ui/components/OxyProvider.js +23 -7
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/index.js +7 -0
- package/lib/commonjs/ui/components/index.js.map +1 -1
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +1 -1
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/commonjs/ui/components/internal/TextField.js +606 -546
- package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
- package/lib/commonjs/ui/components/internal/TextField.md +436 -0
- package/lib/commonjs/ui/context/OxyContext.js +122 -78
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +5 -2
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/navigation/OxyRouter.js +1 -1
- package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +6 -6
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountManagementDemo.js +3 -3
- package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +241 -598
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +1151 -406
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +135 -237
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js +246 -463
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FeedbackScreen.js +3 -3
- package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +808 -650
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js +51 -72
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SessionManagementScreen.js +11 -29
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +30 -303
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +4 -4
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +19 -31
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +7 -10
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +11 -5
- package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +11 -4
- package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +12 -0
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/styles/authStyles.js +337 -0
- package/lib/commonjs/ui/styles/authStyles.js.map +1 -0
- package/lib/commonjs/ui/styles/index.js +11 -0
- package/lib/commonjs/ui/styles/index.js.map +1 -1
- package/lib/module/core/index.js +177 -41
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +26 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/node/createAuth.js +584 -7
- package/lib/module/node/createAuth.js.map +1 -1
- package/lib/module/node/index.js +7 -1
- package/lib/module/node/index.js.map +1 -1
- package/lib/module/ui/components/Avatar.js +15 -6
- package/lib/module/ui/components/Avatar.js.map +1 -1
- package/lib/module/ui/components/GroupedItem.js +59 -14
- package/lib/module/ui/components/GroupedItem.js.map +1 -1
- package/lib/module/ui/components/GroupedSection.js +7 -1
- package/lib/module/ui/components/GroupedSection.js.map +1 -1
- package/lib/module/ui/components/Header.js +317 -0
- package/lib/module/ui/components/Header.js.map +1 -0
- package/lib/module/ui/components/OxyProvider.js +25 -9
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/index.js +1 -0
- package/lib/module/ui/components/index.js.map +1 -1
- package/lib/module/ui/components/internal/GroupedPillButtons.js +1 -1
- package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/module/ui/components/internal/TextField.js +607 -547
- package/lib/module/ui/components/internal/TextField.js.map +1 -1
- package/lib/module/ui/components/internal/TextField.md +436 -0
- package/lib/module/ui/context/OxyContext.js +121 -77
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +5 -2
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/navigation/OxyRouter.js +1 -1
- package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +6 -6
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountManagementDemo.js +3 -3
- package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +242 -597
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +1152 -407
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +135 -237
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js +248 -465
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/FeedbackScreen.js +3 -3
- package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js +809 -651
- package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/module/ui/screens/RecoverAccountScreen.js +53 -74
- package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js +11 -29
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +32 -305
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +5 -5
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInPasswordStep.js +19 -31
- package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInUsernameStep.js +7 -10
- package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/module/ui/screens/internal/SignUpIdentityStep.js +11 -5
- package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
- package/lib/module/ui/screens/internal/SignUpSecurityStep.js +11 -4
- package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +12 -0
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/styles/authStyles.js +332 -0
- package/lib/module/ui/styles/authStyles.js.map +1 -0
- package/lib/module/ui/styles/index.js +1 -0
- package/lib/module/ui/styles/index.js.map +1 -1
- package/lib/typescript/core/index.d.ts +68 -24
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +13 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/node/createAuth.d.ts +112 -0
- package/lib/typescript/node/createAuth.d.ts.map +1 -1
- package/lib/typescript/node/index.d.ts +2 -0
- package/lib/typescript/node/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
- package/lib/typescript/ui/components/GroupedItem.d.ts +6 -0
- package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
- package/lib/typescript/ui/components/GroupedSection.d.ts +6 -0
- package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -1
- package/lib/typescript/ui/components/Header.d.ts +22 -0
- package/lib/typescript/ui/components/Header.d.ts.map +1 -0
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/index.d.ts +1 -0
- package/lib/typescript/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/internal/TextField.d.ts +31 -16
- package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +5 -2
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/types.d.ts +9 -2
- package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +5 -1
- package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +1 -1
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +0 -1
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/styles/authStyles.d.ts +326 -0
- package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -0
- package/lib/typescript/ui/styles/index.d.ts +1 -0
- package/lib/typescript/ui/styles/index.d.ts.map +1 -1
- package/package.json +1 -4
- package/src/core/index.ts +195 -41
- package/src/index.ts +72 -4
- package/src/node/createAuth.ts +623 -7
- package/src/node/index.ts +19 -1
- package/src/ui/components/Avatar.tsx +11 -5
- package/src/ui/components/GroupedItem.tsx +57 -9
- package/src/ui/components/GroupedSection.tsx +12 -0
- package/src/ui/components/Header.tsx +364 -0
- package/src/ui/components/OxyProvider.tsx +31 -15
- package/src/ui/components/index.ts +1 -0
- package/src/ui/components/internal/GroupedPillButtons.tsx +1 -1
- package/src/ui/components/internal/TextField.md +436 -0
- package/src/ui/components/internal/TextField.tsx +720 -620
- package/src/ui/context/OxyContext.tsx +150 -63
- package/src/ui/hooks/useSessionSocket.ts +5 -2
- package/src/ui/navigation/OxyRouter.tsx +1 -1
- package/src/ui/navigation/types.ts +10 -2
- package/src/ui/screens/AccountCenterScreen.tsx +5 -5
- package/src/ui/screens/AccountManagementDemo.tsx +9 -9
- package/src/ui/screens/AccountOverviewScreen.tsx +265 -414
- package/src/ui/screens/AccountSettingsScreen.tsx +1165 -403
- package/src/ui/screens/AccountSwitcherScreen.tsx +158 -202
- package/src/ui/screens/AppInfoScreen.tsx +270 -497
- package/src/ui/screens/FeedbackScreen.tsx +3 -3
- package/src/ui/screens/PaymentGatewayScreen.tsx +668 -365
- package/src/ui/screens/ProfileScreen.tsx +5 -5
- package/src/ui/screens/RecoverAccountScreen.tsx +46 -74
- package/src/ui/screens/SessionManagementScreen.tsx +14 -22
- package/src/ui/screens/SignInScreen.tsx +27 -294
- package/src/ui/screens/SignUpScreen.tsx +5 -5
- package/src/ui/screens/internal/SignInPasswordStep.tsx +11 -22
- package/src/ui/screens/internal/SignInUsernameStep.tsx +3 -10
- package/src/ui/screens/internal/SignUpIdentityStep.tsx +2 -5
- package/src/ui/screens/internal/SignUpSecurityStep.tsx +3 -4
- package/src/ui/stores/authStore.ts +12 -0
- package/src/ui/styles/authStyles.ts +352 -0
- package/src/ui/styles/index.ts +1 -0
- package/lib/commonjs/core/auth-manager.js +0 -440
- package/lib/commonjs/core/auth-manager.js.map +0 -1
- package/lib/commonjs/core/use-auth.js +0 -244
- package/lib/commonjs/core/use-auth.js.map +0 -1
- package/lib/module/core/auth-manager.js +0 -432
- package/lib/module/core/auth-manager.js.map +0 -1
- package/lib/module/core/use-auth.js +0 -235
- package/lib/module/core/use-auth.js.map +0 -1
- package/lib/typescript/core/auth-manager.d.ts +0 -136
- package/lib/typescript/core/auth-manager.d.ts.map +0 -1
- package/lib/typescript/core/use-auth.d.ts +0 -79
- package/lib/typescript/core/use-auth.d.ts.map +0 -1
- package/src/__tests__/middleware.test.ts +0 -105
- package/src/__tests__/setup.ts +0 -10
- package/src/__tests__/zero-config-auth.test.ts +0 -607
- package/src/core/auth-manager.ts +0 -500
- package/src/core/use-auth.tsx +0 -245
package/src/node/createAuth.ts
CHANGED
|
@@ -1,13 +1,463 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import type {
|
|
1
|
+
import express, { Request, Response } from 'express';
|
|
2
|
+
import type { NextFunction } from 'express-serve-static-core';
|
|
3
3
|
import { OxyServices } from '../core';
|
|
4
|
+
import { jwtDecode } from 'jwt-decode';
|
|
5
|
+
|
|
6
|
+
// Types for enhanced authentication
|
|
7
|
+
export interface AuthRequest extends Request {
|
|
8
|
+
user?: any;
|
|
9
|
+
userId?: string;
|
|
10
|
+
accessToken?: string;
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
deviceFingerprint?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AuthOptions {
|
|
16
|
+
baseURL: string;
|
|
17
|
+
jwtSecret?: string; // For local JWT validation
|
|
18
|
+
loadFullUser?: boolean;
|
|
19
|
+
enableSessionAuth?: boolean;
|
|
20
|
+
enableDeviceAuth?: boolean;
|
|
21
|
+
cacheUserData?: boolean;
|
|
22
|
+
userCacheTTL?: number; // in seconds
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AuthMiddlewareOptions {
|
|
26
|
+
required?: boolean;
|
|
27
|
+
loadFullUser?: boolean;
|
|
28
|
+
roles?: string[];
|
|
29
|
+
permissions?: string[];
|
|
30
|
+
onError?: (error: any, req: AuthRequest, res: Response) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TokenValidationResult {
|
|
34
|
+
valid: boolean;
|
|
35
|
+
userId?: string;
|
|
36
|
+
user?: any;
|
|
37
|
+
error?: string;
|
|
38
|
+
code?: string;
|
|
39
|
+
expiresAt?: number;
|
|
40
|
+
cached?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// User cache for performance
|
|
44
|
+
class UserCache {
|
|
45
|
+
private cache = new Map<string, { user: any; expiresAt: number }>();
|
|
46
|
+
private ttl: number;
|
|
47
|
+
|
|
48
|
+
constructor(ttl: number = 300) { // 5 minutes default
|
|
49
|
+
this.ttl = ttl * 1000;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
set(userId: string, user: any): void {
|
|
53
|
+
this.cache.set(userId, {
|
|
54
|
+
user,
|
|
55
|
+
expiresAt: Date.now() + this.ttl
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get(userId: string): any | null {
|
|
60
|
+
const item = this.cache.get(userId);
|
|
61
|
+
if (!item || Date.now() > item.expiresAt) {
|
|
62
|
+
this.cache.delete(userId);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return item.user;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
clear(): void {
|
|
69
|
+
this.cache.clear();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Enhanced OxyAuth class for backend authentication
|
|
75
|
+
*/
|
|
76
|
+
export class OxyAuth {
|
|
77
|
+
private oxy: OxyServices;
|
|
78
|
+
private options: AuthOptions;
|
|
79
|
+
private userCache: UserCache | null = null;
|
|
80
|
+
|
|
81
|
+
constructor(options: AuthOptions) {
|
|
82
|
+
this.options = {
|
|
83
|
+
loadFullUser: true,
|
|
84
|
+
enableSessionAuth: true,
|
|
85
|
+
enableDeviceAuth: true,
|
|
86
|
+
cacheUserData: true,
|
|
87
|
+
userCacheTTL: 300,
|
|
88
|
+
...options
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
this.oxy = new OxyServices({ baseURL: options.baseURL });
|
|
92
|
+
|
|
93
|
+
if (this.options.cacheUserData) {
|
|
94
|
+
this.userCache = new UserCache(this.options.userCacheTTL);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Create authentication middleware
|
|
100
|
+
*/
|
|
101
|
+
createAuthMiddleware(options: AuthMiddlewareOptions = {}): (req: AuthRequest, res: Response, next: NextFunction) => Promise<void> {
|
|
102
|
+
return async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
103
|
+
try {
|
|
104
|
+
const result = await this.authenticateRequest(req);
|
|
105
|
+
|
|
106
|
+
if (!result.valid && options.required !== false) {
|
|
107
|
+
const error = { message: 'Authentication required', code: 'AUTH_REQUIRED' };
|
|
108
|
+
if (options.onError) {
|
|
109
|
+
options.onError(error, req, res);
|
|
110
|
+
} else {
|
|
111
|
+
res.status(401).json(error);
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check roles if specified
|
|
117
|
+
if (result.valid && options.roles && result.user) {
|
|
118
|
+
const hasRole = options.roles.some(role =>
|
|
119
|
+
result.user.roles?.includes(role) || result.user.role === role
|
|
120
|
+
);
|
|
121
|
+
if (!hasRole) {
|
|
122
|
+
const error = { message: 'Insufficient permissions', code: 'INSUFFICIENT_ROLES' };
|
|
123
|
+
if (options.onError) {
|
|
124
|
+
options.onError(error, req, res);
|
|
125
|
+
} else {
|
|
126
|
+
res.status(403).json(error);
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check permissions if specified
|
|
133
|
+
if (result.valid && options.permissions && result.userId) {
|
|
134
|
+
for (const permission of options.permissions) {
|
|
135
|
+
const hasPermission = await this.hasPermission(result.userId!, permission);
|
|
136
|
+
if (!hasPermission) {
|
|
137
|
+
const error = { message: 'Insufficient permissions', code: 'INSUFFICIENT_PERMISSIONS' };
|
|
138
|
+
if (options.onError) {
|
|
139
|
+
options.onError(error, req, res);
|
|
140
|
+
} else {
|
|
141
|
+
res.status(403).json(error);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
next();
|
|
149
|
+
} catch (error) {
|
|
150
|
+
if (options.onError) {
|
|
151
|
+
options.onError(error, req, res);
|
|
152
|
+
} else {
|
|
153
|
+
res.status(500).json({ message: 'Authentication error' });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Authenticate request and populate user data
|
|
161
|
+
*/
|
|
162
|
+
private async authenticateRequest(req: AuthRequest): Promise<TokenValidationResult & { accessToken?: string }> {
|
|
163
|
+
// Try JWT token first
|
|
164
|
+
const authHeader = req.headers.authorization;
|
|
165
|
+
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
166
|
+
const token = authHeader.substring(7);
|
|
167
|
+
const result = await this.validateToken(token);
|
|
168
|
+
if (result.valid) {
|
|
169
|
+
req.user = result.user;
|
|
170
|
+
req.userId = result.userId;
|
|
171
|
+
req.accessToken = token;
|
|
172
|
+
return { ...result, accessToken: token };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Try session-based auth
|
|
177
|
+
if (this.options.enableSessionAuth) {
|
|
178
|
+
const sessionId = req.headers['x-session-id'] as string;
|
|
179
|
+
if (sessionId) {
|
|
180
|
+
const result = await this.validateSession(sessionId);
|
|
181
|
+
if (result.valid) {
|
|
182
|
+
req.user = result.user;
|
|
183
|
+
req.userId = result.userId;
|
|
184
|
+
req.sessionId = sessionId;
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Try device-based auth
|
|
191
|
+
if (this.options.enableDeviceAuth) {
|
|
192
|
+
const deviceFingerprint = req.headers['x-device-fingerprint'] as string;
|
|
193
|
+
const userId = req.headers['x-user-id'] as string;
|
|
194
|
+
if (deviceFingerprint && userId) {
|
|
195
|
+
const result = await this.validateDevice(userId, deviceFingerprint);
|
|
196
|
+
if (result.valid) {
|
|
197
|
+
req.user = result.user;
|
|
198
|
+
req.userId = result.userId;
|
|
199
|
+
req.deviceFingerprint = deviceFingerprint;
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { valid: false, error: 'No valid authentication found' };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Validate JWT token
|
|
210
|
+
*/
|
|
211
|
+
async validateToken(token: string): Promise<TokenValidationResult> {
|
|
212
|
+
try {
|
|
213
|
+
// Local JWT validation if secret is provided
|
|
214
|
+
if (this.options.jwtSecret) {
|
|
215
|
+
const decoded = jwtDecode(token) as any;
|
|
216
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
217
|
+
|
|
218
|
+
if (decoded.exp && decoded.exp < currentTime) {
|
|
219
|
+
return {
|
|
220
|
+
valid: false,
|
|
221
|
+
error: 'Token expired',
|
|
222
|
+
code: 'TOKEN_EXPIRED',
|
|
223
|
+
expiresAt: decoded.exp
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const userId = decoded.userId || decoded.id;
|
|
228
|
+
if (!userId) {
|
|
229
|
+
return {
|
|
230
|
+
valid: false,
|
|
231
|
+
error: 'Invalid token payload',
|
|
232
|
+
code: 'INVALID_PAYLOAD'
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Get user data from cache or API
|
|
237
|
+
let user = this.userCache?.get(userId);
|
|
238
|
+
const cached = !!user;
|
|
239
|
+
|
|
240
|
+
if (!user && this.options.loadFullUser) {
|
|
241
|
+
try {
|
|
242
|
+
user = await this.oxy.getUserById(userId);
|
|
243
|
+
this.userCache?.set(userId, user);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
user = { id: userId };
|
|
246
|
+
}
|
|
247
|
+
} else if (!user) {
|
|
248
|
+
user = { id: userId };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
valid: true,
|
|
253
|
+
userId,
|
|
254
|
+
user,
|
|
255
|
+
expiresAt: decoded.exp,
|
|
256
|
+
cached
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Remote validation using OxyServices
|
|
261
|
+
const tempOxy = new OxyServices({ baseURL: this.oxy.getBaseURL() });
|
|
262
|
+
tempOxy.setTokens(token, '');
|
|
263
|
+
|
|
264
|
+
const isValid = await tempOxy.validate();
|
|
265
|
+
if (!isValid) {
|
|
266
|
+
return {
|
|
267
|
+
valid: false,
|
|
268
|
+
error: 'Invalid token',
|
|
269
|
+
code: 'INVALID_TOKEN'
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const userId = tempOxy.getCurrentUserId();
|
|
274
|
+
if (!userId) {
|
|
275
|
+
return {
|
|
276
|
+
valid: false,
|
|
277
|
+
error: 'Invalid token payload',
|
|
278
|
+
code: 'INVALID_PAYLOAD'
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Get user data
|
|
283
|
+
let user = this.userCache?.get(userId);
|
|
284
|
+
const cached = !!user;
|
|
285
|
+
|
|
286
|
+
if (!user && this.options.loadFullUser) {
|
|
287
|
+
try {
|
|
288
|
+
user = await tempOxy.getUserById(userId);
|
|
289
|
+
this.userCache?.set(userId, user);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
user = { id: userId };
|
|
292
|
+
}
|
|
293
|
+
} else if (!user) {
|
|
294
|
+
user = { id: userId };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
valid: true,
|
|
299
|
+
userId,
|
|
300
|
+
user,
|
|
301
|
+
cached
|
|
302
|
+
};
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return {
|
|
305
|
+
valid: false,
|
|
306
|
+
error: 'Token validation failed',
|
|
307
|
+
code: 'VALIDATION_ERROR'
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Validate session-based authentication
|
|
314
|
+
*/
|
|
315
|
+
async validateSession(sessionId: string, deviceFingerprint?: string): Promise<TokenValidationResult> {
|
|
316
|
+
try {
|
|
317
|
+
// This would integrate with your session management system
|
|
318
|
+
// For now, it's a placeholder implementation
|
|
319
|
+
return {
|
|
320
|
+
valid: false,
|
|
321
|
+
error: 'Session validation not implemented',
|
|
322
|
+
code: 'NOT_IMPLEMENTED'
|
|
323
|
+
};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
return {
|
|
326
|
+
valid: false,
|
|
327
|
+
error: 'Session validation failed',
|
|
328
|
+
code: 'VALIDATION_ERROR'
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Validate device-based authentication
|
|
335
|
+
*/
|
|
336
|
+
async validateDevice(userId: string, deviceFingerprint: string): Promise<TokenValidationResult> {
|
|
337
|
+
try {
|
|
338
|
+
// This would validate device fingerprint against stored data
|
|
339
|
+
// For now, it's a placeholder implementation
|
|
340
|
+
return {
|
|
341
|
+
valid: false,
|
|
342
|
+
error: 'Device validation not implemented',
|
|
343
|
+
code: 'NOT_IMPLEMENTED'
|
|
344
|
+
};
|
|
345
|
+
} catch (error) {
|
|
346
|
+
return {
|
|
347
|
+
valid: false,
|
|
348
|
+
error: 'Device validation failed',
|
|
349
|
+
code: 'VALIDATION_ERROR'
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Create role-based middleware
|
|
356
|
+
*/
|
|
357
|
+
requireRole(roles: string | string[]): (req: AuthRequest, res: Response, next: NextFunction) => Promise<void> {
|
|
358
|
+
const roleArray = Array.isArray(roles) ? roles : [roles];
|
|
359
|
+
return this.createAuthMiddleware({
|
|
360
|
+
required: true,
|
|
361
|
+
roles: roleArray
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Create permission-based middleware
|
|
367
|
+
*/
|
|
368
|
+
requirePermission(permissions: string | string[]): (req: AuthRequest, res: Response, next: NextFunction) => Promise<void> {
|
|
369
|
+
const permissionArray = Array.isArray(permissions) ? permissions : [permissions];
|
|
370
|
+
return this.createAuthMiddleware({
|
|
371
|
+
required: true,
|
|
372
|
+
permissions: permissionArray
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Create optional authentication middleware
|
|
378
|
+
*/
|
|
379
|
+
optionalAuth(): (req: AuthRequest, res: Response, next: NextFunction) => Promise<void> {
|
|
380
|
+
return this.createAuthMiddleware({
|
|
381
|
+
required: false,
|
|
382
|
+
onError: () => {} // No error thrown for optional auth
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Clear user cache
|
|
388
|
+
*/
|
|
389
|
+
clearCache(): void {
|
|
390
|
+
this.userCache?.clear();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Check if user data is cached for a given token
|
|
395
|
+
*/
|
|
396
|
+
isUserCached(token: string): boolean {
|
|
397
|
+
try {
|
|
398
|
+
const decoded = jwtDecode(token) as any;
|
|
399
|
+
const userId = decoded.userId || decoded.id;
|
|
400
|
+
return userId ? this.userCache?.get(userId) !== null : false;
|
|
401
|
+
} catch {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Check if user has a specific permission
|
|
408
|
+
*/
|
|
409
|
+
async hasPermission(userId: string, permission: string): Promise<boolean> {
|
|
410
|
+
try {
|
|
411
|
+
// This is a placeholder implementation
|
|
412
|
+
// In a real implementation, you would check against user roles/permissions
|
|
413
|
+
const user = this.userCache?.get(userId) || await this.oxy.getUserById(userId);
|
|
414
|
+
return user?.permissions?.includes(permission) || user?.role === 'admin' || false;
|
|
415
|
+
} catch {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Get OxyServices instance
|
|
422
|
+
*/
|
|
423
|
+
getOxyServices(): OxyServices {
|
|
424
|
+
return this.oxy;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
4
427
|
|
|
5
428
|
export interface CreateAuthOptions {
|
|
6
429
|
baseURL: string;
|
|
430
|
+
jwtSecret?: string;
|
|
431
|
+
loadFullUser?: boolean;
|
|
432
|
+
enableSessionAuth?: boolean;
|
|
433
|
+
enableDeviceAuth?: boolean;
|
|
434
|
+
cacheUserData?: boolean;
|
|
435
|
+
userCacheTTL?: number;
|
|
7
436
|
}
|
|
8
437
|
|
|
438
|
+
/**
|
|
439
|
+
* Enhanced createAuth function that provides both router and middleware capabilities
|
|
440
|
+
*
|
|
441
|
+
* This is a unified authentication system that:
|
|
442
|
+
* 1. Maintains backward compatibility with the old router-based approach
|
|
443
|
+
* 2. Adds powerful new middleware capabilities
|
|
444
|
+
* 3. Includes caching, role-based access, and performance optimizations
|
|
445
|
+
* 4. Supports multiple authentication strategies
|
|
446
|
+
*/
|
|
9
447
|
export function createAuth(options: CreateAuthOptions) {
|
|
10
|
-
|
|
448
|
+
// Create the enhanced OxyAuth instance
|
|
449
|
+
const authOptions: AuthOptions = {
|
|
450
|
+
baseURL: options.baseURL,
|
|
451
|
+
jwtSecret: options.jwtSecret,
|
|
452
|
+
loadFullUser: options.loadFullUser ?? true,
|
|
453
|
+
enableSessionAuth: options.enableSessionAuth ?? true,
|
|
454
|
+
enableDeviceAuth: options.enableDeviceAuth ?? true,
|
|
455
|
+
cacheUserData: options.cacheUserData ?? true,
|
|
456
|
+
userCacheTTL: options.userCacheTTL ?? 300
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const oxyAuth = new OxyAuth(authOptions);
|
|
460
|
+
const oxy = oxyAuth.getOxyServices();
|
|
11
461
|
const router = express.Router();
|
|
12
462
|
|
|
13
463
|
// Helper to handle async route functions
|
|
@@ -22,95 +472,261 @@ export function createAuth(options: CreateAuthOptions) {
|
|
|
22
472
|
}
|
|
23
473
|
};
|
|
24
474
|
|
|
475
|
+
// Enhanced signup with validation
|
|
25
476
|
router.post(
|
|
26
477
|
'/signup',
|
|
27
478
|
wrap(async (req, res) => {
|
|
28
479
|
const { username, email, password } = req.body;
|
|
480
|
+
|
|
481
|
+
// Enhanced validation
|
|
482
|
+
if (!username || !email || !password) {
|
|
483
|
+
return res.status(400).json({
|
|
484
|
+
message: 'Username, email, and password are required'
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
29
488
|
const result = await oxy.signUp(username, email, password);
|
|
30
489
|
res.json(result);
|
|
31
490
|
})
|
|
32
491
|
);
|
|
33
492
|
|
|
493
|
+
// Enhanced login with device fingerprinting
|
|
34
494
|
router.post(
|
|
35
495
|
'/login',
|
|
36
496
|
wrap(async (req, res) => {
|
|
37
|
-
const { username, password } = req.body;
|
|
497
|
+
const { username, password, deviceFingerprint } = req.body;
|
|
498
|
+
|
|
499
|
+
if (!username || !password) {
|
|
500
|
+
return res.status(400).json({
|
|
501
|
+
message: 'Username and password are required'
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
38
505
|
const result = await oxy.login(username, password);
|
|
506
|
+
|
|
507
|
+
// Store device fingerprint if provided
|
|
508
|
+
if (deviceFingerprint && result.user?.id) {
|
|
509
|
+
// This could be stored in a database for device tracking
|
|
510
|
+
console.log(`Device login: ${deviceFingerprint} for user ${result.user.id}`);
|
|
511
|
+
}
|
|
512
|
+
|
|
39
513
|
res.json(result);
|
|
40
514
|
})
|
|
41
515
|
);
|
|
42
516
|
|
|
517
|
+
// Enhanced logout with session management
|
|
43
518
|
router.post(
|
|
44
519
|
'/logout',
|
|
45
520
|
wrap(async (req, res) => {
|
|
46
521
|
const token = req.headers.authorization?.split(' ')[1];
|
|
47
522
|
const refreshToken = req.body.refreshToken;
|
|
523
|
+
const sessionId = req.body.sessionId;
|
|
524
|
+
|
|
48
525
|
if (token) oxy.setTokens(token, refreshToken);
|
|
49
|
-
|
|
526
|
+
|
|
527
|
+
// Enhanced logout with session tracking
|
|
528
|
+
if (sessionId) {
|
|
529
|
+
await oxy.logoutSession(sessionId);
|
|
530
|
+
} else {
|
|
531
|
+
await oxy.logout();
|
|
532
|
+
}
|
|
533
|
+
|
|
50
534
|
res.json({ success: true });
|
|
51
535
|
})
|
|
52
536
|
);
|
|
53
537
|
|
|
538
|
+
// Enhanced token refresh
|
|
54
539
|
router.post(
|
|
55
540
|
'/refresh',
|
|
56
541
|
wrap(async (req, res) => {
|
|
57
542
|
const refreshToken = req.body.refreshToken;
|
|
58
543
|
const accessToken = req.headers.authorization?.split(' ')[1] || '';
|
|
544
|
+
|
|
545
|
+
if (!refreshToken) {
|
|
546
|
+
return res.status(400).json({ message: 'Refresh token is required' });
|
|
547
|
+
}
|
|
548
|
+
|
|
59
549
|
oxy.setTokens(accessToken, refreshToken);
|
|
60
550
|
const tokens = await oxy.refreshTokens();
|
|
61
551
|
res.json(tokens);
|
|
62
552
|
})
|
|
63
553
|
);
|
|
64
554
|
|
|
555
|
+
// Enhanced token validation with caching
|
|
65
556
|
router.get(
|
|
66
557
|
'/validate',
|
|
67
558
|
wrap(async (req, res) => {
|
|
68
559
|
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
560
|
+
|
|
561
|
+
if (!token) {
|
|
562
|
+
return res.status(401).json({ valid: false, message: 'No token provided' });
|
|
563
|
+
}
|
|
564
|
+
|
|
69
565
|
oxy.setTokens(token, '');
|
|
70
566
|
const valid = await oxy.validate();
|
|
71
|
-
|
|
567
|
+
|
|
568
|
+
// Enhanced response with more details
|
|
569
|
+
res.json({
|
|
570
|
+
valid,
|
|
571
|
+
timestamp: new Date().toISOString(),
|
|
572
|
+
cached: oxyAuth.isUserCached(token) // Check if user data is cached
|
|
573
|
+
});
|
|
72
574
|
})
|
|
73
575
|
);
|
|
74
576
|
|
|
577
|
+
// Enhanced sessions management
|
|
75
578
|
router.get(
|
|
76
579
|
'/sessions',
|
|
77
580
|
wrap(async (req, res) => {
|
|
78
581
|
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
582
|
+
|
|
583
|
+
if (!token) {
|
|
584
|
+
return res.status(401).json({ message: 'Authentication required' });
|
|
585
|
+
}
|
|
586
|
+
|
|
79
587
|
oxy.setTokens(token, '');
|
|
80
588
|
const sessions = await oxy.getUserSessions();
|
|
81
589
|
res.json(sessions);
|
|
82
590
|
})
|
|
83
591
|
);
|
|
84
592
|
|
|
593
|
+
// Enhanced session deletion
|
|
85
594
|
router.delete(
|
|
86
595
|
'/sessions/:id',
|
|
87
596
|
wrap(async (req, res) => {
|
|
88
597
|
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
598
|
+
|
|
599
|
+
if (!token) {
|
|
600
|
+
return res.status(401).json({ message: 'Authentication required' });
|
|
601
|
+
}
|
|
602
|
+
|
|
89
603
|
oxy.setTokens(token, '');
|
|
90
604
|
const result = await oxy.logoutSession(req.params.id);
|
|
605
|
+
|
|
606
|
+
// Clear cache for this user if logout was successful
|
|
607
|
+
if (result.success) {
|
|
608
|
+
oxyAuth.clearCache();
|
|
609
|
+
}
|
|
610
|
+
|
|
91
611
|
res.json(result);
|
|
92
612
|
})
|
|
93
613
|
);
|
|
94
614
|
|
|
615
|
+
// Enhanced logout other sessions
|
|
95
616
|
router.post(
|
|
96
617
|
'/sessions/logout-others',
|
|
97
618
|
wrap(async (req, res) => {
|
|
98
619
|
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
620
|
+
|
|
621
|
+
if (!token) {
|
|
622
|
+
return res.status(401).json({ message: 'Authentication required' });
|
|
623
|
+
}
|
|
624
|
+
|
|
99
625
|
oxy.setTokens(token, '');
|
|
100
626
|
const result = await oxy.logoutOtherSessions();
|
|
627
|
+
|
|
628
|
+
// Clear cache for this user
|
|
629
|
+
if (result.success) {
|
|
630
|
+
oxyAuth.clearCache();
|
|
631
|
+
}
|
|
632
|
+
|
|
101
633
|
res.json(result);
|
|
102
634
|
})
|
|
103
635
|
);
|
|
104
636
|
|
|
637
|
+
// Enhanced logout all sessions
|
|
105
638
|
router.post(
|
|
106
639
|
'/sessions/logout-all',
|
|
107
640
|
wrap(async (req, res) => {
|
|
108
641
|
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
642
|
+
|
|
643
|
+
if (!token) {
|
|
644
|
+
return res.status(401).json({ message: 'Authentication required' });
|
|
645
|
+
}
|
|
646
|
+
|
|
109
647
|
oxy.setTokens(token, '');
|
|
110
648
|
const result = await oxy.logoutAllSessions();
|
|
649
|
+
|
|
650
|
+
// Clear all cache
|
|
651
|
+
if (result.success) {
|
|
652
|
+
oxyAuth.clearCache();
|
|
653
|
+
}
|
|
654
|
+
|
|
111
655
|
res.json(result);
|
|
112
656
|
})
|
|
113
657
|
);
|
|
114
658
|
|
|
115
|
-
|
|
659
|
+
// NEW: Get current user profile with caching
|
|
660
|
+
router.get(
|
|
661
|
+
'/profile',
|
|
662
|
+
wrap(async (req, res) => {
|
|
663
|
+
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
664
|
+
|
|
665
|
+
if (!token) {
|
|
666
|
+
return res.status(401).json({ message: 'Authentication required' });
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Use the enhanced auth system for better performance
|
|
670
|
+
const validation = await oxyAuth.validateToken(token);
|
|
671
|
+
|
|
672
|
+
if (!validation.valid) {
|
|
673
|
+
return res.status(401).json({ message: 'Invalid token' });
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
res.json({
|
|
677
|
+
user: validation.user,
|
|
678
|
+
cached: validation.cached,
|
|
679
|
+
expiresAt: validation.expiresAt
|
|
680
|
+
});
|
|
681
|
+
})
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
// NEW: Check user permissions
|
|
685
|
+
router.post(
|
|
686
|
+
'/check-permissions',
|
|
687
|
+
wrap(async (req, res) => {
|
|
688
|
+
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
689
|
+
const { permissions } = req.body;
|
|
690
|
+
|
|
691
|
+
if (!token) {
|
|
692
|
+
return res.status(401).json({ message: 'Authentication required' });
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (!permissions || !Array.isArray(permissions)) {
|
|
696
|
+
return res.status(400).json({ message: 'Permissions array is required' });
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const validation = await oxyAuth.validateToken(token);
|
|
700
|
+
|
|
701
|
+
if (!validation.valid) {
|
|
702
|
+
return res.status(401).json({ message: 'Invalid token' });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Check each permission
|
|
706
|
+
const results = await Promise.all(
|
|
707
|
+
permissions.map(async (permission) => {
|
|
708
|
+
const hasPermission = await oxyAuth.hasPermission(validation.userId!, permission);
|
|
709
|
+
return { permission, granted: hasPermission };
|
|
710
|
+
})
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
res.json({ permissions: results });
|
|
714
|
+
})
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
return {
|
|
718
|
+
middleware: router,
|
|
719
|
+
// NEW: Expose the enhanced auth system
|
|
720
|
+
auth: oxyAuth,
|
|
721
|
+
// NEW: Convenience methods for middleware
|
|
722
|
+
requireAuth: (roles?: string | string[], permissions?: string | string[]) =>
|
|
723
|
+
oxyAuth.createAuthMiddleware({
|
|
724
|
+
required: true,
|
|
725
|
+
roles: Array.isArray(roles) ? roles : roles ? [roles] : undefined,
|
|
726
|
+
permissions: Array.isArray(permissions) ? permissions : permissions ? [permissions] : undefined
|
|
727
|
+
}),
|
|
728
|
+
optionalAuth: () => oxyAuth.optionalAuth(),
|
|
729
|
+
requireRole: (roles: string | string[]) => oxyAuth.requireRole(roles),
|
|
730
|
+
requirePermission: (permissions: string | string[]) => oxyAuth.requirePermission(permissions)
|
|
731
|
+
};
|
|
116
732
|
}
|