@passlock/client 0.9.32 → 2.0.0-beta.2

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 (180) hide show
  1. package/README.md +14 -86
  2. package/README.template.md +16 -88
  3. package/dist/index.d.ts +4 -206
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +2 -158
  6. package/dist/index.js.map +1 -1
  7. package/dist/logger/index.d.ts +24 -0
  8. package/dist/logger/index.d.ts.map +1 -0
  9. package/dist/logger/index.js +47 -0
  10. package/dist/logger/index.js.map +1 -0
  11. package/dist/network.d.ts +39 -0
  12. package/dist/network.d.ts.map +1 -0
  13. package/dist/network.js +83 -0
  14. package/dist/network.js.map +1 -0
  15. package/dist/passkey/authentication/index.d.ts +21 -0
  16. package/dist/passkey/authentication/index.d.ts.map +1 -0
  17. package/dist/passkey/authentication/index.js +22 -0
  18. package/dist/passkey/authentication/index.js.map +1 -0
  19. package/dist/passkey/authentication/micro.d.ts +71 -0
  20. package/dist/passkey/authentication/micro.d.ts.map +1 -0
  21. package/dist/passkey/authentication/micro.js +107 -0
  22. package/dist/passkey/authentication/micro.js.map +1 -0
  23. package/dist/passkey/index.d.ts +7 -0
  24. package/dist/passkey/index.d.ts.map +1 -0
  25. package/dist/passkey/index.js +5 -0
  26. package/dist/passkey/index.js.map +1 -0
  27. package/dist/passkey/registration/index.d.ts +19 -0
  28. package/dist/passkey/registration/index.d.ts.map +1 -0
  29. package/dist/passkey/registration/index.js +20 -0
  30. package/dist/passkey/registration/index.js.map +1 -0
  31. package/dist/passkey/registration/micro.d.ts +101 -0
  32. package/dist/passkey/registration/micro.d.ts.map +1 -0
  33. package/dist/passkey/registration/micro.js +126 -0
  34. package/dist/passkey/registration/micro.js.map +1 -0
  35. package/dist/passkey/shared.d.ts +24 -0
  36. package/dist/passkey/shared.d.ts.map +1 -0
  37. package/dist/passkey/shared.js +10 -0
  38. package/dist/passkey/shared.js.map +1 -0
  39. package/dist/passkey/support.d.ts +3 -0
  40. package/dist/passkey/support.d.ts.map +1 -0
  41. package/dist/passkey/support.js +4 -0
  42. package/dist/passkey/support.js.map +1 -0
  43. package/dist/passkey/types.d.ts +26 -0
  44. package/dist/passkey/types.d.ts.map +1 -0
  45. package/dist/passkey/types.js +2 -0
  46. package/dist/passkey/types.js.map +1 -0
  47. package/dist/promise.d.ts +15 -0
  48. package/dist/promise.d.ts.map +1 -0
  49. package/dist/promise.js +46 -0
  50. package/dist/promise.js.map +1 -0
  51. package/dist/shared.d.ts +15 -0
  52. package/dist/shared.d.ts.map +1 -0
  53. package/dist/shared.js +2 -0
  54. package/dist/shared.js.map +1 -0
  55. package/dist/tenancy.d.ts +8 -0
  56. package/dist/tenancy.d.ts.map +1 -0
  57. package/dist/tenancy.js +4 -0
  58. package/dist/tenancy.js.map +1 -0
  59. package/package.json +52 -58
  60. package/LICENSE +0 -21
  61. package/dist/authentication/authenticate.d.ts +0 -23
  62. package/dist/authentication/authenticate.fixture.d.ts +0 -52
  63. package/dist/authentication/authenticate.fixture.js +0 -50
  64. package/dist/authentication/authenticate.fixture.js.map +0 -1
  65. package/dist/authentication/authenticate.js +0 -72
  66. package/dist/authentication/authenticate.js.map +0 -1
  67. package/dist/capabilities/capabilities.d.ts +0 -19
  68. package/dist/capabilities/capabilities.js +0 -37
  69. package/dist/capabilities/capabilities.js.map +0 -1
  70. package/dist/connection/connection.d.ts +0 -15
  71. package/dist/connection/connection.fixture.d.ts +0 -10
  72. package/dist/connection/connection.fixture.js +0 -13
  73. package/dist/connection/connection.fixture.js.map +0 -1
  74. package/dist/connection/connection.js +0 -23
  75. package/dist/connection/connection.js.map +0 -1
  76. package/dist/effect.d.ts +0 -26
  77. package/dist/effect.js +0 -78
  78. package/dist/effect.js.map +0 -1
  79. package/dist/email/email.d.ts +0 -70
  80. package/dist/email/email.fixture.d.ts +0 -46
  81. package/dist/email/email.fixture.js +0 -25
  82. package/dist/email/email.fixture.js.map +0 -1
  83. package/dist/email/email.js +0 -83
  84. package/dist/email/email.js.map +0 -1
  85. package/dist/event/event.d.ts +0 -8
  86. package/dist/event/event.js +0 -23
  87. package/dist/event/event.js.map +0 -1
  88. package/dist/logging/eventLogger.d.ts +0 -17
  89. package/dist/logging/eventLogger.js +0 -38
  90. package/dist/logging/eventLogger.js.map +0 -1
  91. package/dist/registration/register.d.ts +0 -25
  92. package/dist/registration/register.fixture.d.ts +0 -53
  93. package/dist/registration/register.fixture.js +0 -66
  94. package/dist/registration/register.fixture.js.map +0 -1
  95. package/dist/registration/register.js +0 -77
  96. package/dist/registration/register.js.map +0 -1
  97. package/dist/rpc/client.d.ts +0 -30
  98. package/dist/rpc/client.js +0 -101
  99. package/dist/rpc/client.js.map +0 -1
  100. package/dist/rpc/config.d.ts +0 -15
  101. package/dist/rpc/config.js +0 -6
  102. package/dist/rpc/config.js.map +0 -1
  103. package/dist/rpc/connection.d.ts +0 -8
  104. package/dist/rpc/connection.js +0 -16
  105. package/dist/rpc/connection.js.map +0 -1
  106. package/dist/rpc/passkey/authentication.d.ts +0 -8
  107. package/dist/rpc/passkey/authentication.js +0 -17
  108. package/dist/rpc/passkey/authentication.js.map +0 -1
  109. package/dist/rpc/passkey/registration.d.ts +0 -8
  110. package/dist/rpc/passkey/registration.js +0 -17
  111. package/dist/rpc/passkey/registration.js.map +0 -1
  112. package/dist/rpc/social.d.ts +0 -10
  113. package/dist/rpc/social.js +0 -19
  114. package/dist/rpc/social.js.map +0 -1
  115. package/dist/rpc/user.d.ts +0 -8
  116. package/dist/rpc/user.js +0 -20
  117. package/dist/rpc/user.js.map +0 -1
  118. package/dist/social/social.d.ts +0 -23
  119. package/dist/social/social.fixture.d.ts +0 -46
  120. package/dist/social/social.fixture.js +0 -31
  121. package/dist/social/social.fixture.js.map +0 -1
  122. package/dist/social/social.js +0 -38
  123. package/dist/social/social.js.map +0 -1
  124. package/dist/storage/storage.d.ts +0 -56
  125. package/dist/storage/storage.fixture.d.ts +0 -4
  126. package/dist/storage/storage.fixture.js +0 -10
  127. package/dist/storage/storage.fixture.js.map +0 -1
  128. package/dist/storage/storage.js +0 -111
  129. package/dist/storage/storage.js.map +0 -1
  130. package/dist/test/fixtures.d.ts +0 -15
  131. package/dist/test/fixtures.js +0 -56
  132. package/dist/test/fixtures.js.map +0 -1
  133. package/dist/tsconfig.tsbuildinfo +0 -1
  134. package/dist/user/user.d.ts +0 -25
  135. package/dist/user/user.fixture.d.ts +0 -12
  136. package/dist/user/user.fixture.js +0 -20
  137. package/dist/user/user.fixture.js.map +0 -1
  138. package/dist/user/user.js +0 -37
  139. package/dist/user/user.js.map +0 -1
  140. package/dist/version.d.ts +0 -1
  141. package/dist/version.js +0 -2
  142. package/dist/version.js.map +0 -1
  143. package/src/authentication/authenticate.fixture.ts +0 -73
  144. package/src/authentication/authenticate.test.ts +0 -249
  145. package/src/authentication/authenticate.ts +0 -143
  146. package/src/capabilities/capabilities.ts +0 -83
  147. package/src/connection/connection.fixture.ts +0 -20
  148. package/src/connection/connection.test.ts +0 -60
  149. package/src/connection/connection.ts +0 -51
  150. package/src/effect.ts +0 -280
  151. package/src/email/email.fixture.ts +0 -44
  152. package/src/email/email.test.ts +0 -186
  153. package/src/email/email.ts +0 -148
  154. package/src/event/event.node.test.ts +0 -21
  155. package/src/event/event.test.ts +0 -37
  156. package/src/event/event.ts +0 -25
  157. package/src/index.ts +0 -407
  158. package/src/logging/eventLogger.test.ts +0 -104
  159. package/src/logging/eventLogger.ts +0 -41
  160. package/src/registration/register.fixture.ts +0 -96
  161. package/src/registration/register.test.ts +0 -216
  162. package/src/registration/register.ts +0 -156
  163. package/src/rpc/client.ts +0 -174
  164. package/src/rpc/config.ts +0 -18
  165. package/src/rpc/connection.ts +0 -32
  166. package/src/rpc/passkey/authentication.ts +0 -52
  167. package/src/rpc/passkey/registration.ts +0 -52
  168. package/src/rpc/social.ts +0 -55
  169. package/src/rpc/user.ts +0 -68
  170. package/src/social/social.fixture.ts +0 -44
  171. package/src/social/social.test.ts +0 -179
  172. package/src/social/social.ts +0 -79
  173. package/src/storage/storage.fixture.ts +0 -16
  174. package/src/storage/storage.test.ts +0 -206
  175. package/src/storage/storage.ts +0 -168
  176. package/src/test/fixtures.ts +0 -70
  177. package/src/user/user.fixture.ts +0 -33
  178. package/src/user/user.test.ts +0 -84
  179. package/src/user/user.ts +0 -71
  180. package/src/version.ts +0 -1
@@ -1,104 +0,0 @@
1
- import { Effect as E, LogLevel, Logger } from 'effect'
2
- import { describe, expect, test, vi } from 'vitest'
3
-
4
- import { eventLoggerLive, logRaw } from './eventLogger.js'
5
-
6
- /**
7
- * Although the core log functionality is tested alongside the logger in the @passlock/shared
8
- * package, those tests deliberately exclude the event dispatch elements as the package
9
- * is intended to be agnostic to the runtime environment. This client package however is
10
- * intented to be run in the browser, so we can plugin a real event dispatcher and ensure
11
- * it's working as expected.
12
- */
13
-
14
- describe('log', () => {
15
- test('log DEBUG to the console', () => {
16
- const logStatement = E.logDebug('hello world')
17
-
18
- const logSpy = vi.spyOn(globalThis.console, 'log').mockImplementation(() => undefined)
19
- const withLogLevel = logStatement.pipe(Logger.withMinimumLogLevel(LogLevel.Debug))
20
-
21
- const effect = E.provide(withLogLevel, eventLoggerLive)
22
- E.runSync(effect)
23
-
24
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('hello world'))
25
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('DEBUG'))
26
- })
27
-
28
- test('log INFO to the console', () => {
29
- const logStatement = E.logInfo('hello world')
30
-
31
- const logSpy = vi.spyOn(globalThis.console, 'log').mockImplementation(() => undefined)
32
- const withLogLevel = logStatement.pipe(Logger.withMinimumLogLevel(LogLevel.Info))
33
-
34
- const effect = E.provide(withLogLevel, eventLoggerLive)
35
- E.runSync(effect)
36
-
37
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('hello world'))
38
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('INFO'))
39
- })
40
-
41
- test('log WARN to the console', () => {
42
- const logStatement = E.logWarning('hello world')
43
-
44
- const logSpy = vi.spyOn(globalThis.console, 'log').mockImplementation(() => undefined)
45
- const withLogLevel = logStatement.pipe(Logger.withMinimumLogLevel(LogLevel.Warning))
46
-
47
- const effect = E.provide(withLogLevel, eventLoggerLive)
48
- E.runSync(effect)
49
-
50
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('hello world'))
51
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('WARN'))
52
- })
53
-
54
- test('log ERROR to the console', () => {
55
- const logStatement = E.logError('hello world')
56
-
57
- const logSpy = vi.spyOn(globalThis.console, 'log').mockImplementation(() => undefined)
58
- const withLogLevel = logStatement.pipe(Logger.withMinimumLogLevel(LogLevel.Error))
59
-
60
- const effect = E.provide(withLogLevel, eventLoggerLive)
61
- E.runSync(effect)
62
-
63
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('hello world'))
64
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('ERROR'))
65
- })
66
-
67
- test('log raw data to the console', () => {
68
- const logStatement = logRaw('hello world')
69
- const logSpy = vi.spyOn(globalThis.console, 'log').mockImplementation(() => undefined)
70
-
71
- E.runSync(logStatement)
72
-
73
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('hello world'))
74
- })
75
-
76
- test('fire a custom log event', () => {
77
- const logStatement = E.logWarning('hello world')
78
-
79
- const eventSpy = vi.spyOn(globalThis, 'dispatchEvent').mockImplementation(() => false)
80
- const withLogLevel = logStatement.pipe(Logger.withMinimumLogLevel(LogLevel.Warning))
81
-
82
- const effect = E.provide(withLogLevel, eventLoggerLive)
83
-
84
- E.runSync(effect)
85
-
86
- const expectedEvent = new CustomEvent('PasslogDebugMessage', {
87
- detail: 'hello world',
88
- })
89
-
90
- expect(eventSpy).toHaveBeenCalledWith(expectedEvent)
91
- })
92
-
93
- test('not fire a log event for a debug message', () => {
94
- const logStatement = E.logDebug('hello world')
95
-
96
- const eventSpy = vi.spyOn(globalThis, 'dispatchEvent').mockImplementation(() => false)
97
- const withDebugLevel = logStatement.pipe(Logger.withMinimumLogLevel(LogLevel.Debug))
98
-
99
- const effect = E.provide(withDebugLevel, eventLoggerLive)
100
- E.runSync(effect)
101
-
102
- expect(eventSpy).not.toHaveBeenCalled()
103
- })
104
- })
@@ -1,41 +0,0 @@
1
- /**
2
- * Logger implementation that also fires DOM events.
3
- * This is useful to allow external code to plug into the logging
4
- * mechanism. E.g. the Passlock demo subscribes to events to generate
5
- * a typewriter style effect
6
- */
7
- import { Effect as E, LogLevel, Logger } from 'effect'
8
-
9
- /**
10
- * Some log messages span multiple lines/include json etc which is
11
- * better output without being formatted by Effect's logging framework
12
- *
13
- * @param message
14
- * @returns
15
- */
16
- export const logRaw = <T>(message: T) => {
17
- return E.sync(() => {
18
- console.log(message)
19
- })
20
- }
21
-
22
- export const DebugMessage = 'PasslogDebugMessage'
23
-
24
- const dispatch = (message: string) => {
25
- try {
26
- const evt = new CustomEvent(DebugMessage, { detail: message })
27
- globalThis.dispatchEvent(evt)
28
- } catch {
29
- globalThis.console.log('Unable to fire custom event')
30
- }
31
- }
32
-
33
- export const eventLoggerLive = Logger.add(
34
- Logger.make(({ logLevel, message }) => {
35
- if (typeof message === 'string' && logLevel !== LogLevel.Debug) {
36
- dispatch(message)
37
- } else if (Array.isArray(message) && logLevel !== LogLevel.Debug) {
38
- message.forEach(dispatch)
39
- }
40
- }),
41
- )
@@ -1,96 +0,0 @@
1
- import { Effect as E, Layer as L, Option as O } from 'effect'
2
-
3
- import {
4
- OptionsRequest,
5
- OptionsResponse,
6
- VerificationRequest,
7
- VerificationResponse,
8
- } from '@passlock/shared/dist/rpc/passkey/registration.js'
9
- import type { RegistrationCredential } from '@passlock/shared/dist/schema/passkey.js'
10
-
11
- import * as Fixtures from '../test/fixtures.js'
12
- import { RegistrationClient } from '../rpc/passkey/registration.js'
13
- import { UserService } from '../user/user.js'
14
- import { CreateCredential, type RegistrationRequest } from './register.js'
15
-
16
- export const session = 'session'
17
- export const token = 'token'
18
- export const code = 'code'
19
- export const authType = 'passkey'
20
- export const expireAt = Date.now() + 10000
21
-
22
- export const registrationRequest: RegistrationRequest = {
23
- email: 'jdoe@gmail.com',
24
- givenName: O.some('john'),
25
- familyName: O.some('doe'),
26
- userVerification: O.none(),
27
- verifyEmail: O.none(),
28
- }
29
-
30
- export const rpcOptionsReq = new OptionsRequest(registrationRequest)
31
-
32
- export const registrationOptions: OptionsResponse = {
33
- session,
34
- publicKey: {
35
- rp: {
36
- name: 'passlock',
37
- id: 'passlock.dev',
38
- },
39
- user: {
40
- name: 'john doe',
41
- id: 'jdoe',
42
- displayName: 'john doe',
43
- },
44
- challenge: 'FKZSl_saKu5OXjLLwoq8eK3wlD8XgpGiS10SszW5RiE',
45
- pubKeyCredParams: [],
46
- },
47
- }
48
-
49
- export const rpcOptionsRes = new OptionsResponse(registrationOptions)
50
-
51
- export const credential: RegistrationCredential = {
52
- type: 'public-key',
53
- id: '1',
54
- rawId: '1',
55
- response: {
56
- transports: [],
57
- clientDataJSON: '',
58
- attestationObject: '',
59
- },
60
- clientExtensionResults: {},
61
- }
62
-
63
- export const rpcVerificationReq = new VerificationRequest({
64
- session,
65
- credential,
66
- verifyEmail: O.none(),
67
- })
68
-
69
- export const rpcVerificationRes = new VerificationResponse({ principal: Fixtures.principal })
70
-
71
- export const createCredentialTest = L.succeed(
72
- CreateCredential,
73
- CreateCredential.of({ createCredential: () => E.succeed(credential) }),
74
- )
75
-
76
- export const userServiceTest = L.succeed(
77
- UserService,
78
- UserService.of({
79
- isExistingUser: () => E.succeed(false),
80
- resendVerificationEmail: () => E.succeed(true),
81
- }),
82
- )
83
-
84
- export const rpcClientTest = L.succeed(
85
- RegistrationClient,
86
- RegistrationClient.of({
87
- getRegistrationOptions: () => E.succeed(rpcOptionsRes),
88
- verifyRegistrationCredential: () => E.succeed(rpcVerificationRes),
89
- }),
90
- )
91
-
92
- export const principal = Fixtures.principal
93
-
94
- export const capabilitiesTest = Fixtures.capabilitiesTest
95
-
96
- export const storageServiceTest = Fixtures.storageServiceTest
@@ -1,216 +0,0 @@
1
- import { Effect as E, Layer as L, Layer, LogLevel, Logger, pipe } from 'effect'
2
- import { describe, expect, test, vi } from 'vitest'
3
- import { mock } from 'vitest-mock-extended'
4
-
5
- import { Duplicate, InternalBrowserError } from '@passlock/shared/dist/error/error.js'
6
-
7
- import * as Fixture from './register.fixture.js'
8
- import { RegistrationClient } from '../rpc/passkey/registration.js'
9
- import { CreateCredential, RegistrationService, RegistrationServiceLive } from './register.js'
10
-
11
- describe('register should', () => {
12
- test('return a valid credential', async () => {
13
- const assertions = E.gen(function* (_) {
14
- const service = yield* _(RegistrationService)
15
- const result = yield* _(service.registerPasskey(Fixture.registrationRequest))
16
- expect(result).toEqual(Fixture.principal)
17
- })
18
-
19
- const service = pipe(
20
- RegistrationServiceLive,
21
- L.provide(Fixture.createCredentialTest),
22
- L.provide(Fixture.userServiceTest),
23
- L.provide(Fixture.capabilitiesTest),
24
- L.provide(Fixture.storageServiceTest),
25
- L.provide(Fixture.rpcClientTest),
26
- )
27
-
28
- const effect = pipe(E.provide(assertions, service), Logger.withMinimumLogLevel(LogLevel.None))
29
-
30
- return E.runPromise(effect)
31
- })
32
-
33
- test('pass the registration data to the backend', async () => {
34
- const assertions = E.gen(function* (_) {
35
- const service = yield* _(RegistrationService)
36
- yield* _(service.registerPasskey(Fixture.registrationRequest))
37
-
38
- const rpcClient = yield* _(RegistrationClient)
39
- expect(rpcClient.getRegistrationOptions).toHaveBeenCalledWith(Fixture.rpcOptionsReq)
40
- })
41
-
42
- const rpcClientTest = L.effect(
43
- RegistrationClient,
44
- E.sync(() => {
45
- const rpcMock = mock<RegistrationClient['Type']>()
46
-
47
- rpcMock.getRegistrationOptions.mockReturnValue(E.succeed(Fixture.rpcOptionsRes))
48
- rpcMock.verifyRegistrationCredential.mockReturnValue(E.succeed(Fixture.rpcVerificationRes))
49
-
50
- return rpcMock
51
- }),
52
- )
53
-
54
- const service = pipe(
55
- RegistrationServiceLive,
56
- L.provide(Fixture.createCredentialTest),
57
- L.provide(Fixture.capabilitiesTest),
58
- L.provide(Fixture.storageServiceTest),
59
- L.provide(Fixture.userServiceTest),
60
- L.provide(rpcClientTest),
61
- )
62
-
63
- const layers = Layer.merge(service, rpcClientTest)
64
- const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
65
-
66
- return E.runPromise(effect)
67
- })
68
-
69
- test('send the new credential to the backend', async () => {
70
- const assertions = E.gen(function* (_) {
71
- const service = yield* _(RegistrationService)
72
- yield* _(service.registerPasskey(Fixture.registrationRequest))
73
-
74
- const rpcClient = yield* _(RegistrationClient)
75
- expect(rpcClient.verifyRegistrationCredential).toHaveBeenCalledWith(
76
- Fixture.rpcVerificationReq,
77
- )
78
- })
79
-
80
- const rpcClientTest = L.effect(
81
- RegistrationClient,
82
- E.sync(() => {
83
- const rpcMock = mock<RegistrationClient['Type']>()
84
-
85
- rpcMock.getRegistrationOptions.mockReturnValue(E.succeed(Fixture.rpcOptionsRes))
86
- rpcMock.verifyRegistrationCredential.mockReturnValue(E.succeed(Fixture.rpcVerificationRes))
87
-
88
- return rpcMock
89
- }),
90
- )
91
-
92
- const service = pipe(
93
- RegistrationServiceLive,
94
- L.provide(Fixture.createCredentialTest),
95
- L.provide(Fixture.capabilitiesTest),
96
- L.provide(Fixture.storageServiceTest),
97
- L.provide(Fixture.userServiceTest),
98
- L.provide(rpcClientTest),
99
- )
100
-
101
- const layers = Layer.merge(service, rpcClientTest)
102
- const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
103
-
104
- return E.runPromise(effect)
105
- })
106
-
107
- test('short-circuit if the user is already registered', async () => {
108
- const assertions = E.gen(function* (_) {
109
- const service = yield* _(RegistrationService)
110
-
111
- const error = yield* _(service.registerPasskey(Fixture.registrationRequest), E.flip)
112
-
113
- expect(error).toBeInstanceOf(Duplicate)
114
- })
115
-
116
- const rpcClientTest = L.effect(
117
- RegistrationClient,
118
- E.sync(() => {
119
- const rpcMock = mock<RegistrationClient['Type']>()
120
-
121
- rpcMock.getRegistrationOptions.mockReturnValue(
122
- E.fail(new Duplicate({ message: 'User already exists' })),
123
- )
124
-
125
- return rpcMock
126
- }),
127
- )
128
-
129
- const service = pipe(
130
- RegistrationServiceLive,
131
- L.provide(Fixture.createCredentialTest),
132
- L.provide(Fixture.capabilitiesTest),
133
- L.provide(Fixture.storageServiceTest),
134
- L.provide(Fixture.userServiceTest),
135
- L.provide(rpcClientTest),
136
- )
137
-
138
- const layers = Layer.merge(service, rpcClientTest)
139
- const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
140
-
141
- return E.runPromise(effect)
142
- })
143
-
144
- test('return an error if we try to re-register a credential', async () => {
145
- const assertions = E.gen(function* (_) {
146
- const service = yield* _(RegistrationService)
147
-
148
- const defect = yield* _(service.registerPasskey(Fixture.registrationRequest), E.flip)
149
-
150
- expect(defect).toBeInstanceOf(Duplicate)
151
- })
152
-
153
- const createTest = L.effect(
154
- CreateCredential,
155
- E.sync(() => {
156
- const createCredential = vi.fn()
157
-
158
- createCredential.mockReturnValue(E.fail(new Duplicate({ message: 'boom!' })))
159
-
160
- return { createCredential }
161
- }),
162
- )
163
-
164
- const service = pipe(
165
- RegistrationServiceLive,
166
- L.provide(Fixture.userServiceTest),
167
- L.provide(Fixture.capabilitiesTest),
168
- L.provide(Fixture.storageServiceTest),
169
- L.provide(Fixture.rpcClientTest),
170
- L.provide(createTest),
171
- )
172
-
173
- const layers = Layer.merge(service, createTest)
174
- const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
175
-
176
- return E.runPromise(effect)
177
- })
178
-
179
- test("throw an error if the browser can't create a credential", async () => {
180
- const assertions = E.gen(function* (_) {
181
- const service = yield* _(RegistrationService)
182
-
183
- const defect = yield* _(
184
- service.registerPasskey(Fixture.registrationRequest),
185
- E.catchAllDefect(defect => E.succeed(defect)),
186
- )
187
-
188
- expect(defect).toBeInstanceOf(InternalBrowserError)
189
- })
190
-
191
- const createTest = L.effect(
192
- CreateCredential,
193
- E.sync(() => {
194
- const createCredential = vi.fn()
195
-
196
- createCredential.mockReturnValue(E.fail(new InternalBrowserError({ message: 'boom!' })))
197
-
198
- return { createCredential }
199
- }),
200
- )
201
-
202
- const service = pipe(
203
- RegistrationServiceLive,
204
- L.provide(Fixture.userServiceTest),
205
- L.provide(Fixture.capabilitiesTest),
206
- L.provide(Fixture.storageServiceTest),
207
- L.provide(Fixture.rpcClientTest),
208
- L.provide(createTest),
209
- )
210
-
211
- const layers = Layer.merge(service, createTest)
212
- const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
213
-
214
- return E.runPromise(effect)
215
- })
216
- })
@@ -1,156 +0,0 @@
1
- /**
2
- * User & passkey registration effects
3
- */
4
- import {
5
- type CredentialCreationOptionsJSON,
6
- parseCreationOptionsFromJSON,
7
- } from '@github/webauthn-json/browser-ponyfill'
8
- import { Context, Effect as E, Layer, flow, pipe } from 'effect'
9
-
10
- import type { Duplicate, NotSupported } from '@passlock/shared/dist/error/error.js'
11
- import { InternalBrowserError } from '@passlock/shared/dist/error/error.js'
12
- import * as RPC from '../rpc/passkey/registration.js'
13
- import type { RegistrationCredential } from '@passlock/shared/dist/schema/passkey.js'
14
- import type { Principal } from '@passlock/shared/dist/schema/principal.js'
15
-
16
- import { Capabilities } from '../capabilities/capabilities.js'
17
- import { StorageService } from '../storage/storage.js'
18
- import type { UserService } from '../user/user.js'
19
-
20
- /* Requests */
21
-
22
- export type RegistrationRequest = RPC.OptionsRequest
23
-
24
- /* Dependencies */
25
-
26
- export class CreateCredential extends Context.Tag('@services/CreateCredential')<
27
- CreateCredential,
28
- {
29
- createCredential: (
30
- request: CredentialCreationOptions,
31
- ) => E.Effect<RegistrationCredential, InternalBrowserError | Duplicate>
32
- }
33
- >() {}
34
-
35
- /* Errors */
36
-
37
- export type RegistrationErrors = NotSupported | RPC.OptionsErrors | RPC.VerificationErrors
38
-
39
- /* Service */
40
-
41
- export class RegistrationService extends Context.Tag('@services/RegistrationService')<
42
- RegistrationService,
43
- {
44
- registerPasskey: (request: RegistrationRequest) => E.Effect<Principal, RegistrationErrors>
45
- }
46
- >() {}
47
-
48
- /* Utilities */
49
-
50
- const fetchOptions = (request: RPC.OptionsRequest) => {
51
- return E.gen(function* (_) {
52
- yield* _(E.logDebug('Making request'))
53
-
54
- const rpcClient = yield* _(RPC.RegistrationClient)
55
- const { publicKey, session } = yield* _(rpcClient.getRegistrationOptions(request))
56
-
57
- yield* _(E.logDebug('Converting Passlock options to CredentialCreationOptions'))
58
- const options = yield* _(toCreationOptions({ publicKey }))
59
-
60
- return { options, session }
61
- })
62
- }
63
-
64
- const toCreationOptions = (jsonOptions: CredentialCreationOptionsJSON) => {
65
- return pipe(
66
- E.try(() => parseCreationOptionsFromJSON(jsonOptions)),
67
- E.mapError(
68
- error =>
69
- new InternalBrowserError({
70
- message: 'Browser was unable to create credential creation options',
71
- detail: String(error.error),
72
- }),
73
- ),
74
- )
75
- }
76
-
77
- const verifyCredential = (request: RPC.VerificationRequest) => {
78
- return E.gen(function* (_) {
79
- yield* _(E.logDebug('Making request'))
80
-
81
- const rpcClient = yield* _(RPC.RegistrationClient)
82
- const { principal } = yield* _(rpcClient.verifyRegistrationCredential(request))
83
-
84
- return principal
85
- })
86
- }
87
-
88
- /* Effects */
89
-
90
- type Dependencies =
91
- | Capabilities
92
- | CreateCredential
93
- | StorageService
94
- | UserService
95
- | RPC.RegistrationClient
96
-
97
- export const registerPasskey = (
98
- request: RegistrationRequest,
99
- ): E.Effect<Principal, RegistrationErrors, Dependencies> => {
100
- const effect = E.gen(function* (_) {
101
- yield* _(E.logInfo('Checking if browser supports Passkeys'))
102
- const capabilities = yield* _(Capabilities)
103
- yield* _(capabilities.passkeySupport)
104
-
105
- yield* _(E.logInfo('Fetching registration options from Passlock'))
106
- const { options, session } = yield* fetchOptions(new RPC.OptionsRequest(request))
107
-
108
- yield* _(E.logInfo('Building new credential'))
109
- const { createCredential } = yield* _(CreateCredential)
110
- const credential = yield* _(createCredential(options))
111
-
112
- yield* _(E.logInfo('Storing credential public key in Passlock'))
113
- const verificationRequest = new RPC.VerificationRequest({
114
- ...request,
115
- credential,
116
- session,
117
- })
118
-
119
- const principal = yield* _(verifyCredential(verificationRequest))
120
-
121
- const storageService = yield* _(StorageService)
122
- yield* _(storageService.storeToken(principal))
123
- yield* _(E.logDebug('Storing token in local storage'))
124
-
125
- yield* _(E.logDebug('Defering local token deletion'))
126
- const delayedClearTokenE = pipe(
127
- storageService.clearExpiredToken('passkey'),
128
- E.delay('6 minutes'),
129
- E.fork,
130
- )
131
- yield* _(delayedClearTokenE)
132
-
133
- return principal
134
- })
135
-
136
- return E.catchTag(effect, 'InternalBrowserError', e => E.die(e))
137
- }
138
-
139
- /* Live */
140
-
141
- /* v8 ignore start */
142
- export const RegistrationServiceLive = Layer.effect(
143
- RegistrationService,
144
- E.gen(function* (_) {
145
- const context = yield* _(
146
- E.context<
147
- CreateCredential | RPC.RegistrationClient | Capabilities | StorageService | UserService
148
- >(),
149
- )
150
-
151
- return RegistrationService.of({
152
- registerPasskey: flow(registerPasskey, E.provide(context)),
153
- })
154
- }),
155
- )
156
- /* v8 ignore stop */