@passlock/client 0.9.6 → 0.9.7
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 +1 -1
- package/dist/authentication/authenticate.d.ts +6 -5
- package/dist/authentication/authenticate.d.ts.map +1 -1
- package/dist/authentication/authenticate.fixture.d.ts +8 -5
- package/dist/authentication/authenticate.fixture.d.ts.map +1 -1
- package/dist/authentication/authenticate.fixture.js +12 -7
- package/dist/authentication/authenticate.fixture.js.map +1 -1
- package/dist/authentication/authenticate.js +6 -6
- package/dist/authentication/authenticate.js.map +1 -1
- package/dist/authentication/authenticate.test.js +4 -4
- package/dist/capabilities/capabilities.js +3 -3
- package/dist/connection/connection.fixture.d.ts +1 -1
- package/dist/connection/connection.fixture.d.ts.map +1 -1
- package/dist/connection/connection.fixture.js +13 -10
- package/dist/connection/connection.fixture.js.map +1 -1
- package/dist/connection/connection.js +3 -3
- package/dist/effect.d.ts +21 -5
- package/dist/effect.d.ts.map +1 -1
- package/dist/effect.js +12 -9
- package/dist/effect.js.map +1 -1
- package/dist/email/email.d.ts +24 -8
- package/dist/email/email.d.ts.map +1 -1
- package/dist/email/email.fixture.d.ts +8 -8
- package/dist/email/email.fixture.d.ts.map +1 -1
- package/dist/email/email.fixture.js +12 -10
- package/dist/email/email.fixture.js.map +1 -1
- package/dist/email/email.js +5 -5
- package/dist/email/email.js.map +1 -1
- package/dist/email/email.test.js +4 -4
- package/dist/email/email.test.js.map +1 -1
- package/dist/index.d.ts +52 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -14
- package/dist/index.js.map +1 -1
- package/dist/registration/register.d.ts +3 -4
- package/dist/registration/register.d.ts.map +1 -1
- package/dist/registration/register.fixture.d.ts +4 -4
- package/dist/registration/register.fixture.d.ts.map +1 -1
- package/dist/registration/register.fixture.js +12 -8
- package/dist/registration/register.fixture.js.map +1 -1
- package/dist/registration/register.js +4 -13
- package/dist/registration/register.js.map +1 -1
- package/dist/social/social.d.ts +22 -0
- package/dist/social/social.d.ts.map +1 -0
- package/dist/social/social.js +29 -0
- package/dist/social/social.js.map +1 -0
- package/dist/storage/storage.d.ts +1 -1
- package/dist/storage/storage.d.ts.map +1 -1
- package/dist/storage/storage.fixture.d.ts +1 -2
- package/dist/storage/storage.fixture.d.ts.map +1 -1
- package/dist/storage/storage.fixture.js +1 -16
- package/dist/storage/storage.fixture.js.map +1 -1
- package/dist/storage/storage.js +4 -3
- package/dist/storage/storage.js.map +1 -1
- package/dist/test/fixtures.d.ts +4 -0
- package/dist/test/fixtures.d.ts.map +1 -1
- package/dist/test/fixtures.js +13 -9
- package/dist/test/fixtures.js.map +1 -1
- package/dist/user/user.d.ts +9 -2
- package/dist/user/user.d.ts.map +1 -1
- package/dist/user/user.fixture.d.ts +3 -2
- package/dist/user/user.fixture.d.ts.map +1 -1
- package/dist/user/user.fixture.js +13 -9
- package/dist/user/user.fixture.js.map +1 -1
- package/dist/user/user.js +11 -1
- package/dist/user/user.js.map +1 -1
- package/package.json +14 -13
- package/src/authentication/authenticate.fixture.ts +14 -7
- package/src/authentication/authenticate.test.ts +4 -4
- package/src/authentication/authenticate.ts +15 -12
- package/src/capabilities/capabilities.ts +3 -3
- package/src/connection/connection.fixture.ts +14 -11
- package/src/connection/connection.ts +4 -4
- package/src/effect.ts +28 -26
- package/src/email/email.fixture.ts +15 -13
- package/src/email/email.test.ts +4 -4
- package/src/email/email.ts +13 -12
- package/src/index.ts +91 -34
- package/src/registration/register.fixture.ts +12 -8
- package/src/registration/register.test.ts +8 -43
- package/src/registration/register.ts +8 -24
- package/src/social/social.ts +86 -0
- package/src/storage/storage.fixture.ts +2 -19
- package/src/storage/storage.ts +5 -4
- package/src/test/fixtures.ts +15 -10
- package/src/user/user.fixture.ts +14 -11
- package/src/user/user.ts +22 -3
- package/dist/exit.d.ts +0 -64
- package/dist/exit.d.ts.map +0 -1
- package/dist/exit.js +0 -106
- package/dist/exit.js.map +0 -1
|
@@ -6,11 +6,10 @@ import {
|
|
|
6
6
|
parseRequestOptionsFromJSON,
|
|
7
7
|
} from '@github/webauthn-json/browser-ponyfill'
|
|
8
8
|
import {
|
|
9
|
-
type BadRequest,
|
|
10
9
|
InternalBrowserError,
|
|
11
10
|
type NotSupported,
|
|
12
11
|
} from '@passlock/shared/dist/error/error.js'
|
|
13
|
-
import type { VerificationErrors } from '@passlock/shared/dist/rpc/authentication.js'
|
|
12
|
+
import type { OptionsErrors, VerificationErrors } from '@passlock/shared/dist/rpc/authentication.js'
|
|
14
13
|
import { OptionsReq, VerificationReq } from '@passlock/shared/dist/rpc/authentication.js'
|
|
15
14
|
import { RpcClient } from '@passlock/shared/dist/rpc/rpc.js'
|
|
16
15
|
import type {
|
|
@@ -24,23 +23,27 @@ import { StorageService } from '../storage/storage.js'
|
|
|
24
23
|
|
|
25
24
|
/* Requests */
|
|
26
25
|
|
|
27
|
-
export type AuthenticationRequest = {
|
|
26
|
+
export type AuthenticationRequest = {
|
|
27
|
+
email?: string,
|
|
28
|
+
userVerification?: UserVerification
|
|
29
|
+
}
|
|
28
30
|
|
|
29
31
|
/* Errors */
|
|
30
32
|
|
|
31
|
-
export type AuthenticationErrors = NotSupported |
|
|
33
|
+
export type AuthenticationErrors = NotSupported | OptionsErrors | VerificationErrors
|
|
32
34
|
|
|
33
35
|
/* Dependencies */
|
|
34
36
|
|
|
35
37
|
export type GetCredential = (
|
|
36
|
-
|
|
38
|
+
request: CredentialRequestOptions,
|
|
37
39
|
) => E.Effect<AuthenticationCredential, InternalBrowserError>
|
|
40
|
+
|
|
38
41
|
export const GetCredential = Context.GenericTag<GetCredential>('@services/Get')
|
|
39
42
|
|
|
40
43
|
/* Service */
|
|
41
44
|
|
|
42
45
|
export type AuthenticationService = {
|
|
43
|
-
authenticatePasskey: (
|
|
46
|
+
authenticatePasskey: (request: AuthenticationRequest) => E.Effect<Principal, AuthenticationErrors>
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export const AuthenticationService = Context.GenericTag<AuthenticationService>(
|
|
@@ -49,12 +52,12 @@ export const AuthenticationService = Context.GenericTag<AuthenticationService>(
|
|
|
49
52
|
|
|
50
53
|
/* Utilities */
|
|
51
54
|
|
|
52
|
-
const fetchOptions = (
|
|
55
|
+
const fetchOptions = (request: OptionsReq) => {
|
|
53
56
|
return E.gen(function* (_) {
|
|
54
57
|
yield* _(E.logDebug('Making request'))
|
|
55
58
|
|
|
56
59
|
const rpcClient = yield* _(RpcClient)
|
|
57
|
-
const { publicKey, session } = yield* _(rpcClient.getAuthenticationOptions(
|
|
60
|
+
const { publicKey, session } = yield* _(rpcClient.getAuthenticationOptions(request))
|
|
58
61
|
|
|
59
62
|
yield* _(E.logDebug('Converting Passlock options to CredentialRequestOptions'))
|
|
60
63
|
const options = yield* _(toRequestOptions({ publicKey }))
|
|
@@ -63,9 +66,9 @@ const fetchOptions = (req: OptionsReq) => {
|
|
|
63
66
|
})
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
const toRequestOptions = (
|
|
69
|
+
const toRequestOptions = (request: CredentialRequestOptionsJSON) => {
|
|
67
70
|
return pipe(
|
|
68
|
-
E.try(() => parseRequestOptionsFromJSON(
|
|
71
|
+
E.try(() => parseRequestOptionsFromJSON(request)),
|
|
69
72
|
E.mapError(
|
|
70
73
|
error =>
|
|
71
74
|
new InternalBrowserError({
|
|
@@ -76,12 +79,12 @@ const toRequestOptions = (options: CredentialRequestOptionsJSON) => {
|
|
|
76
79
|
)
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
const verifyCredential = (
|
|
82
|
+
const verifyCredential = (request: VerificationReq) => {
|
|
80
83
|
return E.gen(function* (_) {
|
|
81
84
|
yield* _(E.logDebug('Making request'))
|
|
82
85
|
|
|
83
86
|
const rpcClient = yield* _(RpcClient)
|
|
84
|
-
const { principal } = yield* _(rpcClient.verifyAuthenticationCredential(
|
|
87
|
+
const { principal } = yield* _(rpcClient.verifyAuthenticationCredential(request))
|
|
85
88
|
|
|
86
89
|
return principal
|
|
87
90
|
})
|
|
@@ -19,7 +19,7 @@ export const Capabilities = Context.GenericTag<Capabilities>('@services/Capabili
|
|
|
19
19
|
|
|
20
20
|
const hasWebAuthn = E.suspend(() =>
|
|
21
21
|
typeof window.PublicKeyCredential === 'function'
|
|
22
|
-
? E.
|
|
22
|
+
? E.void
|
|
23
23
|
: new NotSupported({ message: 'WebAuthn API is not supported on this device' }),
|
|
24
24
|
)
|
|
25
25
|
|
|
@@ -29,7 +29,7 @@ const hasPlatformAuth = pipe(
|
|
|
29
29
|
identity,
|
|
30
30
|
() => new NotSupported({ message: 'No platform authenticator available on this device' }),
|
|
31
31
|
),
|
|
32
|
-
E.
|
|
32
|
+
E.asVoid,
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
const hasConditionalUi = pipe(
|
|
@@ -42,7 +42,7 @@ const hasConditionalUi = pipe(
|
|
|
42
42
|
identity,
|
|
43
43
|
() => new NotSupported({ message: 'Conditional mediation not available on this device' }),
|
|
44
44
|
),
|
|
45
|
-
E.
|
|
45
|
+
E.asVoid,
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
export const passkeySupport = pipe(
|
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
import { BadRequest } from '@passlock/shared/dist/error/error.js'
|
|
2
1
|
import { PreConnectReq, PreConnectRes } from '@passlock/shared/dist/rpc/connection.js'
|
|
3
2
|
import { RpcClient } from '@passlock/shared/dist/rpc/rpc.js'
|
|
4
3
|
import { Effect as E, Layer as L } from 'effect'
|
|
4
|
+
import * as Fixtures from '../test/fixtures.js'
|
|
5
|
+
|
|
6
|
+
export const preConnectReq = new PreConnectReq({})
|
|
7
|
+
export const preConnectRes = new PreConnectRes({ warmed: true })
|
|
5
8
|
|
|
6
9
|
export const rpcClientTest = L.succeed(
|
|
7
10
|
RpcClient,
|
|
8
11
|
RpcClient.of({
|
|
9
|
-
preConnect: () => E.succeed(
|
|
10
|
-
isExistingUser: () => E.
|
|
11
|
-
verifyEmail: () => E.
|
|
12
|
-
getRegistrationOptions: () => E.fail(
|
|
13
|
-
verifyRegistrationCredential: () => E.fail(
|
|
14
|
-
getAuthenticationOptions: () => E.fail(
|
|
15
|
-
verifyAuthenticationCredential: () => E.fail(
|
|
12
|
+
preConnect: () => E.succeed(preConnectRes),
|
|
13
|
+
isExistingUser: () => E.fail(Fixtures.notImplemented),
|
|
14
|
+
verifyEmail: () => E.fail(Fixtures.notImplemented),
|
|
15
|
+
getRegistrationOptions: () => E.fail(Fixtures.notImplemented),
|
|
16
|
+
verifyRegistrationCredential: () => E.fail(Fixtures.notImplemented),
|
|
17
|
+
getAuthenticationOptions: () => E.fail(Fixtures.notImplemented),
|
|
18
|
+
verifyAuthenticationCredential: () => E.fail(Fixtures.notImplemented),
|
|
19
|
+
registerOidc: () => E.fail(Fixtures.notImplemented),
|
|
20
|
+
authenticateOidc: () => E.fail(Fixtures.notImplemented),
|
|
21
|
+
resendVerificationEmail: () => E.fail(Fixtures.notImplemented),
|
|
16
22
|
}),
|
|
17
23
|
)
|
|
18
24
|
|
|
19
|
-
export const preConnectReq = new PreConnectReq({})
|
|
20
|
-
export const preConnectRes = new PreConnectRes({ warmed: true })
|
|
21
|
-
|
|
22
25
|
export const rpcConfig = {
|
|
23
26
|
endpoint: 'https://example.com',
|
|
24
27
|
tenancyId: 'tenancyId',
|
|
@@ -20,18 +20,18 @@ const hitPrincipal = pipe(
|
|
|
20
20
|
E.logInfo('Pre-connecting to Principal endpoint'),
|
|
21
21
|
E.zipRight(Dispatcher),
|
|
22
22
|
E.flatMap(dispatcher => dispatcher.get('/token/token?warm=true')),
|
|
23
|
-
E.
|
|
24
|
-
E.catchAll(() => E.
|
|
23
|
+
E.asVoid,
|
|
24
|
+
E.catchAll(() => E.void),
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
const hitRpc = pipe(
|
|
28
28
|
E.logInfo('Pre-connecting to RPC endpoint'),
|
|
29
29
|
E.zipRight(RpcClient),
|
|
30
30
|
E.flatMap(rpcClient => rpcClient.preConnect(new PreConnectReq({}))),
|
|
31
|
-
E.
|
|
31
|
+
E.asVoid,
|
|
32
32
|
)
|
|
33
33
|
|
|
34
|
-
export const preConnect = () => pipe(E.all([hitPrincipal, hitRpc], { concurrency: 2 }), E.
|
|
34
|
+
export const preConnect = () => pipe(E.all([hitPrincipal, hitRpc], { concurrency: 2 }), E.asVoid)
|
|
35
35
|
|
|
36
36
|
/* Live */
|
|
37
37
|
|
package/src/effect.ts
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
import { create, get as getCredential } from '@github/webauthn-json/browser-ponyfill'
|
|
2
2
|
|
|
3
|
-
export type {
|
|
4
|
-
BadRequest,
|
|
5
|
-
Disabled,
|
|
6
|
-
Duplicate,
|
|
7
|
-
Forbidden,
|
|
8
|
-
InternalBrowserError,
|
|
9
|
-
NotFound,
|
|
10
|
-
NotSupported,
|
|
11
|
-
Unauthorized,
|
|
12
|
-
} from '@passlock/shared/dist/error/error.js'
|
|
13
|
-
|
|
14
|
-
export type { Principal } from '@passlock/shared/dist/schema/schema.js'
|
|
15
|
-
|
|
16
3
|
import {
|
|
17
4
|
type BadRequest,
|
|
18
5
|
type Disabled,
|
|
@@ -32,6 +19,7 @@ import {
|
|
|
32
19
|
} from '@passlock/shared/dist/rpc/rpc.js'
|
|
33
20
|
|
|
34
21
|
import type { Principal } from '@passlock/shared/dist/schema/schema.js'
|
|
22
|
+
|
|
35
23
|
import { Context, Effect as E, Layer as L, Layer, Schedule, pipe } from 'effect'
|
|
36
24
|
import type { NoSuchElementException } from 'effect/Cause'
|
|
37
25
|
|
|
@@ -44,7 +32,7 @@ import {
|
|
|
44
32
|
|
|
45
33
|
import { capabilitiesLive } from './capabilities/capabilities.js'
|
|
46
34
|
import { ConnectionService, ConnectionServiceLive } from './connection/connection.js'
|
|
47
|
-
import { EmailService, EmailServiceLive,
|
|
35
|
+
import { EmailService, EmailServiceLive, URLQueryString, type VerifyRequest } from './email/email.js'
|
|
48
36
|
|
|
49
37
|
import {
|
|
50
38
|
CreateCredential,
|
|
@@ -62,10 +50,11 @@ import {
|
|
|
62
50
|
} from './storage/storage.js'
|
|
63
51
|
|
|
64
52
|
import { type Email, UserService, UserServiceLive } from './user/user.js'
|
|
53
|
+
import { SocialService, SocialServiceLive, type OidcRequest } from './social/social.js'
|
|
65
54
|
|
|
66
55
|
/* Layers */
|
|
67
56
|
|
|
68
|
-
const
|
|
57
|
+
const createCredentialLive = L.succeed(
|
|
69
58
|
CreateCredential,
|
|
70
59
|
CreateCredential.of((options: CredentialCreationOptions) =>
|
|
71
60
|
pipe(
|
|
@@ -74,7 +63,7 @@ const createLive = L.succeed(
|
|
|
74
63
|
catch: e => {
|
|
75
64
|
if (e instanceof Error && e.message.includes('excludeCredentials')) {
|
|
76
65
|
return new Duplicate({
|
|
77
|
-
message: 'Passkey already registered
|
|
66
|
+
message: 'Passkey already registered to this device or cloud account',
|
|
78
67
|
})
|
|
79
68
|
} else {
|
|
80
69
|
return new InternalBrowserError({
|
|
@@ -84,12 +73,12 @@ const createLive = L.succeed(
|
|
|
84
73
|
}
|
|
85
74
|
},
|
|
86
75
|
}),
|
|
87
|
-
E.map(credential => credential.toJSON())
|
|
76
|
+
E.map(credential => credential.toJSON())
|
|
88
77
|
),
|
|
89
78
|
),
|
|
90
79
|
)
|
|
91
80
|
|
|
92
|
-
const
|
|
81
|
+
const getCredentialLive = L.succeed(
|
|
93
82
|
GetCredential,
|
|
94
83
|
GetCredential.of((options: CredentialRequestOptions) =>
|
|
95
84
|
pipe(
|
|
@@ -123,7 +112,7 @@ const registrationServiceLive = pipe(
|
|
|
123
112
|
L.provide(rpcClientLive),
|
|
124
113
|
L.provide(userServiceLive),
|
|
125
114
|
L.provide(capabilitiesLive),
|
|
126
|
-
L.provide(
|
|
115
|
+
L.provide(createCredentialLive),
|
|
127
116
|
L.provide(storageServiceLive),
|
|
128
117
|
)
|
|
129
118
|
|
|
@@ -131,7 +120,7 @@ const authenticationServiceLive = pipe(
|
|
|
131
120
|
AuthenticateServiceLive,
|
|
132
121
|
L.provide(rpcClientLive),
|
|
133
122
|
L.provide(capabilitiesLive),
|
|
134
|
-
L.provide(
|
|
123
|
+
L.provide(getCredentialLive),
|
|
135
124
|
L.provide(storageServiceLive),
|
|
136
125
|
)
|
|
137
126
|
|
|
@@ -141,20 +130,25 @@ const connectionServiceLive = pipe(
|
|
|
141
130
|
L.provide(dispatcherLive),
|
|
142
131
|
)
|
|
143
132
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
133
|
+
const urlQueryStringLive = Layer.succeed(
|
|
134
|
+
URLQueryString,
|
|
135
|
+
URLQueryString.of(E.sync(() => globalThis.window.location.search)),
|
|
147
136
|
)
|
|
148
137
|
|
|
149
138
|
const emailServiceLive = pipe(
|
|
150
139
|
EmailServiceLive,
|
|
151
|
-
L.provide(
|
|
140
|
+
L.provide(urlQueryStringLive),
|
|
152
141
|
L.provide(rpcClientLive),
|
|
153
142
|
L.provide(capabilitiesLive),
|
|
154
143
|
L.provide(authenticationServiceLive),
|
|
155
144
|
L.provide(storageServiceLive),
|
|
156
145
|
)
|
|
157
146
|
|
|
147
|
+
const socialServiceLive = pipe(
|
|
148
|
+
SocialServiceLive,
|
|
149
|
+
L.provide(rpcClientLive),
|
|
150
|
+
)
|
|
151
|
+
|
|
158
152
|
export const allRequirements = Layer.mergeAll(
|
|
159
153
|
capabilitiesLive,
|
|
160
154
|
userServiceLive,
|
|
@@ -163,6 +157,7 @@ export const allRequirements = Layer.mergeAll(
|
|
|
163
157
|
connectionServiceLive,
|
|
164
158
|
emailServiceLive,
|
|
165
159
|
storageServiceLive,
|
|
160
|
+
socialServiceLive,
|
|
166
161
|
)
|
|
167
162
|
|
|
168
163
|
export class Config extends Context.Tag('Config')<
|
|
@@ -243,7 +238,7 @@ export type VerifyEmailErrors =
|
|
|
243
238
|
|
|
244
239
|
export const verifyEmailCode = (
|
|
245
240
|
request: VerifyRequest,
|
|
246
|
-
): E.Effect<
|
|
241
|
+
): E.Effect<Principal, VerifyEmailErrors, Config> =>
|
|
247
242
|
pipe(
|
|
248
243
|
EmailService,
|
|
249
244
|
E.flatMap(service => service.verifyEmailCode(request)),
|
|
@@ -251,7 +246,7 @@ export const verifyEmailCode = (
|
|
|
251
246
|
exchangeConfig,
|
|
252
247
|
)
|
|
253
248
|
|
|
254
|
-
export const verifyEmailLink = (): E.Effect<
|
|
249
|
+
export const verifyEmailLink = (): E.Effect<Principal, VerifyEmailErrors, Config> =>
|
|
255
250
|
pipe(
|
|
256
251
|
EmailService,
|
|
257
252
|
E.flatMap(service => service.verifyEmailLink()),
|
|
@@ -276,3 +271,10 @@ export const clearExpiredTokens = (): E.Effect<void> =>
|
|
|
276
271
|
E.provide(storageServiceLive),
|
|
277
272
|
E.provide(storageLive),
|
|
278
273
|
)
|
|
274
|
+
|
|
275
|
+
export const authenticateOIDC = (request: OidcRequest) =>
|
|
276
|
+
pipe(
|
|
277
|
+
SocialService,
|
|
278
|
+
E.flatMap(service => service.registerOidc(request)),
|
|
279
|
+
E.provide(socialServiceLive)
|
|
280
|
+
)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { BadRequest } from '@passlock/shared/dist/error/error.js'
|
|
2
1
|
import { RpcClient } from '@passlock/shared/dist/rpc/rpc.js'
|
|
3
2
|
import { VerifyEmailReq, VerifyEmailRes } from '@passlock/shared/dist/rpc/user.js'
|
|
4
3
|
import { Effect as E, Layer as L } from 'effect'
|
|
5
|
-
import {
|
|
4
|
+
import { URLQueryString } from './email.js'
|
|
6
5
|
import { AuthenticationService } from '../authentication/authenticate.js'
|
|
7
6
|
import * as Fixtures from '../test/fixtures.js'
|
|
8
7
|
|
|
@@ -12,8 +11,8 @@ export const authType = 'passkey'
|
|
|
12
11
|
export const expireAt = Date.now() + 10000
|
|
13
12
|
|
|
14
13
|
export const locationSearchTest = L.succeed(
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
URLQueryString,
|
|
15
|
+
URLQueryString.of(E.succeed(`?code=${code}`)),
|
|
17
16
|
)
|
|
18
17
|
|
|
19
18
|
export const authenticationServiceTest = L.succeed(
|
|
@@ -23,23 +22,26 @@ export const authenticationServiceTest = L.succeed(
|
|
|
23
22
|
}),
|
|
24
23
|
)
|
|
25
24
|
|
|
25
|
+
export const verifyEmailReq = new VerifyEmailReq({ token, code })
|
|
26
|
+
|
|
27
|
+
export const verifyEmailRes = new VerifyEmailRes({ principal: Fixtures.principal })
|
|
28
|
+
|
|
26
29
|
export const rpcClientTest = L.succeed(
|
|
27
30
|
RpcClient,
|
|
28
31
|
RpcClient.of({
|
|
29
32
|
preConnect: () => E.succeed({ warmed: true }),
|
|
30
33
|
isExistingUser: () => E.succeed({ existingUser: true }),
|
|
31
|
-
verifyEmail: () => E.succeed(
|
|
32
|
-
getRegistrationOptions: () => E.fail(
|
|
33
|
-
verifyRegistrationCredential: () => E.fail(
|
|
34
|
-
getAuthenticationOptions: () => E.fail(
|
|
35
|
-
verifyAuthenticationCredential: () => E.fail(
|
|
34
|
+
verifyEmail: () => E.succeed(verifyEmailRes),
|
|
35
|
+
getRegistrationOptions: () => E.fail(Fixtures.notImplemented),
|
|
36
|
+
verifyRegistrationCredential: () => E.fail(Fixtures.notImplemented),
|
|
37
|
+
getAuthenticationOptions: () => E.fail(Fixtures.notImplemented),
|
|
38
|
+
verifyAuthenticationCredential: () => E.fail(Fixtures.notImplemented),
|
|
39
|
+
registerOidc: () => E.fail(Fixtures.notImplemented),
|
|
40
|
+
authenticateOidc: () => E.fail(Fixtures.notImplemented),
|
|
41
|
+
resendVerificationEmail: () => E.fail(Fixtures.notImplemented),
|
|
36
42
|
}),
|
|
37
43
|
)
|
|
38
44
|
|
|
39
|
-
export const verifyEmailReq = new VerifyEmailReq({ token, code })
|
|
40
|
-
|
|
41
|
-
export const verifyEmailRes = new VerifyEmailRes({ verified: true })
|
|
42
|
-
|
|
43
45
|
export const principal = Fixtures.principal
|
|
44
46
|
|
|
45
47
|
export const storedToken = Fixtures.storedToken
|
package/src/email/email.test.ts
CHANGED
|
@@ -9,12 +9,12 @@ import { AuthenticationService } from '../authentication/authenticate.js'
|
|
|
9
9
|
import { StorageService } from '../storage/storage.js'
|
|
10
10
|
|
|
11
11
|
describe('verifyEmailCode should', () => {
|
|
12
|
-
test('return
|
|
12
|
+
test('return a principal when the verification is successful', async () => {
|
|
13
13
|
const assertions = E.gen(function* (_) {
|
|
14
14
|
const service = yield* _(EmailService)
|
|
15
15
|
const result = yield* _(service.verifyEmailCode({ code: '123' }))
|
|
16
16
|
|
|
17
|
-
expect(result).
|
|
17
|
+
expect(result).toEqual(Fixture.principal)
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
const service = pipe(
|
|
@@ -45,7 +45,7 @@ describe('verifyEmailCode should', () => {
|
|
|
45
45
|
const storageServiceMock = mock<StorageService>()
|
|
46
46
|
|
|
47
47
|
storageServiceMock.getToken.mockReturnValue(E.succeed(Fixture.storedToken))
|
|
48
|
-
storageServiceMock.clearToken.mockReturnValue(E.
|
|
48
|
+
storageServiceMock.clearToken.mockReturnValue(E.void)
|
|
49
49
|
|
|
50
50
|
return storageServiceMock
|
|
51
51
|
}),
|
|
@@ -80,7 +80,7 @@ describe('verifyEmailCode should', () => {
|
|
|
80
80
|
const storageServiceMock = mock<StorageService>()
|
|
81
81
|
|
|
82
82
|
storageServiceMock.getToken.mockReturnValue(E.fail(new NoSuchElementException()))
|
|
83
|
-
storageServiceMock.clearToken.mockReturnValue(E.
|
|
83
|
+
storageServiceMock.clearToken.mockReturnValue(E.void)
|
|
84
84
|
|
|
85
85
|
return storageServiceMock
|
|
86
86
|
}),
|
package/src/email/email.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { VerifyEmailReq } from '@passlock/shared/dist/rpc/user.js'
|
|
|
8
8
|
import { Context, Effect as E, Layer, Option as O, flow, identity, pipe } from 'effect'
|
|
9
9
|
import { type AuthenticationErrors, AuthenticationService } from '../authentication/authenticate.js'
|
|
10
10
|
import { StorageService } from '../storage/storage.js'
|
|
11
|
+
import type { Principal } from '@passlock/shared/dist/schema/schema.js'
|
|
11
12
|
|
|
12
13
|
/* Requests */
|
|
13
14
|
|
|
@@ -21,16 +22,16 @@ export type VerifyEmailErrors = RpcErrors | AuthenticationErrors
|
|
|
21
22
|
|
|
22
23
|
/* Dependencies */
|
|
23
24
|
|
|
24
|
-
export class
|
|
25
|
-
|
|
25
|
+
export class URLQueryString extends Context.Tag('URLQueryString')<
|
|
26
|
+
URLQueryString,
|
|
26
27
|
E.Effect<string>
|
|
27
28
|
>() {}
|
|
28
29
|
|
|
29
30
|
/* Service */
|
|
30
31
|
|
|
31
32
|
export type EmailService = {
|
|
32
|
-
verifyEmailCode: (request: VerifyRequest) => E.Effect<
|
|
33
|
-
verifyEmailLink: () => E.Effect<
|
|
33
|
+
verifyEmailCode: (request: VerifyRequest) => E.Effect<Principal, VerifyEmailErrors>
|
|
34
|
+
verifyEmailLink: () => E.Effect<Principal, VerifyEmailErrors>
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export const EmailService = Context.GenericTag<EmailService>('@services/EmailService')
|
|
@@ -78,7 +79,7 @@ const getToken = () => {
|
|
|
78
79
|
*/
|
|
79
80
|
export const extractCodeFromHref = () => {
|
|
80
81
|
return pipe(
|
|
81
|
-
|
|
82
|
+
URLQueryString,
|
|
82
83
|
E.flatMap(identity),
|
|
83
84
|
E.map(search => new URLSearchParams(search)),
|
|
84
85
|
E.flatMap(params => O.fromNullable(params.get('code'))),
|
|
@@ -89,23 +90,23 @@ export const extractCodeFromHref = () => {
|
|
|
89
90
|
|
|
90
91
|
/**
|
|
91
92
|
* Verify the mailbox using the given code
|
|
92
|
-
* @param
|
|
93
|
+
* @param request
|
|
93
94
|
* @returns
|
|
94
95
|
*/
|
|
95
96
|
export const verifyEmail = (
|
|
96
|
-
|
|
97
|
-
): E.Effect<
|
|
97
|
+
request: VerifyRequest,
|
|
98
|
+
): E.Effect<Principal, VerifyEmailErrors, Dependencies> => {
|
|
98
99
|
return E.gen(function* (_) {
|
|
99
100
|
// Re-authenticate the user if required
|
|
100
101
|
const { token } = yield* _(getToken())
|
|
101
102
|
|
|
102
103
|
yield* _(E.logDebug('Making request'))
|
|
103
104
|
const client = yield* _(RpcClient)
|
|
104
|
-
const {
|
|
105
|
-
client.verifyEmail(new VerifyEmailReq({ token, code:
|
|
105
|
+
const { principal } = yield* _(
|
|
106
|
+
client.verifyEmail(new VerifyEmailReq({ token, code: request.code })),
|
|
106
107
|
)
|
|
107
108
|
|
|
108
|
-
return
|
|
109
|
+
return principal
|
|
109
110
|
})
|
|
110
111
|
}
|
|
111
112
|
|
|
@@ -127,7 +128,7 @@ export const EmailServiceLive = Layer.effect(
|
|
|
127
128
|
EmailService,
|
|
128
129
|
E.gen(function* (_) {
|
|
129
130
|
const context = yield* _(
|
|
130
|
-
E.context<RpcClient | AuthenticationService | StorageService |
|
|
131
|
+
E.context<RpcClient | AuthenticationService | StorageService | URLQueryString>(),
|
|
131
132
|
)
|
|
132
133
|
return EmailService.of({
|
|
133
134
|
verifyEmailCode: flow(verifyEmail, E.provide(context)),
|