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