@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
package/dist/user/user.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Check for an existing user
|
|
3
|
-
*/
|
|
4
|
-
import { Context, Effect as E, Layer } from 'effect';
|
|
5
|
-
import type { BadRequest, Disabled, NotFound } from '@passlock/shared/dist/error/error.js';
|
|
6
|
-
import type { VerifyEmail } from '@passlock/shared/dist/schema/email.js';
|
|
7
|
-
import { UserClient } from '../rpc/user.js';
|
|
8
|
-
export type Email = {
|
|
9
|
-
email: string;
|
|
10
|
-
};
|
|
11
|
-
export type ResendEmail = VerifyEmail & {
|
|
12
|
-
userId: string;
|
|
13
|
-
};
|
|
14
|
-
export type ResendEmailErrors = BadRequest | NotFound | Disabled;
|
|
15
|
-
declare const UserService_base: Context.TagClass<UserService, "@services/UserService", {
|
|
16
|
-
isExistingUser: (request: Email) => E.Effect<boolean, BadRequest>;
|
|
17
|
-
resendVerificationEmail: (request: ResendEmail) => E.Effect<void, ResendEmailErrors>;
|
|
18
|
-
}>;
|
|
19
|
-
export declare class UserService extends UserService_base {
|
|
20
|
-
}
|
|
21
|
-
type Dependencies = UserClient;
|
|
22
|
-
export declare const isExistingUser: (request: Email) => E.Effect<boolean, BadRequest, Dependencies>;
|
|
23
|
-
export declare const resendVerificationEmail: (request: ResendEmail) => E.Effect<void, ResendEmailErrors, Dependencies>;
|
|
24
|
-
export declare const UserServiceLive: Layer.Layer<UserService, never, UserClient>;
|
|
25
|
-
export {};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Layer as L } from 'effect';
|
|
2
|
-
import { IsExistingUserReq, IsExistingUserRes, ResendEmailReq, ResendEmailRes, VerifyEmailRes } from '@passlock/shared/dist/rpc/user.js';
|
|
3
|
-
import { UserClient } from '../rpc/user.js';
|
|
4
|
-
import type { ResendEmail } from './user.js';
|
|
5
|
-
export declare const email = "jdoe@gmail.com";
|
|
6
|
-
export declare const isRegisteredReq: IsExistingUserReq;
|
|
7
|
-
export declare const isRegisteredRes: IsExistingUserRes;
|
|
8
|
-
export declare const verifyEmailRes: VerifyEmailRes;
|
|
9
|
-
export declare const resendEmailReq: ResendEmail;
|
|
10
|
-
export declare const rpcResendEmailReq: ResendEmailReq;
|
|
11
|
-
export declare const rpcResendEmailRes: ResendEmailRes;
|
|
12
|
-
export declare const rpcClientTest: L.Layer<UserClient, never, never>;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Effect as E, Layer as L, Option as O } from 'effect';
|
|
2
|
-
import { IsExistingUserReq, IsExistingUserRes, ResendEmailReq, ResendEmailRes, VerifyEmailRes, } from '@passlock/shared/dist/rpc/user.js';
|
|
3
|
-
import * as Fixtures from '../test/fixtures.js';
|
|
4
|
-
import { UserClient } from '../rpc/user.js';
|
|
5
|
-
export const email = 'jdoe@gmail.com';
|
|
6
|
-
export const isRegisteredReq = new IsExistingUserReq({ email });
|
|
7
|
-
export const isRegisteredRes = new IsExistingUserRes({ existingUser: false, detail: O.none() });
|
|
8
|
-
export const verifyEmailRes = new VerifyEmailRes({ principal: Fixtures.principal });
|
|
9
|
-
export const resendEmailReq = { userId: '123', method: 'code' };
|
|
10
|
-
export const rpcResendEmailReq = new ResendEmailReq({
|
|
11
|
-
userId: '123',
|
|
12
|
-
verifyEmail: { method: 'code' },
|
|
13
|
-
});
|
|
14
|
-
export const rpcResendEmailRes = new ResendEmailRes({});
|
|
15
|
-
export const rpcClientTest = L.succeed(UserClient, UserClient.of({
|
|
16
|
-
isExistingUser: () => E.succeed({ existingUser: true, detail: O.none() }),
|
|
17
|
-
verifyEmail: () => E.succeed(verifyEmailRes),
|
|
18
|
-
resendVerificationEmail: () => E.fail(Fixtures.notImplemented),
|
|
19
|
-
}));
|
|
20
|
-
//# sourceMappingURL=user.fixture.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"user.fixture.js","sourceRoot":"","sources":["../../src/user/user.fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAE7D,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,mCAAmC,CAAA;AAE1C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3C,MAAM,CAAC,MAAM,KAAK,GAAG,gBAAgB,CAAA;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,iBAAiB,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,iBAAiB,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AAC/F,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAA;AACnF,MAAM,CAAC,MAAM,cAAc,GAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;AAC5E,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,cAAc,CAAC;IAClD,MAAM,EAAE,KAAK;IACb,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;CAChC,CAAC,CAAA;AACF,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,cAAc,CAAC,EAAE,CAAC,CAAA;AAEvD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,OAAO,CACpC,UAAU,EACV,UAAU,CAAC,EAAE,CAAC;IACZ,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;IACzE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC;IAC5C,uBAAuB,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;CAC/D,CAAC,CACH,CAAA"}
|
package/dist/user/user.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Check for an existing user
|
|
3
|
-
*/
|
|
4
|
-
import { Context, Effect as E, Layer, flow } from 'effect';
|
|
5
|
-
import { IsExistingUserReq, ResendEmailReq } from '@passlock/shared/dist/rpc/user.js';
|
|
6
|
-
import { UserClient } from '../rpc/user.js';
|
|
7
|
-
/* Service */
|
|
8
|
-
export class UserService extends Context.Tag('@services/UserService')() {
|
|
9
|
-
}
|
|
10
|
-
export const isExistingUser = (request) => {
|
|
11
|
-
return E.gen(function* (_) {
|
|
12
|
-
yield* _(E.logInfo('Checking registration status'));
|
|
13
|
-
const rpcClient = yield* _(UserClient);
|
|
14
|
-
yield* _(E.logDebug('Making RPC request'));
|
|
15
|
-
const { existingUser } = yield* _(rpcClient.isExistingUser(new IsExistingUserReq(request)));
|
|
16
|
-
return existingUser;
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
export const resendVerificationEmail = (request) => {
|
|
20
|
-
return E.gen(function* (_) {
|
|
21
|
-
yield* _(E.logInfo('Resending verification email'));
|
|
22
|
-
const rpcClient = yield* _(UserClient);
|
|
23
|
-
yield* _(E.logDebug('Making RPC request'));
|
|
24
|
-
const { userId, ...verifyEmail } = request;
|
|
25
|
-
yield* _(rpcClient.resendVerificationEmail(new ResendEmailReq({ userId, verifyEmail })));
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
/* Live */
|
|
29
|
-
/* v8 ignore start */
|
|
30
|
-
export const UserServiceLive = Layer.effect(UserService, E.gen(function* (_) {
|
|
31
|
-
const context = yield* _(E.context());
|
|
32
|
-
return UserService.of({
|
|
33
|
-
isExistingUser: flow(isExistingUser, E.provide(context)),
|
|
34
|
-
resendVerificationEmail: flow(resendVerificationEmail, E.provide(context)),
|
|
35
|
-
});
|
|
36
|
-
}));
|
|
37
|
-
/* v8 ignore stop */
|
|
38
|
-
//# sourceMappingURL=user.js.map
|
package/dist/user/user.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/user/user.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAG1D,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAGrF,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAW3C,aAAa;AAEb,MAAM,OAAO,WAAY,SAAQ,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAMlE;CAAG;AAMN,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAc,EAA+C,EAAE;IAC5F,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC,CAAA;QACnD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;QAEtC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAA;QAC1C,MAAM,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAE3F,OAAO,YAAY,CAAA;IACrB,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,OAAoB,EAC6B,EAAE;IACnD,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC,CAAA;QACnD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;QAEtC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAA;QAC1C,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,CAAA;QAC1C,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAA;IAC1F,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,UAAU;AAEV,qBAAqB;AACrB,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CACzC,WAAW,EACX,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;IAChB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAc,CAAC,CAAA;IACjD,OAAO,WAAW,CAAC,EAAE,CAAC;QACpB,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxD,uBAAuB,EAAE,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;KAC3E,CAAC,CAAA;AACJ,CAAC,CAAC,CACH,CAAA;AACD,oBAAoB"}
|
package/dist/version.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const PASSLOCK_CLIENT_VERSION = "0.9.30";
|
package/dist/version.js
DELETED
package/dist/version.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,YAAY,CAAA"}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Effect as E, Layer as L, Option as O } from 'effect'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
OptionsRes,
|
|
5
|
-
VerificationReq,
|
|
6
|
-
VerificationRes,
|
|
7
|
-
} from '@passlock/shared/dist/rpc/authentication.js'
|
|
8
|
-
import { IsExistingUserRes, VerifyEmailRes } from '@passlock/shared/dist/rpc/user.js'
|
|
9
|
-
import type { AuthenticationCredential } from '@passlock/shared/dist/schema/passkey.js'
|
|
10
|
-
|
|
11
|
-
import * as Fixtures from '../test/fixtures.js'
|
|
12
|
-
import { AuthenticationClient } from '../rpc/authentication.js'
|
|
13
|
-
import { type AuthenticationRequest, GetCredential } from './authenticate.js'
|
|
14
|
-
|
|
15
|
-
export const session = 'session'
|
|
16
|
-
export const token = 'token'
|
|
17
|
-
export const code = 'code'
|
|
18
|
-
export const authType = 'passkey'
|
|
19
|
-
export const expireAt = Date.now() + 10000
|
|
20
|
-
|
|
21
|
-
export const request: AuthenticationRequest = {
|
|
22
|
-
userVerification: O.some('preferred'),
|
|
23
|
-
email: O.none(),
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const rpcOptionsRes = new OptionsRes({
|
|
27
|
-
session,
|
|
28
|
-
publicKey: {
|
|
29
|
-
rpId: 'passlock.dev',
|
|
30
|
-
challenge: 'FKZSl_saKu5OXjLLwoq8eK3wlD8XgpGiS10SszW5RiE',
|
|
31
|
-
timeout: 60000,
|
|
32
|
-
userVerification: 'preferred',
|
|
33
|
-
},
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
export const credential: AuthenticationCredential = {
|
|
37
|
-
id: '1',
|
|
38
|
-
type: 'public-key',
|
|
39
|
-
rawId: 'id',
|
|
40
|
-
response: {
|
|
41
|
-
clientDataJSON: '',
|
|
42
|
-
authenticatorData: '',
|
|
43
|
-
signature: '',
|
|
44
|
-
userHandle: null,
|
|
45
|
-
},
|
|
46
|
-
clientExtensionResults: {},
|
|
47
|
-
authenticatorAttachment: null,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const rpcVerificationReq = new VerificationReq({ session, credential })
|
|
51
|
-
|
|
52
|
-
export const rpcVerificationRes = new VerificationRes({ principal: Fixtures.principal })
|
|
53
|
-
|
|
54
|
-
export const rpcIsExistingUserRes = new IsExistingUserRes({ existingUser: true, detail: O.none() })
|
|
55
|
-
|
|
56
|
-
export const rpcVerifyEmailRes = new VerifyEmailRes({ principal: Fixtures.principal })
|
|
57
|
-
|
|
58
|
-
export const getCredentialTest = L.succeed(
|
|
59
|
-
GetCredential,
|
|
60
|
-
GetCredential.of({ getCredential: () => E.succeed(credential) }),
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
export const rpcClientTest = L.succeed(
|
|
64
|
-
AuthenticationClient,
|
|
65
|
-
AuthenticationClient.of({
|
|
66
|
-
getAuthenticationOptions: () => E.succeed(rpcOptionsRes),
|
|
67
|
-
verifyAuthenticationCredential: () => E.succeed(rpcVerificationRes),
|
|
68
|
-
}),
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
export const principal = Fixtures.principal
|
|
72
|
-
export const capabilitiesTest = Fixtures.capabilitiesTest
|
|
73
|
-
export const storageServiceTest = Fixtures.storageServiceTest
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { Effect as E, Layer as L, Layer, LogLevel, Logger, Option as O, pipe } from 'effect'
|
|
2
|
-
import { describe, expect, test, vi } from 'vitest'
|
|
3
|
-
import { mock } from 'vitest-mock-extended'
|
|
4
|
-
|
|
5
|
-
import * as Fixture from './authenticate.fixture.js'
|
|
6
|
-
import { AuthenticationClient } from '../rpc/authentication.js'
|
|
7
|
-
import { StorageService } from '../storage/storage.js'
|
|
8
|
-
import { AuthenticateServiceLive, AuthenticationService, GetCredential } from './authenticate.js'
|
|
9
|
-
|
|
10
|
-
describe('authenticate should', () => {
|
|
11
|
-
test('return a valid principal', async () => {
|
|
12
|
-
const assertions = E.gen(function* (_) {
|
|
13
|
-
const service = yield* _(AuthenticationService)
|
|
14
|
-
|
|
15
|
-
const result = yield* _(
|
|
16
|
-
service.authenticatePasskey({
|
|
17
|
-
email: O.none(),
|
|
18
|
-
userVerification: O.some('preferred'),
|
|
19
|
-
}),
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
expect(result).toEqual(Fixture.principal)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
const service = pipe(
|
|
26
|
-
AuthenticateServiceLive,
|
|
27
|
-
L.provide(Fixture.getCredentialTest),
|
|
28
|
-
L.provide(Fixture.capabilitiesTest),
|
|
29
|
-
L.provide(Fixture.storageServiceTest),
|
|
30
|
-
L.provide(Fixture.rpcClientTest),
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
const effect = pipe(E.provide(assertions, service), Logger.withMinimumLogLevel(LogLevel.None))
|
|
34
|
-
|
|
35
|
-
return E.runPromise(effect)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
test('pass the authentication request to the backend', async () => {
|
|
39
|
-
const assertions = E.gen(function* (_) {
|
|
40
|
-
const service = yield* _(AuthenticationService)
|
|
41
|
-
|
|
42
|
-
yield* _(
|
|
43
|
-
service.authenticatePasskey({
|
|
44
|
-
email: O.none(),
|
|
45
|
-
userVerification: O.some('preferred'),
|
|
46
|
-
}),
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
const rpcClient = yield* _(AuthenticationClient)
|
|
50
|
-
expect(rpcClient.getAuthenticationOptions).toHaveBeenCalledOnce()
|
|
51
|
-
expect(rpcClient.verifyAuthenticationCredential).toHaveBeenCalledOnce()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const rpcClientTest = L.effect(
|
|
55
|
-
AuthenticationClient,
|
|
56
|
-
E.sync(() => {
|
|
57
|
-
const rpcMock = mock<AuthenticationClient['Type']>()
|
|
58
|
-
|
|
59
|
-
rpcMock.getAuthenticationOptions.mockReturnValue(E.succeed(Fixture.rpcOptionsRes))
|
|
60
|
-
rpcMock.verifyAuthenticationCredential.mockReturnValue(
|
|
61
|
-
E.succeed(Fixture.rpcVerificationRes),
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
return rpcMock
|
|
65
|
-
}),
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
const service = pipe(
|
|
69
|
-
AuthenticateServiceLive,
|
|
70
|
-
L.provide(Fixture.getCredentialTest),
|
|
71
|
-
L.provide(Fixture.capabilitiesTest),
|
|
72
|
-
L.provide(Fixture.storageServiceTest),
|
|
73
|
-
L.provide(rpcClientTest),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
const layers = Layer.merge(service, rpcClientTest)
|
|
77
|
-
const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
|
|
78
|
-
|
|
79
|
-
return E.runPromise(effect)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('send the credential to the backend', async () => {
|
|
83
|
-
const assertions = E.gen(function* (_) {
|
|
84
|
-
const service = yield* _(AuthenticationService)
|
|
85
|
-
|
|
86
|
-
yield* _(
|
|
87
|
-
service.authenticatePasskey({
|
|
88
|
-
email: O.none(),
|
|
89
|
-
userVerification: O.some('preferred'),
|
|
90
|
-
}),
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
const rpcClient = yield* _(AuthenticationClient)
|
|
94
|
-
expect(rpcClient.getAuthenticationOptions).toHaveBeenCalledOnce()
|
|
95
|
-
expect(rpcClient.verifyAuthenticationCredential).toHaveBeenCalledWith(
|
|
96
|
-
Fixture.rpcVerificationReq,
|
|
97
|
-
)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
const rpcClientTest = L.effect(
|
|
101
|
-
AuthenticationClient,
|
|
102
|
-
E.sync(() => {
|
|
103
|
-
const rpcMock = mock<AuthenticationClient['Type']>()
|
|
104
|
-
|
|
105
|
-
rpcMock.getAuthenticationOptions.mockReturnValue(E.succeed(Fixture.rpcOptionsRes))
|
|
106
|
-
rpcMock.verifyAuthenticationCredential.mockReturnValue(
|
|
107
|
-
E.succeed(Fixture.rpcVerificationRes),
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
return rpcMock
|
|
111
|
-
}),
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
const service = pipe(
|
|
115
|
-
AuthenticateServiceLive,
|
|
116
|
-
L.provide(Fixture.getCredentialTest),
|
|
117
|
-
L.provide(Fixture.capabilitiesTest),
|
|
118
|
-
L.provide(Fixture.storageServiceTest),
|
|
119
|
-
L.provide(rpcClientTest),
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
const layers = Layer.merge(service, rpcClientTest)
|
|
123
|
-
const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
|
|
124
|
-
|
|
125
|
-
return E.runPromise(effect)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
test('store the credential in local storage', async () => {
|
|
129
|
-
const assertions = E.gen(function* (_) {
|
|
130
|
-
const service = yield* _(AuthenticationService)
|
|
131
|
-
|
|
132
|
-
yield* _(
|
|
133
|
-
service.authenticatePasskey({
|
|
134
|
-
email: O.none(),
|
|
135
|
-
userVerification: O.some('preferred'),
|
|
136
|
-
}),
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
const storageService = yield* _(StorageService)
|
|
140
|
-
expect(storageService.storeToken).toHaveBeenCalledWith(Fixture.principal)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
const storageServiceTest = L.effect(
|
|
144
|
-
StorageService,
|
|
145
|
-
E.sync(() => {
|
|
146
|
-
const storageMock = mock<StorageService['Type']>()
|
|
147
|
-
|
|
148
|
-
storageMock.storeToken.mockReturnValue(E.void)
|
|
149
|
-
storageMock.clearExpiredToken.mockReturnValue(E.void)
|
|
150
|
-
|
|
151
|
-
return storageMock
|
|
152
|
-
}),
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
const service = pipe(
|
|
156
|
-
AuthenticateServiceLive,
|
|
157
|
-
L.provide(Fixture.getCredentialTest),
|
|
158
|
-
L.provide(Fixture.capabilitiesTest),
|
|
159
|
-
L.provide(Fixture.rpcClientTest),
|
|
160
|
-
L.provide(storageServiceTest),
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
const layers = Layer.merge(service, storageServiceTest)
|
|
164
|
-
const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
|
|
165
|
-
|
|
166
|
-
return E.runPromise(effect)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
test('schedule deletion of the local token', async () => {
|
|
170
|
-
const assertions = E.gen(function* (_) {
|
|
171
|
-
const service = yield* _(AuthenticationService)
|
|
172
|
-
|
|
173
|
-
yield* _(
|
|
174
|
-
service.authenticatePasskey({
|
|
175
|
-
email: O.none(),
|
|
176
|
-
userVerification: O.some('preferred'),
|
|
177
|
-
}),
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
const storageService = yield* _(StorageService)
|
|
181
|
-
expect(storageService.clearExpiredToken).toHaveBeenCalledWith('passkey')
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
const storageServiceTest = L.effect(
|
|
185
|
-
StorageService,
|
|
186
|
-
E.sync(() => {
|
|
187
|
-
const storageMock = mock<StorageService['Type']>()
|
|
188
|
-
|
|
189
|
-
storageMock.storeToken.mockReturnValue(E.void)
|
|
190
|
-
storageMock.clearExpiredToken.mockReturnValue(E.void)
|
|
191
|
-
|
|
192
|
-
return storageMock
|
|
193
|
-
}),
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
const service = pipe(
|
|
197
|
-
AuthenticateServiceLive,
|
|
198
|
-
L.provide(Fixture.getCredentialTest),
|
|
199
|
-
L.provide(Fixture.capabilitiesTest),
|
|
200
|
-
L.provide(Fixture.rpcClientTest),
|
|
201
|
-
L.provide(storageServiceTest),
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
const layers = Layer.merge(service, storageServiceTest)
|
|
205
|
-
const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
|
|
206
|
-
|
|
207
|
-
return E.runPromise(effect)
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
test("return an error if the browser can't create a credential", async () => {
|
|
211
|
-
const assertions = E.gen(function* (_) {
|
|
212
|
-
const service = yield* _(AuthenticationService)
|
|
213
|
-
|
|
214
|
-
yield* _(
|
|
215
|
-
service.authenticatePasskey({
|
|
216
|
-
email: O.none(),
|
|
217
|
-
userVerification: O.some('preferred'),
|
|
218
|
-
}),
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
const { getCredential } = yield* _(GetCredential)
|
|
222
|
-
expect(getCredential).toHaveBeenCalledOnce()
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
const getCredentialTest = L.effect(
|
|
226
|
-
GetCredential,
|
|
227
|
-
E.sync(() => {
|
|
228
|
-
const getCredential = vi.fn()
|
|
229
|
-
|
|
230
|
-
getCredential.mockReturnValue(E.succeed(Fixture.credential))
|
|
231
|
-
|
|
232
|
-
return { getCredential }
|
|
233
|
-
}),
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
const service = pipe(
|
|
237
|
-
AuthenticateServiceLive,
|
|
238
|
-
L.provide(Fixture.storageServiceTest),
|
|
239
|
-
L.provide(Fixture.capabilitiesTest),
|
|
240
|
-
L.provide(Fixture.rpcClientTest),
|
|
241
|
-
L.provide(getCredentialTest),
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
const layers = Layer.merge(service, getCredentialTest)
|
|
245
|
-
const effect = pipe(E.provide(assertions, layers), Logger.withMinimumLogLevel(LogLevel.None))
|
|
246
|
-
|
|
247
|
-
return E.runPromise(effect)
|
|
248
|
-
})
|
|
249
|
-
})
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Passkey authentication effects
|
|
3
|
-
*/
|
|
4
|
-
import {
|
|
5
|
-
type CredentialRequestOptionsJSON,
|
|
6
|
-
parseRequestOptionsFromJSON,
|
|
7
|
-
} from '@github/webauthn-json/browser-ponyfill'
|
|
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
|
-
import { Capabilities } from '../capabilities/capabilities.js'
|
|
21
|
-
import { AuthenticationClient } from '../rpc/authentication.js'
|
|
22
|
-
import { StorageService } from '../storage/storage.js'
|
|
23
|
-
|
|
24
|
-
/* Requests */
|
|
25
|
-
|
|
26
|
-
export type AuthenticationRequest = OptionsReq
|
|
27
|
-
/* Errors */
|
|
28
|
-
|
|
29
|
-
export type AuthenticationErrors = NotSupported | OptionsErrors | VerificationErrors
|
|
30
|
-
|
|
31
|
-
/* Dependencies */
|
|
32
|
-
|
|
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
|
-
|
|
42
|
-
/* Service */
|
|
43
|
-
|
|
44
|
-
export class AuthenticationService extends Context.Tag('@services/AuthenticationService')<
|
|
45
|
-
AuthenticationService,
|
|
46
|
-
{
|
|
47
|
-
authenticatePasskey: (
|
|
48
|
-
request: AuthenticationRequest,
|
|
49
|
-
) => E.Effect<Principal, AuthenticationErrors>
|
|
50
|
-
}
|
|
51
|
-
>() {}
|
|
52
|
-
|
|
53
|
-
/* Utilities */
|
|
54
|
-
|
|
55
|
-
const fetchOptions = (request: OptionsReq) => {
|
|
56
|
-
return E.gen(function* (_) {
|
|
57
|
-
yield* _(E.logDebug('Making request'))
|
|
58
|
-
|
|
59
|
-
const rpcClient = yield* _(AuthenticationClient)
|
|
60
|
-
const { publicKey, session } = yield* _(rpcClient.getAuthenticationOptions(request))
|
|
61
|
-
|
|
62
|
-
yield* _(E.logDebug('Converting Passlock options to CredentialRequestOptions'))
|
|
63
|
-
const options = yield* _(toRequestOptions({ publicKey }))
|
|
64
|
-
|
|
65
|
-
return { options, session }
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const toRequestOptions = (request: CredentialRequestOptionsJSON) => {
|
|
70
|
-
return pipe(
|
|
71
|
-
E.try(() => parseRequestOptionsFromJSON(request)),
|
|
72
|
-
E.mapError(
|
|
73
|
-
error =>
|
|
74
|
-
new InternalBrowserError({
|
|
75
|
-
message: 'Browser was unable to create credential request options',
|
|
76
|
-
detail: String(error.error),
|
|
77
|
-
}),
|
|
78
|
-
),
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const verifyCredential = (request: VerificationReq) => {
|
|
83
|
-
return E.gen(function* (_) {
|
|
84
|
-
yield* _(E.logDebug('Making request'))
|
|
85
|
-
|
|
86
|
-
const rpcClient = yield* _(AuthenticationClient)
|
|
87
|
-
const { principal } = yield* _(rpcClient.verifyAuthenticationCredential(request))
|
|
88
|
-
|
|
89
|
-
return principal
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/* Effects */
|
|
94
|
-
|
|
95
|
-
type Dependencies = GetCredential | Capabilities | StorageService | AuthenticationClient
|
|
96
|
-
|
|
97
|
-
export const authenticatePasskey = (
|
|
98
|
-
request: AuthenticationRequest,
|
|
99
|
-
): E.Effect<Principal, AuthenticationErrors, 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 authentication options from Passlock'))
|
|
106
|
-
|
|
107
|
-
const { options, session } = yield* _(fetchOptions(request))
|
|
108
|
-
|
|
109
|
-
yield* _(E.logInfo('Looking up credential'))
|
|
110
|
-
const { getCredential } = yield* _(GetCredential)
|
|
111
|
-
const credential = yield* _(getCredential(options))
|
|
112
|
-
|
|
113
|
-
yield* _(E.logInfo('Verifying credential with Passlock'))
|
|
114
|
-
const principal = yield* _(verifyCredential(new VerificationReq({ credential, session })))
|
|
115
|
-
|
|
116
|
-
const storageService = yield* _(StorageService)
|
|
117
|
-
yield* _(storageService.storeToken(principal))
|
|
118
|
-
yield* _(E.logDebug('Stored token in local storage'))
|
|
119
|
-
|
|
120
|
-
yield* _(E.logDebug('Defering local token deletion'))
|
|
121
|
-
const delayedClearTokenE = pipe(
|
|
122
|
-
storageService.clearExpiredToken('passkey'),
|
|
123
|
-
E.delay('6 minutes'),
|
|
124
|
-
E.fork,
|
|
125
|
-
)
|
|
126
|
-
yield* _(delayedClearTokenE)
|
|
127
|
-
|
|
128
|
-
return principal
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
return E.catchTag(effect, 'InternalBrowserError', e => E.die(e))
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/* Live */
|
|
135
|
-
|
|
136
|
-
/* v8 ignore start */
|
|
137
|
-
export const AuthenticateServiceLive = Layer.effect(
|
|
138
|
-
AuthenticationService,
|
|
139
|
-
E.gen(function* (_) {
|
|
140
|
-
const context = yield* _(
|
|
141
|
-
E.context<GetCredential | AuthenticationClient | Capabilities | StorageService>(),
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
return AuthenticationService.of({
|
|
145
|
-
authenticatePasskey: flow(authenticatePasskey, E.provide(context)),
|
|
146
|
-
})
|
|
147
|
-
}),
|
|
148
|
-
)
|
|
149
|
-
/* v8 ignore stop */
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test if the browser supports passkeys, conditional UI etc
|
|
3
|
-
*/
|
|
4
|
-
import { Context, Effect as E, Layer, identity, pipe } from 'effect'
|
|
5
|
-
|
|
6
|
-
import { NotSupported } from '@passlock/shared/dist/error/error.js'
|
|
7
|
-
|
|
8
|
-
/* Service */
|
|
9
|
-
|
|
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
|
-
>() {}
|
|
19
|
-
|
|
20
|
-
/* Effects */
|
|
21
|
-
|
|
22
|
-
const hasWebAuthn = E.suspend(() =>
|
|
23
|
-
typeof window.PublicKeyCredential === 'function'
|
|
24
|
-
? E.void
|
|
25
|
-
: new NotSupported({ message: 'WebAuthn API is not supported on this device' }),
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
const hasPlatformAuth = pipe(
|
|
29
|
-
E.tryPromise(() => window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()),
|
|
30
|
-
E.filterOrFail(
|
|
31
|
-
identity,
|
|
32
|
-
() => new NotSupported({ message: 'No platform authenticator available on this device' }),
|
|
33
|
-
),
|
|
34
|
-
E.asVoid,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const hasConditionalUi = pipe(
|
|
38
|
-
E.tryPromise({
|
|
39
|
-
try: () => window.PublicKeyCredential.isConditionalMediationAvailable(),
|
|
40
|
-
catch: () =>
|
|
41
|
-
new NotSupported({ message: 'Conditional mediation not available on this device' }),
|
|
42
|
-
}),
|
|
43
|
-
E.filterOrFail(
|
|
44
|
-
identity,
|
|
45
|
-
() => new NotSupported({ message: 'Conditional mediation not available on this device' }),
|
|
46
|
-
),
|
|
47
|
-
E.asVoid,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
export const passkeySupport = pipe(
|
|
51
|
-
hasWebAuthn,
|
|
52
|
-
E.andThen(hasPlatformAuth),
|
|
53
|
-
E.catchTag('UnknownException', e => E.die(e)),
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
export const isPasskeySupport = pipe(
|
|
57
|
-
passkeySupport,
|
|
58
|
-
E.match({
|
|
59
|
-
onFailure: () => false,
|
|
60
|
-
onSuccess: () => true,
|
|
61
|
-
}),
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
export const autofillSupport = pipe(passkeySupport, E.andThen(hasConditionalUi))
|
|
65
|
-
|
|
66
|
-
export const isAutofillSupport = pipe(
|
|
67
|
-
autofillSupport,
|
|
68
|
-
E.match({
|
|
69
|
-
onFailure: () => false,
|
|
70
|
-
onSuccess: () => true,
|
|
71
|
-
}),
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
/* Live */
|
|
75
|
-
|
|
76
|
-
/* v8 ignore start */
|
|
77
|
-
export const capabilitiesLive = Layer.succeed(Capabilities, {
|
|
78
|
-
passkeySupport,
|
|
79
|
-
isPasskeySupport,
|
|
80
|
-
autofillSupport,
|
|
81
|
-
isAutofillSupport,
|
|
82
|
-
})
|
|
83
|
-
/* v8 ignore stop */
|