@luxexchange/sessions 1.0.0
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/.depcheckrc +20 -0
- package/.eslintrc.js +21 -0
- package/README.md +1 -0
- package/env.d.ts +12 -0
- package/package.json +50 -0
- package/project.json +42 -0
- package/src/challenge-solvers/createChallengeSolverService.ts +64 -0
- package/src/challenge-solvers/createHashcashMockSolver.ts +39 -0
- package/src/challenge-solvers/createHashcashSolver.test.ts +385 -0
- package/src/challenge-solvers/createHashcashSolver.ts +255 -0
- package/src/challenge-solvers/createNoneMockSolver.ts +11 -0
- package/src/challenge-solvers/createTurnstileMockSolver.ts +30 -0
- package/src/challenge-solvers/createTurnstileSolver.ts +353 -0
- package/src/challenge-solvers/hashcash/core.native.ts +34 -0
- package/src/challenge-solvers/hashcash/core.test.ts +314 -0
- package/src/challenge-solvers/hashcash/core.ts +35 -0
- package/src/challenge-solvers/hashcash/core.web.ts +123 -0
- package/src/challenge-solvers/hashcash/createWorkerHashcashSolver.test.ts +195 -0
- package/src/challenge-solvers/hashcash/createWorkerHashcashSolver.ts +120 -0
- package/src/challenge-solvers/hashcash/shared.ts +70 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashMultiWorkerChannel.native.ts +22 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashMultiWorkerChannel.ts +22 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashMultiWorkerChannel.web.ts +212 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashWorkerChannel.native.ts +16 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashWorkerChannel.ts +16 -0
- package/src/challenge-solvers/hashcash/worker/createHashcashWorkerChannel.web.ts +97 -0
- package/src/challenge-solvers/hashcash/worker/hashcash.worker.ts +91 -0
- package/src/challenge-solvers/hashcash/worker/types.ts +69 -0
- package/src/challenge-solvers/turnstileErrors.ts +49 -0
- package/src/challenge-solvers/turnstileScriptLoader.ts +325 -0
- package/src/challenge-solvers/turnstileSolver.integration.test.ts +331 -0
- package/src/challenge-solvers/types.ts +55 -0
- package/src/challengeFlow.integration.test.ts +627 -0
- package/src/device-id/createDeviceIdService.ts +19 -0
- package/src/device-id/types.ts +11 -0
- package/src/index.ts +137 -0
- package/src/lux-identifier/createLuxIdentifierService.ts +19 -0
- package/src/lux-identifier/luxIdentifierQuery.ts +20 -0
- package/src/lux-identifier/types.ts +11 -0
- package/src/oauth-service/createOAuthService.ts +125 -0
- package/src/oauth-service/types.ts +104 -0
- package/src/performance/createNoopPerformanceTracker.ts +12 -0
- package/src/performance/createPerformanceTracker.ts +43 -0
- package/src/performance/index.ts +7 -0
- package/src/performance/types.ts +11 -0
- package/src/session-initialization/createSessionInitializationService.test.ts +557 -0
- package/src/session-initialization/createSessionInitializationService.ts +193 -0
- package/src/session-initialization/sessionErrors.ts +32 -0
- package/src/session-repository/createSessionClient.ts +10 -0
- package/src/session-repository/createSessionRepository.test.ts +313 -0
- package/src/session-repository/createSessionRepository.ts +242 -0
- package/src/session-repository/errors.ts +22 -0
- package/src/session-repository/types.ts +289 -0
- package/src/session-service/createNoopSessionService.ts +24 -0
- package/src/session-service/createSessionService.test.ts +388 -0
- package/src/session-service/createSessionService.ts +61 -0
- package/src/session-service/types.ts +59 -0
- package/src/session-storage/createSessionStorage.ts +28 -0
- package/src/session-storage/types.ts +15 -0
- package/src/session.integration.test.ts +480 -0
- package/src/sessionLifecycle.integration.test.ts +264 -0
- package/src/test-utils/createLocalCookieTransport.ts +52 -0
- package/src/test-utils/createLocalHeaderTransport.ts +45 -0
- package/src/test-utils/mocks.ts +122 -0
- package/src/test-utils.ts +200 -0
- package/tsconfig.json +19 -0
- package/tsconfig.lint.json +8 -0
- package/tsconfig.spec.json +8 -0
- package/vitest.config.ts +20 -0
- package/vitest.integration.config.ts +14 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import type { DeviceIdService } from '@luxfi/sessions/src/device-id/types'
|
|
2
|
+
import type { SessionRepository } from '@luxfi/sessions/src/session-repository/types'
|
|
3
|
+
import { createSessionService } from '@luxfi/sessions/src/session-service/createSessionService'
|
|
4
|
+
import type { SessionService } from '@luxfi/sessions/src/session-service/types'
|
|
5
|
+
import type { SessionStorage } from '@luxfi/sessions/src/session-storage/types'
|
|
6
|
+
import type { LuxIdentifierService } from '@luxfi/sessions/src/lux-identifier/types'
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
8
|
+
|
|
9
|
+
describe('createSessionService', () => {
|
|
10
|
+
let storage: SessionStorage
|
|
11
|
+
let repository: SessionRepository
|
|
12
|
+
let deviceIdService: DeviceIdService
|
|
13
|
+
let luxIdentifierService: LuxIdentifierService
|
|
14
|
+
let service: SessionService
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
// In-memory storage implementation
|
|
18
|
+
let data: { sessionId: string } | null = null
|
|
19
|
+
storage = {
|
|
20
|
+
get: async (): Promise<{ sessionId: string } | null> => data,
|
|
21
|
+
set: async (newData): Promise<void> => {
|
|
22
|
+
data = newData
|
|
23
|
+
},
|
|
24
|
+
clear: async (): Promise<void> => {
|
|
25
|
+
data = null
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let deviceIdData: string | null = null
|
|
30
|
+
deviceIdService = {
|
|
31
|
+
getDeviceId: async (): Promise<string> => deviceIdData || '',
|
|
32
|
+
setDeviceId: async (deviceId: string): Promise<void> => {
|
|
33
|
+
deviceIdData = deviceId
|
|
34
|
+
},
|
|
35
|
+
removeDeviceId: async (): Promise<void> => {
|
|
36
|
+
deviceIdData = null
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let luxIdentifierData: string | null = null
|
|
41
|
+
luxIdentifierService = {
|
|
42
|
+
getLuxIdentifier: async (): Promise<string | null> => luxIdentifierData,
|
|
43
|
+
setLuxIdentifier: async (identifier: string): Promise<void> => {
|
|
44
|
+
luxIdentifierData = identifier
|
|
45
|
+
},
|
|
46
|
+
removeLuxIdentifier: async (): Promise<void> => {
|
|
47
|
+
luxIdentifierData = null
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// In-memory repository implementation
|
|
52
|
+
repository = {
|
|
53
|
+
initSession: async (): Promise<{
|
|
54
|
+
sessionId?: string
|
|
55
|
+
needChallenge: boolean
|
|
56
|
+
extra: Record<string, string>
|
|
57
|
+
}> => ({
|
|
58
|
+
sessionId: 'test-session-123',
|
|
59
|
+
needChallenge: false,
|
|
60
|
+
extra: {},
|
|
61
|
+
}),
|
|
62
|
+
challenge: vi.fn(),
|
|
63
|
+
verifySession: vi.fn(),
|
|
64
|
+
deleteSession: vi.fn(),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
service = createSessionService({
|
|
68
|
+
sessionStorage: storage,
|
|
69
|
+
sessionRepository: repository,
|
|
70
|
+
deviceIdService,
|
|
71
|
+
luxIdentifierService,
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('session lifecycle', () => {
|
|
76
|
+
it('starts with no session', async () => {
|
|
77
|
+
expect(await service.getSessionState()).toBeNull()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('creates and retrieves sessions', async () => {
|
|
81
|
+
await service.initSession()
|
|
82
|
+
expect(await service.getSessionState()).toEqual({ sessionId: 'test-session-123' })
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('removes sessions', async () => {
|
|
86
|
+
await service.initSession()
|
|
87
|
+
await service.removeSession()
|
|
88
|
+
expect(await service.getSessionState()).toBeNull()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('uses repository to generate session', async () => {
|
|
92
|
+
repository.initSession = async (): Promise<{
|
|
93
|
+
sessionId?: string
|
|
94
|
+
needChallenge: boolean
|
|
95
|
+
extra: Record<string, string>
|
|
96
|
+
}> => ({
|
|
97
|
+
sessionId: 'custom-session-456',
|
|
98
|
+
needChallenge: false,
|
|
99
|
+
extra: {},
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
service = createSessionService({
|
|
103
|
+
sessionStorage: storage,
|
|
104
|
+
sessionRepository: repository,
|
|
105
|
+
deviceIdService,
|
|
106
|
+
luxIdentifierService,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
await service.initSession()
|
|
110
|
+
expect(await service.getSessionState()).toEqual({ sessionId: 'custom-session-456' })
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('platform-specific behaviors', () => {
|
|
115
|
+
it('stores session ID when provided (mobile/extension)', async () => {
|
|
116
|
+
repository.initSession = async (): Promise<{
|
|
117
|
+
sessionId?: string
|
|
118
|
+
needChallenge: boolean
|
|
119
|
+
extra: Record<string, string>
|
|
120
|
+
}> => ({
|
|
121
|
+
sessionId: 'mobile-session-123',
|
|
122
|
+
needChallenge: false,
|
|
123
|
+
extra: {},
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
await service.initSession()
|
|
127
|
+
expect(await service.getSessionState()).toEqual({ sessionId: 'mobile-session-123' })
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('does not store session when ID is undefined (web)', async () => {
|
|
131
|
+
repository.initSession = async (): Promise<{
|
|
132
|
+
sessionId?: string
|
|
133
|
+
needChallenge: boolean
|
|
134
|
+
extra: Record<string, string>
|
|
135
|
+
}> => ({
|
|
136
|
+
sessionId: undefined, // Web uses cookies
|
|
137
|
+
needChallenge: true,
|
|
138
|
+
extra: { sitekey: 'turnstile-key' },
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
await service.initSession()
|
|
142
|
+
expect(await service.getSessionState()).toBeNull()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('handles different session ID values correctly', async () => {
|
|
146
|
+
const sessionIds = ['mobile-123', 'extension-456', undefined]
|
|
147
|
+
|
|
148
|
+
for (const sessionId of sessionIds) {
|
|
149
|
+
// Reset storage
|
|
150
|
+
await service.removeSession()
|
|
151
|
+
|
|
152
|
+
repository.initSession = async (): Promise<{
|
|
153
|
+
sessionId?: string
|
|
154
|
+
needChallenge: boolean
|
|
155
|
+
extra: Record<string, string>
|
|
156
|
+
}> => ({
|
|
157
|
+
sessionId,
|
|
158
|
+
needChallenge: false,
|
|
159
|
+
extra: {},
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
await service.initSession()
|
|
163
|
+
|
|
164
|
+
if (sessionId) {
|
|
165
|
+
expect(await service.getSessionState()).toEqual({ sessionId })
|
|
166
|
+
} else {
|
|
167
|
+
expect(await service.getSessionState()).toBeNull()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
describe('state persistence', () => {
|
|
174
|
+
it('shares state between service instances using same storage', async () => {
|
|
175
|
+
await service.initSession()
|
|
176
|
+
|
|
177
|
+
const service2 = createSessionService({
|
|
178
|
+
sessionStorage: storage,
|
|
179
|
+
sessionRepository: repository,
|
|
180
|
+
deviceIdService,
|
|
181
|
+
luxIdentifierService,
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
expect(await service2.getSessionState()).toEqual({ sessionId: 'test-session-123' })
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('maintains independent state with different storage instances', async () => {
|
|
188
|
+
// Create second set of dependencies
|
|
189
|
+
let data2: { sessionId: string } | null = null
|
|
190
|
+
const storage2: SessionStorage = {
|
|
191
|
+
get: async (): Promise<{ sessionId: string } | null> => data2,
|
|
192
|
+
set: async (newData): Promise<void> => {
|
|
193
|
+
data2 = newData
|
|
194
|
+
},
|
|
195
|
+
clear: async (): Promise<void> => {
|
|
196
|
+
data2 = null
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const service2 = createSessionService({
|
|
201
|
+
sessionStorage: storage2,
|
|
202
|
+
sessionRepository: repository,
|
|
203
|
+
deviceIdService,
|
|
204
|
+
luxIdentifierService,
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
await service.initSession()
|
|
208
|
+
|
|
209
|
+
// Second service with its own storage should have its own session
|
|
210
|
+
repository.initSession = async (): Promise<{
|
|
211
|
+
sessionId?: string
|
|
212
|
+
needChallenge: boolean
|
|
213
|
+
extra: Record<string, string>
|
|
214
|
+
}> => ({
|
|
215
|
+
sessionId: 'test-session-456',
|
|
216
|
+
needChallenge: false,
|
|
217
|
+
extra: {},
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
await service2.initSession()
|
|
221
|
+
|
|
222
|
+
expect(await service.getSessionState()).toEqual({ sessionId: 'test-session-123' })
|
|
223
|
+
expect(await service2.getSessionState()).toEqual({ sessionId: 'test-session-456' })
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
describe('error handling', () => {
|
|
228
|
+
it('propagates storage read errors', async () => {
|
|
229
|
+
storage.get = async (): Promise<{ sessionId: string } | null> => {
|
|
230
|
+
throw new Error('Storage read failed')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
await expect(service.getSessionState()).rejects.toThrow('Storage read failed')
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('propagates storage write errors', async () => {
|
|
237
|
+
storage.set = async (): Promise<void> => {
|
|
238
|
+
throw new Error('Storage write failed')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await expect(service.initSession()).rejects.toThrow('Storage write failed')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('propagates repository errors', async () => {
|
|
245
|
+
repository.initSession = async (): Promise<{
|
|
246
|
+
sessionId?: string
|
|
247
|
+
needChallenge: boolean
|
|
248
|
+
extra: Record<string, string>
|
|
249
|
+
}> => {
|
|
250
|
+
throw new Error('API call failed')
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
await expect(service.initSession()).rejects.toThrow('API call failed')
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('preserves state when operations fail', async () => {
|
|
257
|
+
await service.initSession()
|
|
258
|
+
const originalState = await service.getSessionState()
|
|
259
|
+
|
|
260
|
+
// Make clear fail
|
|
261
|
+
storage.clear = async (): Promise<void> => {
|
|
262
|
+
throw new Error('Clear failed')
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
await expect(service.removeSession()).rejects.toThrow('Clear failed')
|
|
266
|
+
expect(await service.getSessionState()).toEqual(originalState)
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
describe('integration scenarios', () => {
|
|
271
|
+
it('handles complete session lifecycle', async () => {
|
|
272
|
+
// Start with no session
|
|
273
|
+
expect(await service.getSessionState()).toBeNull()
|
|
274
|
+
|
|
275
|
+
// Create session
|
|
276
|
+
await service.initSession()
|
|
277
|
+
const session = await service.getSessionState()
|
|
278
|
+
expect(session).toBeTruthy()
|
|
279
|
+
expect(session?.sessionId).toBe('test-session-123')
|
|
280
|
+
|
|
281
|
+
// Remove session
|
|
282
|
+
await service.removeSession()
|
|
283
|
+
expect(await service.getSessionState()).toBeNull()
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('handles multiple initialization attempts', async () => {
|
|
287
|
+
// First initialization
|
|
288
|
+
await service.initSession()
|
|
289
|
+
const firstSession = await service.getSessionState()
|
|
290
|
+
|
|
291
|
+
// Second initialization should replace
|
|
292
|
+
repository.initSession = async (): Promise<{
|
|
293
|
+
sessionId?: string
|
|
294
|
+
needChallenge: boolean
|
|
295
|
+
extra: Record<string, string>
|
|
296
|
+
}> => ({
|
|
297
|
+
sessionId: 'new-session-789',
|
|
298
|
+
needChallenge: false,
|
|
299
|
+
extra: {},
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
await service.initSession()
|
|
303
|
+
const secondSession = await service.getSessionState()
|
|
304
|
+
|
|
305
|
+
expect(secondSession).not.toEqual(firstSession)
|
|
306
|
+
expect(secondSession).toEqual({ sessionId: 'new-session-789' })
|
|
307
|
+
})
|
|
308
|
+
})
|
|
309
|
+
describe('device ID handling', () => {
|
|
310
|
+
it('sets and gets device ID', async () => {
|
|
311
|
+
// Second initialization should replace
|
|
312
|
+
repository.initSession = async (): Promise<{
|
|
313
|
+
sessionId?: string
|
|
314
|
+
deviceId?: string
|
|
315
|
+
needChallenge: boolean
|
|
316
|
+
extra: Record<string, string>
|
|
317
|
+
}> => ({
|
|
318
|
+
sessionId: 'new-session-789',
|
|
319
|
+
deviceId: 'test-device-id',
|
|
320
|
+
needChallenge: false,
|
|
321
|
+
extra: {},
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
await service.initSession()
|
|
325
|
+
expect(await deviceIdService.getDeviceId()).toBe('test-device-id')
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
describe('lux identifier handling', () => {
|
|
330
|
+
it('persists luxIdentifier when provided in extra', async () => {
|
|
331
|
+
repository.initSession = async (): Promise<{
|
|
332
|
+
sessionId?: string
|
|
333
|
+
needChallenge: boolean
|
|
334
|
+
extra: Record<string, string>
|
|
335
|
+
}> => ({
|
|
336
|
+
sessionId: 'test-session-123',
|
|
337
|
+
needChallenge: false,
|
|
338
|
+
extra: { luxIdentifier: '71cef16f-4d99-4082-987c-a6f810f9ca7f' },
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
await service.initSession()
|
|
342
|
+
expect(await luxIdentifierService.getLuxIdentifier()).toBe('71cef16f-4d99-4082-987c-a6f810f9ca7f')
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
it('does not persist luxIdentifier when not provided', async () => {
|
|
346
|
+
repository.initSession = async (): Promise<{
|
|
347
|
+
sessionId?: string
|
|
348
|
+
needChallenge: boolean
|
|
349
|
+
extra: Record<string, string>
|
|
350
|
+
}> => ({
|
|
351
|
+
sessionId: 'test-session-123',
|
|
352
|
+
needChallenge: false,
|
|
353
|
+
extra: {},
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
await service.initSession()
|
|
357
|
+
expect(await luxIdentifierService.getLuxIdentifier()).toBeNull()
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('updates luxIdentifier on subsequent initSession calls', async () => {
|
|
361
|
+
repository.initSession = async (): Promise<{
|
|
362
|
+
sessionId?: string
|
|
363
|
+
needChallenge: boolean
|
|
364
|
+
extra: Record<string, string>
|
|
365
|
+
}> => ({
|
|
366
|
+
sessionId: 'test-session-123',
|
|
367
|
+
needChallenge: false,
|
|
368
|
+
extra: { luxIdentifier: 'first-identifier' },
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
await service.initSession()
|
|
372
|
+
expect(await luxIdentifierService.getLuxIdentifier()).toBe('first-identifier')
|
|
373
|
+
|
|
374
|
+
repository.initSession = async (): Promise<{
|
|
375
|
+
sessionId?: string
|
|
376
|
+
needChallenge: boolean
|
|
377
|
+
extra: Record<string, string>
|
|
378
|
+
}> => ({
|
|
379
|
+
sessionId: 'test-session-456',
|
|
380
|
+
needChallenge: false,
|
|
381
|
+
extra: { luxIdentifier: 'second-identifier' },
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
await service.initSession()
|
|
385
|
+
expect(await luxIdentifierService.getLuxIdentifier()).toBe('second-identifier')
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { DeviceIdService } from '@luxexchange/sessions/src/device-id/types'
|
|
2
|
+
import type { SessionRepository } from '@luxexchange/sessions/src/session-repository/types'
|
|
3
|
+
import type {
|
|
4
|
+
ChallengeRequest,
|
|
5
|
+
ChallengeResponse,
|
|
6
|
+
InitSessionResponse,
|
|
7
|
+
SessionService,
|
|
8
|
+
VerifySessionRequest,
|
|
9
|
+
VerifySessionResponse,
|
|
10
|
+
} from '@luxexchange/sessions/src/session-service/types'
|
|
11
|
+
import type { SessionStorage } from '@luxexchange/sessions/src/session-storage/types'
|
|
12
|
+
import type { LuxIdentifierService } from '@luxexchange/sessions/src/lux-identifier/types'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates a Session Service instance.
|
|
16
|
+
* Orchestrates usage of the Session Repository (remote) and Session Storage (local).
|
|
17
|
+
*/
|
|
18
|
+
export function createSessionService(ctx: {
|
|
19
|
+
sessionStorage: SessionStorage
|
|
20
|
+
deviceIdService: DeviceIdService
|
|
21
|
+
luxIdentifierService: LuxIdentifierService
|
|
22
|
+
sessionRepository: SessionRepository
|
|
23
|
+
}): SessionService {
|
|
24
|
+
async function initSession(): Promise<InitSessionResponse> {
|
|
25
|
+
const result = await ctx.sessionRepository.initSession()
|
|
26
|
+
if (result.sessionId) {
|
|
27
|
+
await ctx.sessionStorage.set({ sessionId: result.sessionId })
|
|
28
|
+
}
|
|
29
|
+
if (result.deviceId) {
|
|
30
|
+
await ctx.deviceIdService.setDeviceId(result.deviceId)
|
|
31
|
+
}
|
|
32
|
+
if (result.extra['luxIdentifier']) {
|
|
33
|
+
await ctx.luxIdentifierService.setLuxIdentifier(result.extra['luxIdentifier'])
|
|
34
|
+
}
|
|
35
|
+
return result
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function requestChallenge(request?: ChallengeRequest): Promise<ChallengeResponse> {
|
|
39
|
+
return ctx.sessionRepository.challenge(request ?? {})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function verifySession(input: VerifySessionRequest): Promise<VerifySessionResponse> {
|
|
43
|
+
return ctx.sessionRepository.verifySession(input)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function removeSession(): Promise<void> {
|
|
47
|
+
await ctx.sessionStorage.clear()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function getSessionState(): Promise<{ sessionId: string } | null> {
|
|
51
|
+
return ctx.sessionStorage.get()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
initSession,
|
|
56
|
+
requestChallenge,
|
|
57
|
+
verifySession,
|
|
58
|
+
removeSession,
|
|
59
|
+
getSessionState,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ChallengeType } from '@uniswap/client-platform-service/dist/uniswap/platformservice/v1/sessionService_pb'
|
|
2
|
+
import type { TypedChallengeData } from '@luxexchange/sessions/src/session-repository/types'
|
|
3
|
+
import { SessionState } from '@luxexchange/sessions/src/session-storage/types'
|
|
4
|
+
|
|
5
|
+
interface InitSessionResponse {
|
|
6
|
+
sessionId?: string
|
|
7
|
+
needChallenge: boolean
|
|
8
|
+
/** @deprecated Kept for backwards compatibility */
|
|
9
|
+
extra: Record<string, string>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ChallengeRequest {
|
|
13
|
+
challengeType?: ChallengeType
|
|
14
|
+
redirectUrl?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ChallengeResponse {
|
|
18
|
+
challengeId: string
|
|
19
|
+
challengeType: ChallengeType
|
|
20
|
+
/** @deprecated Use challengeData instead */
|
|
21
|
+
extra: Record<string, string>
|
|
22
|
+
/** Type-safe challenge-specific data (replaces extra) */
|
|
23
|
+
challengeData?: TypedChallengeData
|
|
24
|
+
authorizeUrl?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface VerifySessionRequest {
|
|
28
|
+
solution: string
|
|
29
|
+
challengeId: string
|
|
30
|
+
challengeType: ChallengeType
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface VerifySessionResponse {
|
|
34
|
+
retry: boolean
|
|
35
|
+
waitSeconds?: number
|
|
36
|
+
redirectUrl?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Interface used by clients to interact with Sessions.
|
|
41
|
+
* For business logic and dependencies, see {@link createSessionService}
|
|
42
|
+
*/
|
|
43
|
+
interface SessionService {
|
|
44
|
+
initSession: () => Promise<InitSessionResponse>
|
|
45
|
+
requestChallenge: (request?: ChallengeRequest) => Promise<ChallengeResponse>
|
|
46
|
+
verifySession: (input: VerifySessionRequest) => Promise<VerifySessionResponse>
|
|
47
|
+
removeSession: () => Promise<void>
|
|
48
|
+
getSessionState: () => Promise<SessionState | null>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type {
|
|
52
|
+
SessionService,
|
|
53
|
+
InitSessionResponse,
|
|
54
|
+
ChallengeRequest,
|
|
55
|
+
ChallengeResponse,
|
|
56
|
+
VerifySessionRequest,
|
|
57
|
+
VerifySessionResponse,
|
|
58
|
+
}
|
|
59
|
+
export { ChallengeType }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { SessionStorage } from '@luxfi/sessions/src/session-storage/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Session Storage instance, given a set of functions to interact with a storage driver.
|
|
5
|
+
*/
|
|
6
|
+
function createSessionStorage(ctx: {
|
|
7
|
+
getSessionId: () => Promise<string | null>
|
|
8
|
+
setSessionId: (sessionId: string) => Promise<void>
|
|
9
|
+
clearSessionId: () => Promise<void>
|
|
10
|
+
}): SessionStorage {
|
|
11
|
+
const get: SessionStorage['get'] = async () => {
|
|
12
|
+
const stored = await ctx.getSessionId()
|
|
13
|
+
return stored ? { sessionId: stored } : null
|
|
14
|
+
}
|
|
15
|
+
const set: SessionStorage['set'] = async (sessionState) => {
|
|
16
|
+
await ctx.setSessionId(sessionState.sessionId)
|
|
17
|
+
}
|
|
18
|
+
const clear: SessionStorage['clear'] = async () => {
|
|
19
|
+
await ctx.clearSessionId()
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
get,
|
|
23
|
+
set,
|
|
24
|
+
clear,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { createSessionStorage }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface SessionState {
|
|
2
|
+
sessionId: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Interface to interact with session storage.
|
|
7
|
+
* For business logic and dependencies, see {@link createSessionStorage}
|
|
8
|
+
*/
|
|
9
|
+
interface SessionStorage {
|
|
10
|
+
get(): Promise<SessionState | null>
|
|
11
|
+
set(session: SessionState): Promise<void>
|
|
12
|
+
clear(): Promise<void>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type { SessionStorage, SessionState }
|