@passlock/client 0.9.30 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -86
- package/README.template.md +16 -88
- package/dist/index.d.ts +4 -206
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -158
- package/dist/index.js.map +1 -1
- package/dist/logger/index.d.ts +24 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +47 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/network.d.ts +39 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +83 -0
- package/dist/network.js.map +1 -0
- package/dist/passkey/authentication/index.d.ts +21 -0
- package/dist/passkey/authentication/index.d.ts.map +1 -0
- package/dist/passkey/authentication/index.js +22 -0
- package/dist/passkey/authentication/index.js.map +1 -0
- package/dist/passkey/authentication/micro.d.ts +71 -0
- package/dist/passkey/authentication/micro.d.ts.map +1 -0
- package/dist/passkey/authentication/micro.js +107 -0
- package/dist/passkey/authentication/micro.js.map +1 -0
- package/dist/passkey/index.d.ts +7 -0
- package/dist/passkey/index.d.ts.map +1 -0
- package/dist/passkey/index.js +5 -0
- package/dist/passkey/index.js.map +1 -0
- package/dist/passkey/registration/index.d.ts +19 -0
- package/dist/passkey/registration/index.d.ts.map +1 -0
- package/dist/passkey/registration/index.js +20 -0
- package/dist/passkey/registration/index.js.map +1 -0
- package/dist/passkey/registration/micro.d.ts +101 -0
- package/dist/passkey/registration/micro.d.ts.map +1 -0
- package/dist/passkey/registration/micro.js +126 -0
- package/dist/passkey/registration/micro.js.map +1 -0
- package/dist/passkey/shared.d.ts +24 -0
- package/dist/passkey/shared.d.ts.map +1 -0
- package/dist/passkey/shared.js +10 -0
- package/dist/passkey/shared.js.map +1 -0
- package/dist/passkey/support.d.ts +3 -0
- package/dist/passkey/support.d.ts.map +1 -0
- package/dist/passkey/support.js +4 -0
- package/dist/passkey/support.js.map +1 -0
- package/dist/passkey/types.d.ts +26 -0
- package/dist/passkey/types.d.ts.map +1 -0
- package/dist/passkey/types.js +2 -0
- package/dist/passkey/types.js.map +1 -0
- package/dist/promise.d.ts +15 -0
- package/dist/promise.d.ts.map +1 -0
- package/dist/promise.js +46 -0
- package/dist/promise.js.map +1 -0
- package/dist/shared.d.ts +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +2 -0
- package/dist/shared.js.map +1 -0
- package/dist/tenancy.d.ts +8 -0
- package/dist/tenancy.d.ts.map +1 -0
- package/dist/tenancy.js +4 -0
- package/dist/tenancy.js.map +1 -0
- package/package.json +52 -58
- package/LICENSE +0 -21
- package/dist/authentication/authenticate.d.ts +0 -24
- package/dist/authentication/authenticate.fixture.d.ts +0 -52
- package/dist/authentication/authenticate.fixture.js +0 -50
- package/dist/authentication/authenticate.fixture.js.map +0 -1
- package/dist/authentication/authenticate.js +0 -73
- package/dist/authentication/authenticate.js.map +0 -1
- package/dist/capabilities/capabilities.d.ts +0 -19
- package/dist/capabilities/capabilities.js +0 -37
- package/dist/capabilities/capabilities.js.map +0 -1
- package/dist/connection/connection.d.ts +0 -15
- package/dist/connection/connection.fixture.d.ts +0 -10
- package/dist/connection/connection.fixture.js +0 -13
- package/dist/connection/connection.fixture.js.map +0 -1
- package/dist/connection/connection.js +0 -23
- package/dist/connection/connection.js.map +0 -1
- package/dist/effect.d.ts +0 -26
- package/dist/effect.js +0 -78
- package/dist/effect.js.map +0 -1
- package/dist/email/email.d.ts +0 -70
- package/dist/email/email.fixture.d.ts +0 -46
- package/dist/email/email.fixture.js +0 -25
- package/dist/email/email.fixture.js.map +0 -1
- package/dist/email/email.js +0 -83
- package/dist/email/email.js.map +0 -1
- package/dist/event/event.d.ts +0 -8
- package/dist/event/event.js +0 -23
- package/dist/event/event.js.map +0 -1
- package/dist/logging/eventLogger.d.ts +0 -17
- package/dist/logging/eventLogger.js +0 -38
- package/dist/logging/eventLogger.js.map +0 -1
- package/dist/registration/register.d.ts +0 -27
- package/dist/registration/register.fixture.d.ts +0 -53
- package/dist/registration/register.fixture.js +0 -66
- package/dist/registration/register.fixture.js.map +0 -1
- package/dist/registration/register.js +0 -78
- package/dist/registration/register.js.map +0 -1
- package/dist/rpc/authentication.d.ts +0 -8
- package/dist/rpc/authentication.js +0 -16
- package/dist/rpc/authentication.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -30
- package/dist/rpc/client.js +0 -101
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/config.d.ts +0 -15
- package/dist/rpc/config.js +0 -6
- package/dist/rpc/config.js.map +0 -1
- package/dist/rpc/connection.d.ts +0 -8
- package/dist/rpc/connection.js +0 -15
- package/dist/rpc/connection.js.map +0 -1
- package/dist/rpc/registration.d.ts +0 -8
- package/dist/rpc/registration.js +0 -16
- package/dist/rpc/registration.js.map +0 -1
- package/dist/rpc/social.d.ts +0 -10
- package/dist/rpc/social.js +0 -18
- package/dist/rpc/social.js.map +0 -1
- package/dist/rpc/user.d.ts +0 -8
- package/dist/rpc/user.js +0 -19
- package/dist/rpc/user.js.map +0 -1
- package/dist/social/social.d.ts +0 -24
- package/dist/social/social.fixture.d.ts +0 -46
- package/dist/social/social.fixture.js +0 -31
- package/dist/social/social.fixture.js.map +0 -1
- package/dist/social/social.js +0 -39
- package/dist/social/social.js.map +0 -1
- package/dist/storage/storage.d.ts +0 -56
- package/dist/storage/storage.fixture.d.ts +0 -4
- package/dist/storage/storage.fixture.js +0 -10
- package/dist/storage/storage.fixture.js.map +0 -1
- package/dist/storage/storage.js +0 -111
- package/dist/storage/storage.js.map +0 -1
- package/dist/test/fixtures.d.ts +0 -15
- package/dist/test/fixtures.js +0 -56
- package/dist/test/fixtures.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/user/user.d.ts +0 -25
- package/dist/user/user.fixture.d.ts +0 -12
- package/dist/user/user.fixture.js +0 -20
- package/dist/user/user.fixture.js.map +0 -1
- package/dist/user/user.js +0 -38
- package/dist/user/user.js.map +0 -1
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -2
- package/dist/version.js.map +0 -1
- package/src/authentication/authenticate.fixture.ts +0 -73
- package/src/authentication/authenticate.test.ts +0 -249
- package/src/authentication/authenticate.ts +0 -149
- package/src/capabilities/capabilities.ts +0 -83
- package/src/connection/connection.fixture.ts +0 -20
- package/src/connection/connection.test.ts +0 -60
- package/src/connection/connection.ts +0 -51
- package/src/effect.ts +0 -280
- package/src/email/email.fixture.ts +0 -44
- package/src/email/email.test.ts +0 -186
- package/src/email/email.ts +0 -148
- package/src/event/event.node.test.ts +0 -21
- package/src/event/event.test.ts +0 -37
- package/src/event/event.ts +0 -25
- package/src/index.ts +0 -407
- package/src/logging/eventLogger.test.ts +0 -104
- package/src/logging/eventLogger.ts +0 -41
- package/src/registration/register.fixture.ts +0 -96
- package/src/registration/register.test.ts +0 -216
- package/src/registration/register.ts +0 -158
- package/src/rpc/authentication.ts +0 -43
- package/src/rpc/client.ts +0 -174
- package/src/rpc/config.ts +0 -18
- package/src/rpc/connection.ts +0 -30
- package/src/rpc/registration.ts +0 -41
- package/src/rpc/social.ts +0 -45
- package/src/rpc/user.ts +0 -57
- package/src/social/social.fixture.ts +0 -45
- package/src/social/social.test.ts +0 -179
- package/src/social/social.ts +0 -82
- package/src/storage/storage.fixture.ts +0 -16
- package/src/storage/storage.test.ts +0 -206
- package/src/storage/storage.ts +0 -168
- package/src/test/fixtures.ts +0 -70
- package/src/user/user.fixture.ts +0 -33
- package/src/user/user.test.ts +0 -84
- package/src/user/user.ts +0 -73
- 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
|
-
OptionsReq,
|
|
5
|
-
OptionsRes,
|
|
6
|
-
VerificationReq,
|
|
7
|
-
VerificationRes,
|
|
8
|
-
} from '@passlock/shared/dist/rpc/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/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 OptionsReq(registrationRequest)
|
|
31
|
-
|
|
32
|
-
export const registrationOptions: OptionsRes = {
|
|
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 OptionsRes(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 VerificationReq({
|
|
64
|
-
session,
|
|
65
|
-
credential,
|
|
66
|
-
verifyEmail: O.none(),
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
export const rpcVerificationRes = new VerificationRes({ 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/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,158 +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 type { OptionsErrors, VerificationErrors } from '@passlock/shared/dist/rpc/registration.js'
|
|
13
|
-
import { OptionsReq, VerificationReq } from '@passlock/shared/dist/rpc/registration.js'
|
|
14
|
-
import type { RegistrationCredential } from '@passlock/shared/dist/schema/passkey.js'
|
|
15
|
-
import type { Principal } from '@passlock/shared/dist/schema/principal.js'
|
|
16
|
-
|
|
17
|
-
import { Capabilities } from '../capabilities/capabilities.js'
|
|
18
|
-
import { RegistrationClient } from '../rpc/registration.js'
|
|
19
|
-
import { StorageService } from '../storage/storage.js'
|
|
20
|
-
import type { UserService } from '../user/user.js'
|
|
21
|
-
|
|
22
|
-
/* Requests */
|
|
23
|
-
|
|
24
|
-
export type RegistrationRequest = OptionsReq
|
|
25
|
-
|
|
26
|
-
/* Dependencies */
|
|
27
|
-
|
|
28
|
-
export class CreateCredential extends Context.Tag('@services/CreateCredential')<
|
|
29
|
-
CreateCredential,
|
|
30
|
-
{
|
|
31
|
-
createCredential: (
|
|
32
|
-
request: CredentialCreationOptions,
|
|
33
|
-
) => E.Effect<RegistrationCredential, InternalBrowserError | Duplicate>
|
|
34
|
-
}
|
|
35
|
-
>() {}
|
|
36
|
-
|
|
37
|
-
/* Errors */
|
|
38
|
-
|
|
39
|
-
export type RegistrationErrors = NotSupported | OptionsErrors | VerificationErrors
|
|
40
|
-
|
|
41
|
-
/* Service */
|
|
42
|
-
|
|
43
|
-
export class RegistrationService extends Context.Tag('@services/RegistrationService')<
|
|
44
|
-
RegistrationService,
|
|
45
|
-
{
|
|
46
|
-
registerPasskey: (request: RegistrationRequest) => E.Effect<Principal, RegistrationErrors>
|
|
47
|
-
}
|
|
48
|
-
>() {}
|
|
49
|
-
|
|
50
|
-
/* Utilities */
|
|
51
|
-
|
|
52
|
-
const fetchOptions = (request: OptionsReq) => {
|
|
53
|
-
return E.gen(function* (_) {
|
|
54
|
-
yield* _(E.logDebug('Making request'))
|
|
55
|
-
|
|
56
|
-
const rpcClient = yield* _(RegistrationClient)
|
|
57
|
-
const { publicKey, session } = yield* _(rpcClient.getRegistrationOptions(request))
|
|
58
|
-
|
|
59
|
-
yield* _(E.logDebug('Converting Passlock options to CredentialCreationOptions'))
|
|
60
|
-
const options = yield* _(toCreationOptions({ publicKey }))
|
|
61
|
-
|
|
62
|
-
return { options, session }
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const toCreationOptions = (jsonOptions: CredentialCreationOptionsJSON) => {
|
|
67
|
-
return pipe(
|
|
68
|
-
E.try(() => parseCreationOptionsFromJSON(jsonOptions)),
|
|
69
|
-
E.mapError(
|
|
70
|
-
error =>
|
|
71
|
-
new InternalBrowserError({
|
|
72
|
-
message: 'Browser was unable to create credential creation options',
|
|
73
|
-
detail: String(error.error),
|
|
74
|
-
}),
|
|
75
|
-
),
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const verifyCredential = (request: VerificationReq) => {
|
|
80
|
-
return E.gen(function* (_) {
|
|
81
|
-
yield* _(E.logDebug('Making request'))
|
|
82
|
-
|
|
83
|
-
const rpcClient = yield* _(RegistrationClient)
|
|
84
|
-
const { principal } = yield* _(rpcClient.verifyRegistrationCredential(request))
|
|
85
|
-
|
|
86
|
-
return principal
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* Effects */
|
|
91
|
-
|
|
92
|
-
type Dependencies =
|
|
93
|
-
| Capabilities
|
|
94
|
-
| CreateCredential
|
|
95
|
-
| StorageService
|
|
96
|
-
| UserService
|
|
97
|
-
| RegistrationClient
|
|
98
|
-
|
|
99
|
-
export const registerPasskey = (
|
|
100
|
-
request: RegistrationRequest,
|
|
101
|
-
): E.Effect<Principal, RegistrationErrors, Dependencies> => {
|
|
102
|
-
const effect = E.gen(function* (_) {
|
|
103
|
-
yield* _(E.logInfo('Checking if browser supports Passkeys'))
|
|
104
|
-
const capabilities = yield* _(Capabilities)
|
|
105
|
-
yield* _(capabilities.passkeySupport)
|
|
106
|
-
|
|
107
|
-
yield* _(E.logInfo('Fetching registration options from Passlock'))
|
|
108
|
-
const { options, session } = yield* fetchOptions(new OptionsReq(request))
|
|
109
|
-
|
|
110
|
-
yield* _(E.logInfo('Building new credential'))
|
|
111
|
-
const { createCredential } = yield* _(CreateCredential)
|
|
112
|
-
const credential = yield* _(createCredential(options))
|
|
113
|
-
|
|
114
|
-
yield* _(E.logInfo('Storing credential public key in Passlock'))
|
|
115
|
-
const verificationRequest = new VerificationReq({
|
|
116
|
-
...request,
|
|
117
|
-
credential,
|
|
118
|
-
session,
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
const principal = yield* _(verifyCredential(verificationRequest))
|
|
122
|
-
|
|
123
|
-
const storageService = yield* _(StorageService)
|
|
124
|
-
yield* _(storageService.storeToken(principal))
|
|
125
|
-
yield* _(E.logDebug('Storing token in local storage'))
|
|
126
|
-
|
|
127
|
-
yield* _(E.logDebug('Defering local token deletion'))
|
|
128
|
-
const delayedClearTokenE = pipe(
|
|
129
|
-
storageService.clearExpiredToken('passkey'),
|
|
130
|
-
E.delay('6 minutes'),
|
|
131
|
-
E.fork,
|
|
132
|
-
)
|
|
133
|
-
yield* _(delayedClearTokenE)
|
|
134
|
-
|
|
135
|
-
return principal
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
return E.catchTag(effect, 'InternalBrowserError', e => E.die(e))
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/* Live */
|
|
142
|
-
|
|
143
|
-
/* v8 ignore start */
|
|
144
|
-
export const RegistrationServiceLive = Layer.effect(
|
|
145
|
-
RegistrationService,
|
|
146
|
-
E.gen(function* (_) {
|
|
147
|
-
const context = yield* _(
|
|
148
|
-
E.context<
|
|
149
|
-
CreateCredential | RegistrationClient | Capabilities | StorageService | UserService
|
|
150
|
-
>(),
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
return RegistrationService.of({
|
|
154
|
-
registerPasskey: flow(registerPasskey, E.provide(context)),
|
|
155
|
-
})
|
|
156
|
-
}),
|
|
157
|
-
)
|
|
158
|
-
/* v8 ignore stop */
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Context, Effect as E, Layer } from 'effect'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
type AuthenticationService,
|
|
5
|
-
OPTIONS_ENDPOINT,
|
|
6
|
-
OptionsErrors,
|
|
7
|
-
OptionsReq,
|
|
8
|
-
OptionsRes,
|
|
9
|
-
VERIFY_ENDPOINT,
|
|
10
|
-
VerificationErrors,
|
|
11
|
-
VerificationReq,
|
|
12
|
-
VerificationRes,
|
|
13
|
-
} from '@passlock/shared/dist/rpc/authentication.js'
|
|
14
|
-
|
|
15
|
-
import { Dispatcher, makePostRequest } from './client.js'
|
|
16
|
-
|
|
17
|
-
/* Client */
|
|
18
|
-
|
|
19
|
-
export class AuthenticationClient extends Context.Tag('@passkey/auth/client')<
|
|
20
|
-
AuthenticationClient,
|
|
21
|
-
AuthenticationService
|
|
22
|
-
>() {}
|
|
23
|
-
|
|
24
|
-
export const AuthenticationClientLive = Layer.effect(
|
|
25
|
-
AuthenticationClient,
|
|
26
|
-
E.gen(function* (_) {
|
|
27
|
-
const dispatcher = yield* _(Dispatcher)
|
|
28
|
-
|
|
29
|
-
const optionsResolver = makePostRequest(OptionsReq, OptionsRes, OptionsErrors, dispatcher)
|
|
30
|
-
|
|
31
|
-
const verifyResolver = makePostRequest(
|
|
32
|
-
VerificationReq,
|
|
33
|
-
VerificationRes,
|
|
34
|
-
VerificationErrors,
|
|
35
|
-
dispatcher,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
getAuthenticationOptions: req => optionsResolver(OPTIONS_ENDPOINT, req),
|
|
40
|
-
verifyAuthenticationCredential: req => verifyResolver(VERIFY_ENDPOINT, req),
|
|
41
|
-
}
|
|
42
|
-
}),
|
|
43
|
-
)
|