@passlock/client 0.9.22 → 0.9.24
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 +10 -5
- package/README.template.md +130 -0
- package/dist/authentication/authenticate.d.ts +16 -16
- package/dist/authentication/authenticate.fixture.d.ts +21 -7
- package/dist/authentication/authenticate.fixture.js +7 -5
- package/dist/authentication/authenticate.fixture.js.map +1 -1
- package/dist/authentication/authenticate.js +19 -8
- package/dist/authentication/authenticate.js.map +1 -1
- package/dist/capabilities/capabilities.d.ts +9 -5
- package/dist/capabilities/capabilities.js +11 -2
- package/dist/capabilities/capabilities.js.map +1 -1
- package/dist/connection/connection.d.ts +11 -7
- package/dist/connection/connection.fixture.d.ts +2 -2
- package/dist/connection/connection.fixture.js +2 -1
- package/dist/connection/connection.fixture.js.map +1 -1
- package/dist/connection/connection.js +12 -3
- package/dist/connection/connection.js.map +1 -1
- package/dist/effect.d.ts +23 -46
- package/dist/effect.js +55 -51
- package/dist/effect.js.map +1 -1
- package/dist/email/email.d.ts +42 -12
- package/dist/email/email.fixture.d.ts +19 -5
- package/dist/email/email.fixture.js +5 -4
- package/dist/email/email.fixture.js.map +1 -1
- package/dist/email/email.js +44 -8
- package/dist/email/email.js.map +1 -1
- package/dist/event/event.d.ts +4 -2
- package/dist/event/event.js +4 -1
- package/dist/event/event.js.map +1 -1
- package/dist/index.d.ts +105 -27
- package/dist/index.js +101 -50
- package/dist/index.js.map +1 -1
- package/dist/logging/eventLogger.d.ts +13 -1
- package/dist/logging/eventLogger.js +14 -1
- package/dist/logging/eventLogger.js.map +1 -1
- package/dist/registration/register.d.ts +19 -22
- package/dist/registration/register.fixture.d.ts +20 -6
- package/dist/registration/register.fixture.js +14 -7
- package/dist/registration/register.fixture.js.map +1 -1
- package/dist/registration/register.js +18 -9
- package/dist/registration/register.js.map +1 -1
- package/dist/rpc/authentication.d.ts +1 -2
- package/dist/rpc/authentication.js +2 -1
- package/dist/rpc/authentication.js.map +1 -1
- package/dist/rpc/client.d.ts +5 -2
- package/dist/rpc/client.js +12 -3
- package/dist/rpc/client.js.map +1 -1
- package/dist/rpc/config.d.ts +0 -1
- package/dist/rpc/connection.d.ts +1 -2
- package/dist/rpc/connection.js +2 -1
- package/dist/rpc/connection.js.map +1 -1
- package/dist/rpc/registration.d.ts +1 -2
- package/dist/rpc/registration.js +2 -1
- package/dist/rpc/registration.js.map +1 -1
- package/dist/rpc/social.d.ts +1 -2
- package/dist/rpc/social.js +2 -1
- package/dist/rpc/social.js.map +1 -1
- package/dist/rpc/user.d.ts +1 -2
- package/dist/rpc/user.js +2 -1
- package/dist/rpc/user.js.map +1 -1
- package/dist/social/social.d.ts +17 -24
- package/dist/social/social.fixture.d.ts +22 -10
- package/dist/social/social.fixture.js +8 -14
- package/dist/social/social.fixture.js.map +1 -1
- package/dist/social/social.js +15 -11
- package/dist/social/social.js.map +1 -1
- package/dist/storage/storage.d.ts +41 -13
- package/dist/storage/storage.fixture.d.ts +2 -2
- package/dist/storage/storage.fixture.js +2 -2
- package/dist/storage/storage.fixture.js.map +1 -1
- package/dist/storage/storage.js +52 -15
- package/dist/storage/storage.js.map +1 -1
- package/dist/test/fixtures.d.ts +2 -3
- package/dist/test/fixtures.js +20 -5
- package/dist/test/fixtures.js.map +1 -1
- package/dist/user/user.d.ts +9 -6
- package/dist/user/user.fixture.d.ts +2 -2
- package/dist/user/user.fixture.js +9 -5
- package/dist/user/user.fixture.js.map +1 -1
- package/dist/user/user.js +12 -3
- package/dist/user/user.js.map +1 -1
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +38 -30
- package/src/authentication/authenticate.fixture.ts +10 -7
- package/src/authentication/authenticate.test.ts +61 -18
- package/src/authentication/authenticate.ts +37 -33
- package/src/capabilities/capabilities.ts +11 -9
- package/src/connection/connection.fixture.ts +4 -1
- package/src/connection/connection.test.ts +4 -3
- package/src/connection/connection.ts +10 -8
- package/src/effect.ts +129 -134
- package/src/email/email.fixture.ts +7 -4
- package/src/email/email.test.ts +6 -5
- package/src/email/email.ts +27 -17
- package/src/event/event.node.test.ts +1 -0
- package/src/event/event.test.ts +1 -0
- package/src/event/event.ts +2 -1
- package/src/index.ts +235 -173
- package/src/logging/eventLogger.test.ts +2 -1
- package/src/logging/eventLogger.ts +3 -3
- package/src/registration/register.fixture.ts +16 -8
- package/src/registration/register.test.ts +16 -10
- package/src/registration/register.ts +37 -35
- package/src/rpc/authentication.ts +43 -0
- package/src/rpc/client.ts +174 -0
- package/src/rpc/config.ts +18 -0
- package/src/rpc/connection.ts +30 -0
- package/src/rpc/registration.ts +41 -0
- package/src/rpc/social.ts +45 -0
- package/src/rpc/user.ts +57 -0
- package/src/social/social.fixture.ts +12 -18
- package/src/social/social.test.ts +16 -30
- package/src/social/social.ts +22 -47
- package/src/storage/storage.fixture.ts +4 -4
- package/src/storage/storage.test.ts +29 -19
- package/src/storage/storage.ts +37 -36
- package/src/test/fixtures.ts +23 -6
- package/src/user/user.fixture.ts +19 -7
- package/src/user/user.test.ts +3 -5
- package/src/user/user.ts +16 -10
- package/src/version.ts +1 -0
- package/dist/authentication/authenticate.d.ts.map +0 -1
- package/dist/authentication/authenticate.fixture.d.ts.map +0 -1
- package/dist/capabilities/capabilities.d.ts.map +0 -1
- package/dist/config.d.ts +0 -18
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -20
- package/dist/config.js.map +0 -1
- package/dist/connection/connection.d.ts.map +0 -1
- package/dist/connection/connection.fixture.d.ts.map +0 -1
- package/dist/effect.d.ts.map +0 -1
- package/dist/email/email.d.ts.map +0 -1
- package/dist/email/email.fixture.d.ts.map +0 -1
- package/dist/event/event.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/logging/eventLogger.d.ts.map +0 -1
- package/dist/registration/register.d.ts.map +0 -1
- package/dist/registration/register.fixture.d.ts.map +0 -1
- package/dist/rpc/authentication.d.ts.map +0 -1
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/config.d.ts.map +0 -1
- package/dist/rpc/connection.d.ts.map +0 -1
- package/dist/rpc/registration.d.ts.map +0 -1
- package/dist/rpc/social.d.ts.map +0 -1
- package/dist/rpc/user.d.ts.map +0 -1
- package/dist/social/social.d.ts.map +0 -1
- package/dist/social/social.fixture.d.ts.map +0 -1
- package/dist/storage/storage.d.ts.map +0 -1
- package/dist/storage/storage.fixture.d.ts.map +0 -1
- package/dist/test/fixtures.d.ts.map +0 -1
- package/dist/user/user.d.ts.map +0 -1
- package/dist/user/user.fixture.d.ts.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/src/config.ts +0 -42
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passlock/client",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.9.24",
|
|
4
|
+
"description": "Passkey authentication and social login for web apps (Typescript). Framework agnostic",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"passkey",
|
|
7
7
|
"passkeys",
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"google one tap",
|
|
10
10
|
"sign in with google",
|
|
11
11
|
"sign in with apple",
|
|
12
|
+
"svelte",
|
|
13
|
+
"sveltekit",
|
|
12
14
|
"react",
|
|
13
15
|
"next",
|
|
14
16
|
"vue",
|
|
@@ -19,10 +21,11 @@
|
|
|
19
21
|
"email": "toby@passlock.dev"
|
|
20
22
|
},
|
|
21
23
|
"license": "MIT",
|
|
22
|
-
"homepage": "https://
|
|
24
|
+
"homepage": "https://passlock.dev",
|
|
23
25
|
"repository": {
|
|
24
26
|
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/passlock-dev/ts-clients.git"
|
|
27
|
+
"url": "git+https://github.com/passlock-dev/ts-clients.git",
|
|
28
|
+
"directory": "packages/client"
|
|
26
29
|
},
|
|
27
30
|
"bugs": {
|
|
28
31
|
"url": "https://github.com/passlock-dev/ts-clients/issues",
|
|
@@ -42,45 +45,50 @@
|
|
|
42
45
|
"!dist/**/*.spec.*"
|
|
43
46
|
],
|
|
44
47
|
"dependencies": {
|
|
48
|
+
"@effect/schema": "0.71.0",
|
|
45
49
|
"@github/webauthn-json": "^2.1.1",
|
|
46
|
-
"effect": "3.4
|
|
47
|
-
"@passlock/shared": "0.9.
|
|
50
|
+
"effect": "3.6.4",
|
|
51
|
+
"@passlock/shared": "0.9.24"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
56
|
-
"eslint": "^8.
|
|
54
|
+
"@qetza/replacetokens": "^1.7.0",
|
|
55
|
+
"@total-typescript/tsconfig": "^1.0.4",
|
|
56
|
+
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
|
57
|
+
"@tsconfig/node20": "^20.1.4",
|
|
58
|
+
"@types/node": "^22.4.0",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
|
60
|
+
"@typescript-eslint/parser": "^8.1.0",
|
|
61
|
+
"@vitest/coverage-v8": "^2.0.5",
|
|
62
|
+
"@vitest/ui": "^2.0.5",
|
|
63
|
+
"eslint": "^9.9.0",
|
|
57
64
|
"eslint-config-prettier": "^9.1.0",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"rimraf": "^5.0.8",
|
|
65
|
+
"globals": "^15.9.0",
|
|
66
|
+
"jsdom": "^24.1.1",
|
|
67
|
+
"prettier": "^3.3.3",
|
|
68
|
+
"publint": "^0.2.10",
|
|
69
|
+
"rimraf": "^6.0.1",
|
|
64
70
|
"tslib": "^2.6.3",
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"vitest
|
|
71
|
+
"tsx": "^4.17.0",
|
|
72
|
+
"typescript": "^5.5.4",
|
|
73
|
+
"vite": "^5.4.1",
|
|
74
|
+
"vitest": "^2.0.5",
|
|
75
|
+
"vitest-mock-extended": "^2.0.0"
|
|
69
76
|
},
|
|
70
77
|
"scripts": {
|
|
71
|
-
"clean": "
|
|
78
|
+
"clean": "rimraf ./dist",
|
|
72
79
|
"typecheck": "tsc --noEmit",
|
|
73
80
|
"test": "vitest run",
|
|
74
81
|
"test:watch": "vitest dev",
|
|
75
82
|
"test:ui": "vitest --coverage.enabled=true --ui",
|
|
76
83
|
"test:coverage": "vitest run --coverage",
|
|
77
|
-
"build": "tsc --build
|
|
84
|
+
"build": "tsc --build",
|
|
78
85
|
"build:clean": "pnpm run clean && pnpm run build",
|
|
79
|
-
"build:
|
|
80
|
-
"
|
|
81
|
-
"
|
|
86
|
+
"build:readme": "LATEST=${npm_package_version} tsx ../shared/scripts/replace-readme-tokens.ts ./packages/client",
|
|
87
|
+
"replaceTokens": "LATEST=${npm_package_version} tsx ../shared/scripts/replace-tokens.ts ./packages/client",
|
|
88
|
+
"build:production": "pnpm run build:clean && pnpm run replaceTokens && echo '' && publint",
|
|
89
|
+
"format": "prettier --write \"(src|scripts)/**/*.+(js|ts|json)\"",
|
|
90
|
+
"lint": "eslint ./src",
|
|
82
91
|
"lint:fix": "pnpm run lint --fix",
|
|
83
|
-
"
|
|
84
|
-
"ncu:save": "ncu --peer -x @effect/* -x effect -u"
|
|
92
|
+
"upgrade:deps": "ncu --peer -x effect -x @effect/* -i -u"
|
|
85
93
|
}
|
|
86
94
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { Effect as E, Layer as L, Option as O } from 'effect'
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
|
-
AuthenticationClient,
|
|
3
4
|
OptionsRes,
|
|
4
5
|
VerificationReq,
|
|
5
6
|
VerificationRes,
|
|
6
7
|
} from '@passlock/shared/dist/rpc/authentication.js'
|
|
7
8
|
import { IsExistingUserRes, VerifyEmailRes } from '@passlock/shared/dist/rpc/user.js'
|
|
8
9
|
import type { AuthenticationCredential } from '@passlock/shared/dist/schema/passkey.js'
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
import * as Fixtures from '../test/fixtures.js'
|
|
11
|
-
import {
|
|
12
|
+
import { AuthenticationClient } from '../rpc/authentication.js'
|
|
13
|
+
import { type AuthenticationRequest, GetCredential } from './authenticate.js'
|
|
12
14
|
|
|
13
15
|
export const session = 'session'
|
|
14
16
|
export const token = 'token'
|
|
@@ -17,7 +19,8 @@ export const authType = 'passkey'
|
|
|
17
19
|
export const expireAt = Date.now() + 10000
|
|
18
20
|
|
|
19
21
|
export const request: AuthenticationRequest = {
|
|
20
|
-
userVerification: 'preferred',
|
|
22
|
+
userVerification: O.some('preferred'),
|
|
23
|
+
email: O.none(),
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
export const rpcOptionsRes = new OptionsRes({
|
|
@@ -48,13 +51,13 @@ export const rpcVerificationReq = new VerificationReq({ session, credential })
|
|
|
48
51
|
|
|
49
52
|
export const rpcVerificationRes = new VerificationRes({ principal: Fixtures.principal })
|
|
50
53
|
|
|
51
|
-
export const rpcIsExistingUserRes = new IsExistingUserRes({ existingUser: true })
|
|
54
|
+
export const rpcIsExistingUserRes = new IsExistingUserRes({ existingUser: true, detail: O.none() })
|
|
52
55
|
|
|
53
56
|
export const rpcVerifyEmailRes = new VerifyEmailRes({ principal: Fixtures.principal })
|
|
54
57
|
|
|
55
58
|
export const getCredentialTest = L.succeed(
|
|
56
59
|
GetCredential,
|
|
57
|
-
GetCredential.of(() => E.succeed(credential)),
|
|
60
|
+
GetCredential.of({ getCredential: () => E.succeed(credential) }),
|
|
58
61
|
)
|
|
59
62
|
|
|
60
63
|
export const rpcClientTest = L.succeed(
|
|
@@ -62,7 +65,7 @@ export const rpcClientTest = L.succeed(
|
|
|
62
65
|
AuthenticationClient.of({
|
|
63
66
|
getAuthenticationOptions: () => E.succeed(rpcOptionsRes),
|
|
64
67
|
verifyAuthenticationCredential: () => E.succeed(rpcVerificationRes),
|
|
65
|
-
})
|
|
68
|
+
}),
|
|
66
69
|
)
|
|
67
70
|
|
|
68
71
|
export const principal = Fixtures.principal
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Effect as E, Layer as L, Layer, LogLevel, Logger, pipe } from 'effect'
|
|
1
|
+
import { Effect as E, Layer as L, Layer, LogLevel, Logger, Option as O, pipe } from 'effect'
|
|
3
2
|
import { describe, expect, test, vi } from 'vitest'
|
|
4
3
|
import { mock } from 'vitest-mock-extended'
|
|
5
|
-
|
|
4
|
+
|
|
6
5
|
import * as Fixture from './authenticate.fixture.js'
|
|
6
|
+
import { AuthenticationClient } from '../rpc/authentication.js'
|
|
7
|
+
import { StorageService } from '../storage/storage.js'
|
|
7
8
|
import { AuthenticateServiceLive, AuthenticationService, GetCredential } from './authenticate.js'
|
|
8
9
|
|
|
9
10
|
describe('authenticate should', () => {
|
|
10
11
|
test('return a valid principal', async () => {
|
|
11
12
|
const assertions = E.gen(function* (_) {
|
|
12
13
|
const service = yield* _(AuthenticationService)
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
const result = yield* _(
|
|
16
|
+
service.authenticatePasskey({
|
|
17
|
+
email: O.none(),
|
|
18
|
+
userVerification: O.some('preferred'),
|
|
19
|
+
}),
|
|
20
|
+
)
|
|
14
21
|
|
|
15
22
|
expect(result).toEqual(Fixture.principal)
|
|
16
23
|
})
|
|
@@ -31,7 +38,13 @@ describe('authenticate should', () => {
|
|
|
31
38
|
test('pass the authentication request to the backend', async () => {
|
|
32
39
|
const assertions = E.gen(function* (_) {
|
|
33
40
|
const service = yield* _(AuthenticationService)
|
|
34
|
-
|
|
41
|
+
|
|
42
|
+
yield* _(
|
|
43
|
+
service.authenticatePasskey({
|
|
44
|
+
email: O.none(),
|
|
45
|
+
userVerification: O.some('preferred'),
|
|
46
|
+
}),
|
|
47
|
+
)
|
|
35
48
|
|
|
36
49
|
const rpcClient = yield* _(AuthenticationClient)
|
|
37
50
|
expect(rpcClient.getAuthenticationOptions).toHaveBeenCalledOnce()
|
|
@@ -44,7 +57,9 @@ describe('authenticate should', () => {
|
|
|
44
57
|
const rpcMock = mock<AuthenticationClient['Type']>()
|
|
45
58
|
|
|
46
59
|
rpcMock.getAuthenticationOptions.mockReturnValue(E.succeed(Fixture.rpcOptionsRes))
|
|
47
|
-
rpcMock.verifyAuthenticationCredential.mockReturnValue(
|
|
60
|
+
rpcMock.verifyAuthenticationCredential.mockReturnValue(
|
|
61
|
+
E.succeed(Fixture.rpcVerificationRes),
|
|
62
|
+
)
|
|
48
63
|
|
|
49
64
|
return rpcMock
|
|
50
65
|
}),
|
|
@@ -67,11 +82,19 @@ describe('authenticate should', () => {
|
|
|
67
82
|
test('send the credential to the backend', async () => {
|
|
68
83
|
const assertions = E.gen(function* (_) {
|
|
69
84
|
const service = yield* _(AuthenticationService)
|
|
70
|
-
|
|
85
|
+
|
|
86
|
+
yield* _(
|
|
87
|
+
service.authenticatePasskey({
|
|
88
|
+
email: O.none(),
|
|
89
|
+
userVerification: O.some('preferred'),
|
|
90
|
+
}),
|
|
91
|
+
)
|
|
71
92
|
|
|
72
93
|
const rpcClient = yield* _(AuthenticationClient)
|
|
73
94
|
expect(rpcClient.getAuthenticationOptions).toHaveBeenCalledOnce()
|
|
74
|
-
expect(rpcClient.verifyAuthenticationCredential).toHaveBeenCalledWith(
|
|
95
|
+
expect(rpcClient.verifyAuthenticationCredential).toHaveBeenCalledWith(
|
|
96
|
+
Fixture.rpcVerificationReq,
|
|
97
|
+
)
|
|
75
98
|
})
|
|
76
99
|
|
|
77
100
|
const rpcClientTest = L.effect(
|
|
@@ -80,7 +103,9 @@ describe('authenticate should', () => {
|
|
|
80
103
|
const rpcMock = mock<AuthenticationClient['Type']>()
|
|
81
104
|
|
|
82
105
|
rpcMock.getAuthenticationOptions.mockReturnValue(E.succeed(Fixture.rpcOptionsRes))
|
|
83
|
-
rpcMock.verifyAuthenticationCredential.mockReturnValue(
|
|
106
|
+
rpcMock.verifyAuthenticationCredential.mockReturnValue(
|
|
107
|
+
E.succeed(Fixture.rpcVerificationRes),
|
|
108
|
+
)
|
|
84
109
|
|
|
85
110
|
return rpcMock
|
|
86
111
|
}),
|
|
@@ -103,7 +128,13 @@ describe('authenticate should', () => {
|
|
|
103
128
|
test('store the credential in local storage', async () => {
|
|
104
129
|
const assertions = E.gen(function* (_) {
|
|
105
130
|
const service = yield* _(AuthenticationService)
|
|
106
|
-
|
|
131
|
+
|
|
132
|
+
yield* _(
|
|
133
|
+
service.authenticatePasskey({
|
|
134
|
+
email: O.none(),
|
|
135
|
+
userVerification: O.some('preferred'),
|
|
136
|
+
}),
|
|
137
|
+
)
|
|
107
138
|
|
|
108
139
|
const storageService = yield* _(StorageService)
|
|
109
140
|
expect(storageService.storeToken).toHaveBeenCalledWith(Fixture.principal)
|
|
@@ -112,7 +143,7 @@ describe('authenticate should', () => {
|
|
|
112
143
|
const storageServiceTest = L.effect(
|
|
113
144
|
StorageService,
|
|
114
145
|
E.sync(() => {
|
|
115
|
-
const storageMock = mock<StorageService>()
|
|
146
|
+
const storageMock = mock<StorageService['Type']>()
|
|
116
147
|
|
|
117
148
|
storageMock.storeToken.mockReturnValue(E.void)
|
|
118
149
|
storageMock.clearExpiredToken.mockReturnValue(E.void)
|
|
@@ -138,7 +169,13 @@ describe('authenticate should', () => {
|
|
|
138
169
|
test('schedule deletion of the local token', async () => {
|
|
139
170
|
const assertions = E.gen(function* (_) {
|
|
140
171
|
const service = yield* _(AuthenticationService)
|
|
141
|
-
|
|
172
|
+
|
|
173
|
+
yield* _(
|
|
174
|
+
service.authenticatePasskey({
|
|
175
|
+
email: O.none(),
|
|
176
|
+
userVerification: O.some('preferred'),
|
|
177
|
+
}),
|
|
178
|
+
)
|
|
142
179
|
|
|
143
180
|
const storageService = yield* _(StorageService)
|
|
144
181
|
expect(storageService.clearExpiredToken).toHaveBeenCalledWith('passkey')
|
|
@@ -147,7 +184,7 @@ describe('authenticate should', () => {
|
|
|
147
184
|
const storageServiceTest = L.effect(
|
|
148
185
|
StorageService,
|
|
149
186
|
E.sync(() => {
|
|
150
|
-
const storageMock = mock<StorageService>()
|
|
187
|
+
const storageMock = mock<StorageService['Type']>()
|
|
151
188
|
|
|
152
189
|
storageMock.storeToken.mockReturnValue(E.void)
|
|
153
190
|
storageMock.clearExpiredToken.mockReturnValue(E.void)
|
|
@@ -173,20 +210,26 @@ describe('authenticate should', () => {
|
|
|
173
210
|
test("return an error if the browser can't create a credential", async () => {
|
|
174
211
|
const assertions = E.gen(function* (_) {
|
|
175
212
|
const service = yield* _(AuthenticationService)
|
|
176
|
-
yield* _(service.authenticatePasskey({ userVerification: 'preferred' }))
|
|
177
213
|
|
|
178
|
-
|
|
214
|
+
yield* _(
|
|
215
|
+
service.authenticatePasskey({
|
|
216
|
+
email: O.none(),
|
|
217
|
+
userVerification: O.some('preferred'),
|
|
218
|
+
}),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
const { getCredential } = yield* _(GetCredential)
|
|
179
222
|
expect(getCredential).toHaveBeenCalledOnce()
|
|
180
223
|
})
|
|
181
224
|
|
|
182
225
|
const getCredentialTest = L.effect(
|
|
183
226
|
GetCredential,
|
|
184
227
|
E.sync(() => {
|
|
185
|
-
const
|
|
228
|
+
const getCredential = vi.fn()
|
|
186
229
|
|
|
187
|
-
|
|
230
|
+
getCredential.mockReturnValue(E.succeed(Fixture.credential))
|
|
188
231
|
|
|
189
|
-
return
|
|
232
|
+
return { getCredential }
|
|
190
233
|
}),
|
|
191
234
|
)
|
|
192
235
|
|
|
@@ -2,52 +2,53 @@
|
|
|
2
2
|
* Passkey authentication effects
|
|
3
3
|
*/
|
|
4
4
|
import {
|
|
5
|
-
parseRequestOptionsFromJSON,
|
|
6
5
|
type CredentialRequestOptionsJSON,
|
|
6
|
+
parseRequestOptionsFromJSON,
|
|
7
7
|
} from '@github/webauthn-json/browser-ponyfill'
|
|
8
|
-
import {
|
|
9
|
-
InternalBrowserError,
|
|
10
|
-
type NotSupported,
|
|
11
|
-
} from '@passlock/shared/dist/error/error.js'
|
|
12
|
-
import type { OptionsErrors, VerificationErrors } from '@passlock/shared/dist/rpc/authentication.js'
|
|
13
|
-
import { AuthenticationClient, OptionsReq, VerificationReq } from '@passlock/shared/dist/rpc/authentication.js'
|
|
14
|
-
import type {
|
|
15
|
-
AuthenticationCredential,
|
|
16
|
-
UserVerification,
|
|
17
|
-
} from '@passlock/shared/dist/schema/passkey.js'
|
|
18
|
-
import { Principal } from '@passlock/shared/dist/schema/principal.js'
|
|
19
8
|
import { Context, Effect as E, Layer, flow, pipe } from 'effect'
|
|
9
|
+
|
|
10
|
+
import { InternalBrowserError, type NotSupported } from '@passlock/shared/dist/error/error.js'
|
|
11
|
+
import {
|
|
12
|
+
type OptionsErrors,
|
|
13
|
+
type OptionsReq,
|
|
14
|
+
type VerificationErrors,
|
|
15
|
+
VerificationReq,
|
|
16
|
+
} from '@passlock/shared/dist/rpc/authentication.js'
|
|
17
|
+
import type { AuthenticationCredential } from '@passlock/shared/dist/schema/passkey.js'
|
|
18
|
+
import type { Principal } from '@passlock/shared/dist/schema/principal.js'
|
|
19
|
+
|
|
20
20
|
import { Capabilities } from '../capabilities/capabilities.js'
|
|
21
|
+
import { AuthenticationClient } from '../rpc/authentication.js'
|
|
21
22
|
import { StorageService } from '../storage/storage.js'
|
|
22
23
|
|
|
23
24
|
/* Requests */
|
|
24
25
|
|
|
25
|
-
export type AuthenticationRequest =
|
|
26
|
-
email?: string,
|
|
27
|
-
userVerification?: UserVerification
|
|
28
|
-
}
|
|
29
|
-
|
|
26
|
+
export type AuthenticationRequest = OptionsReq
|
|
30
27
|
/* Errors */
|
|
31
28
|
|
|
32
29
|
export type AuthenticationErrors = NotSupported | OptionsErrors | VerificationErrors
|
|
33
30
|
|
|
34
31
|
/* Dependencies */
|
|
35
32
|
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
export class GetCredential extends Context.Tag('@services/GetCredential')<
|
|
34
|
+
GetCredential,
|
|
35
|
+
{
|
|
36
|
+
getCredential: (
|
|
37
|
+
request: CredentialRequestOptions,
|
|
38
|
+
) => E.Effect<AuthenticationCredential, InternalBrowserError>
|
|
39
|
+
}
|
|
40
|
+
>() {}
|
|
41
41
|
|
|
42
42
|
/* Service */
|
|
43
43
|
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
export class AuthenticationService extends Context.Tag('@services/AuthenticationService')<
|
|
45
|
+
AuthenticationService,
|
|
46
|
+
{
|
|
47
|
+
authenticatePasskey: (
|
|
48
|
+
request: AuthenticationRequest,
|
|
49
|
+
) => E.Effect<Principal, AuthenticationErrors>
|
|
50
|
+
}
|
|
51
|
+
>() {}
|
|
51
52
|
|
|
52
53
|
/* Utilities */
|
|
53
54
|
|
|
@@ -102,11 +103,12 @@ export const authenticatePasskey = (
|
|
|
102
103
|
yield* _(capabilities.passkeySupport)
|
|
103
104
|
|
|
104
105
|
yield* _(E.logInfo('Fetching authentication options from Passlock'))
|
|
105
|
-
|
|
106
|
+
|
|
107
|
+
const { options, session } = yield* _(fetchOptions(request))
|
|
106
108
|
|
|
107
109
|
yield* _(E.logInfo('Looking up credential'))
|
|
108
|
-
const
|
|
109
|
-
const credential = yield* _(
|
|
110
|
+
const { getCredential } = yield* _(GetCredential)
|
|
111
|
+
const credential = yield* _(getCredential(options))
|
|
110
112
|
|
|
111
113
|
yield* _(E.logInfo('Verifying credential with Passlock'))
|
|
112
114
|
const principal = yield* _(verifyCredential(new VerificationReq({ credential, session })))
|
|
@@ -135,7 +137,9 @@ export const authenticatePasskey = (
|
|
|
135
137
|
export const AuthenticateServiceLive = Layer.effect(
|
|
136
138
|
AuthenticationService,
|
|
137
139
|
E.gen(function* (_) {
|
|
138
|
-
const context = yield* _(
|
|
140
|
+
const context = yield* _(
|
|
141
|
+
E.context<GetCredential | AuthenticationClient | Capabilities | StorageService>(),
|
|
142
|
+
)
|
|
139
143
|
|
|
140
144
|
return AuthenticationService.of({
|
|
141
145
|
authenticatePasskey: flow(authenticatePasskey, E.provide(context)),
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Test if the browser supports passkeys, conditional UI etc
|
|
3
3
|
*/
|
|
4
|
-
import { NotSupported } from '@passlock/shared/dist/error/error.js'
|
|
5
4
|
import { Context, Effect as E, Layer, identity, pipe } from 'effect'
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
import { NotSupported } from '@passlock/shared/dist/error/error.js'
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
passkeySupport: E.Effect<void, NotSupported>
|
|
11
|
-
isPasskeySupport: E.Effect<boolean>
|
|
12
|
-
autofillSupport: E.Effect<void, NotSupported>
|
|
13
|
-
isAutofillSupport: E.Effect<boolean>
|
|
14
|
-
}
|
|
8
|
+
/* Service */
|
|
15
9
|
|
|
16
|
-
export
|
|
10
|
+
export class Capabilities extends Context.Tag('@services/Capabilities')<
|
|
11
|
+
Capabilities,
|
|
12
|
+
{
|
|
13
|
+
passkeySupport: E.Effect<void, NotSupported>
|
|
14
|
+
isPasskeySupport: E.Effect<boolean>
|
|
15
|
+
autofillSupport: E.Effect<void, NotSupported>
|
|
16
|
+
isAutofillSupport: E.Effect<boolean>
|
|
17
|
+
}
|
|
18
|
+
>() {}
|
|
17
19
|
|
|
18
20
|
/* Effects */
|
|
19
21
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { ConnectionClient, ConnectRes } from '@passlock/shared/dist/rpc/connection.js'
|
|
2
1
|
import { Effect as E, Layer as L } from 'effect'
|
|
3
2
|
|
|
3
|
+
import { ConnectRes } from '@passlock/shared/dist/rpc/connection.js'
|
|
4
|
+
|
|
5
|
+
import { ConnectionClient } from '../rpc/connection.js'
|
|
6
|
+
|
|
4
7
|
export const preConnectRes = new ConnectRes({ warmed: true })
|
|
5
8
|
|
|
6
9
|
export const rpcClientTest = L.succeed(
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { RpcConfig } from '@passlock/shared/dist/rpc/config.js'
|
|
2
|
-
import { ConnectionClient } from '@passlock/shared/dist/rpc/connection.js'
|
|
3
|
-
import { Dispatcher } from '@passlock/shared/dist/rpc/dispatcher.js'
|
|
4
1
|
import { Effect as E, Layer as L, Layer, LogLevel, Logger, pipe } from 'effect'
|
|
5
2
|
import { describe, expect, test } from 'vitest'
|
|
6
3
|
import { mock } from 'vitest-mock-extended'
|
|
4
|
+
|
|
7
5
|
import * as Fixture from './connection.fixture.js'
|
|
6
|
+
import { Dispatcher } from '../rpc/client.js'
|
|
7
|
+
import { RpcConfig } from '../rpc/config.js'
|
|
8
|
+
import { ConnectionClient } from '../rpc/connection.js'
|
|
8
9
|
import { ConnectionService, ConnectionServiceLive } from './connection.js'
|
|
9
10
|
|
|
10
11
|
describe('preConnect should', () => {
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hits the rpc endpoint to warm up a lambda
|
|
3
3
|
*/
|
|
4
|
-
import type { RpcConfig } from '@passlock/shared/dist/rpc/config.js'
|
|
5
|
-
import { ConnectionClient } from '@passlock/shared/dist/rpc/connection.js'
|
|
6
|
-
import { Dispatcher } from '@passlock/shared/dist/rpc/dispatcher.js'
|
|
7
4
|
import { Context, Effect as E, Layer, flow, pipe } from 'effect'
|
|
8
5
|
|
|
9
|
-
|
|
6
|
+
import { Dispatcher } from '../rpc/client.js'
|
|
7
|
+
import type { RpcConfig } from '../rpc/config.js'
|
|
8
|
+
import { ConnectionClient } from '../rpc/connection.js'
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
preConnect: () => E.Effect<void>
|
|
13
|
-
}
|
|
10
|
+
/* Service */
|
|
14
11
|
|
|
15
|
-
export
|
|
12
|
+
export class ConnectionService extends Context.Tag('@services/ConnectionService')<
|
|
13
|
+
ConnectionService,
|
|
14
|
+
{
|
|
15
|
+
preConnect: () => E.Effect<void>
|
|
16
|
+
}
|
|
17
|
+
>() {}
|
|
16
18
|
|
|
17
19
|
/* Effects */
|
|
18
20
|
|