@oxyhq/services 5.8.11 → 5.9.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/lib/commonjs/core/index.js +192 -346
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +6 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/node/index.js +0 -9
- package/lib/commonjs/node/index.js.map +1 -1
- package/lib/commonjs/types/middleware.js +6 -0
- package/lib/commonjs/types/middleware.js.map +1 -0
- package/lib/commonjs/ui/components/OxyProvider.js +3 -9
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/screens/FeedbackScreen.js +0 -4
- package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js +0 -4
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +16 -27
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +4 -18
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/styles/authStyles.js +1 -2
- package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
- package/lib/module/core/index.js +190 -345
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/node/index.js +0 -4
- package/lib/module/node/index.js.map +1 -1
- package/lib/module/types/middleware.js +4 -0
- package/lib/module/types/middleware.js.map +1 -0
- package/lib/module/ui/components/OxyProvider.js +3 -9
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/screens/FeedbackScreen.js +0 -4
- package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/module/ui/screens/RecoverAccountScreen.js +0 -4
- package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +16 -27
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +4 -18
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/styles/authStyles.js +1 -2
- package/lib/module/ui/styles/authStyles.js.map +1 -1
- package/lib/typescript/core/index.d.ts +34 -86
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.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/types/middleware.d.ts +19 -0
- package/lib/typescript/types/middleware.d.ts.map +1 -0
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
- package/lib/typescript/ui/styles/authStyles.d.ts +0 -1
- package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/core/index.ts +196 -328
- package/src/index.ts +4 -0
- package/src/node/index.ts +0 -4
- package/src/types/middleware.ts +20 -0
- package/src/ui/components/OxyProvider.tsx +3 -9
- package/src/ui/screens/FeedbackScreen.tsx +0 -4
- package/src/ui/screens/RecoverAccountScreen.tsx +0 -4
- package/src/ui/screens/SignInScreen.tsx +16 -20
- package/src/ui/screens/SignUpScreen.tsx +4 -14
- package/src/ui/styles/authStyles.ts +0 -1
- package/lib/commonjs/node/createAuth.js +0 -95
- package/lib/commonjs/node/createAuth.js.map +0 -1
- package/lib/module/node/createAuth.js +0 -90
- package/lib/module/node/createAuth.js.map +0 -1
- package/lib/typescript/node/createAuth.d.ts +0 -7
- package/lib/typescript/node/createAuth.d.ts.map +0 -1
- package/src/node/createAuth.ts +0 -116
package/src/core/index.ts
CHANGED
|
@@ -66,7 +66,6 @@ export class OxyServices {
|
|
|
66
66
|
private client: AxiosInstance;
|
|
67
67
|
private accessToken: string | null = null;
|
|
68
68
|
private refreshToken: string | null = null;
|
|
69
|
-
private refreshPromise: Promise<{ accessToken: string; refreshToken: string }> | null = null;
|
|
70
69
|
|
|
71
70
|
/**
|
|
72
71
|
* Creates a new instance of the OxyServices client
|
|
@@ -82,14 +81,25 @@ export class OxyServices {
|
|
|
82
81
|
this.client.interceptors.request.use(async (req: InternalAxiosRequestConfig) => {
|
|
83
82
|
if (!this.accessToken) {
|
|
84
83
|
return req;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check if token is expired and refresh if needed
|
|
87
|
+
try {
|
|
88
|
+
const decoded = jwtDecode<JwtPayload>(this.accessToken);
|
|
89
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
90
|
+
|
|
90
91
|
// If token expires in less than 60 seconds, refresh it
|
|
91
92
|
if (decoded.exp - currentTime < 60) {
|
|
92
|
-
|
|
93
|
+
// For session-based tokens, get a new token from the session
|
|
94
|
+
if (decoded.sessionId) {
|
|
95
|
+
try {
|
|
96
|
+
const res = await this.client.get(`/secure-session/token/${decoded.sessionId}`);
|
|
97
|
+
this.accessToken = res.data.accessToken;
|
|
98
|
+
} catch (refreshError) {
|
|
99
|
+
// If refresh fails, clear tokens
|
|
100
|
+
this.clearTokens();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
} catch (error) {
|
|
95
105
|
// If token can't be decoded, continue with request and let server handle it
|
|
@@ -109,19 +119,25 @@ export class OxyServices {
|
|
|
109
119
|
// If the error is due to an expired token and we haven't tried refreshing yet
|
|
110
120
|
if (
|
|
111
121
|
error.response?.status === 401 &&
|
|
112
|
-
this.
|
|
122
|
+
this.accessToken &&
|
|
113
123
|
originalRequest &&
|
|
114
124
|
!originalRequest.headers?.['X-Retry-After-Refresh']
|
|
115
125
|
) {
|
|
116
126
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
// Check if token is session-based and try to refresh
|
|
128
|
+
const decoded = jwtDecode<JwtPayload>(this.accessToken);
|
|
129
|
+
if (decoded.sessionId) {
|
|
130
|
+
const res = await this.client.get(`/secure-session/token/${decoded.sessionId}`);
|
|
131
|
+
this.accessToken = res.data.accessToken;
|
|
132
|
+
|
|
133
|
+
// Retry the original request with new token
|
|
134
|
+
const newRequest = { ...originalRequest };
|
|
135
|
+
if (newRequest.headers) {
|
|
136
|
+
newRequest.headers.Authorization = `Bearer ${this.accessToken}`;
|
|
137
|
+
newRequest.headers['X-Retry-After-Refresh'] = 'true';
|
|
138
|
+
}
|
|
139
|
+
return this.client(newRequest);
|
|
123
140
|
}
|
|
124
|
-
return this.client(newRequest);
|
|
125
141
|
} catch (refreshError) {
|
|
126
142
|
// If refresh fails, force user to login again
|
|
127
143
|
this.clearTokens();
|
|
@@ -156,44 +172,17 @@ export class OxyServices {
|
|
|
156
172
|
}
|
|
157
173
|
|
|
158
174
|
/**
|
|
159
|
-
*
|
|
160
|
-
* @
|
|
161
|
-
|
|
162
|
-
public getCurrentUserId(): string | null {
|
|
163
|
-
if (!this.accessToken) return null;
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const decoded = jwtDecode<JwtPayload>(this.accessToken);
|
|
167
|
-
|
|
168
|
-
// Check for both userId (preferred) and id (fallback) for compatibility
|
|
169
|
-
return decoded.userId || (decoded as any).id || null;
|
|
170
|
-
} catch (error) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Internal method to check if we have an access token
|
|
177
|
-
* @private
|
|
178
|
-
* @returns Boolean indicating if access token exists
|
|
179
|
-
* @internal - Use `isAuthenticated` from useOxy() context in UI components instead
|
|
180
|
-
*/
|
|
181
|
-
private hasAccessToken(): boolean {
|
|
182
|
-
return this.accessToken !== null;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Sets authentication tokens directly (useful for initializing from storage)
|
|
187
|
-
* @param accessToken - JWT access token
|
|
188
|
-
* @param refreshToken - Refresh token for getting new access tokens
|
|
175
|
+
* Set authentication tokens manually
|
|
176
|
+
* @param accessToken - The access token
|
|
177
|
+
* @param refreshToken - The refresh token (optional for session-based auth)
|
|
189
178
|
*/
|
|
190
|
-
public setTokens(accessToken: string, refreshToken: string): void {
|
|
179
|
+
public setTokens(accessToken: string, refreshToken: string = ''): void {
|
|
191
180
|
this.accessToken = accessToken;
|
|
192
181
|
this.refreshToken = refreshToken;
|
|
193
182
|
}
|
|
194
|
-
|
|
183
|
+
|
|
195
184
|
/**
|
|
196
|
-
*
|
|
185
|
+
* Clear stored authentication tokens
|
|
197
186
|
*/
|
|
198
187
|
public clearTokens(): void {
|
|
199
188
|
this.accessToken = null;
|
|
@@ -201,89 +190,28 @@ export class OxyServices {
|
|
|
201
190
|
}
|
|
202
191
|
|
|
203
192
|
/**
|
|
204
|
-
*
|
|
205
|
-
* @
|
|
206
|
-
* @param email - User's email address
|
|
207
|
-
* @param password - User's password
|
|
208
|
-
* @returns Object containing the message, token and user data
|
|
209
|
-
*/
|
|
210
|
-
async signUp(username: string, email: string, password: string): Promise<{ message: string; token: string; user: User }> {
|
|
211
|
-
try {
|
|
212
|
-
const res = await this.client.post('/auth/signup', { username, email, password });
|
|
213
|
-
const { message, token, user } = res.data;
|
|
214
|
-
this.accessToken = token;
|
|
215
|
-
return { message, token, user };
|
|
216
|
-
} catch (error) {
|
|
217
|
-
throw this.handleError(error);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Log in and store tokens
|
|
223
|
-
* @param username - User's username or email
|
|
224
|
-
* @param password - User's password
|
|
225
|
-
* @returns Login response containing tokens and user data
|
|
226
|
-
*/
|
|
227
|
-
async login(username: string, password: string): Promise<LoginResponse> {
|
|
228
|
-
try {
|
|
229
|
-
const res = await this.client.post('/auth/login', { username, password });
|
|
230
|
-
const { accessToken, refreshToken, user } = res.data;
|
|
231
|
-
this.accessToken = accessToken;
|
|
232
|
-
this.refreshToken = refreshToken;
|
|
233
|
-
return { accessToken, refreshToken, user };
|
|
234
|
-
} catch (error) {
|
|
235
|
-
throw this.handleError(error);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Log out user
|
|
193
|
+
* Get the current user ID from the stored token
|
|
194
|
+
* @returns User ID or null if not authenticated
|
|
241
195
|
*/
|
|
242
|
-
|
|
243
|
-
if (!this.
|
|
196
|
+
public getCurrentUserId(): string | null {
|
|
197
|
+
if (!this.accessToken) return null;
|
|
244
198
|
|
|
245
199
|
try {
|
|
246
|
-
|
|
200
|
+
const decoded = jwtDecode<JwtPayload>(this.accessToken);
|
|
201
|
+
return decoded.userId || decoded.id || null;
|
|
247
202
|
} catch (error) {
|
|
248
|
-
|
|
249
|
-
} finally {
|
|
250
|
-
this.accessToken = null;
|
|
251
|
-
this.refreshToken = null;
|
|
203
|
+
return null;
|
|
252
204
|
}
|
|
253
205
|
}
|
|
254
|
-
|
|
206
|
+
|
|
255
207
|
/**
|
|
256
|
-
*
|
|
257
|
-
* @
|
|
208
|
+
* Internal method to check if we have an access token
|
|
209
|
+
* @private
|
|
210
|
+
* @returns Boolean indicating if access token exists
|
|
211
|
+
* @internal - Use `isAuthenticated` from useOxy() context in UI components instead
|
|
258
212
|
*/
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
throw new Error('No refresh token available');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// If a refresh is already in progress, return that promise
|
|
265
|
-
if (this.refreshPromise) {
|
|
266
|
-
return this.refreshPromise;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Create a new refresh promise
|
|
270
|
-
this.refreshPromise = (async () => {
|
|
271
|
-
try {
|
|
272
|
-
const res = await this.client.post('/auth/refresh', { refreshToken: this.refreshToken });
|
|
273
|
-
const { accessToken, refreshToken } = res.data;
|
|
274
|
-
this.accessToken = accessToken;
|
|
275
|
-
this.refreshToken = refreshToken;
|
|
276
|
-
return { accessToken, refreshToken };
|
|
277
|
-
} catch (error) {
|
|
278
|
-
this.accessToken = null;
|
|
279
|
-
this.refreshToken = null;
|
|
280
|
-
throw this.handleError(error);
|
|
281
|
-
} finally {
|
|
282
|
-
this.refreshPromise = null;
|
|
283
|
-
}
|
|
284
|
-
})();
|
|
285
|
-
|
|
286
|
-
return this.refreshPromise;
|
|
213
|
+
private hasAccessToken(): boolean {
|
|
214
|
+
return this.accessToken !== null;
|
|
287
215
|
}
|
|
288
216
|
|
|
289
217
|
/**
|
|
@@ -292,6 +220,22 @@ export class OxyServices {
|
|
|
292
220
|
*/
|
|
293
221
|
async validate(): Promise<boolean> {
|
|
294
222
|
try {
|
|
223
|
+
// Check if token contains sessionId (new session-based system)
|
|
224
|
+
if (this.accessToken) {
|
|
225
|
+
try {
|
|
226
|
+
const decoded = jwtDecode<JwtPayload>(this.accessToken);
|
|
227
|
+
if (decoded.sessionId) {
|
|
228
|
+
// Use session-based validation
|
|
229
|
+
const res = await this.client.get(`/secure-session/validate/${decoded.sessionId}`);
|
|
230
|
+
return res.data.valid;
|
|
231
|
+
}
|
|
232
|
+
} catch (decodeError) {
|
|
233
|
+
// If token can't be decoded, fall back to old validation
|
|
234
|
+
console.warn('Error decoding JWT token for session validation:', decodeError);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Fall back to old validation method
|
|
295
239
|
const res = await this.client.get('/auth/validate');
|
|
296
240
|
return res.data.valid;
|
|
297
241
|
} catch (error) {
|
|
@@ -301,59 +245,6 @@ export class OxyServices {
|
|
|
301
245
|
|
|
302
246
|
/* Session Management Methods */
|
|
303
247
|
|
|
304
|
-
/**
|
|
305
|
-
* Get active sessions for the authenticated user
|
|
306
|
-
* @returns Array of active session objects
|
|
307
|
-
*/
|
|
308
|
-
async getUserSessions(): Promise<any[]> {
|
|
309
|
-
try {
|
|
310
|
-
const res = await this.client.get('/sessions');
|
|
311
|
-
return res.data;
|
|
312
|
-
} catch (error) {
|
|
313
|
-
throw this.handleError(error);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Logout from a specific session
|
|
319
|
-
* @param sessionId - The session ID to logout from
|
|
320
|
-
* @returns Success status
|
|
321
|
-
*/
|
|
322
|
-
async logoutSession(sessionId: string): Promise<{ success: boolean; message: string }> {
|
|
323
|
-
try {
|
|
324
|
-
const res = await this.client.delete(`/sessions/${sessionId}`);
|
|
325
|
-
return res.data;
|
|
326
|
-
} catch (error) {
|
|
327
|
-
throw this.handleError(error);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Logout from all other sessions (keep current session active)
|
|
333
|
-
* @returns Success status
|
|
334
|
-
*/
|
|
335
|
-
async logoutOtherSessions(): Promise<{ success: boolean; message: string }> {
|
|
336
|
-
try {
|
|
337
|
-
const res = await this.client.post('/sessions/logout-others');
|
|
338
|
-
return res.data;
|
|
339
|
-
} catch (error) {
|
|
340
|
-
throw this.handleError(error);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Logout from all sessions
|
|
346
|
-
* @returns Success status
|
|
347
|
-
*/
|
|
348
|
-
async logoutAllSessions(): Promise<{ success: boolean; message: string }> {
|
|
349
|
-
try {
|
|
350
|
-
const res = await this.client.post('/sessions/logout-all');
|
|
351
|
-
return res.data;
|
|
352
|
-
} catch (error) {
|
|
353
|
-
throw this.handleError(error);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
248
|
/**
|
|
358
249
|
* Get device sessions for a specific session ID
|
|
359
250
|
* @param sessionId - The session ID to get device sessions for
|
|
@@ -1177,12 +1068,41 @@ export class OxyServices {
|
|
|
1177
1068
|
}
|
|
1178
1069
|
|
|
1179
1070
|
/**
|
|
1180
|
-
*
|
|
1071
|
+
* Sign up a new user and create a session
|
|
1072
|
+
* @param username - Desired username
|
|
1073
|
+
* @param email - User's email address
|
|
1074
|
+
* @param password - User's password
|
|
1075
|
+
* @returns Object containing the message, token and user data
|
|
1076
|
+
*/
|
|
1077
|
+
async signUp(username: string, email: string, password: string): Promise<{ message: string; token: string; user: User }> {
|
|
1078
|
+
try {
|
|
1079
|
+
// First, create the user account
|
|
1080
|
+
const res = await this.client.post('/secure-session/register', { username, email, password });
|
|
1081
|
+
const { message, user } = res.data;
|
|
1082
|
+
|
|
1083
|
+
// Then log them in to create a session
|
|
1084
|
+
const loginRes = await this.secureLogin(username, password);
|
|
1085
|
+
|
|
1086
|
+
// Get the access token for the session
|
|
1087
|
+
const tokenRes = await this.getTokenBySession(loginRes.sessionId);
|
|
1088
|
+
|
|
1089
|
+
return {
|
|
1090
|
+
message,
|
|
1091
|
+
token: tokenRes.accessToken,
|
|
1092
|
+
user: loginRes.user as User
|
|
1093
|
+
};
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
throw this.handleError(error);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Secure login that creates a device-based session
|
|
1181
1101
|
* @param username - User's username or email
|
|
1182
1102
|
* @param password - User's password
|
|
1183
|
-
* @param deviceName - Optional device name
|
|
1184
|
-
* @param deviceFingerprint -
|
|
1185
|
-
* @returns
|
|
1103
|
+
* @param deviceName - Optional device name
|
|
1104
|
+
* @param deviceFingerprint - Optional device fingerprint
|
|
1105
|
+
* @returns Login response with session data
|
|
1186
1106
|
*/
|
|
1187
1107
|
async secureLogin(username: string, password: string, deviceName?: string, deviceFingerprint?: any): Promise<SecureLoginResponse> {
|
|
1188
1108
|
try {
|
|
@@ -1362,132 +1282,50 @@ export class OxyServices {
|
|
|
1362
1282
|
}
|
|
1363
1283
|
|
|
1364
1284
|
/**
|
|
1365
|
-
*
|
|
1366
|
-
* This
|
|
1367
|
-
* @param options - Configuration options for the middleware
|
|
1285
|
+
* Create authentication middleware for Express.js applications
|
|
1286
|
+
* This is the recommended way to protect routes in server applications
|
|
1368
1287
|
* @returns Express middleware function
|
|
1369
1288
|
*/
|
|
1370
|
-
public
|
|
1371
|
-
loadFullUser?: boolean; // Whether to load full user object or just user ID
|
|
1372
|
-
onError?: (error: ApiError) => any; // Custom error handler
|
|
1373
|
-
} = {}) {
|
|
1374
|
-
const { loadFullUser = true, onError } = options;
|
|
1375
|
-
|
|
1289
|
+
public createAuthMiddleware() {
|
|
1376
1290
|
return async (req: any, res: any, next: any) => {
|
|
1377
1291
|
try {
|
|
1378
|
-
const authHeader = req.headers
|
|
1379
|
-
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
|
1292
|
+
const authHeader = req.headers.authorization;
|
|
1380
1293
|
|
|
1381
|
-
if (!
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
status: 401
|
|
1386
|
-
};
|
|
1387
|
-
|
|
1388
|
-
if (onError) {
|
|
1389
|
-
return onError(error);
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
return res.status(401).json({
|
|
1393
|
-
message: 'Access token required',
|
|
1394
|
-
code: 'MISSING_TOKEN'
|
|
1395
|
-
});
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
// Create a temporary OxyServices instance with the token to validate it
|
|
1399
|
-
const tempOxyServices = new OxyServices({
|
|
1400
|
-
baseURL: this.client.defaults.baseURL || ''
|
|
1401
|
-
});
|
|
1402
|
-
tempOxyServices.setTokens(token, ''); // Set access token
|
|
1403
|
-
|
|
1404
|
-
// Validate token using the validate method
|
|
1405
|
-
const isValid = await tempOxyServices.validate();
|
|
1406
|
-
|
|
1407
|
-
if (!isValid) {
|
|
1408
|
-
const error = {
|
|
1409
|
-
message: 'Invalid or expired token',
|
|
1410
|
-
code: 'INVALID_TOKEN',
|
|
1411
|
-
status: 403
|
|
1412
|
-
};
|
|
1413
|
-
|
|
1414
|
-
if (onError) {
|
|
1415
|
-
return onError(error);
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
return res.status(403).json({
|
|
1419
|
-
message: 'Invalid or expired token',
|
|
1420
|
-
code: 'INVALID_TOKEN'
|
|
1294
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
1295
|
+
return res.status(401).json({
|
|
1296
|
+
error: 'Authentication required',
|
|
1297
|
+
message: 'Invalid or missing authorization header'
|
|
1421
1298
|
});
|
|
1422
1299
|
}
|
|
1300
|
+
|
|
1301
|
+
const token = authHeader.split(' ')[1];
|
|
1423
1302
|
|
|
1424
|
-
//
|
|
1425
|
-
|
|
1426
|
-
try {
|
|
1427
|
-
const decoded = jwtDecode<JwtPayload>(token);
|
|
1428
|
-
userId = decoded.userId || decoded.id;
|
|
1429
|
-
} catch (decodeError) {
|
|
1430
|
-
const error = {
|
|
1431
|
-
message: 'Invalid token payload',
|
|
1432
|
-
code: 'INVALID_PAYLOAD',
|
|
1433
|
-
status: 403
|
|
1434
|
-
};
|
|
1435
|
-
|
|
1436
|
-
if (onError) {
|
|
1437
|
-
return onError(error);
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
return res.status(403).json({
|
|
1441
|
-
message: 'Invalid token payload',
|
|
1442
|
-
code: 'INVALID_PAYLOAD'
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1303
|
+
// Use the authenticateToken method
|
|
1304
|
+
const result = await this.authenticateToken(token);
|
|
1445
1305
|
|
|
1446
|
-
if (!
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
status: 403
|
|
1451
|
-
};
|
|
1452
|
-
|
|
1453
|
-
if (onError) {
|
|
1454
|
-
return onError(error);
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
return res.status(403).json({
|
|
1458
|
-
message: 'Invalid token payload',
|
|
1459
|
-
code: 'INVALID_PAYLOAD'
|
|
1306
|
+
if (!result.valid) {
|
|
1307
|
+
return res.status(401).json({
|
|
1308
|
+
error: 'Invalid token',
|
|
1309
|
+
message: result.error || 'The provided authentication token is invalid'
|
|
1460
1310
|
});
|
|
1461
1311
|
}
|
|
1462
1312
|
|
|
1463
1313
|
// Set user information on request object
|
|
1464
|
-
req.userId = userId;
|
|
1314
|
+
req.userId = result.userId || undefined;
|
|
1465
1315
|
req.accessToken = token;
|
|
1466
1316
|
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
req.user = userProfile;
|
|
1472
|
-
} catch (userError) {
|
|
1473
|
-
// If we can't load user, continue with just ID
|
|
1474
|
-
req.user = { id: userId };
|
|
1475
|
-
}
|
|
1476
|
-
} else {
|
|
1477
|
-
req.user = { id: userId };
|
|
1317
|
+
if (result.user) {
|
|
1318
|
+
req.user = result.user;
|
|
1319
|
+
} else if (result.userId) {
|
|
1320
|
+
req.user = { id: result.userId };
|
|
1478
1321
|
}
|
|
1479
1322
|
|
|
1480
1323
|
next();
|
|
1481
1324
|
} catch (error) {
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
return res.status(apiError.status || 500).json({
|
|
1489
|
-
message: apiError.message,
|
|
1490
|
-
code: apiError.code
|
|
1325
|
+
console.error('Auth middleware error:', error);
|
|
1326
|
+
return res.status(500).json({
|
|
1327
|
+
error: 'Server error',
|
|
1328
|
+
message: 'An error occurred while authenticating your request'
|
|
1491
1329
|
});
|
|
1492
1330
|
}
|
|
1493
1331
|
};
|
|
@@ -1513,55 +1351,76 @@ export class OxyServices {
|
|
|
1513
1351
|
};
|
|
1514
1352
|
}
|
|
1515
1353
|
|
|
1516
|
-
//
|
|
1517
|
-
const tempOxyServices = new OxyServices({
|
|
1518
|
-
baseURL: this.client.defaults.baseURL || ''
|
|
1519
|
-
});
|
|
1520
|
-
tempOxyServices.setTokens(token, '');
|
|
1521
|
-
|
|
1522
|
-
// Validate token
|
|
1523
|
-
const isValid = await tempOxyServices.validate();
|
|
1524
|
-
|
|
1525
|
-
if (!isValid) {
|
|
1526
|
-
return {
|
|
1527
|
-
valid: false,
|
|
1528
|
-
error: 'Invalid or expired token'
|
|
1529
|
-
};
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
// Get user ID from token using JWT decode
|
|
1533
|
-
let userId: string | null = null;
|
|
1354
|
+
// Check if token contains sessionId (new session-based system)
|
|
1534
1355
|
try {
|
|
1535
1356
|
const decoded = jwtDecode<JwtPayload>(token);
|
|
1536
|
-
userId = decoded.userId || decoded.id;
|
|
1357
|
+
const userId = decoded.userId || decoded.id;
|
|
1358
|
+
|
|
1359
|
+
if (decoded.sessionId) {
|
|
1360
|
+
// Use session-based validation
|
|
1361
|
+
const tempOxyServices = new OxyServices({
|
|
1362
|
+
baseURL: this.client.defaults.baseURL || ''
|
|
1363
|
+
});
|
|
1364
|
+
tempOxyServices.setTokens(token, '');
|
|
1365
|
+
|
|
1366
|
+
const validation = await tempOxyServices.validateSession(decoded.sessionId);
|
|
1367
|
+
|
|
1368
|
+
if (validation.valid) {
|
|
1369
|
+
return {
|
|
1370
|
+
valid: true,
|
|
1371
|
+
userId,
|
|
1372
|
+
user: validation.user
|
|
1373
|
+
};
|
|
1374
|
+
} else {
|
|
1375
|
+
return {
|
|
1376
|
+
valid: false,
|
|
1377
|
+
error: 'Invalid or expired session'
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
} else {
|
|
1381
|
+
// Use old validation method
|
|
1382
|
+
const tempOxyServices = new OxyServices({
|
|
1383
|
+
baseURL: this.client.defaults.baseURL || ''
|
|
1384
|
+
});
|
|
1385
|
+
tempOxyServices.setTokens(token, '');
|
|
1386
|
+
|
|
1387
|
+
const isValid = await tempOxyServices.validate();
|
|
1388
|
+
|
|
1389
|
+
if (!isValid) {
|
|
1390
|
+
return {
|
|
1391
|
+
valid: false,
|
|
1392
|
+
error: 'Invalid or expired token'
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
if (!userId) {
|
|
1397
|
+
return {
|
|
1398
|
+
valid: false,
|
|
1399
|
+
error: 'Invalid token payload'
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// Try to get user profile
|
|
1404
|
+
let user;
|
|
1405
|
+
try {
|
|
1406
|
+
user = await tempOxyServices.getUserById(userId);
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
// Continue without full user data
|
|
1409
|
+
user = { id: userId };
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
return {
|
|
1413
|
+
valid: true,
|
|
1414
|
+
userId,
|
|
1415
|
+
user
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1537
1418
|
} catch (decodeError) {
|
|
1538
1419
|
return {
|
|
1539
1420
|
valid: false,
|
|
1540
1421
|
error: 'Invalid token payload'
|
|
1541
1422
|
};
|
|
1542
1423
|
}
|
|
1543
|
-
|
|
1544
|
-
if (!userId) {
|
|
1545
|
-
return {
|
|
1546
|
-
valid: false,
|
|
1547
|
-
error: 'Invalid token payload'
|
|
1548
|
-
};
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
// Try to get user profile
|
|
1552
|
-
let user;
|
|
1553
|
-
try {
|
|
1554
|
-
user = await tempOxyServices.getUserById(userId);
|
|
1555
|
-
} catch (error) {
|
|
1556
|
-
// Continue without full user data
|
|
1557
|
-
user = { id: userId };
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
return {
|
|
1561
|
-
valid: true,
|
|
1562
|
-
userId,
|
|
1563
|
-
user
|
|
1564
|
-
};
|
|
1565
1424
|
} catch (error) {
|
|
1566
1425
|
return {
|
|
1567
1426
|
valid: false,
|
|
@@ -1909,6 +1768,15 @@ export default OxyServices;
|
|
|
1909
1768
|
export * from '../models/interfaces';
|
|
1910
1769
|
export * from '../models/secureSession';
|
|
1911
1770
|
|
|
1771
|
+
// Clean middleware exports - these will be available for server-side use
|
|
1772
|
+
// Note: These require Express.js and are only for server-side applications
|
|
1773
|
+
export type { AuthRequest, SimpleAuthRequest } from '../types/middleware';
|
|
1774
|
+
|
|
1775
|
+
// Export a simple function to create auth middleware
|
|
1776
|
+
export const createAuthMiddleware = (oxyServices: OxyServices) => {
|
|
1777
|
+
return oxyServices.createAuthMiddleware();
|
|
1778
|
+
};
|
|
1779
|
+
|
|
1912
1780
|
if (typeof FormData === 'undefined') {
|
|
1913
1781
|
console.warn('[OxyHQ/Services] FormData is not available. If you are using Hermes, add "import \'react-native-url-polyfill/auto\'" at the top of your app entry file.');
|
|
1914
1782
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
export { OxyServices } from './core';
|
|
11
11
|
export { OXY_CLOUD_URL } from './core';
|
|
12
12
|
|
|
13
|
+
// Middleware exports (for server-side use)
|
|
14
|
+
export type { AuthRequest, SimpleAuthRequest } from './types/middleware';
|
|
15
|
+
export { createAuthMiddleware } from './core';
|
|
16
|
+
|
|
13
17
|
// React context
|
|
14
18
|
export {
|
|
15
19
|
OxyContextProvider, // Backward compatibility
|
package/src/node/index.ts
CHANGED
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
// ------------- Core Imports -------------
|
|
6
6
|
import { OxyServices, OXY_CLOUD_URL } from '../core'; // Adjusted path
|
|
7
|
-
import { createAuth } from './createAuth';
|
|
8
7
|
import * as Models from '../models/interfaces'; // Adjusted path
|
|
9
8
|
|
|
10
9
|
// ------------- Core Exports -------------
|
|
11
10
|
export { OxyServices, OXY_CLOUD_URL };
|
|
12
11
|
|
|
13
|
-
// Zero-config auth and session router
|
|
14
|
-
export { createAuth };
|
|
15
|
-
|
|
16
12
|
// ------------- Model Exports -------------
|
|
17
13
|
export { Models }; // Export all models as a namespace
|
|
18
14
|
export * from '../models/interfaces'; // Export all models directly
|