@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.
Files changed (239) hide show
  1. package/README.md +76 -76
  2. package/lib/commonjs/core/index.js +177 -102
  3. package/lib/commonjs/core/index.js.map +1 -1
  4. package/lib/commonjs/index.js +88 -29
  5. package/lib/commonjs/index.js.map +1 -1
  6. package/lib/commonjs/node/createAuth.js +585 -7
  7. package/lib/commonjs/node/createAuth.js.map +1 -1
  8. package/lib/commonjs/node/index.js +38 -1
  9. package/lib/commonjs/node/index.js.map +1 -1
  10. package/lib/commonjs/ui/components/Avatar.js +15 -6
  11. package/lib/commonjs/ui/components/Avatar.js.map +1 -1
  12. package/lib/commonjs/ui/components/GroupedItem.js +58 -13
  13. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
  14. package/lib/commonjs/ui/components/GroupedSection.js +7 -1
  15. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
  16. package/lib/commonjs/ui/components/Header.js +322 -0
  17. package/lib/commonjs/ui/components/Header.js.map +1 -0
  18. package/lib/commonjs/ui/components/OxyProvider.js +23 -7
  19. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  20. package/lib/commonjs/ui/components/index.js +7 -0
  21. package/lib/commonjs/ui/components/index.js.map +1 -1
  22. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +1 -1
  23. package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
  24. package/lib/commonjs/ui/components/internal/TextField.js +606 -546
  25. package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
  26. package/lib/commonjs/ui/components/internal/TextField.md +436 -0
  27. package/lib/commonjs/ui/context/OxyContext.js +122 -78
  28. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  29. package/lib/commonjs/ui/hooks/useSessionSocket.js +5 -2
  30. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  31. package/lib/commonjs/ui/navigation/OxyRouter.js +1 -1
  32. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  33. package/lib/commonjs/ui/screens/AccountCenterScreen.js +6 -6
  34. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/AccountManagementDemo.js +3 -3
  36. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -1
  37. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +241 -598
  38. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +1151 -406
  40. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +135 -237
  42. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/AppInfoScreen.js +246 -463
  44. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  45. package/lib/commonjs/ui/screens/FeedbackScreen.js +3 -3
  46. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  47. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +808 -650
  48. package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/RecoverAccountScreen.js +51 -72
  50. package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
  51. package/lib/commonjs/ui/screens/SessionManagementScreen.js +11 -29
  52. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  53. package/lib/commonjs/ui/screens/SignInScreen.js +30 -303
  54. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/SignUpScreen.js +4 -4
  56. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  57. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +19 -31
  58. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  59. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +7 -10
  60. package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  61. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js +11 -5
  62. package/lib/commonjs/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  63. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js +11 -4
  64. package/lib/commonjs/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  65. package/lib/commonjs/ui/stores/authStore.js +12 -0
  66. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  67. package/lib/commonjs/ui/styles/authStyles.js +337 -0
  68. package/lib/commonjs/ui/styles/authStyles.js.map +1 -0
  69. package/lib/commonjs/ui/styles/index.js +11 -0
  70. package/lib/commonjs/ui/styles/index.js.map +1 -1
  71. package/lib/module/core/index.js +177 -41
  72. package/lib/module/core/index.js.map +1 -1
  73. package/lib/module/index.js +26 -4
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/node/createAuth.js +584 -7
  76. package/lib/module/node/createAuth.js.map +1 -1
  77. package/lib/module/node/index.js +7 -1
  78. package/lib/module/node/index.js.map +1 -1
  79. package/lib/module/ui/components/Avatar.js +15 -6
  80. package/lib/module/ui/components/Avatar.js.map +1 -1
  81. package/lib/module/ui/components/GroupedItem.js +59 -14
  82. package/lib/module/ui/components/GroupedItem.js.map +1 -1
  83. package/lib/module/ui/components/GroupedSection.js +7 -1
  84. package/lib/module/ui/components/GroupedSection.js.map +1 -1
  85. package/lib/module/ui/components/Header.js +317 -0
  86. package/lib/module/ui/components/Header.js.map +1 -0
  87. package/lib/module/ui/components/OxyProvider.js +25 -9
  88. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  89. package/lib/module/ui/components/index.js +1 -0
  90. package/lib/module/ui/components/index.js.map +1 -1
  91. package/lib/module/ui/components/internal/GroupedPillButtons.js +1 -1
  92. package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
  93. package/lib/module/ui/components/internal/TextField.js +607 -547
  94. package/lib/module/ui/components/internal/TextField.js.map +1 -1
  95. package/lib/module/ui/components/internal/TextField.md +436 -0
  96. package/lib/module/ui/context/OxyContext.js +121 -77
  97. package/lib/module/ui/context/OxyContext.js.map +1 -1
  98. package/lib/module/ui/hooks/useSessionSocket.js +5 -2
  99. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  100. package/lib/module/ui/navigation/OxyRouter.js +1 -1
  101. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  102. package/lib/module/ui/screens/AccountCenterScreen.js +6 -6
  103. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  104. package/lib/module/ui/screens/AccountManagementDemo.js +3 -3
  105. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -1
  106. package/lib/module/ui/screens/AccountOverviewScreen.js +242 -597
  107. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  108. package/lib/module/ui/screens/AccountSettingsScreen.js +1152 -407
  109. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  110. package/lib/module/ui/screens/AccountSwitcherScreen.js +135 -237
  111. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  112. package/lib/module/ui/screens/AppInfoScreen.js +248 -465
  113. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  114. package/lib/module/ui/screens/FeedbackScreen.js +3 -3
  115. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  116. package/lib/module/ui/screens/PaymentGatewayScreen.js +809 -651
  117. package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
  118. package/lib/module/ui/screens/RecoverAccountScreen.js +53 -74
  119. package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
  120. package/lib/module/ui/screens/SessionManagementScreen.js +11 -29
  121. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  122. package/lib/module/ui/screens/SignInScreen.js +32 -305
  123. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  124. package/lib/module/ui/screens/SignUpScreen.js +5 -5
  125. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  126. package/lib/module/ui/screens/internal/SignInPasswordStep.js +19 -31
  127. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  128. package/lib/module/ui/screens/internal/SignInUsernameStep.js +7 -10
  129. package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
  130. package/lib/module/ui/screens/internal/SignUpIdentityStep.js +11 -5
  131. package/lib/module/ui/screens/internal/SignUpIdentityStep.js.map +1 -1
  132. package/lib/module/ui/screens/internal/SignUpSecurityStep.js +11 -4
  133. package/lib/module/ui/screens/internal/SignUpSecurityStep.js.map +1 -1
  134. package/lib/module/ui/stores/authStore.js +12 -0
  135. package/lib/module/ui/stores/authStore.js.map +1 -1
  136. package/lib/module/ui/styles/authStyles.js +332 -0
  137. package/lib/module/ui/styles/authStyles.js.map +1 -0
  138. package/lib/module/ui/styles/index.js +1 -0
  139. package/lib/module/ui/styles/index.js.map +1 -1
  140. package/lib/typescript/core/index.d.ts +68 -24
  141. package/lib/typescript/core/index.d.ts.map +1 -1
  142. package/lib/typescript/index.d.ts +13 -3
  143. package/lib/typescript/index.d.ts.map +1 -1
  144. package/lib/typescript/node/createAuth.d.ts +112 -0
  145. package/lib/typescript/node/createAuth.d.ts.map +1 -1
  146. package/lib/typescript/node/index.d.ts +2 -0
  147. package/lib/typescript/node/index.d.ts.map +1 -1
  148. package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
  149. package/lib/typescript/ui/components/GroupedItem.d.ts +6 -0
  150. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
  151. package/lib/typescript/ui/components/GroupedSection.d.ts +6 -0
  152. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -1
  153. package/lib/typescript/ui/components/Header.d.ts +22 -0
  154. package/lib/typescript/ui/components/Header.d.ts.map +1 -0
  155. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  156. package/lib/typescript/ui/components/index.d.ts +1 -0
  157. package/lib/typescript/ui/components/index.d.ts.map +1 -1
  158. package/lib/typescript/ui/components/internal/TextField.d.ts +31 -16
  159. package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
  160. package/lib/typescript/ui/context/OxyContext.d.ts +5 -2
  161. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  162. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  163. package/lib/typescript/ui/navigation/types.d.ts +9 -2
  164. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  165. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  166. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  167. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  168. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  169. package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
  170. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +5 -1
  171. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
  172. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  173. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  174. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +1 -1
  175. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
  176. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +0 -1
  177. package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +1 -1
  178. package/lib/typescript/ui/screens/internal/SignUpIdentityStep.d.ts.map +1 -1
  179. package/lib/typescript/ui/screens/internal/SignUpSecurityStep.d.ts.map +1 -1
  180. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  181. package/lib/typescript/ui/styles/authStyles.d.ts +326 -0
  182. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -0
  183. package/lib/typescript/ui/styles/index.d.ts +1 -0
  184. package/lib/typescript/ui/styles/index.d.ts.map +1 -1
  185. package/package.json +1 -4
  186. package/src/core/index.ts +195 -41
  187. package/src/index.ts +72 -4
  188. package/src/node/createAuth.ts +623 -7
  189. package/src/node/index.ts +19 -1
  190. package/src/ui/components/Avatar.tsx +11 -5
  191. package/src/ui/components/GroupedItem.tsx +57 -9
  192. package/src/ui/components/GroupedSection.tsx +12 -0
  193. package/src/ui/components/Header.tsx +364 -0
  194. package/src/ui/components/OxyProvider.tsx +31 -15
  195. package/src/ui/components/index.ts +1 -0
  196. package/src/ui/components/internal/GroupedPillButtons.tsx +1 -1
  197. package/src/ui/components/internal/TextField.md +436 -0
  198. package/src/ui/components/internal/TextField.tsx +720 -620
  199. package/src/ui/context/OxyContext.tsx +150 -63
  200. package/src/ui/hooks/useSessionSocket.ts +5 -2
  201. package/src/ui/navigation/OxyRouter.tsx +1 -1
  202. package/src/ui/navigation/types.ts +10 -2
  203. package/src/ui/screens/AccountCenterScreen.tsx +5 -5
  204. package/src/ui/screens/AccountManagementDemo.tsx +9 -9
  205. package/src/ui/screens/AccountOverviewScreen.tsx +265 -414
  206. package/src/ui/screens/AccountSettingsScreen.tsx +1165 -403
  207. package/src/ui/screens/AccountSwitcherScreen.tsx +158 -202
  208. package/src/ui/screens/AppInfoScreen.tsx +270 -497
  209. package/src/ui/screens/FeedbackScreen.tsx +3 -3
  210. package/src/ui/screens/PaymentGatewayScreen.tsx +668 -365
  211. package/src/ui/screens/ProfileScreen.tsx +5 -5
  212. package/src/ui/screens/RecoverAccountScreen.tsx +46 -74
  213. package/src/ui/screens/SessionManagementScreen.tsx +14 -22
  214. package/src/ui/screens/SignInScreen.tsx +27 -294
  215. package/src/ui/screens/SignUpScreen.tsx +5 -5
  216. package/src/ui/screens/internal/SignInPasswordStep.tsx +11 -22
  217. package/src/ui/screens/internal/SignInUsernameStep.tsx +3 -10
  218. package/src/ui/screens/internal/SignUpIdentityStep.tsx +2 -5
  219. package/src/ui/screens/internal/SignUpSecurityStep.tsx +3 -4
  220. package/src/ui/stores/authStore.ts +12 -0
  221. package/src/ui/styles/authStyles.ts +352 -0
  222. package/src/ui/styles/index.ts +1 -0
  223. package/lib/commonjs/core/auth-manager.js +0 -440
  224. package/lib/commonjs/core/auth-manager.js.map +0 -1
  225. package/lib/commonjs/core/use-auth.js +0 -244
  226. package/lib/commonjs/core/use-auth.js.map +0 -1
  227. package/lib/module/core/auth-manager.js +0 -432
  228. package/lib/module/core/auth-manager.js.map +0 -1
  229. package/lib/module/core/use-auth.js +0 -235
  230. package/lib/module/core/use-auth.js.map +0 -1
  231. package/lib/typescript/core/auth-manager.d.ts +0 -136
  232. package/lib/typescript/core/auth-manager.d.ts.map +0 -1
  233. package/lib/typescript/core/use-auth.d.ts +0 -79
  234. package/lib/typescript/core/use-auth.d.ts.map +0 -1
  235. package/src/__tests__/middleware.test.ts +0 -105
  236. package/src/__tests__/setup.ts +0 -10
  237. package/src/__tests__/zero-config-auth.test.ts +0 -607
  238. package/src/core/auth-manager.ts +0 -500
  239. package/src/core/use-auth.tsx +0 -245
@@ -1,13 +1,463 @@
1
- import express from 'express';
2
- import type { Request, Response } from 'express';
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
- const oxy = new OxyServices({ baseURL: options.baseURL });
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
- await oxy.logout();
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
- res.json({ valid });
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
- return { middleware: router };
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
  }