@passlock/client 0.9.21 → 0.9.23

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 (192) hide show
  1. package/README.md +125 -0
  2. package/dist/authentication/authenticate.d.ts +15 -15
  3. package/dist/authentication/authenticate.fixture.d.ts +20 -6
  4. package/dist/authentication/authenticate.fixture.js +7 -5
  5. package/dist/authentication/authenticate.fixture.js.map +1 -1
  6. package/dist/authentication/authenticate.js +19 -8
  7. package/dist/authentication/authenticate.js.map +1 -1
  8. package/dist/capabilities/capabilities.d.ts +8 -4
  9. package/dist/capabilities/capabilities.js +10 -1
  10. package/dist/capabilities/capabilities.js.map +1 -1
  11. package/dist/connection/connection.d.ts +11 -7
  12. package/dist/connection/connection.fixture.d.ts +2 -2
  13. package/dist/connection/connection.fixture.js +2 -1
  14. package/dist/connection/connection.fixture.js.map +1 -1
  15. package/dist/connection/connection.js +12 -3
  16. package/dist/connection/connection.js.map +1 -1
  17. package/dist/effect.d.ts +22 -45
  18. package/dist/effect.js +55 -51
  19. package/dist/effect.js.map +1 -1
  20. package/dist/email/email.d.ts +38 -11
  21. package/dist/email/email.fixture.d.ts +19 -5
  22. package/dist/email/email.fixture.js +4 -3
  23. package/dist/email/email.fixture.js.map +1 -1
  24. package/dist/email/email.js +43 -7
  25. package/dist/email/email.js.map +1 -1
  26. package/dist/event/event.d.ts +3 -1
  27. package/dist/event/event.js +3 -0
  28. package/dist/event/event.js.map +1 -1
  29. package/dist/index.d.ts +105 -27
  30. package/dist/index.js +101 -50
  31. package/dist/index.js.map +1 -1
  32. package/dist/logging/eventLogger.d.ts +13 -1
  33. package/dist/logging/eventLogger.js +13 -0
  34. package/dist/logging/eventLogger.js.map +1 -1
  35. package/dist/registration/register.d.ts +18 -21
  36. package/dist/registration/register.fixture.d.ts +19 -5
  37. package/dist/registration/register.fixture.js +14 -7
  38. package/dist/registration/register.fixture.js.map +1 -1
  39. package/dist/registration/register.js +18 -9
  40. package/dist/registration/register.js.map +1 -1
  41. package/dist/rpc/authentication.d.ts +0 -1
  42. package/dist/rpc/authentication.js +1 -0
  43. package/dist/rpc/authentication.js.map +1 -1
  44. package/dist/rpc/client.d.ts +4 -1
  45. package/dist/rpc/client.js +12 -2
  46. package/dist/rpc/client.js.map +1 -1
  47. package/dist/rpc/config.d.ts +0 -1
  48. package/dist/rpc/connection.d.ts +0 -1
  49. package/dist/rpc/connection.js +1 -0
  50. package/dist/rpc/connection.js.map +1 -1
  51. package/dist/rpc/registration.d.ts +0 -1
  52. package/dist/rpc/registration.js +1 -0
  53. package/dist/rpc/registration.js.map +1 -1
  54. package/dist/rpc/social.d.ts +0 -1
  55. package/dist/rpc/social.js +1 -0
  56. package/dist/rpc/social.js.map +1 -1
  57. package/dist/rpc/user.d.ts +0 -1
  58. package/dist/rpc/user.js +1 -0
  59. package/dist/rpc/user.js.map +1 -1
  60. package/dist/social/social.d.ts +16 -23
  61. package/dist/social/social.fixture.d.ts +21 -9
  62. package/dist/social/social.fixture.js +8 -14
  63. package/dist/social/social.fixture.js.map +1 -1
  64. package/dist/social/social.js +14 -10
  65. package/dist/social/social.js.map +1 -1
  66. package/dist/storage/storage.d.ts +40 -12
  67. package/dist/storage/storage.fixture.d.ts +2 -2
  68. package/dist/storage/storage.fixture.js +2 -2
  69. package/dist/storage/storage.fixture.js.map +1 -1
  70. package/dist/storage/storage.js +48 -15
  71. package/dist/storage/storage.js.map +1 -1
  72. package/dist/test/fixtures.d.ts +1 -2
  73. package/dist/test/fixtures.js +20 -5
  74. package/dist/test/fixtures.js.map +1 -1
  75. package/dist/user/user.d.ts +8 -5
  76. package/dist/user/user.fixture.d.ts +2 -2
  77. package/dist/user/user.fixture.js +9 -5
  78. package/dist/user/user.fixture.js.map +1 -1
  79. package/dist/user/user.js +9 -3
  80. package/dist/user/user.js.map +1 -1
  81. package/dist/version.d.ts +1 -2
  82. package/dist/version.js +1 -1
  83. package/dist/version.js.map +1 -1
  84. package/package.json +39 -33
  85. package/src/authentication/authenticate.fixture.ts +8 -7
  86. package/src/authentication/authenticate.test.ts +59 -17
  87. package/src/authentication/authenticate.ts +34 -32
  88. package/src/capabilities/capabilities.ts +9 -8
  89. package/src/connection/connection.fixture.ts +2 -1
  90. package/src/connection/connection.test.ts +3 -3
  91. package/src/connection/connection.ts +9 -8
  92. package/src/effect.ts +129 -128
  93. package/src/email/email.fixture.ts +4 -3
  94. package/src/email/email.test.ts +4 -4
  95. package/src/email/email.ts +24 -16
  96. package/src/index.ts +225 -169
  97. package/src/logging/eventLogger.test.ts +1 -1
  98. package/src/logging/eventLogger.ts +2 -2
  99. package/src/registration/register.fixture.ts +14 -8
  100. package/src/registration/register.test.ts +13 -9
  101. package/src/registration/register.ts +37 -34
  102. package/src/rpc/authentication.ts +31 -0
  103. package/src/rpc/client.ts +173 -0
  104. package/src/rpc/config.ts +18 -0
  105. package/src/rpc/connection.ts +24 -0
  106. package/src/rpc/registration.ts +31 -0
  107. package/src/rpc/social.ts +36 -0
  108. package/src/rpc/user.ts +42 -0
  109. package/src/social/social.fixture.ts +10 -18
  110. package/src/social/social.test.ts +13 -29
  111. package/src/social/social.ts +20 -47
  112. package/src/storage/storage.fixture.ts +3 -4
  113. package/src/storage/storage.test.ts +28 -19
  114. package/src/storage/storage.ts +36 -36
  115. package/src/test/fixtures.ts +21 -6
  116. package/src/user/user.fixture.ts +17 -7
  117. package/src/user/user.test.ts +2 -5
  118. package/src/user/user.ts +13 -9
  119. package/src/version.ts +1 -0
  120. package/dist/authentication/authenticate.d.ts.map +0 -1
  121. package/dist/authentication/authenticate.fixture.d.ts.map +0 -1
  122. package/dist/authentication/authenticate.test.d.ts +0 -2
  123. package/dist/authentication/authenticate.test.d.ts.map +0 -1
  124. package/dist/authentication/authenticate.test.js +0 -111
  125. package/dist/authentication/authenticate.test.js.map +0 -1
  126. package/dist/capabilities/capabilities.d.ts.map +0 -1
  127. package/dist/config.d.ts +0 -18
  128. package/dist/config.d.ts.map +0 -1
  129. package/dist/config.js +0 -20
  130. package/dist/config.js.map +0 -1
  131. package/dist/connection/connection.d.ts.map +0 -1
  132. package/dist/connection/connection.fixture.d.ts.map +0 -1
  133. package/dist/connection/connection.test.d.ts +0 -2
  134. package/dist/connection/connection.test.d.ts.map +0 -1
  135. package/dist/connection/connection.test.js +0 -36
  136. package/dist/connection/connection.test.js.map +0 -1
  137. package/dist/effect.d.ts.map +0 -1
  138. package/dist/email/email.d.ts.map +0 -1
  139. package/dist/email/email.fixture.d.ts.map +0 -1
  140. package/dist/email/email.test.d.ts +0 -2
  141. package/dist/email/email.test.d.ts.map +0 -1
  142. package/dist/email/email.test.js +0 -99
  143. package/dist/email/email.test.js.map +0 -1
  144. package/dist/event/event.d.ts.map +0 -1
  145. package/dist/event/event.node.test.d.ts +0 -2
  146. package/dist/event/event.node.test.d.ts.map +0 -1
  147. package/dist/event/event.node.test.js +0 -13
  148. package/dist/event/event.node.test.js.map +0 -1
  149. package/dist/event/event.test.d.ts +0 -2
  150. package/dist/event/event.test.d.ts.map +0 -1
  151. package/dist/event/event.test.js +0 -30
  152. package/dist/event/event.test.js.map +0 -1
  153. package/dist/index.d.ts.map +0 -1
  154. package/dist/logging/eventLogger.d.ts.map +0 -1
  155. package/dist/logging/eventLogger.test.d.ts +0 -2
  156. package/dist/logging/eventLogger.test.d.ts.map +0 -1
  157. package/dist/logging/eventLogger.test.js +0 -67
  158. package/dist/logging/eventLogger.test.js.map +0 -1
  159. package/dist/registration/register.d.ts.map +0 -1
  160. package/dist/registration/register.fixture.d.ts.map +0 -1
  161. package/dist/registration/register.test.d.ts +0 -2
  162. package/dist/registration/register.test.d.ts.map +0 -1
  163. package/dist/registration/register.test.js +0 -104
  164. package/dist/registration/register.test.js.map +0 -1
  165. package/dist/rpc/authentication.d.ts.map +0 -1
  166. package/dist/rpc/client.d.ts.map +0 -1
  167. package/dist/rpc/config.d.ts.map +0 -1
  168. package/dist/rpc/connection.d.ts.map +0 -1
  169. package/dist/rpc/registration.d.ts.map +0 -1
  170. package/dist/rpc/social.d.ts.map +0 -1
  171. package/dist/rpc/user.d.ts.map +0 -1
  172. package/dist/social/social.d.ts.map +0 -1
  173. package/dist/social/social.fixture.d.ts.map +0 -1
  174. package/dist/social/social.test.d.ts +0 -2
  175. package/dist/social/social.test.d.ts.map +0 -1
  176. package/dist/social/social.test.js +0 -110
  177. package/dist/social/social.test.js.map +0 -1
  178. package/dist/storage/storage.d.ts.map +0 -1
  179. package/dist/storage/storage.fixture.d.ts.map +0 -1
  180. package/dist/storage/storage.test.d.ts +0 -2
  181. package/dist/storage/storage.test.d.ts.map +0 -1
  182. package/dist/storage/storage.test.js +0 -120
  183. package/dist/storage/storage.test.js.map +0 -1
  184. package/dist/test/fixtures.d.ts.map +0 -1
  185. package/dist/user/user.d.ts.map +0 -1
  186. package/dist/user/user.fixture.d.ts.map +0 -1
  187. package/dist/user/user.test.d.ts +0 -2
  188. package/dist/user/user.test.d.ts.map +0 -1
  189. package/dist/user/user.test.js +0 -56
  190. package/dist/user/user.test.js.map +0 -1
  191. package/dist/version.d.ts.map +0 -1
  192. package/src/config.ts +0 -42
@@ -1,51 +1,35 @@
1
1
  /**
2
2
  * Passkey authentication effects
3
3
  */
4
- import {
5
- type BadRequest,
6
- type NotSupported
7
- } from '@passlock/shared/dist/error/error.js'
8
- import * as Shared from '@passlock/shared/dist/rpc/social.js'
9
- import { SocialClient } from '@passlock/shared/dist/rpc/social.js'
10
- import type {
11
- Principal
12
- } from '@passlock/shared/dist/schema/principal.js'
4
+ import { type BadRequest, type NotSupported } from '@passlock/shared/dist/error/error.js'
5
+ import * as RPC from '@passlock/shared/dist/rpc/social.js'
6
+ import type { Principal } from '@passlock/shared/dist/schema/principal.js'
13
7
  import { Context, Effect as E, Layer, flow } from 'effect'
8
+ import { SocialClient } from '../rpc/social.js'
14
9
 
15
10
  /* Requests */
16
11
 
17
12
  export type Provider = 'apple' | 'google'
18
13
 
19
- export type RegisterOidcReq = {
20
- provider: Provider
21
- idToken: string
22
- nonce: string
23
- givenName?: string
24
- familyName?: string
25
- }
14
+ export type RegisterOidcReq = RPC.RegisterOidcReq
26
15
 
27
- export type AuthenticateOidcReq = {
28
- provider: Provider
29
- idToken: string
30
- nonce: string
31
- }
16
+ export type AuthenticateOidcReq = RPC.AuthOidcReq
32
17
 
33
18
  /* Errors */
34
19
 
35
- export type RegistrationErrors = NotSupported | BadRequest | Shared.RegisterOidcErrors
20
+ export type RegistrationErrors = NotSupported | BadRequest | RPC.RegisterOidcErrors
36
21
 
37
- export type AuthenticationErrors = NotSupported | BadRequest | Shared.AuthOidcErrors
22
+ export type AuthenticationErrors = NotSupported | BadRequest | RPC.AuthOidcErrors
38
23
 
39
24
  /* Service */
40
25
 
41
- export type SocialService = {
42
- registerOidc: (data: RegisterOidcReq) => E.Effect<Principal, RegistrationErrors>
43
- authenticateOidc: (data: AuthenticateOidcReq) => E.Effect<Principal, AuthenticationErrors>
44
- }
45
-
46
- export const SocialService = Context.GenericTag<SocialService>(
47
- '@services/SocialService',
48
- )
26
+ export class SocialService extends Context.Tag('@services/SocialService')<
27
+ SocialService,
28
+ {
29
+ registerOidc: (req: RegisterOidcReq) => E.Effect<Principal, RegistrationErrors>
30
+ authenticateOidc: (req: AuthenticateOidcReq) => E.Effect<Principal, AuthenticationErrors>
31
+ }
32
+ >() {}
49
33
 
50
34
  /* Effects */
51
35
 
@@ -58,16 +42,8 @@ export const registerOidc = (
58
42
  yield* _(E.logInfo('Registering social account'))
59
43
 
60
44
  const rpcClient = yield* _(SocialClient)
61
-
62
- const rpcRequest = new Shared.RegisterOidcReq({
63
- ...request,
64
- ...(request.givenName ? { givenName: request.givenName } : {}),
65
- ...(request.familyName ? { familyName: request.familyName } : {})
66
- })
67
-
68
- const { principal } = yield* _(
69
- rpcClient.registerOidc(rpcRequest)
70
- )
45
+ const rpcRequest = new RPC.RegisterOidcReq(request)
46
+ const { principal } = yield* _(rpcClient.registerOidc(rpcRequest))
71
47
 
72
48
  return principal
73
49
  })
@@ -80,11 +56,8 @@ export const authenticateOidc = (
80
56
  yield* _(E.logInfo('Authenticating with social account'))
81
57
 
82
58
  const rpcClient = yield* _(SocialClient)
83
- const rpcRequest = new Shared.AuthOidcReq(request)
84
-
85
- const { principal } = yield* _(
86
- rpcClient.authenticateOidc(rpcRequest)
87
- )
59
+ const rpcRequest = new RPC.AuthOidcReq(request)
60
+ const { principal } = yield* _(rpcClient.authenticateOidc(rpcRequest))
88
61
 
89
62
  return principal
90
63
  })
@@ -100,7 +73,7 @@ export const SocialServiceLive = Layer.effect(
100
73
 
101
74
  return SocialService.of({
102
75
  registerOidc: flow(registerOidc, E.provide(context)),
103
- authenticateOidc: flow(authenticateOidc, E.provide(context))
76
+ authenticateOidc: flow(authenticateOidc, E.provide(context)),
104
77
  })
105
78
  }),
106
79
  )
@@ -1,15 +1,14 @@
1
1
  import { Effect as E, Layer, pipe } from 'effect'
2
2
  import { mock } from 'vitest-mock-extended'
3
- import { Storage, StorageServiceLive } from './storage.js'
3
+ import { BrowserStorage, StorageServiceLive } from './storage.js'
4
4
 
5
5
  const storageTest = Layer.effect(
6
- Storage,
6
+ BrowserStorage,
7
7
  E.sync(() => mock<Storage>()),
8
8
  )
9
9
 
10
- export const testLayers = (storage: Layer.Layer<Storage> = storageTest) => {
10
+ export const testLayers = (storage: Layer.Layer<BrowserStorage> = storageTest) => {
11
11
  const storageService = pipe(StorageServiceLive, Layer.provide(storage))
12
-
13
12
  return Layer.merge(storage, storageService)
14
13
  }
15
14
 
@@ -2,7 +2,13 @@ import { Effect as E, Layer, LogLevel, Logger, identity, pipe } from 'effect'
2
2
  import { describe, expect, test } from 'vitest'
3
3
  import { mock } from 'vitest-mock-extended'
4
4
  import { principal, testLayers } from './storage.fixture.js'
5
- import { Storage, StorageService, clearExpiredToken, clearToken, getToken } from './storage.js'
5
+ import {
6
+ BrowserStorage,
7
+ StorageService,
8
+ clearExpiredToken,
9
+ clearToken,
10
+ getToken,
11
+ } from './storage.js'
6
12
 
7
13
  // eslint chokes on expect(storage.setItem) etc
8
14
  /* eslint @typescript-eslint/unbound-method: 0 */
@@ -13,7 +19,7 @@ describe('storeToken should', () => {
13
19
  const service = yield* _(StorageService)
14
20
  yield* _(service.storeToken(principal))
15
21
 
16
- const storage = yield* _(Storage)
22
+ const storage = yield* _(BrowserStorage)
17
23
  expect(storage.setItem).toHaveBeenCalled()
18
24
  })
19
25
 
@@ -30,7 +36,7 @@ describe('storeToken should', () => {
30
36
  const service = yield* _(StorageService)
31
37
  yield* _(service.storeToken(principal))
32
38
 
33
- const storage = yield* _(Storage)
39
+ const storage = yield* _(BrowserStorage)
34
40
  expect(storage.setItem).toHaveBeenCalledWith('passlock:passkey:token', expect.any(String))
35
41
  })
36
42
 
@@ -47,10 +53,13 @@ describe('storeToken should', () => {
47
53
  const service = yield* _(StorageService)
48
54
  yield* _(service.storeToken(principal))
49
55
 
50
- const storage = yield* _(Storage)
51
- const token = principal.token
52
- const expiry = principal.expireAt.getTime()
53
- expect(storage.setItem).toHaveBeenCalledWith('passlock:passkey:token', `${token}:${expiry}`)
56
+ const storage = yield* _(BrowserStorage)
57
+ const token = principal.jti
58
+ const expiry = principal.exp.getTime()
59
+ expect(storage.setItem).toHaveBeenCalledWith(
60
+ 'passlock:passkey:token',
61
+ `${token}:${expiry.toFixed(0)}`,
62
+ )
54
63
  })
55
64
 
56
65
  const effect = pipe(
@@ -68,17 +77,17 @@ describe('getToken should', () => {
68
77
  const service = yield* _(StorageService)
69
78
  yield* _(service.getToken('passkey'))
70
79
 
71
- const storage = yield* _(Storage)
80
+ const storage = yield* _(BrowserStorage)
72
81
  expect(storage.getItem).toHaveBeenCalled()
73
82
  expect(storage.getItem).toHaveBeenCalledWith('passlock:passkey:token')
74
83
  })
75
84
 
76
85
  const storageTest = Layer.effect(
77
- Storage,
86
+ BrowserStorage,
78
87
  E.sync(() => {
79
88
  const mockStorage = mock<Storage>()
80
89
  const expiry = Date.now() + 1000
81
- mockStorage.getItem.mockReturnValue(`token:${expiry}`)
90
+ mockStorage.getItem.mockReturnValue(`token:${expiry.toFixed(0)}`)
82
91
  return mockStorage
83
92
  }),
84
93
  )
@@ -106,11 +115,11 @@ describe('getToken should', () => {
106
115
  )
107
116
 
108
117
  const storageTest = Layer.effect(
109
- Storage,
118
+ BrowserStorage,
110
119
  E.sync(() => {
111
120
  const mockStorage = mock<Storage>()
112
121
  const expiry = Date.now() - 1000
113
- mockStorage.getItem.mockReturnValue(`token:${expiry}`)
122
+ mockStorage.getItem.mockReturnValue(`token:${expiry.toFixed(0)}`)
114
123
  return mockStorage
115
124
  }),
116
125
  )
@@ -127,7 +136,7 @@ describe('getToken should', () => {
127
136
  describe('clearToken should', () => {
128
137
  test('clear the token in local storage', () => {
129
138
  const assertions = E.gen(function* (_) {
130
- const storage = yield* _(Storage)
139
+ const storage = yield* _(BrowserStorage)
131
140
  yield* _(clearToken('passkey'))
132
141
  expect(storage.removeItem).toHaveBeenCalledWith('passlock:passkey:token')
133
142
  })
@@ -144,18 +153,18 @@ describe('clearToken should', () => {
144
153
  describe('clearExpiredToken should', () => {
145
154
  test('clear an expired token from local storage', () => {
146
155
  const assertions = E.gen(function* (_) {
147
- const storage = yield* _(Storage)
156
+ const storage = yield* _(BrowserStorage)
148
157
  yield* _(clearExpiredToken('passkey'))
149
158
  expect(storage.getItem).toHaveBeenCalledWith('passlock:passkey:token')
150
159
  expect(storage.removeItem).toHaveBeenCalledWith('passlock:passkey:token')
151
160
  })
152
161
 
153
162
  const storageTest = Layer.effect(
154
- Storage,
163
+ BrowserStorage,
155
164
  E.sync(() => {
156
165
  const mockStorage = mock<Storage>()
157
166
  const expiry = Date.now() - 1000
158
- mockStorage.getItem.mockReturnValue(`token:${expiry}`)
167
+ mockStorage.getItem.mockReturnValue(`token:${expiry.toFixed(0)}`)
159
168
  return mockStorage
160
169
  }),
161
170
  )
@@ -170,18 +179,18 @@ describe('clearExpiredToken should', () => {
170
179
 
171
180
  test('leave a live token in local storage', () => {
172
181
  const assertions = E.gen(function* (_) {
173
- const storage = yield* _(Storage)
182
+ const storage = yield* _(BrowserStorage)
174
183
  yield* _(clearExpiredToken('passkey'))
175
184
  expect(storage.getItem).toHaveBeenCalledWith('passlock:passkey:token')
176
185
  expect(storage.removeItem).not.toHaveBeenCalled()
177
186
  })
178
187
 
179
188
  const storageTest = Layer.effect(
180
- Storage,
189
+ BrowserStorage,
181
190
  E.sync(() => {
182
191
  const mockStorage = mock<Storage>()
183
192
  const expiry = Date.now() + 1000
184
- mockStorage.getItem.mockReturnValue(`token:${expiry}`)
193
+ mockStorage.getItem.mockReturnValue(`token:${expiry.toFixed(0)}`)
185
194
  return mockStorage
186
195
  }),
187
196
  )
@@ -13,33 +13,31 @@ export type AuthType = 'email' | 'passkey' | 'apple' | 'google'
13
13
  export type StoredToken = {
14
14
  token: string
15
15
  authType: AuthType
16
- expireAt: number
16
+ expiry: number
17
17
  }
18
18
 
19
19
  /* Service */
20
20
 
21
- export type StorageService = {
22
- storeToken: (principal: Principal) => E.Effect<void>
23
- getToken: (authType: AuthType) => E.Effect<StoredToken, NoSuchElementException>
24
- clearToken: (authType: AuthType) => E.Effect<void>
25
- clearExpiredToken: (authType: AuthType) => E.Effect<void>
26
- clearExpiredTokens: E.Effect<void>
27
- }
28
-
29
- /* Utilities */
30
-
31
- export const StorageService = Context.GenericTag<StorageService>('@services/StorageService')
21
+ export class StorageService extends Context.Tag('@services/StorageService')<
22
+ StorageService,
23
+ {
24
+ storeToken: (principal: Principal) => E.Effect<void>
25
+ getToken: (authType: AuthType) => E.Effect<StoredToken, NoSuchElementException>
26
+ clearToken: (authType: AuthType) => E.Effect<void>
27
+ clearExpiredToken: (authType: AuthType) => E.Effect<void>
28
+ clearExpiredTokens: E.Effect<void>
29
+ }
30
+ >() {}
32
31
 
33
- // inject window.localStorage to make testing easier
34
- export const Storage = Context.GenericTag<Storage>('@services/Storage')
32
+ export class BrowserStorage extends Context.Tag('@services/Storage')<BrowserStorage, Storage>() {}
35
33
 
36
34
  export const buildKey = (authType: AuthType) => `passlock:${authType}:token`
37
35
 
38
36
  // principal => token:expireAt
39
37
  export const compressToken = (principal: Principal): string => {
40
- const expireAt = principal.expireAt.getTime()
41
- const token = principal.token
42
- return `${token}:${expireAt}`
38
+ const expireAt = principal.exp.getTime()
39
+ const token = principal.jti
40
+ return `${token}:${expireAt.toFixed(0)}`
43
41
  }
44
42
 
45
43
  // token:expireAt => { authType, token, expireAt }
@@ -48,12 +46,14 @@ export const expandToken =
48
46
  (s: string): O.Option<StoredToken> => {
49
47
  const tokens = s.split(':')
50
48
  if (tokens.length !== 2) return O.none()
49
+
50
+ const [token, expireAtString] = tokens
51
+ if (token === undefined || expireAtString === undefined) return O.none()
51
52
 
52
- const [token, expireAtString] = tokens
53
53
  const parse = O.liftThrowable(Number.parseInt)
54
54
  const expireAt = parse(expireAtString)
55
55
 
56
- return O.map(expireAt, expireAt => ({ authType, token, expireAt }))
56
+ return O.map(expireAt, expiry => ({ authType, token, expiry }))
57
57
  }
58
58
 
59
59
  /* Effects */
@@ -63,13 +63,13 @@ export const expandToken =
63
63
  * @param principal
64
64
  * @returns
65
65
  */
66
- export const storeToken = (principal: Principal): E.Effect<void, never, Storage> => {
66
+ export const storeToken = (principal: Principal): E.Effect<void, never, BrowserStorage> => {
67
67
  return E.gen(function* (_) {
68
- const localStorage = yield* _(Storage)
68
+ const localStorage = yield* _(BrowserStorage)
69
69
 
70
70
  const storeEffect = E.try(() => {
71
71
  const compressed = compressToken(principal)
72
- const key = buildKey(principal.authStatement.authType)
72
+ const key = buildKey(principal.authType)
73
73
  localStorage.setItem(key, compressed)
74
74
  }).pipe(E.orElse(() => E.void)) // We dont care if it fails
75
75
 
@@ -79,20 +79,20 @@ export const storeToken = (principal: Principal): E.Effect<void, never, Storage>
79
79
 
80
80
  /**
81
81
  * Get stored token from local storage
82
- * @param authType
82
+ * @param authenticator
83
83
  * @returns
84
84
  */
85
85
  export const getToken = (
86
- authType: AuthType,
87
- ): E.Effect<StoredToken, NoSuchElementException, Storage> => {
86
+ authenticator: AuthType,
87
+ ): E.Effect<StoredToken, NoSuchElementException, BrowserStorage> => {
88
88
  return E.gen(function* (_) {
89
- const localStorage = yield* _(Storage)
89
+ const localStorage = yield* _(BrowserStorage)
90
90
 
91
91
  const getEffect = pipe(
92
- O.some(buildKey(authType)),
92
+ O.some(buildKey(authenticator)),
93
93
  O.flatMap(key => pipe(localStorage.getItem(key), O.fromNullable)),
94
- O.flatMap(expandToken(authType)),
95
- O.filter(({ expireAt: expireAt }) => expireAt > Date.now()),
94
+ O.flatMap(expandToken(authenticator)),
95
+ O.filter(({ expiry }) => expiry > Date.now()),
96
96
  )
97
97
 
98
98
  return yield* _(getEffect)
@@ -104,9 +104,9 @@ export const getToken = (
104
104
  * @param authType
105
105
  * @returns
106
106
  */
107
- export const clearToken = (authType: AuthType): E.Effect<void, never, Storage> => {
107
+ export const clearToken = (authType: AuthType): E.Effect<void, never, BrowserStorage> => {
108
108
  return E.gen(function* (_) {
109
- const localStorage = yield* _(Storage)
109
+ const localStorage = yield* _(BrowserStorage)
110
110
  localStorage.removeItem(buildKey(authType))
111
111
  })
112
112
  }
@@ -117,15 +117,15 @@ export const clearToken = (authType: AuthType): E.Effect<void, never, Storage> =
117
117
  * @param defer
118
118
  * @returns
119
119
  */
120
- export const clearExpiredToken = (authType: AuthType): E.Effect<void, never, Storage> => {
120
+ export const clearExpiredToken = (authType: AuthType): E.Effect<void, never, BrowserStorage> => {
121
121
  const key = buildKey(authType)
122
122
 
123
123
  const effect = E.gen(function* (_) {
124
- const storage = yield* _(Storage)
124
+ const storage = yield* _(BrowserStorage)
125
125
  const item = yield* _(O.fromNullable(storage.getItem(key)))
126
126
  const token = yield* _(expandToken(authType)(item))
127
127
 
128
- if (token.expireAt < Date.now()) {
128
+ if (token.expiry < Date.now()) {
129
129
  storage.removeItem(key)
130
130
  }
131
131
  })
@@ -140,7 +140,7 @@ export const clearExpiredToken = (authType: AuthType): E.Effect<void, never, Sto
140
140
  )
141
141
  }
142
142
 
143
- export const clearExpiredTokens: E.Effect<void, never, Storage> = E.all([
143
+ export const clearExpiredTokens: E.Effect<void, never, BrowserStorage> = E.all([
144
144
  clearExpiredToken('passkey'),
145
145
  clearExpiredToken('email'),
146
146
  clearExpiredToken('google'),
@@ -153,7 +153,7 @@ export const clearExpiredTokens: E.Effect<void, never, Storage> = E.all([
153
153
  export const StorageServiceLive = Layer.effect(
154
154
  StorageService,
155
155
  E.gen(function* (_) {
156
- const context = yield* _(E.context<Storage>())
156
+ const context = yield* _(E.context<BrowserStorage>())
157
157
 
158
158
  return {
159
159
  storeToken: flow(storeToken, E.provide(context)),
@@ -8,16 +8,31 @@ export const session = 'session'
8
8
  export const token = 'token'
9
9
  export const code = 'code'
10
10
  export const authType = 'passkey'
11
- export const expireAt = Date.now() + 10000
11
+ export const expiry = Date.now() + 10000
12
12
 
13
13
  export const principal: Principal = {
14
+ jti: 'token',
14
15
  token: 'token',
16
+ sub: 'user-1',
17
+ iss: 'idp.passlock.dev',
18
+ aud: 'tenancy_id',
19
+ iat: new Date(),
20
+ nbf: new Date(),
21
+ exp: new Date(Date.now() + 5 * 60 * 1000),
22
+ email: 'john.doe@gmail.com',
23
+ givenName: 'john',
24
+ familyName: 'doe',
25
+ emailVerified: false,
26
+ authType: 'passkey',
27
+ authId: 'auth-1',
28
+ userVerified: true,
29
+ // legacy
15
30
  user: {
16
- id: '1',
17
- email: 'john.doe@gmail.com',
31
+ id: 'user-1',
18
32
  givenName: 'john',
19
33
  familyName: 'doe',
20
- emailVerified: false,
34
+ email: 'john.doe@gmail.com',
35
+ emailVerified: false
21
36
  },
22
37
  authStatement: {
23
38
  authType: 'passkey',
@@ -37,7 +52,7 @@ export const capabilitiesTest = L.succeed(
37
52
  }),
38
53
  )
39
54
 
40
- export const storedToken: StoredToken = { token, authType, expireAt }
55
+ export const storedToken: StoredToken = { token, authType, expiry }
41
56
 
42
57
  export const storageServiceTest = L.succeed(
43
58
  StorageService,
@@ -50,4 +65,4 @@ export const storageServiceTest = L.succeed(
50
65
  }),
51
66
  )
52
67
 
53
- export const notImplemented = new BadRequest({ message: 'Not implemented' })
68
+ export const notImplemented = new BadRequest({ message: 'Not implemented' })
@@ -1,21 +1,31 @@
1
- import { IsExistingUserReq, IsExistingUserRes, ResendEmailReq, ResendEmailRes, UserClient, VerifyEmailRes } from '@passlock/shared/dist/rpc/user.js'
2
- import { Effect as E, Layer as L } from 'effect'
1
+ import {
2
+ IsExistingUserReq,
3
+ IsExistingUserRes,
4
+ ResendEmailReq,
5
+ ResendEmailRes,
6
+ VerifyEmailRes,
7
+ } from '@passlock/shared/dist/rpc/user.js'
8
+ import { Effect as E, Layer as L, Option as O } from 'effect'
9
+ import { UserClient } from '../rpc/user.js'
3
10
  import * as Fixtures from '../test/fixtures.js'
4
11
  import type { ResendEmail } from './user.js'
5
12
 
6
13
  export const email = 'jdoe@gmail.com'
7
14
  export const isRegisteredReq = new IsExistingUserReq({ email })
8
- export const isRegisteredRes = new IsExistingUserRes({ existingUser: false })
15
+ export const isRegisteredRes = new IsExistingUserRes({ existingUser: false, detail: O.none() })
9
16
  export const verifyEmailRes = new VerifyEmailRes({ principal: Fixtures.principal })
10
17
  export const resendEmailReq: ResendEmail = { userId: '123', method: 'code' }
11
- export const rpcResendEmailReq = new ResendEmailReq({ userId: '123', verifyEmail: { method: 'code' }})
12
- export const rpcResendEmailRes = new ResendEmailRes({ })
18
+ export const rpcResendEmailReq = new ResendEmailReq({
19
+ userId: '123',
20
+ verifyEmail: { method: 'code' },
21
+ })
22
+ export const rpcResendEmailRes = new ResendEmailRes({})
13
23
 
14
24
  export const rpcClientTest = L.succeed(
15
25
  UserClient,
16
26
  UserClient.of({
17
- isExistingUser: () => E.succeed({ existingUser: true }),
27
+ isExistingUser: () => E.succeed({ existingUser: true, detail: O.none() }),
18
28
  verifyEmail: () => E.succeed(verifyEmailRes),
19
29
  resendVerificationEmail: () => E.fail(Fixtures.notImplemented),
20
30
  }),
21
- )
31
+ )
@@ -1,7 +1,7 @@
1
- import { UserClient } from '@passlock/shared/dist/rpc/user.js'
2
1
  import { Effect as E, Layer as L, Layer, LogLevel, Logger, pipe } from 'effect'
3
2
  import { describe, expect, test } from 'vitest'
4
3
  import { mock } from 'vitest-mock-extended'
4
+ import { UserClient } from '../rpc/user.js'
5
5
  import * as Fixture from './user.fixture.js'
6
6
  import { UserService, UserServiceLive } from './user.js'
7
7
 
@@ -76,10 +76,7 @@ describe('resendVerificationEmail should', () => {
76
76
 
77
77
  const layers = L.merge(service, rpcClientTest)
78
78
 
79
- const effect = pipe(
80
- E.provide(assertions, layers),
81
- Logger.withMinimumLogLevel(LogLevel.None)
82
- )
79
+ const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
83
80
 
84
81
  return E.runPromise(effect)
85
82
  })
package/src/user/user.ts CHANGED
@@ -2,9 +2,10 @@
2
2
  * Check for an existing user
3
3
  */
4
4
  import type { BadRequest, Disabled, NotFound } from '@passlock/shared/dist/error/error.js'
5
- import { IsExistingUserReq, ResendEmailReq, UserClient } from '@passlock/shared/dist/rpc/user.js'
5
+ import { IsExistingUserReq, ResendEmailReq } from '@passlock/shared/dist/rpc/user.js'
6
6
  import type { VerifyEmail } from '@passlock/shared/dist/schema/email.js'
7
7
  import { Context, Effect as E, Layer, flow } from 'effect'
8
+ import { UserClient } from '../rpc/user.js'
8
9
 
9
10
  /* Requests */
10
11
 
@@ -17,12 +18,13 @@ export type ResendEmailErrors = BadRequest | NotFound | Disabled
17
18
 
18
19
  /* Service */
19
20
 
20
- export type UserService = {
21
- isExistingUser: (request: Email) => E.Effect<boolean, BadRequest>
22
- resendVerificationEmail: (request: ResendEmail) => E.Effect<void, ResendEmailErrors>
23
- }
24
-
25
- export const UserService = Context.GenericTag<UserService>('@services/UserService')
21
+ export class UserService extends Context.Tag('@services/UserService')<
22
+ UserService,
23
+ {
24
+ isExistingUser: (request: Email) => E.Effect<boolean, BadRequest>
25
+ resendVerificationEmail: (request: ResendEmail) => E.Effect<void, ResendEmailErrors>
26
+ }
27
+ >() {}
26
28
 
27
29
  /* Effects */
28
30
 
@@ -40,7 +42,9 @@ export const isExistingUser = (request: Email): E.Effect<boolean, BadRequest, De
40
42
  })
41
43
  }
42
44
 
43
- export const resendVerificationEmail = (request: ResendEmail): E.Effect<void, ResendEmailErrors, Dependencies> => {
45
+ export const resendVerificationEmail = (
46
+ request: ResendEmail,
47
+ ): E.Effect<void, ResendEmailErrors, Dependencies> => {
44
48
  return E.gen(function* (_) {
45
49
  yield* _(E.logInfo('Resending verification email'))
46
50
  const rpcClient = yield* _(UserClient)
@@ -60,7 +64,7 @@ export const UserServiceLive = Layer.effect(
60
64
  const context = yield* _(E.context<UserClient>())
61
65
  return UserService.of({
62
66
  isExistingUser: flow(isExistingUser, E.provide(context)),
63
- resendVerificationEmail: flow(resendVerificationEmail, E.provide(context))
67
+ resendVerificationEmail: flow(resendVerificationEmail, E.provide(context)),
64
68
  })
65
69
  }),
66
70
  )
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const PASSLOCK_CLIENT_VERSION = '#{LATEST}#'
@@ -1 +0,0 @@
1
- {"version":3,"file":"authenticate.d.ts","sourceRoot":"","sources":["../../src/authentication/authenticate.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,oBAAoB,EACpB,KAAK,YAAY,EAClB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAA;AACpG,OAAO,EAAE,oBAAoB,EAA+B,MAAM,6CAA6C,CAAA;AAC/G,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EACjB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAA;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,KAAK,EAAc,MAAM,QAAQ,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAItD,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;CACpC,CAAA;AAID,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,aAAa,GAAG,kBAAkB,CAAA;AAIpF,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,wBAAwB,KAC9B,CAAC,CAAC,MAAM,CAAC,wBAAwB,EAAE,oBAAoB,CAAC,CAAA;AAE7D,eAAO,MAAM,aAAa,2CAAqD,CAAA;AAI/E,MAAM,MAAM,qBAAqB,GAAG;IAClC,mBAAmB,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAA;CACnG,CAAA;AAED,eAAO,MAAM,qBAAqB,2DAEjC,CAAA;AA4CD,KAAK,YAAY,GAAG,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,oBAAoB,CAAA;AAExF,eAAO,MAAM,mBAAmB,YACrB,qBAAqB,KAC7B,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,oBAAoB,EAAE,YAAY,CAgCxD,CAAA;AAKD,eAAO,MAAM,uBAAuB,iHASnC,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"authenticate.fixture.d.ts","sourceRoot":"","sources":["../../src/authentication/authenticate.fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,eAAe,EAChB,MAAM,6CAA6C,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AACrF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAA;AACvF,OAAO,EAAe,KAAK,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,EAAE,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAE7E,eAAO,MAAM,OAAO,YAAY,CAAA;AAChC,eAAO,MAAM,KAAK,UAAU,CAAA;AAC5B,eAAO,MAAM,IAAI,SAAS,CAAA;AAC1B,eAAO,MAAM,QAAQ,YAAY,CAAA;AACjC,eAAO,MAAM,QAAQ,QAAqB,CAAA;AAE1C,eAAO,MAAM,OAAO,EAAE,qBAErB,CAAA;AAED,eAAO,MAAM,aAAa,YAQxB,CAAA;AAEF,eAAO,MAAM,UAAU,EAAE,wBAYxB,CAAA;AAED,eAAO,MAAM,kBAAkB,iBAA+C,CAAA;AAE9E,eAAO,MAAM,kBAAkB,iBAAyD,CAAA;AAExF,eAAO,MAAM,oBAAoB,mBAAgD,CAAA;AAEjF,eAAO,MAAM,iBAAiB,gBAAwD,CAAA;AAEtF,eAAO,MAAM,iBAAiB,sCAG7B,CAAA;AAED,eAAO,MAAM,aAAa,6CAMzB,CAAA;AAED,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;CAAqB,CAAA;AAC3C,eAAO,MAAM,gBAAgB,+EAA4B,CAAA;AACzD,eAAO,MAAM,kBAAkB,uEAA8B,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=authenticate.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"authenticate.test.d.ts","sourceRoot":"","sources":["../../src/authentication/authenticate.test.ts"],"names":[],"mappings":""}