@oxyhq/services 5.5.7 → 5.5.8
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 +13 -13
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +467 -87
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuthFetch.js +79 -45
- package/lib/commonjs/ui/hooks/useAuthFetch.js.map +1 -1
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/core/index.js +13 -6
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +467 -87
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useAuthFetch.js +79 -45
- package/lib/module/ui/hooks/useAuthFetch.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/typescript/core/index.d.ts +7 -6
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +11 -7
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts +10 -4
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/backend-middleware.test.ts +209 -0
- package/src/__tests__/ui/hooks/useAuthFetch.test.ts +70 -0
- package/src/core/index.ts +13 -7
- package/src/ui/context/OxyContext.tsx +522 -99
- package/src/ui/hooks/useAuthFetch.ts +81 -47
- package/src/ui/screens/SessionManagementScreen.tsx +9 -9
- package/lib/commonjs/core/AuthManager.js +0 -378
- package/lib/commonjs/core/AuthManager.js.map +0 -1
- package/lib/module/core/AuthManager.js +0 -373
- package/lib/module/core/AuthManager.js.map +0 -1
- package/lib/typescript/core/AuthManager.d.ts +0 -100
- package/lib/typescript/core/AuthManager.d.ts.map +0 -1
- package/src/core/AuthManager.ts +0 -389
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test for refactored authentication system
|
|
3
|
+
* Tests the backend middleware functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { OxyServices } from '../../core';
|
|
7
|
+
|
|
8
|
+
describe('Authentication System Integration', () => {
|
|
9
|
+
let oxyServices: OxyServices;
|
|
10
|
+
let mockReq: any;
|
|
11
|
+
let mockRes: any;
|
|
12
|
+
let mockNext: any;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
oxyServices = new OxyServices({
|
|
16
|
+
baseURL: 'https://api.test.com'
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Mock Express request/response objects
|
|
20
|
+
mockReq = {
|
|
21
|
+
headers: {},
|
|
22
|
+
user: undefined,
|
|
23
|
+
userId: undefined,
|
|
24
|
+
accessToken: undefined
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
mockRes = {
|
|
28
|
+
status: jest.fn().mockReturnThis(),
|
|
29
|
+
json: jest.fn().mockReturnThis()
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
mockNext = jest.fn();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('Middleware Creation', () => {
|
|
36
|
+
test('should create authentication middleware', () => {
|
|
37
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware();
|
|
38
|
+
expect(typeof middleware).toBe('function');
|
|
39
|
+
expect(middleware.length).toBe(3); // req, res, next
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('should create middleware with custom options', () => {
|
|
43
|
+
const customErrorHandler = jest.fn();
|
|
44
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware({
|
|
45
|
+
loadFullUser: false,
|
|
46
|
+
onError: customErrorHandler
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(typeof middleware).toBe('function');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('Token Validation', () => {
|
|
54
|
+
test('should handle missing token', async () => {
|
|
55
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware();
|
|
56
|
+
|
|
57
|
+
await middleware(mockReq, mockRes, mockNext);
|
|
58
|
+
|
|
59
|
+
expect(mockRes.status).toHaveBeenCalledWith(401);
|
|
60
|
+
expect(mockRes.json).toHaveBeenCalledWith({
|
|
61
|
+
message: 'Access token required',
|
|
62
|
+
code: 'MISSING_TOKEN'
|
|
63
|
+
});
|
|
64
|
+
expect(mockNext).not.toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('should handle invalid token format', async () => {
|
|
68
|
+
mockReq.headers.authorization = 'InvalidFormat token123';
|
|
69
|
+
|
|
70
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware();
|
|
71
|
+
await middleware(mockReq, mockRes, mockNext);
|
|
72
|
+
|
|
73
|
+
expect(mockRes.status).toHaveBeenCalledWith(401);
|
|
74
|
+
expect(mockNext).not.toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('should extract token from Bearer header', async () => {
|
|
78
|
+
mockReq.headers.authorization = 'Bearer valid.jwt.token';
|
|
79
|
+
|
|
80
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware();
|
|
81
|
+
await middleware(mockReq, mockRes, mockNext);
|
|
82
|
+
|
|
83
|
+
// Note: This will fail validation since it's not a real token,
|
|
84
|
+
// but we can verify the token was extracted
|
|
85
|
+
expect(mockRes.status).toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('Standalone Token Validation', () => {
|
|
90
|
+
test('should validate empty token', async () => {
|
|
91
|
+
const result = await oxyServices.authenticateToken('');
|
|
92
|
+
|
|
93
|
+
expect(result.valid).toBe(false);
|
|
94
|
+
expect(result.error).toBe('Token is required');
|
|
95
|
+
expect(result.userId).toBeUndefined();
|
|
96
|
+
expect(result.user).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('should validate invalid token', async () => {
|
|
100
|
+
const result = await oxyServices.authenticateToken('invalid.jwt.token');
|
|
101
|
+
|
|
102
|
+
expect(result.valid).toBe(false);
|
|
103
|
+
expect(result.error).toBeTruthy();
|
|
104
|
+
expect(result.userId).toBeUndefined();
|
|
105
|
+
expect(result.user).toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('should handle validation errors gracefully', async () => {
|
|
109
|
+
const result = await oxyServices.authenticateToken('malformed-token');
|
|
110
|
+
|
|
111
|
+
expect(result.valid).toBe(false);
|
|
112
|
+
expect(result.error).toMatch(/validation failed|Invalid/i);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('Custom Error Handling', () => {
|
|
117
|
+
test('should use custom error handler when provided', async () => {
|
|
118
|
+
const customErrorHandler = jest.fn();
|
|
119
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware({
|
|
120
|
+
onError: customErrorHandler
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await middleware(mockReq, mockRes, mockNext);
|
|
124
|
+
|
|
125
|
+
expect(customErrorHandler).toHaveBeenCalledWith({
|
|
126
|
+
message: 'Access token required',
|
|
127
|
+
code: 'MISSING_TOKEN',
|
|
128
|
+
status: 401
|
|
129
|
+
});
|
|
130
|
+
expect(mockRes.status).not.toHaveBeenCalled();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('Backend Integration Example', () => {
|
|
135
|
+
test('should work with Express.js route patterns', () => {
|
|
136
|
+
// Simulate Express.js app setup
|
|
137
|
+
const mockApp = {
|
|
138
|
+
get: jest.fn(),
|
|
139
|
+
post: jest.fn(),
|
|
140
|
+
use: jest.fn()
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Create middleware
|
|
144
|
+
const authenticateToken = oxyServices.createAuthenticateTokenMiddleware({
|
|
145
|
+
loadFullUser: true
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Simulate route registration
|
|
149
|
+
mockApp.get('/api/protected', authenticateToken, (req: any, res: any) => {
|
|
150
|
+
res.json({
|
|
151
|
+
message: 'Access granted',
|
|
152
|
+
user: req.user,
|
|
153
|
+
userId: req.userId
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(mockApp.get).toHaveBeenCalledWith(
|
|
158
|
+
'/api/protected',
|
|
159
|
+
authenticateToken,
|
|
160
|
+
expect.any(Function)
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('should provide consistent interface for different auth patterns', () => {
|
|
165
|
+
// Pattern 1: Middleware
|
|
166
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware();
|
|
167
|
+
expect(typeof middleware).toBe('function');
|
|
168
|
+
|
|
169
|
+
// Pattern 2: Standalone validation
|
|
170
|
+
const validateToken = oxyServices.authenticateToken.bind(oxyServices);
|
|
171
|
+
expect(typeof validateToken).toBe('function');
|
|
172
|
+
|
|
173
|
+
// Pattern 3: Direct service methods
|
|
174
|
+
expect(typeof oxyServices.getAccessToken).toBe('function');
|
|
175
|
+
expect(typeof oxyServices.setTokens).toBe('function');
|
|
176
|
+
expect(typeof oxyServices.clearTokens).toBe('function');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('Zero-Config Setup Verification', () => {
|
|
181
|
+
test('should work with minimal configuration', () => {
|
|
182
|
+
// This simulates the zero-config setup promise
|
|
183
|
+
const minimalSetup = () => {
|
|
184
|
+
// Backend: Just create OxyServices and use middleware
|
|
185
|
+
const services = new OxyServices({ baseURL: 'https://api.example.com' });
|
|
186
|
+
const authMiddleware = services.createAuthenticateTokenMiddleware();
|
|
187
|
+
|
|
188
|
+
return { services, authMiddleware };
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const { services, authMiddleware } = minimalSetup();
|
|
192
|
+
|
|
193
|
+
expect(services).toBeInstanceOf(OxyServices);
|
|
194
|
+
expect(typeof authMiddleware).toBe('function');
|
|
195
|
+
expect(services.getBaseURL()).toBe('https://api.example.com');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('should support runtime configuration changes', () => {
|
|
199
|
+
const services = new OxyServices({ baseURL: 'https://dev-api.com' });
|
|
200
|
+
|
|
201
|
+
expect(services.getBaseURL()).toBe('https://dev-api.com');
|
|
202
|
+
|
|
203
|
+
// Simulate runtime change to production
|
|
204
|
+
services.setBaseURL('https://prod-api.com');
|
|
205
|
+
|
|
206
|
+
expect(services.getBaseURL()).toBe('https://prod-api.com');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for refactored useAuthFetch hook
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { OxyServices } from '../../../core';
|
|
6
|
+
|
|
7
|
+
describe('useAuthFetch', () => {
|
|
8
|
+
let oxyServices: OxyServices;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
oxyServices = new OxyServices({
|
|
12
|
+
baseURL: 'https://api.test.com'
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('OxyServices integration', () => {
|
|
17
|
+
test('should set and get base URL', () => {
|
|
18
|
+
expect(oxyServices.getBaseURL()).toBe('https://api.test.com');
|
|
19
|
+
|
|
20
|
+
oxyServices.setBaseURL('https://new-api.test.com');
|
|
21
|
+
expect(oxyServices.getBaseURL()).toBe('https://new-api.test.com');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('should throw error for empty base URL', () => {
|
|
25
|
+
expect(() => oxyServices.setBaseURL('')).toThrow('Base URL cannot be empty');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('should handle token validation methods', () => {
|
|
29
|
+
// Test that core methods exist
|
|
30
|
+
expect(typeof oxyServices.getAccessToken).toBe('function');
|
|
31
|
+
expect(typeof oxyServices.setTokens).toBe('function');
|
|
32
|
+
expect(typeof oxyServices.clearTokens).toBe('function');
|
|
33
|
+
expect(typeof oxyServices.refreshTokens).toBe('function');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('URL resolution', () => {
|
|
38
|
+
test('should resolve relative URLs correctly', () => {
|
|
39
|
+
// This tests the logic that would be used in resolveURL function
|
|
40
|
+
const baseURL = 'https://api.test.com';
|
|
41
|
+
|
|
42
|
+
// Test relative URL with leading slash
|
|
43
|
+
const relativeUrl = '/api/users';
|
|
44
|
+
const expectedUrl = `${baseURL}${relativeUrl}`;
|
|
45
|
+
expect(expectedUrl).toBe('https://api.test.com/api/users');
|
|
46
|
+
|
|
47
|
+
// Test relative URL without leading slash
|
|
48
|
+
const relativeUrl2 = 'api/users';
|
|
49
|
+
const expectedUrl2 = `${baseURL}/${relativeUrl2}`;
|
|
50
|
+
expect(expectedUrl2).toBe('https://api.test.com/api/users');
|
|
51
|
+
|
|
52
|
+
// Test absolute URL (should remain unchanged)
|
|
53
|
+
const absoluteUrl = 'https://other-api.test.com/api/users';
|
|
54
|
+
expect(absoluteUrl).toBe('https://other-api.test.com/api/users');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('Authentication middleware compatibility', () => {
|
|
59
|
+
test('should create middleware function', () => {
|
|
60
|
+
const middleware = oxyServices.createAuthenticateTokenMiddleware();
|
|
61
|
+
expect(typeof middleware).toBe('function');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should validate tokens', async () => {
|
|
65
|
+
const result = await oxyServices.authenticateToken('invalid-token');
|
|
66
|
+
expect(result.valid).toBe(false);
|
|
67
|
+
expect(result.error).toBeTruthy();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
package/src/core/index.ts
CHANGED
|
@@ -68,14 +68,9 @@ interface JwtPayload {
|
|
|
68
68
|
/**
|
|
69
69
|
* OxyServices - Client library for interacting with the Oxy API
|
|
70
70
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @deprecated Direct authentication methods - Use AuthManager instead
|
|
75
|
-
* @see packages/services/docs/authentication-v2.md
|
|
71
|
+
* Note: For authentication status in UI components, use `isAuthenticated` from useOxy() context
|
|
72
|
+
* instead of checking token status directly on this service.
|
|
76
73
|
*/
|
|
77
|
-
export { AuthManager } from './AuthManager';
|
|
78
|
-
|
|
79
74
|
export class OxyServices {
|
|
80
75
|
private client: AxiosInstance;
|
|
81
76
|
private accessToken: string | null = null;
|
|
@@ -169,6 +164,17 @@ export class OxyServices {
|
|
|
169
164
|
return this.client.defaults.baseURL || '';
|
|
170
165
|
}
|
|
171
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Sets/updates the base URL for this OxyServices instance
|
|
169
|
+
* @param baseURL - The new base URL to use for API requests
|
|
170
|
+
*/
|
|
171
|
+
public setBaseURL(baseURL: string): void {
|
|
172
|
+
if (!baseURL) {
|
|
173
|
+
throw new Error('Base URL cannot be empty');
|
|
174
|
+
}
|
|
175
|
+
this.client.defaults.baseURL = baseURL;
|
|
176
|
+
}
|
|
177
|
+
|
|
172
178
|
/**
|
|
173
179
|
* Gets the currently authenticated user ID from the token
|
|
174
180
|
* @returns The user ID or null if not authenticated
|