@yielded/auth-simplewebauthn 0.1.0-beta.24 → 0.1.0-beta.26
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/dist/internal/server/configuration.mjs +31 -23
- package/dist/internal/server/configuration.mjs.map +1 -1
- package/dist/internal/server/models.d.mts +0 -1
- package/dist/internal/server/protocol.d.mts +0 -2
- package/dist/internal/server/protocol.mjs +3 -3
- package/dist/internal/server/protocol.mjs.map +1 -1
- package/package.json +1 -1
- package/src/internal/server/configuration.ts +68 -57
- package/src/internal/server/protocol.ts +10 -4
|
@@ -4,40 +4,48 @@ import { Cause, Effect } from "effect";
|
|
|
4
4
|
import { reportAuthFailure } from "@yielded/auth/Persistence";
|
|
5
5
|
import { parse } from "tldts";
|
|
6
6
|
//#region src/internal/server/configuration.ts
|
|
7
|
+
/** Validate an immutable profile selected at issue. Verification intentionally
|
|
8
|
+
* accepts that persisted selection through its ceremony lifetime during deploys. */
|
|
9
|
+
const validateSimpleWebAuthnProfile = Effect.fn("validateSimpleWebAuthnProfile")(function* (input) {
|
|
10
|
+
const profile = yield* snapshotPasskey(PasskeyProfile, input).pipe(Effect.mapError(() => PasskeyConfigurationError.make({})));
|
|
11
|
+
if (new Set(profile.origins).size !== profile.origins.length || new Set(profile.algorithms).size !== profile.algorithms.length || profile.primarySignIn && (profile.residentKey !== "required" || profile.userVerification !== "required")) return yield* PasskeyConfigurationError.make({});
|
|
12
|
+
if (!(yield* Effect.try({
|
|
13
|
+
try: () => {
|
|
14
|
+
const host = new URL(`https://${profile.rpId}`).hostname;
|
|
15
|
+
if (host !== profile.rpId) return false;
|
|
16
|
+
const domain = parse(host, {
|
|
17
|
+
allowIcannDomains: true,
|
|
18
|
+
allowPrivateDomains: true,
|
|
19
|
+
extractHostname: false,
|
|
20
|
+
validateHostname: true,
|
|
21
|
+
detectIp: true
|
|
22
|
+
});
|
|
23
|
+
if (host === "localhost" ? !profile.developmentLocalhost : domain.isIp || domain.domain === null || domain.publicSuffix === host) return false;
|
|
24
|
+
return profile.origins.every((origin) => {
|
|
25
|
+
const url = new URL(origin);
|
|
26
|
+
return url.origin === origin && url.username === "" && url.password === "" && (url.protocol === "https:" || profile.developmentLocalhost && profile.rpId === "localhost" && url.hostname === "localhost" && url.protocol === "http:") && (url.hostname === profile.rpId || profile.rpId !== "localhost" && url.hostname.endsWith(`.${profile.rpId}`));
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
catch: () => PasskeyConfigurationError.make({})
|
|
30
|
+
}))) return yield* PasskeyConfigurationError.make({});
|
|
31
|
+
return profile;
|
|
32
|
+
});
|
|
7
33
|
const captureSimpleWebAuthnProfiles = Effect.fn("captureSimpleWebAuthnProfiles")(function* (options) {
|
|
8
34
|
const captured = yield* snapshotPasskey(SimpleWebAuthnPasskeyProtocolOptions, options).pipe(Effect.mapError(() => PasskeyConfigurationError.make({})));
|
|
9
35
|
const keys = /* @__PURE__ */ new Set();
|
|
10
36
|
for (const profile of captured.profiles) {
|
|
11
|
-
const key = `${profile.rpId}/${profile.profileId}
|
|
12
|
-
if (keys.has(key)
|
|
37
|
+
const key = `${profile.rpId}/${profile.profileId}`;
|
|
38
|
+
if (keys.has(key)) return yield* PasskeyConfigurationError.make({});
|
|
13
39
|
keys.add(key);
|
|
14
|
-
|
|
15
|
-
try: () => {
|
|
16
|
-
const host = new URL(`https://${profile.rpId}`).hostname;
|
|
17
|
-
if (host !== profile.rpId) return false;
|
|
18
|
-
const domain = parse(host, {
|
|
19
|
-
allowIcannDomains: true,
|
|
20
|
-
allowPrivateDomains: true,
|
|
21
|
-
extractHostname: false,
|
|
22
|
-
validateHostname: true,
|
|
23
|
-
detectIp: true
|
|
24
|
-
});
|
|
25
|
-
if (host === "localhost" ? !profile.developmentLocalhost : domain.isIp || domain.domain === null || domain.publicSuffix === host) return false;
|
|
26
|
-
return profile.origins.every((origin) => {
|
|
27
|
-
const url = new URL(origin);
|
|
28
|
-
return url.origin === origin && url.username === "" && url.password === "" && (url.protocol === "https:" || profile.developmentLocalhost && profile.rpId === "localhost" && url.hostname === "localhost" && url.protocol === "http:") && (url.hostname === profile.rpId || profile.rpId !== "localhost" && url.hostname.endsWith(`.${profile.rpId}`));
|
|
29
|
-
});
|
|
30
|
-
},
|
|
31
|
-
catch: () => PasskeyConfigurationError.make({})
|
|
32
|
-
}))) return yield* PasskeyConfigurationError.make({});
|
|
40
|
+
yield* validateSimpleWebAuthnProfile(profile);
|
|
33
41
|
}
|
|
34
42
|
return Effect.fn("selectSimpleWebAuthnProfile")(function* (input) {
|
|
35
|
-
const profile = captured.profiles.find((value) => value.rpId === input.rpId && value.profileId === input.profileId
|
|
43
|
+
const profile = captured.profiles.find((value) => value.rpId === input.rpId && value.profileId === input.profileId);
|
|
36
44
|
if (profile === void 0 || !(yield* samePasskey(PasskeyProfile, profile, input))) return yield* PasskeyProtocolRejected.make({});
|
|
37
45
|
return profile;
|
|
38
46
|
});
|
|
39
47
|
}, Effect.catchCause((cause) => Cause.hasDies(cause) ? reportAuthFailure("passkey-protocol", cause).pipe(Effect.andThen(Effect.fail(PasskeyConfigurationError.make({})))) : Effect.failCause(cause)));
|
|
40
48
|
//#endregion
|
|
41
|
-
export { captureSimpleWebAuthnProfiles };
|
|
49
|
+
export { captureSimpleWebAuthnProfiles, validateSimpleWebAuthnProfile };
|
|
42
50
|
|
|
43
51
|
//# sourceMappingURL=configuration.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.mjs","names":[],"sources":["../../../src/internal/server/configuration.ts"],"sourcesContent":["import {\n PasskeyConfigurationError,\n PasskeyProtocolRejected,\n PasskeyProfile,\n samePasskey,\n snapshotPasskey,\n} from \"@yielded/auth/Passkey\";\nimport { reportAuthFailure } from \"@yielded/auth/Persistence\";\nimport { Cause, Effect } from \"effect\";\nimport { parse } from \"tldts\";\n\nimport { SimpleWebAuthnPasskeyProtocolOptions } from \"./models\";\n\nexport const
|
|
1
|
+
{"version":3,"file":"configuration.mjs","names":[],"sources":["../../../src/internal/server/configuration.ts"],"sourcesContent":["import {\n PasskeyConfigurationError,\n PasskeyProtocolRejected,\n PasskeyProfile,\n samePasskey,\n snapshotPasskey,\n} from \"@yielded/auth/Passkey\";\nimport { reportAuthFailure } from \"@yielded/auth/Persistence\";\nimport { Cause, Effect } from \"effect\";\nimport { parse } from \"tldts\";\n\nimport { SimpleWebAuthnPasskeyProtocolOptions } from \"./models\";\n\n/** Validate an immutable profile selected at issue. Verification intentionally\n * accepts that persisted selection through its ceremony lifetime during deploys. */\nexport const validateSimpleWebAuthnProfile = Effect.fn(\"validateSimpleWebAuthnProfile\")(function* (\n input: PasskeyProfile,\n) {\n const profile = yield* snapshotPasskey(PasskeyProfile, input).pipe(\n Effect.mapError(() => PasskeyConfigurationError.make({})),\n );\n\n if (\n new Set(profile.origins).size !== profile.origins.length ||\n new Set(profile.algorithms).size !== profile.algorithms.length ||\n (profile.primarySignIn &&\n (profile.residentKey !== \"required\" || profile.userVerification !== \"required\"))\n ) {\n return yield* PasskeyConfigurationError.make({});\n }\n\n const valid = yield* Effect.try({\n try: () => {\n const host = new URL(`https://${profile.rpId}`).hostname;\n\n if (host !== profile.rpId) return false;\n\n const domain = parse(host, {\n allowIcannDomains: true,\n allowPrivateDomains: true,\n extractHostname: false,\n validateHostname: true,\n detectIp: true,\n });\n\n if (\n host === \"localhost\"\n ? !profile.developmentLocalhost\n : domain.isIp || domain.domain === null || domain.publicSuffix === host\n )\n return false;\n\n return profile.origins.every((origin) => {\n const url = new URL(origin);\n\n return (\n url.origin === origin &&\n url.username === \"\" &&\n url.password === \"\" &&\n (url.protocol === \"https:\" ||\n (profile.developmentLocalhost &&\n profile.rpId === \"localhost\" &&\n url.hostname === \"localhost\" &&\n url.protocol === \"http:\")) &&\n (url.hostname === profile.rpId ||\n (profile.rpId !== \"localhost\" && url.hostname.endsWith(`.${profile.rpId}`)))\n );\n });\n },\n catch: () => PasskeyConfigurationError.make({}),\n });\n\n if (!valid) return yield* PasskeyConfigurationError.make({});\n\n return profile;\n});\n\nexport const captureSimpleWebAuthnProfiles = Effect.fn(\"captureSimpleWebAuthnProfiles\")(\n function* (options: SimpleWebAuthnPasskeyProtocolOptions) {\n const captured = yield* snapshotPasskey(SimpleWebAuthnPasskeyProtocolOptions, options).pipe(\n Effect.mapError(() => PasskeyConfigurationError.make({})),\n );\n\n const keys = new Set<string>();\n\n for (const profile of captured.profiles) {\n const key = `${profile.rpId}/${profile.profileId}`;\n\n if (keys.has(key)) return yield* PasskeyConfigurationError.make({});\n keys.add(key);\n yield* validateSimpleWebAuthnProfile(profile);\n }\n\n return Effect.fn(\"selectSimpleWebAuthnProfile\")(function* (input: PasskeyProfile) {\n const profile = captured.profiles.find(\n (value) => value.rpId === input.rpId && value.profileId === input.profileId,\n );\n\n if (profile === undefined || !(yield* samePasskey(PasskeyProfile, profile, input)))\n return yield* PasskeyProtocolRejected.make({});\n\n return profile;\n });\n },\n Effect.catchCause((cause) =>\n Cause.hasDies(cause)\n ? reportAuthFailure(\"passkey-protocol\", cause).pipe(\n Effect.andThen(Effect.fail(PasskeyConfigurationError.make({}))),\n )\n : Effect.failCause(cause),\n ),\n);\n"],"mappings":";;;;;;;;AAeA,MAAa,gCAAgC,OAAO,GAAG,+BAA+B,CAAC,CAAC,WACtF,OACA;CACA,MAAM,UAAU,OAAO,gBAAgB,gBAAgB,KAAK,CAAC,CAAC,KAC5D,OAAO,eAAe,0BAA0B,KAAK,CAAC,CAAC,CAAC,CAC1D;CAEA,IACE,IAAI,IAAI,QAAQ,OAAO,CAAC,CAAC,SAAS,QAAQ,QAAQ,UAClD,IAAI,IAAI,QAAQ,UAAU,CAAC,CAAC,SAAS,QAAQ,WAAW,UACvD,QAAQ,kBACN,QAAQ,gBAAgB,cAAc,QAAQ,qBAAqB,aAEtE,OAAO,OAAO,0BAA0B,KAAK,CAAC,CAAC;CA4CjD,IAAI,EAAC,OAzCgB,OAAO,IAAI;EAC9B,WAAW;GACT,MAAM,OAAO,IAAI,IAAI,WAAW,QAAQ,MAAM,CAAC,CAAC;GAEhD,IAAI,SAAS,QAAQ,MAAM,OAAO;GAElC,MAAM,SAAS,MAAM,MAAM;IACzB,mBAAmB;IACnB,qBAAqB;IACrB,iBAAiB;IACjB,kBAAkB;IAClB,UAAU;GACZ,CAAC;GAED,IACE,SAAS,cACL,CAAC,QAAQ,uBACT,OAAO,QAAQ,OAAO,WAAW,QAAQ,OAAO,iBAAiB,MAErE,OAAO;GAET,OAAO,QAAQ,QAAQ,OAAO,WAAW;IACvC,MAAM,MAAM,IAAI,IAAI,MAAM;IAE1B,OACE,IAAI,WAAW,UACf,IAAI,aAAa,MACjB,IAAI,aAAa,OAChB,IAAI,aAAa,YACf,QAAQ,wBACP,QAAQ,SAAS,eACjB,IAAI,aAAa,eACjB,IAAI,aAAa,aACpB,IAAI,aAAa,QAAQ,QACvB,QAAQ,SAAS,eAAe,IAAI,SAAS,SAAS,IAAI,QAAQ,MAAM;GAE/E,CAAC;EACH;EACA,aAAa,0BAA0B,KAAK,CAAC,CAAC;CAChD,CAAC,IAEW,OAAO,OAAO,0BAA0B,KAAK,CAAC,CAAC;CAE3D,OAAO;AACT,CAAC;AAED,MAAa,gCAAgC,OAAO,GAAG,+BAA+B,CAAC,CACrF,WAAW,SAA+C;CACxD,MAAM,WAAW,OAAO,gBAAgB,sCAAsC,OAAO,CAAC,CAAC,KACrF,OAAO,eAAe,0BAA0B,KAAK,CAAC,CAAC,CAAC,CAC1D;CAEA,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,WAAW,SAAS,UAAU;EACvC,MAAM,MAAM,GAAG,QAAQ,KAAK,GAAG,QAAQ;EAEvC,IAAI,KAAK,IAAI,GAAG,GAAG,OAAO,OAAO,0BAA0B,KAAK,CAAC,CAAC;EAClE,KAAK,IAAI,GAAG;EACZ,OAAO,8BAA8B,OAAO;CAC9C;CAEA,OAAO,OAAO,GAAG,6BAA6B,CAAC,CAAC,WAAW,OAAuB;EAChF,MAAM,UAAU,SAAS,SAAS,MAC/B,UAAU,MAAM,SAAS,MAAM,QAAQ,MAAM,cAAc,MAAM,SACpE;EAEA,IAAI,YAAY,KAAA,KAAa,EAAE,OAAO,YAAY,gBAAgB,SAAS,KAAK,IAC9E,OAAO,OAAO,wBAAwB,KAAK,CAAC,CAAC;EAE/C,OAAO;CACT,CAAC;AACH,GACA,OAAO,YAAY,UACjB,MAAM,QAAQ,KAAK,IACf,kBAAkB,oBAAoB,KAAK,CAAC,CAAC,KAC3C,OAAO,QAAQ,OAAO,KAAK,0BAA0B,KAAK,CAAC,CAAC,CAAC,CAAC,CAChE,IACA,OAAO,UAAU,KAAK,CAC5B,CACF"}
|
|
@@ -3,7 +3,6 @@ import { Schema } from "effect";
|
|
|
3
3
|
export declare const SimpleWebAuthnPasskeyProtocolOptions: Schema.Struct<{
|
|
4
4
|
readonly profiles: Schema.NonEmptyArray<Schema.Struct<{
|
|
5
5
|
readonly profileId: Schema.String;
|
|
6
|
-
readonly generation: Schema.Int;
|
|
7
6
|
readonly rpId: Schema.String;
|
|
8
7
|
readonly rpName: Schema.NonEmptyString;
|
|
9
8
|
readonly origins: Schema.NonEmptyArray<Schema.NonEmptyString>;
|
|
@@ -4,7 +4,6 @@ import { Effect, Layer, Redacted } from "effect";
|
|
|
4
4
|
export declare const makeSimpleWebAuthnPasskeyProtocol: (options: {
|
|
5
5
|
readonly profiles: readonly [{
|
|
6
6
|
readonly profileId: string;
|
|
7
|
-
readonly generation: number;
|
|
8
7
|
readonly rpId: string;
|
|
9
8
|
readonly rpName: string;
|
|
10
9
|
readonly origins: readonly [string, ...string[]];
|
|
@@ -16,7 +15,6 @@ export declare const makeSimpleWebAuthnPasskeyProtocol: (options: {
|
|
|
16
15
|
readonly algorithms: readonly [-257 | -7, ...(-257 | -7)[]];
|
|
17
16
|
}, ...{
|
|
18
17
|
readonly profileId: string;
|
|
19
|
-
readonly generation: number;
|
|
20
18
|
readonly rpId: string;
|
|
21
19
|
readonly rpName: string;
|
|
22
20
|
readonly origins: readonly [string, ...string[]];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { captureSimpleWebAuthnProfiles } from "./configuration.mjs";
|
|
1
|
+
import { captureSimpleWebAuthnProfiles, validateSimpleWebAuthnProfile } from "./configuration.mjs";
|
|
2
2
|
import { PasskeyAssertion, PasskeyAssertionVerified, PasskeyAttestation, PasskeyAuthenticationOptions, PasskeyCeremony, PasskeyChallenge, PasskeyConfig, PasskeyCounter, PasskeyCredential, PasskeyDescriptor, PasskeyLabel, PasskeyProfile, PasskeyProtocol, PasskeyProtocolCredentialId, PasskeyProtocolRejected, PasskeyRegistrationOptions, PasskeyRegistrationVerified, PasskeyUnavailable, PasskeyUserHandle, samePasskey, snapshotPasskey } from "@yielded/auth/Passkey";
|
|
3
3
|
import { Cause, DateTime, Effect, Layer, Redacted, Schema } from "effect";
|
|
4
4
|
import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse, verifyRegistrationResponse } from "@simplewebauthn/server";
|
|
@@ -292,7 +292,7 @@ const makeSimpleWebAuthnPasskeyProtocol = Effect.fn("makeSimpleWebAuthnPasskeyPr
|
|
|
292
292
|
}, unexpected);
|
|
293
293
|
const verifyRegistration = Effect.fn("SimpleWebAuthnPasskeyProtocol.verifyRegistration")(function* (input) {
|
|
294
294
|
const fixed = yield* capture(registrationInput, input), ceremony = fixed.ceremony;
|
|
295
|
-
const profile = yield*
|
|
295
|
+
const profile = yield* validateSimpleWebAuthnProfile(ceremony.profile).pipe(Effect.mapError(rejected));
|
|
296
296
|
if ((ceremony.purpose !== "registration" || ceremony.context._tag !== "Registration") && (ceremony.purpose !== "enrollment" || ceremony.context._tag !== "Enrollment")) return yield* rejected();
|
|
297
297
|
yield* fresh(ceremony);
|
|
298
298
|
const wire = yield* decode(Schema.fromJsonString(registrationResponse), Redacted.value(fixed.response));
|
|
@@ -337,7 +337,7 @@ const makeSimpleWebAuthnPasskeyProtocol = Effect.fn("makeSimpleWebAuthnPasskeyPr
|
|
|
337
337
|
}, unexpected);
|
|
338
338
|
const verifyAuthentication = Effect.fn("SimpleWebAuthnPasskeyProtocol.verifyAuthentication")(function* (input) {
|
|
339
339
|
const fixed = yield* capture(authenticationInput, input), ceremony = fixed.ceremony, credential = fixed.credential;
|
|
340
|
-
const profile = yield*
|
|
340
|
+
const profile = yield* validateSimpleWebAuthnProfile(ceremony.profile).pipe(Effect.mapError(rejected)), original = yield* validateSimpleWebAuthnProfile(credential.profile).pipe(Effect.mapError(rejected));
|
|
341
341
|
if (ceremony.purpose === "registration" || ceremony.purpose === "enrollment" || {
|
|
342
342
|
"sign-in": "SignIn",
|
|
343
343
|
pending: "Pending",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.mjs","names":[],"sources":["../../../src/internal/server/protocol.ts"],"sourcesContent":["import {\n generateAuthenticationOptions,\n generateRegistrationOptions,\n verifyAuthenticationResponse,\n verifyRegistrationResponse,\n} from \"@simplewebauthn/server\";\nimport {\n decodeAttestationObject,\n decodeClientDataJSON,\n decodeCredentialPublicKey,\n isoCBOR,\n parseAuthenticatorData,\n} from \"@simplewebauthn/server/helpers\";\nimport {\n type PasskeyConfigurationError,\n PasskeyProtocolRejected,\n PasskeyUnavailable,\n PasskeyAssertion,\n PasskeyAssertionVerified,\n PasskeyAttestation,\n PasskeyAuthenticationOptions,\n PasskeyCeremony,\n PasskeyChallenge,\n PasskeyCounter,\n PasskeyCredential,\n PasskeyDescriptor,\n PasskeyLabel,\n PasskeyProfile,\n PasskeyProtocolCredentialId,\n PasskeyRegistrationOptions,\n PasskeyRegistrationVerified,\n PasskeyUserHandle,\n PasskeyConfig,\n PasskeyProtocol,\n samePasskey,\n snapshotPasskey,\n} from \"@yielded/auth/Passkey\";\nimport { reportAuthFailure } from \"@yielded/auth/Persistence\";\nimport type { Context } from \"effect\";\nimport { Cause, DateTime, Effect, Layer, Redacted, Schema } from \"effect\";\nimport { Base64Url } from \"effect/encoding\";\n\nimport { captureSimpleWebAuthnProfiles } from \"./configuration\";\nimport type { SimpleWebAuthnPasskeyProtocolOptions } from \"./models\";\n\ntype Protocol = Context.Service.Shape<typeof PasskeyProtocol>;\ntype Input<K extends keyof Protocol> = Parameters<Protocol[K]>[0];\nconst rejected = () => PasskeyProtocolRejected.make({});\nconst unavailable = () => PasskeyUnavailable.make({});\n\nconst unexpected = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n Effect.catchCause((cause): Effect.Effect<never, E | PasskeyUnavailable> =>\n Cause.hasDies(cause)\n ? reportAuthFailure(\"passkey-protocol\", cause).pipe(\n Effect.andThen(Effect.fail(unavailable())),\n )\n : Effect.failCause(cause),\n ),\n );\n\nconst maintained = <A>(call: () => Promise<A>) =>\n Effect.tryPromise(call).pipe(\n Effect.onError((cause) => reportAuthFailure(\"passkey-protocol\", cause)),\n Effect.mapError(unavailable),\n );\n\nconst parsed = <A>(call: () => A) => Effect.try({ try: call, catch: rejected });\n\nconst decode = <S extends Schema.Codec<unknown, unknown, never, never>>(\n schema: S,\n input: unknown,\n) =>\n // Maintained JSON/CBOR parsers and their map lookups return untyped wire data.\n // eslint-disable-next-line no-restricted-properties\n Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError(rejected));\n\nconst capture = <S extends Schema.Codec<unknown, unknown, never, never>>(\n schema: S,\n input: S[\"Type\"],\n) => snapshotPasskey(schema, input).pipe(Effect.mapError(rejected));\n\nconst sameBytes = (left: Uint8Array, right: Uint8Array) =>\n left.length === right.length && left.every((byte, index) => byte === right[index]);\n\nconst binary = (minimum: number, maximum: number) =>\n Schema.String.check(\n Schema.isMaxLength(Math.ceil((maximum * 4) / 3)),\n Schema.makeFilter((value) => {\n const result = Base64Url.decode(value);\n\n return (\n result._tag === \"Success\" &&\n result.success.length >= minimum &&\n result.success.length <= maximum &&\n Base64Url.encode(result.success) === value\n );\n }),\n );\n\nconst bytes = Effect.fn(\"simpleWebAuthnBytes\")(function* (value: string) {\n const result = Base64Url.decode(value);\n\n if (result._tag === \"Failure\") return yield* rejected();\n\n return new Uint8Array(result.success);\n});\n\nconst duration = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 300000 }));\n\nconst prepareAuthenticationInput = Schema.Struct({\n profile: PasskeyProfile,\n challenge: PasskeyChallenge,\n timeoutMillis: duration,\n allowedCredentials: Schema.Array(PasskeyDescriptor).check(Schema.isMaxLength(64)),\n});\n\nconst prepareRegistrationInput = Schema.Struct({\n profile: PasskeyProfile,\n challenge: PasskeyChallenge,\n timeoutMillis: duration,\n userHandle: PasskeyUserHandle,\n name: PasskeyLabel,\n displayName: PasskeyLabel,\n excludedCredentials: Schema.Array(PasskeyDescriptor).check(Schema.isMaxLength(64)),\n});\n\nconst authenticationInput = Schema.Struct({\n ceremony: PasskeyCeremony,\n credential: PasskeyCredential,\n response: PasskeyAssertion,\n});\n\nconst registrationInput = Schema.Struct({\n ceremony: PasskeyCeremony,\n response: PasskeyAttestation,\n});\n\nconst envelope = {\n id: PasskeyProtocolCredentialId,\n rawId: PasskeyProtocolCredentialId,\n type: Schema.Literal(\"public-key\"),\n};\n\nconst authenticationResponse = Schema.Struct({\n ...envelope,\n response: Schema.Struct({\n clientDataJSON: binary(1, 8192),\n authenticatorData: binary(37, 16384),\n signature: binary(1, 1024),\n userHandle: Schema.optionalKey(Schema.NullOr(PasskeyUserHandle)),\n }),\n});\n\nconst registrationResponse = Schema.Struct({\n ...envelope,\n response: Schema.Struct({ clientDataJSON: binary(1, 8192), attestationObject: binary(1, 65536) }),\n});\n\nconst clientData = Schema.Struct({\n type: Schema.String,\n challenge: PasskeyChallenge,\n origin: Schema.String.check(Schema.isMaxLength(2048)),\n crossOrigin: Schema.optionalKey(Schema.Boolean),\n topOrigin: Schema.optionalKey(Schema.Unknown),\n});\n\ntype CborData =\n | string\n | number\n | boolean\n | null\n | Uint8Array\n | ReadonlyArray<CborData>\n | ReadonlyMap<string | number, CborData>;\n\nconst cborLeaf = Schema.Union([\n Schema.String.check(Schema.isMaxLength(8192)),\n Schema.Number.check(Schema.isFinite()),\n Schema.Boolean,\n Schema.Null,\n Schema.Uint8Array.check(Schema.makeFilter((value) => value.length <= 16384)),\n]);\n\nconst cborValue = (depth: number): Schema.Codec<CborData> => {\n if (depth === 0) return cborLeaf;\n const child = cborValue(depth - 1);\n\n return Schema.Union([\n cborLeaf,\n Schema.Array(child).check(Schema.isMaxLength(64)),\n Schema.ReadonlyMap(\n Schema.Union([Schema.String.check(Schema.isMaxLength(128)), Schema.Int]),\n child,\n ).check(Schema.makeFilter((value) => value.size <= 32)),\n ]);\n};\n\nconst cborMap = Schema.ReadonlyMap(\n Schema.Union([Schema.String.check(Schema.isMaxLength(128)), Schema.Int]),\n cborValue(4),\n).check(Schema.makeFilter((value) => value.size <= 32));\n\nconst encodeCbor = (value: CborData) => {\n // The maintained encoder reads these Schema-validated Maps/arrays; its type\n // requires mutable collections although no mutation is performed.\n return isoCBOR.encode(value as Parameters<typeof isoCBOR.encode>[0]);\n};\n\nconst exactMap = Effect.fn(\"simpleWebAuthnExactMap\")(function* (input: Uint8Array, value: unknown) {\n const result = yield* decode(cborMap, value);\n const encoded = yield* parsed(() => encodeCbor(result));\n\n if (!sameBytes(input, encoded)) return yield* rejected();\n\n return result;\n});\n\nconst byteValue = (minimum: number, maximum: number) =>\n Schema.Uint8Array.check(\n Schema.makeFilter((value) => value.length >= minimum && value.length <= maximum),\n );\n\nconst fresh = Effect.fn(\"simpleWebAuthnFresh\")(function* (ceremony: PasskeyCeremony) {\n const now = DateTime.toEpochMillis(yield* DateTime.now);\n\n if (\n ceremony.issuedAtMillis > now ||\n now >= ceremony.expiresAtMillis ||\n now >= ceremony.requestBindingExpiresAtMillis ||\n ceremony.expiresAtMillis <= ceremony.issuedAtMillis\n )\n return yield* rejected();\n});\n\nconst checkClient = Effect.fn(\"simpleWebAuthnClientData\")(function* (\n value: string,\n ceremony: PasskeyCeremony,\n type: \"webauthn.create\" | \"webauthn.get\",\n) {\n const client = yield* decode(clientData, yield* parsed(() => decodeClientDataJSON(value)));\n\n if (\n client.type !== type ||\n client.challenge !== ceremony.challenge ||\n !ceremony.profile.origins.includes(client.origin) ||\n client.crossOrigin === true ||\n \"topOrigin\" in client\n )\n return yield* rejected();\n\n return client;\n});\n\nconst checkAuthenticator = Effect.fn(\"simpleWebAuthnAuthenticatorData\")(function* (\n original: Uint8Array,\n registration: boolean,\n profile: PasskeyProfile,\n) {\n if (\n original.length < 37 ||\n original.length > 16384 ||\n Boolean(original[32]! & 64) !== registration\n )\n return yield* rejected();\n let prefix = 37;\n\n if (registration) {\n if (original.length < 55) return yield* rejected();\n\n const length = new DataView(\n original.buffer,\n original.byteOffset,\n original.byteLength,\n ).getUint16(53, false);\n\n if (length < 1 || length > 1023 || original.length < 55 + length) return yield* rejected();\n prefix = 55 + length;\n }\n const result = yield* parsed(() => parseAuthenticatorData(new Uint8Array(original)));\n\n if (\n !result.flags.up ||\n (profile.userVerification === \"required\" && !result.flags.uv) ||\n (result.flags.bs && !result.flags.be) ||\n result.flags.at !== registration ||\n result.flags.flagsInt !== original[32]\n )\n return yield* rejected();\n yield* decode(PasskeyCounter, result.counter);\n const chunks: Uint8Array[] = [original.slice(0, prefix)];\n\n if (registration) {\n const id = yield* decode(byteValue(1, 1023), result.credentialID);\n const key = yield* decode(byteValue(1, 8192), result.credentialPublicKey);\n\n if (!sameBytes(id, original.slice(55, prefix))) return yield* rejected();\n chunks.push(key);\n }\n if (result.flags.ed) {\n const extension = yield* decode(byteValue(1, 16384), result.extensionsDataBuffer);\n\n yield* exactMap(\n extension,\n yield* parsed(() => isoCBOR.decodeFirst<unknown>(new Uint8Array(extension))),\n );\n chunks.push(extension);\n } else if (result.extensionsDataBuffer !== undefined) return yield* rejected();\n const length = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n\n if (length !== original.length) return yield* rejected();\n let offset = 0;\n\n for (const chunk of chunks) {\n if (!sameBytes(chunk, original.slice(offset, offset + chunk.length))) return yield* rejected();\n offset += chunk.length;\n }\n\n const rpHash = new Uint8Array(\n yield* maintained(() =>\n globalThis.crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(profile.rpId)),\n ),\n );\n\n if (!sameBytes(rpHash, original.slice(0, 32))) return yield* rejected();\n\n return result;\n});\n\nconst checkKey = Effect.fn(\"simpleWebAuthnPublicKey\")(function* (\n input: Uint8Array,\n allowed: ReadonlyArray<-7 | -257>,\n expected?: -7 | -257,\n) {\n const map = yield* exactMap(\n input,\n yield* parsed(() => decodeCredentialPublicKey(new Uint8Array(input))),\n );\n\n const algorithmId = yield* decode(Schema.Literals([-7, -257]), map.get(3));\n\n if (!allowed.includes(algorithmId) || (expected !== undefined && algorithmId !== expected))\n return yield* rejected();\n let jwk: JsonWebKey;\n let algorithm: EcKeyImportParams | RsaHashedImportParams;\n\n if (algorithmId === -7) {\n if (\n map.size !== 5 ||\n ![1, 3, -1, -2, -3].every((key) => map.has(key)) ||\n map.get(1) !== 2 ||\n map.get(-1) !== 1\n )\n return yield* rejected();\n const x = yield* decode(byteValue(32, 32), map.get(-2));\n const y = yield* decode(byteValue(32, 32), map.get(-3));\n\n jwk = {\n kty: \"EC\",\n crv: \"P-256\",\n x: Base64Url.encode(x),\n y: Base64Url.encode(y),\n ext: false,\n };\n algorithm = { name: \"ECDSA\", namedCurve: \"P-256\" };\n } else {\n if (map.size !== 4 || ![1, 3, -1, -2].every((key) => map.has(key)) || map.get(1) !== 3)\n return yield* rejected();\n const n = yield* decode(byteValue(256, 512), map.get(-1));\n const e = yield* decode(byteValue(1, 4), map.get(-2));\n const exponent = e.reduce((value, byte) => value * 256 + byte, 0);\n\n if (\n n[0]! < 128 ||\n (n[n.length - 1]! & 1) === 0 ||\n e[0] === 0 ||\n exponent < 3 ||\n exponent % 2 !== 1\n )\n return yield* rejected();\n jwk = {\n kty: \"RSA\",\n alg: \"RS256\",\n n: Base64Url.encode(n),\n e: Base64Url.encode(e),\n ext: false,\n };\n algorithm = { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" };\n }\n yield* maintained(() =>\n globalThis.crypto.subtle.importKey(\"jwk\", jwk, algorithm, false, [\"verify\"]),\n );\n\n return algorithmId;\n});\n\nexport const makeSimpleWebAuthnPasskeyProtocol = Effect.fn(\"makeSimpleWebAuthnPasskeyProtocol\")(\n function* (\n options: SimpleWebAuthnPasskeyProtocolOptions,\n ): Effect.fn.Return<Protocol, PasskeyConfigurationError> {\n const select = yield* captureSimpleWebAuthnProfiles(options);\n\n const prepareAuthentication = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.prepareAuthentication\")(\n function* (input: Input<\"prepareAuthentication\">) {\n const fixed = yield* capture(prepareAuthenticationInput, input);\n const profile = yield* select(fixed.profile);\n const challenge = yield* bytes(fixed.challenge);\n\n const result = yield* maintained(() =>\n generateAuthenticationOptions({\n rpID: profile.rpId,\n challenge,\n timeout: fixed.timeoutMillis,\n userVerification: profile.userVerification,\n allowCredentials: fixed.allowedCredentials.map((value) => ({ ...value })),\n }),\n );\n\n const value = yield* decode(PasskeyAuthenticationOptions, {\n challenge: result.challenge,\n rpId: result.rpId,\n timeout: result.timeout,\n userVerification: result.userVerification,\n allowCredentials: result.allowCredentials ?? [],\n });\n\n if (\n !(yield* samePasskey(PasskeyAuthenticationOptions, value, {\n challenge: fixed.challenge,\n rpId: profile.rpId,\n timeout: fixed.timeoutMillis,\n userVerification: profile.userVerification,\n allowCredentials: fixed.allowedCredentials,\n }))\n )\n return yield* unavailable();\n\n return yield* capture(PasskeyAuthenticationOptions, value);\n },\n unexpected,\n );\n\n const prepareRegistration = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.prepareRegistration\")(\n function* (input: Input<\"prepareRegistration\">) {\n const fixed = yield* capture(prepareRegistrationInput, input);\n const profile = yield* select(fixed.profile);\n\n const challenge = yield* bytes(fixed.challenge),\n userID = yield* bytes(fixed.userHandle);\n\n const result = yield* maintained(() =>\n generateRegistrationOptions({\n rpName: profile.rpName,\n rpID: profile.rpId,\n userName: fixed.name,\n userDisplayName: fixed.displayName,\n userID,\n challenge,\n timeout: fixed.timeoutMillis,\n attestationType: \"none\",\n supportedAlgorithmIDs: [...profile.algorithms],\n authenticatorSelection: {\n residentKey: profile.residentKey,\n userVerification: profile.userVerification,\n },\n excludeCredentials: fixed.excludedCredentials.map((value) => ({ ...value })),\n }),\n );\n\n const value = yield* decode(PasskeyRegistrationOptions, {\n challenge: result.challenge,\n rp: result.rp,\n user: result.user,\n pubKeyCredParams: result.pubKeyCredParams,\n timeout: result.timeout,\n attestation: result.attestation,\n authenticatorSelection: result.authenticatorSelection,\n excludeCredentials: result.excludeCredentials ?? [],\n });\n\n const expected = {\n challenge: fixed.challenge,\n rp: { id: profile.rpId, name: profile.rpName },\n user: { id: fixed.userHandle, name: fixed.name, displayName: fixed.displayName },\n pubKeyCredParams: profile.algorithms.map((alg) => ({ type: \"public-key\", alg })),\n timeout: fixed.timeoutMillis,\n attestation: \"none\",\n authenticatorSelection: {\n residentKey: profile.residentKey,\n userVerification: profile.userVerification,\n },\n excludeCredentials: fixed.excludedCredentials,\n };\n\n if (\n !(yield* samePasskey(\n PasskeyRegistrationOptions,\n value,\n yield* decode(PasskeyRegistrationOptions, expected),\n ))\n )\n return yield* unavailable();\n\n return yield* capture(PasskeyRegistrationOptions, value);\n },\n unexpected,\n );\n\n const verifyRegistration = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.verifyRegistration\")(\n function* (input: Input<\"verifyRegistration\">) {\n const fixed = yield* capture(registrationInput, input),\n ceremony = fixed.ceremony;\n\n const profile = yield* select(ceremony.profile);\n\n if (\n (ceremony.purpose !== \"registration\" || ceremony.context._tag !== \"Registration\") &&\n (ceremony.purpose !== \"enrollment\" || ceremony.context._tag !== \"Enrollment\")\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n const wire = yield* decode(\n Schema.fromJsonString(registrationResponse),\n Redacted.value(fixed.response),\n );\n\n if (wire.id !== wire.rawId) return yield* rejected();\n\n const client = yield* checkClient(\n wire.response.clientDataJSON,\n ceremony,\n \"webauthn.create\",\n );\n\n const attestationBytes = yield* bytes(wire.response.attestationObject);\n\n const attestation = yield* exactMap(\n attestationBytes,\n yield* parsed(() => decodeAttestationObject(new Uint8Array(attestationBytes))),\n );\n\n if (\n attestation.size !== 3 ||\n attestation.get(\"fmt\") !== \"none\" ||\n !attestation.has(\"attStmt\") ||\n !attestation.has(\"authData\")\n )\n return yield* rejected();\n const statement = yield* decode(cborMap, attestation.get(\"attStmt\"));\n\n if (statement.size !== 0) return yield* rejected();\n const authenticatorBytes = yield* decode(byteValue(37, 16384), attestation.get(\"authData\"));\n const auth = yield* checkAuthenticator(authenticatorBytes, true, profile);\n const id = yield* decode(byteValue(1, 1023), auth.credentialID);\n const key = yield* decode(byteValue(1, 8192), auth.credentialPublicKey);\n\n if (Base64Url.encode(id) !== wire.id) return yield* rejected();\n const algorithm = yield* checkKey(key, profile.algorithms);\n\n const result = yield* maintained(() =>\n verifyRegistrationResponse({\n response: { ...wire, clientExtensionResults: {} },\n expectedChallenge: ceremony.challenge,\n expectedOrigin: [...profile.origins],\n expectedRPID: profile.rpId,\n expectedType: \"webauthn.create\",\n requireUserPresence: true,\n requireUserVerification: profile.userVerification === \"required\",\n supportedAlgorithmIDs: [...profile.algorithms],\n }),\n );\n\n if (!result.verified || result.registrationInfo === undefined) return yield* rejected();\n const info = result.registrationInfo;\n\n if (\n info.fmt !== \"none\" ||\n info.origin !== client.origin ||\n info.rpID !== profile.rpId ||\n info.credential.id !== wire.id ||\n !sameBytes(info.credential.publicKey, key) ||\n info.credential.counter !== auth.counter ||\n info.userVerified !== auth.flags.uv ||\n (info.credentialDeviceType === \"multiDevice\") !== auth.flags.be ||\n info.credentialBackedUp !== auth.flags.bs\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n return yield* capture(\n PasskeyRegistrationVerified,\n yield* decode(PasskeyRegistrationVerified, {\n protocolCredentialId: info.credential.id,\n publicKey: Base64Url.encode(info.credential.publicKey),\n algorithm,\n counter: info.credential.counter,\n userVerified: info.userVerified,\n backupEligible: info.credentialDeviceType === \"multiDevice\",\n backupState: info.credentialBackedUp,\n }),\n );\n },\n unexpected,\n );\n\n const verifyAuthentication = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.verifyAuthentication\")(\n function* (input: Input<\"verifyAuthentication\">) {\n const fixed = yield* capture(authenticationInput, input),\n ceremony = fixed.ceremony,\n credential = fixed.credential;\n\n const profile = yield* select(ceremony.profile),\n original = yield* select(credential.profile);\n\n const contexts = {\n \"sign-in\": \"SignIn\",\n pending: \"Pending\",\n \"step-up\": \"StepUp\",\n action: \"Action\",\n } as const;\n\n if (\n ceremony.purpose === \"registration\" ||\n ceremony.purpose === \"enrollment\" ||\n contexts[ceremony.purpose] !== ceremony.context._tag ||\n !credential.active ||\n credential.rpId !== profile.rpId ||\n original.rpId !== profile.rpId ||\n !original.algorithms.includes(credential.algorithm) ||\n !profile.algorithms.includes(credential.algorithm)\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n const wire = yield* decode(\n Schema.fromJsonString(authenticationResponse),\n Redacted.value(fixed.response),\n );\n\n const { userHandle: rawUserHandle, ...assertionResponse } = wire.response;\n const userHandle = rawUserHandle ?? undefined;\n\n if (\n wire.id !== wire.rawId ||\n wire.id !== credential.protocolCredentialId ||\n (userHandle !== undefined && userHandle !== credential.userHandle) ||\n (ceremony.context._tag === \"SignIn\" && userHandle === undefined) ||\n (ceremony.allowedCredentials.length > 0 &&\n !ceremony.allowedCredentials.some((value) => value.id === wire.id))\n )\n return yield* rejected();\n const client = yield* checkClient(wire.response.clientDataJSON, ceremony, \"webauthn.get\");\n\n const auth = yield* checkAuthenticator(\n yield* bytes(wire.response.authenticatorData),\n false,\n profile,\n );\n\n if (\n auth.flags.be !== credential.backupEligible ||\n (!credential.backupEligible &&\n (credential.counter > 0 || auth.counter > 0) &&\n auth.counter <= credential.counter)\n )\n return yield* rejected();\n const publicKey = yield* bytes(credential.publicKey);\n\n yield* checkKey(publicKey, profile.algorithms, credential.algorithm);\n\n const result = yield* maintained(() =>\n verifyAuthenticationResponse({\n response: {\n ...wire,\n response: {\n ...assertionResponse,\n ...(userHandle === undefined ? {} : { userHandle }),\n },\n clientExtensionResults: {},\n },\n expectedChallenge: ceremony.challenge,\n expectedOrigin: [...profile.origins],\n expectedRPID: profile.rpId,\n expectedType: \"webauthn.get\",\n requireUserVerification: profile.userVerification === \"required\",\n credential: {\n id: credential.protocolCredentialId,\n publicKey,\n counter: credential.backupEligible ? 0 : credential.counter,\n },\n }),\n );\n\n if (!result.verified) return yield* rejected();\n const info = result.authenticationInfo;\n\n if (\n info.credentialID !== wire.id ||\n info.origin !== client.origin ||\n info.rpID !== profile.rpId ||\n info.newCounter !== auth.counter ||\n info.userVerified !== auth.flags.uv ||\n (info.credentialDeviceType === \"multiDevice\") !== credential.backupEligible ||\n info.credentialBackedUp !== auth.flags.bs\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n return yield* capture(\n PasskeyAssertionVerified,\n yield* decode(PasskeyAssertionVerified, {\n protocolCredentialId: info.credentialID,\n ...(userHandle === undefined ? {} : { userHandle }),\n counter: info.newCounter,\n userVerified: info.userVerified,\n backupEligible: info.credentialDeviceType === \"multiDevice\",\n backupState: info.credentialBackedUp,\n }),\n );\n },\n unexpected,\n );\n\n return PasskeyProtocol.of({\n prepareAuthentication,\n prepareRegistration,\n verifyRegistration,\n verifyAuthentication,\n });\n },\n);\n\n/** Requires the same host profiles as the strategy. */\nexport const layerSimpleWebAuthnPasskeyProtocol = Layer.effect(\n PasskeyProtocol,\n Effect.flatMap(PasskeyConfig, makeSimpleWebAuthnPasskeyProtocol),\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"protocol.mjs","names":[],"sources":["../../../src/internal/server/protocol.ts"],"sourcesContent":["import {\n generateAuthenticationOptions,\n generateRegistrationOptions,\n verifyAuthenticationResponse,\n verifyRegistrationResponse,\n} from \"@simplewebauthn/server\";\nimport {\n decodeAttestationObject,\n decodeClientDataJSON,\n decodeCredentialPublicKey,\n isoCBOR,\n parseAuthenticatorData,\n} from \"@simplewebauthn/server/helpers\";\nimport {\n type PasskeyConfigurationError,\n PasskeyProtocolRejected,\n PasskeyUnavailable,\n PasskeyAssertion,\n PasskeyAssertionVerified,\n PasskeyAttestation,\n PasskeyAuthenticationOptions,\n PasskeyCeremony,\n PasskeyChallenge,\n PasskeyCounter,\n PasskeyCredential,\n PasskeyDescriptor,\n PasskeyLabel,\n PasskeyProfile,\n PasskeyProtocolCredentialId,\n PasskeyRegistrationOptions,\n PasskeyRegistrationVerified,\n PasskeyUserHandle,\n PasskeyConfig,\n PasskeyProtocol,\n samePasskey,\n snapshotPasskey,\n} from \"@yielded/auth/Passkey\";\nimport { reportAuthFailure } from \"@yielded/auth/Persistence\";\nimport type { Context } from \"effect\";\nimport { Cause, DateTime, Effect, Layer, Redacted, Schema } from \"effect\";\nimport { Base64Url } from \"effect/encoding\";\n\nimport { captureSimpleWebAuthnProfiles, validateSimpleWebAuthnProfile } from \"./configuration\";\nimport type { SimpleWebAuthnPasskeyProtocolOptions } from \"./models\";\n\ntype Protocol = Context.Service.Shape<typeof PasskeyProtocol>;\ntype Input<K extends keyof Protocol> = Parameters<Protocol[K]>[0];\nconst rejected = () => PasskeyProtocolRejected.make({});\nconst unavailable = () => PasskeyUnavailable.make({});\n\nconst unexpected = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(\n Effect.catchCause((cause): Effect.Effect<never, E | PasskeyUnavailable> =>\n Cause.hasDies(cause)\n ? reportAuthFailure(\"passkey-protocol\", cause).pipe(\n Effect.andThen(Effect.fail(unavailable())),\n )\n : Effect.failCause(cause),\n ),\n );\n\nconst maintained = <A>(call: () => Promise<A>) =>\n Effect.tryPromise(call).pipe(\n Effect.onError((cause) => reportAuthFailure(\"passkey-protocol\", cause)),\n Effect.mapError(unavailable),\n );\n\nconst parsed = <A>(call: () => A) => Effect.try({ try: call, catch: rejected });\n\nconst decode = <S extends Schema.Codec<unknown, unknown, never, never>>(\n schema: S,\n input: unknown,\n) =>\n // Maintained JSON/CBOR parsers and their map lookups return untyped wire data.\n // eslint-disable-next-line no-restricted-properties\n Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError(rejected));\n\nconst capture = <S extends Schema.Codec<unknown, unknown, never, never>>(\n schema: S,\n input: S[\"Type\"],\n) => snapshotPasskey(schema, input).pipe(Effect.mapError(rejected));\n\nconst sameBytes = (left: Uint8Array, right: Uint8Array) =>\n left.length === right.length && left.every((byte, index) => byte === right[index]);\n\nconst binary = (minimum: number, maximum: number) =>\n Schema.String.check(\n Schema.isMaxLength(Math.ceil((maximum * 4) / 3)),\n Schema.makeFilter((value) => {\n const result = Base64Url.decode(value);\n\n return (\n result._tag === \"Success\" &&\n result.success.length >= minimum &&\n result.success.length <= maximum &&\n Base64Url.encode(result.success) === value\n );\n }),\n );\n\nconst bytes = Effect.fn(\"simpleWebAuthnBytes\")(function* (value: string) {\n const result = Base64Url.decode(value);\n\n if (result._tag === \"Failure\") return yield* rejected();\n\n return new Uint8Array(result.success);\n});\n\nconst duration = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 300000 }));\n\nconst prepareAuthenticationInput = Schema.Struct({\n profile: PasskeyProfile,\n challenge: PasskeyChallenge,\n timeoutMillis: duration,\n allowedCredentials: Schema.Array(PasskeyDescriptor).check(Schema.isMaxLength(64)),\n});\n\nconst prepareRegistrationInput = Schema.Struct({\n profile: PasskeyProfile,\n challenge: PasskeyChallenge,\n timeoutMillis: duration,\n userHandle: PasskeyUserHandle,\n name: PasskeyLabel,\n displayName: PasskeyLabel,\n excludedCredentials: Schema.Array(PasskeyDescriptor).check(Schema.isMaxLength(64)),\n});\n\nconst authenticationInput = Schema.Struct({\n ceremony: PasskeyCeremony,\n credential: PasskeyCredential,\n response: PasskeyAssertion,\n});\n\nconst registrationInput = Schema.Struct({\n ceremony: PasskeyCeremony,\n response: PasskeyAttestation,\n});\n\nconst envelope = {\n id: PasskeyProtocolCredentialId,\n rawId: PasskeyProtocolCredentialId,\n type: Schema.Literal(\"public-key\"),\n};\n\nconst authenticationResponse = Schema.Struct({\n ...envelope,\n response: Schema.Struct({\n clientDataJSON: binary(1, 8192),\n authenticatorData: binary(37, 16384),\n signature: binary(1, 1024),\n userHandle: Schema.optionalKey(Schema.NullOr(PasskeyUserHandle)),\n }),\n});\n\nconst registrationResponse = Schema.Struct({\n ...envelope,\n response: Schema.Struct({ clientDataJSON: binary(1, 8192), attestationObject: binary(1, 65536) }),\n});\n\nconst clientData = Schema.Struct({\n type: Schema.String,\n challenge: PasskeyChallenge,\n origin: Schema.String.check(Schema.isMaxLength(2048)),\n crossOrigin: Schema.optionalKey(Schema.Boolean),\n topOrigin: Schema.optionalKey(Schema.Unknown),\n});\n\ntype CborData =\n | string\n | number\n | boolean\n | null\n | Uint8Array\n | ReadonlyArray<CborData>\n | ReadonlyMap<string | number, CborData>;\n\nconst cborLeaf = Schema.Union([\n Schema.String.check(Schema.isMaxLength(8192)),\n Schema.Number.check(Schema.isFinite()),\n Schema.Boolean,\n Schema.Null,\n Schema.Uint8Array.check(Schema.makeFilter((value) => value.length <= 16384)),\n]);\n\nconst cborValue = (depth: number): Schema.Codec<CborData> => {\n if (depth === 0) return cborLeaf;\n const child = cborValue(depth - 1);\n\n return Schema.Union([\n cborLeaf,\n Schema.Array(child).check(Schema.isMaxLength(64)),\n Schema.ReadonlyMap(\n Schema.Union([Schema.String.check(Schema.isMaxLength(128)), Schema.Int]),\n child,\n ).check(Schema.makeFilter((value) => value.size <= 32)),\n ]);\n};\n\nconst cborMap = Schema.ReadonlyMap(\n Schema.Union([Schema.String.check(Schema.isMaxLength(128)), Schema.Int]),\n cborValue(4),\n).check(Schema.makeFilter((value) => value.size <= 32));\n\nconst encodeCbor = (value: CborData) => {\n // The maintained encoder reads these Schema-validated Maps/arrays; its type\n // requires mutable collections although no mutation is performed.\n return isoCBOR.encode(value as Parameters<typeof isoCBOR.encode>[0]);\n};\n\nconst exactMap = Effect.fn(\"simpleWebAuthnExactMap\")(function* (input: Uint8Array, value: unknown) {\n const result = yield* decode(cborMap, value);\n const encoded = yield* parsed(() => encodeCbor(result));\n\n if (!sameBytes(input, encoded)) return yield* rejected();\n\n return result;\n});\n\nconst byteValue = (minimum: number, maximum: number) =>\n Schema.Uint8Array.check(\n Schema.makeFilter((value) => value.length >= minimum && value.length <= maximum),\n );\n\nconst fresh = Effect.fn(\"simpleWebAuthnFresh\")(function* (ceremony: PasskeyCeremony) {\n const now = DateTime.toEpochMillis(yield* DateTime.now);\n\n if (\n ceremony.issuedAtMillis > now ||\n now >= ceremony.expiresAtMillis ||\n now >= ceremony.requestBindingExpiresAtMillis ||\n ceremony.expiresAtMillis <= ceremony.issuedAtMillis\n )\n return yield* rejected();\n});\n\nconst checkClient = Effect.fn(\"simpleWebAuthnClientData\")(function* (\n value: string,\n ceremony: PasskeyCeremony,\n type: \"webauthn.create\" | \"webauthn.get\",\n) {\n const client = yield* decode(clientData, yield* parsed(() => decodeClientDataJSON(value)));\n\n if (\n client.type !== type ||\n client.challenge !== ceremony.challenge ||\n !ceremony.profile.origins.includes(client.origin) ||\n client.crossOrigin === true ||\n \"topOrigin\" in client\n )\n return yield* rejected();\n\n return client;\n});\n\nconst checkAuthenticator = Effect.fn(\"simpleWebAuthnAuthenticatorData\")(function* (\n original: Uint8Array,\n registration: boolean,\n profile: PasskeyProfile,\n) {\n if (\n original.length < 37 ||\n original.length > 16384 ||\n Boolean(original[32]! & 64) !== registration\n )\n return yield* rejected();\n let prefix = 37;\n\n if (registration) {\n if (original.length < 55) return yield* rejected();\n\n const length = new DataView(\n original.buffer,\n original.byteOffset,\n original.byteLength,\n ).getUint16(53, false);\n\n if (length < 1 || length > 1023 || original.length < 55 + length) return yield* rejected();\n prefix = 55 + length;\n }\n const result = yield* parsed(() => parseAuthenticatorData(new Uint8Array(original)));\n\n if (\n !result.flags.up ||\n (profile.userVerification === \"required\" && !result.flags.uv) ||\n (result.flags.bs && !result.flags.be) ||\n result.flags.at !== registration ||\n result.flags.flagsInt !== original[32]\n )\n return yield* rejected();\n yield* decode(PasskeyCounter, result.counter);\n const chunks: Uint8Array[] = [original.slice(0, prefix)];\n\n if (registration) {\n const id = yield* decode(byteValue(1, 1023), result.credentialID);\n const key = yield* decode(byteValue(1, 8192), result.credentialPublicKey);\n\n if (!sameBytes(id, original.slice(55, prefix))) return yield* rejected();\n chunks.push(key);\n }\n if (result.flags.ed) {\n const extension = yield* decode(byteValue(1, 16384), result.extensionsDataBuffer);\n\n yield* exactMap(\n extension,\n yield* parsed(() => isoCBOR.decodeFirst<unknown>(new Uint8Array(extension))),\n );\n chunks.push(extension);\n } else if (result.extensionsDataBuffer !== undefined) return yield* rejected();\n const length = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n\n if (length !== original.length) return yield* rejected();\n let offset = 0;\n\n for (const chunk of chunks) {\n if (!sameBytes(chunk, original.slice(offset, offset + chunk.length))) return yield* rejected();\n offset += chunk.length;\n }\n\n const rpHash = new Uint8Array(\n yield* maintained(() =>\n globalThis.crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(profile.rpId)),\n ),\n );\n\n if (!sameBytes(rpHash, original.slice(0, 32))) return yield* rejected();\n\n return result;\n});\n\nconst checkKey = Effect.fn(\"simpleWebAuthnPublicKey\")(function* (\n input: Uint8Array,\n allowed: ReadonlyArray<-7 | -257>,\n expected?: -7 | -257,\n) {\n const map = yield* exactMap(\n input,\n yield* parsed(() => decodeCredentialPublicKey(new Uint8Array(input))),\n );\n\n const algorithmId = yield* decode(Schema.Literals([-7, -257]), map.get(3));\n\n if (!allowed.includes(algorithmId) || (expected !== undefined && algorithmId !== expected))\n return yield* rejected();\n let jwk: JsonWebKey;\n let algorithm: EcKeyImportParams | RsaHashedImportParams;\n\n if (algorithmId === -7) {\n if (\n map.size !== 5 ||\n ![1, 3, -1, -2, -3].every((key) => map.has(key)) ||\n map.get(1) !== 2 ||\n map.get(-1) !== 1\n )\n return yield* rejected();\n const x = yield* decode(byteValue(32, 32), map.get(-2));\n const y = yield* decode(byteValue(32, 32), map.get(-3));\n\n jwk = {\n kty: \"EC\",\n crv: \"P-256\",\n x: Base64Url.encode(x),\n y: Base64Url.encode(y),\n ext: false,\n };\n algorithm = { name: \"ECDSA\", namedCurve: \"P-256\" };\n } else {\n if (map.size !== 4 || ![1, 3, -1, -2].every((key) => map.has(key)) || map.get(1) !== 3)\n return yield* rejected();\n const n = yield* decode(byteValue(256, 512), map.get(-1));\n const e = yield* decode(byteValue(1, 4), map.get(-2));\n const exponent = e.reduce((value, byte) => value * 256 + byte, 0);\n\n if (\n n[0]! < 128 ||\n (n[n.length - 1]! & 1) === 0 ||\n e[0] === 0 ||\n exponent < 3 ||\n exponent % 2 !== 1\n )\n return yield* rejected();\n jwk = {\n kty: \"RSA\",\n alg: \"RS256\",\n n: Base64Url.encode(n),\n e: Base64Url.encode(e),\n ext: false,\n };\n algorithm = { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" };\n }\n yield* maintained(() =>\n globalThis.crypto.subtle.importKey(\"jwk\", jwk, algorithm, false, [\"verify\"]),\n );\n\n return algorithmId;\n});\n\nexport const makeSimpleWebAuthnPasskeyProtocol = Effect.fn(\"makeSimpleWebAuthnPasskeyProtocol\")(\n function* (\n options: SimpleWebAuthnPasskeyProtocolOptions,\n ): Effect.fn.Return<Protocol, PasskeyConfigurationError> {\n const select = yield* captureSimpleWebAuthnProfiles(options);\n\n const prepareAuthentication = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.prepareAuthentication\")(\n function* (input: Input<\"prepareAuthentication\">) {\n const fixed = yield* capture(prepareAuthenticationInput, input);\n const profile = yield* select(fixed.profile);\n const challenge = yield* bytes(fixed.challenge);\n\n const result = yield* maintained(() =>\n generateAuthenticationOptions({\n rpID: profile.rpId,\n challenge,\n timeout: fixed.timeoutMillis,\n userVerification: profile.userVerification,\n allowCredentials: fixed.allowedCredentials.map((value) => ({ ...value })),\n }),\n );\n\n const value = yield* decode(PasskeyAuthenticationOptions, {\n challenge: result.challenge,\n rpId: result.rpId,\n timeout: result.timeout,\n userVerification: result.userVerification,\n allowCredentials: result.allowCredentials ?? [],\n });\n\n if (\n !(yield* samePasskey(PasskeyAuthenticationOptions, value, {\n challenge: fixed.challenge,\n rpId: profile.rpId,\n timeout: fixed.timeoutMillis,\n userVerification: profile.userVerification,\n allowCredentials: fixed.allowedCredentials,\n }))\n )\n return yield* unavailable();\n\n return yield* capture(PasskeyAuthenticationOptions, value);\n },\n unexpected,\n );\n\n const prepareRegistration = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.prepareRegistration\")(\n function* (input: Input<\"prepareRegistration\">) {\n const fixed = yield* capture(prepareRegistrationInput, input);\n const profile = yield* select(fixed.profile);\n\n const challenge = yield* bytes(fixed.challenge),\n userID = yield* bytes(fixed.userHandle);\n\n const result = yield* maintained(() =>\n generateRegistrationOptions({\n rpName: profile.rpName,\n rpID: profile.rpId,\n userName: fixed.name,\n userDisplayName: fixed.displayName,\n userID,\n challenge,\n timeout: fixed.timeoutMillis,\n attestationType: \"none\",\n supportedAlgorithmIDs: [...profile.algorithms],\n authenticatorSelection: {\n residentKey: profile.residentKey,\n userVerification: profile.userVerification,\n },\n excludeCredentials: fixed.excludedCredentials.map((value) => ({ ...value })),\n }),\n );\n\n const value = yield* decode(PasskeyRegistrationOptions, {\n challenge: result.challenge,\n rp: result.rp,\n user: result.user,\n pubKeyCredParams: result.pubKeyCredParams,\n timeout: result.timeout,\n attestation: result.attestation,\n authenticatorSelection: result.authenticatorSelection,\n excludeCredentials: result.excludeCredentials ?? [],\n });\n\n const expected = {\n challenge: fixed.challenge,\n rp: { id: profile.rpId, name: profile.rpName },\n user: { id: fixed.userHandle, name: fixed.name, displayName: fixed.displayName },\n pubKeyCredParams: profile.algorithms.map((alg) => ({ type: \"public-key\", alg })),\n timeout: fixed.timeoutMillis,\n attestation: \"none\",\n authenticatorSelection: {\n residentKey: profile.residentKey,\n userVerification: profile.userVerification,\n },\n excludeCredentials: fixed.excludedCredentials,\n };\n\n if (\n !(yield* samePasskey(\n PasskeyRegistrationOptions,\n value,\n yield* decode(PasskeyRegistrationOptions, expected),\n ))\n )\n return yield* unavailable();\n\n return yield* capture(PasskeyRegistrationOptions, value);\n },\n unexpected,\n );\n\n const verifyRegistration = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.verifyRegistration\")(\n function* (input: Input<\"verifyRegistration\">) {\n const fixed = yield* capture(registrationInput, input),\n ceremony = fixed.ceremony;\n\n const profile = yield* validateSimpleWebAuthnProfile(ceremony.profile).pipe(\n Effect.mapError(rejected),\n );\n\n if (\n (ceremony.purpose !== \"registration\" || ceremony.context._tag !== \"Registration\") &&\n (ceremony.purpose !== \"enrollment\" || ceremony.context._tag !== \"Enrollment\")\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n const wire = yield* decode(\n Schema.fromJsonString(registrationResponse),\n Redacted.value(fixed.response),\n );\n\n if (wire.id !== wire.rawId) return yield* rejected();\n\n const client = yield* checkClient(\n wire.response.clientDataJSON,\n ceremony,\n \"webauthn.create\",\n );\n\n const attestationBytes = yield* bytes(wire.response.attestationObject);\n\n const attestation = yield* exactMap(\n attestationBytes,\n yield* parsed(() => decodeAttestationObject(new Uint8Array(attestationBytes))),\n );\n\n if (\n attestation.size !== 3 ||\n attestation.get(\"fmt\") !== \"none\" ||\n !attestation.has(\"attStmt\") ||\n !attestation.has(\"authData\")\n )\n return yield* rejected();\n const statement = yield* decode(cborMap, attestation.get(\"attStmt\"));\n\n if (statement.size !== 0) return yield* rejected();\n const authenticatorBytes = yield* decode(byteValue(37, 16384), attestation.get(\"authData\"));\n const auth = yield* checkAuthenticator(authenticatorBytes, true, profile);\n const id = yield* decode(byteValue(1, 1023), auth.credentialID);\n const key = yield* decode(byteValue(1, 8192), auth.credentialPublicKey);\n\n if (Base64Url.encode(id) !== wire.id) return yield* rejected();\n const algorithm = yield* checkKey(key, profile.algorithms);\n\n const result = yield* maintained(() =>\n verifyRegistrationResponse({\n response: { ...wire, clientExtensionResults: {} },\n expectedChallenge: ceremony.challenge,\n expectedOrigin: [...profile.origins],\n expectedRPID: profile.rpId,\n expectedType: \"webauthn.create\",\n requireUserPresence: true,\n requireUserVerification: profile.userVerification === \"required\",\n supportedAlgorithmIDs: [...profile.algorithms],\n }),\n );\n\n if (!result.verified || result.registrationInfo === undefined) return yield* rejected();\n const info = result.registrationInfo;\n\n if (\n info.fmt !== \"none\" ||\n info.origin !== client.origin ||\n info.rpID !== profile.rpId ||\n info.credential.id !== wire.id ||\n !sameBytes(info.credential.publicKey, key) ||\n info.credential.counter !== auth.counter ||\n info.userVerified !== auth.flags.uv ||\n (info.credentialDeviceType === \"multiDevice\") !== auth.flags.be ||\n info.credentialBackedUp !== auth.flags.bs\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n return yield* capture(\n PasskeyRegistrationVerified,\n yield* decode(PasskeyRegistrationVerified, {\n protocolCredentialId: info.credential.id,\n publicKey: Base64Url.encode(info.credential.publicKey),\n algorithm,\n counter: info.credential.counter,\n userVerified: info.userVerified,\n backupEligible: info.credentialDeviceType === \"multiDevice\",\n backupState: info.credentialBackedUp,\n }),\n );\n },\n unexpected,\n );\n\n const verifyAuthentication = Effect.fn(\"SimpleWebAuthnPasskeyProtocol.verifyAuthentication\")(\n function* (input: Input<\"verifyAuthentication\">) {\n const fixed = yield* capture(authenticationInput, input),\n ceremony = fixed.ceremony,\n credential = fixed.credential;\n\n const profile = yield* validateSimpleWebAuthnProfile(ceremony.profile).pipe(\n Effect.mapError(rejected),\n ),\n original = yield* validateSimpleWebAuthnProfile(credential.profile).pipe(\n Effect.mapError(rejected),\n );\n\n const contexts = {\n \"sign-in\": \"SignIn\",\n pending: \"Pending\",\n \"step-up\": \"StepUp\",\n action: \"Action\",\n } as const;\n\n if (\n ceremony.purpose === \"registration\" ||\n ceremony.purpose === \"enrollment\" ||\n contexts[ceremony.purpose] !== ceremony.context._tag ||\n !credential.active ||\n credential.rpId !== profile.rpId ||\n original.rpId !== profile.rpId ||\n !original.algorithms.includes(credential.algorithm) ||\n !profile.algorithms.includes(credential.algorithm)\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n const wire = yield* decode(\n Schema.fromJsonString(authenticationResponse),\n Redacted.value(fixed.response),\n );\n\n const { userHandle: rawUserHandle, ...assertionResponse } = wire.response;\n const userHandle = rawUserHandle ?? undefined;\n\n if (\n wire.id !== wire.rawId ||\n wire.id !== credential.protocolCredentialId ||\n (userHandle !== undefined && userHandle !== credential.userHandle) ||\n (ceremony.context._tag === \"SignIn\" && userHandle === undefined) ||\n (ceremony.allowedCredentials.length > 0 &&\n !ceremony.allowedCredentials.some((value) => value.id === wire.id))\n )\n return yield* rejected();\n const client = yield* checkClient(wire.response.clientDataJSON, ceremony, \"webauthn.get\");\n\n const auth = yield* checkAuthenticator(\n yield* bytes(wire.response.authenticatorData),\n false,\n profile,\n );\n\n if (\n auth.flags.be !== credential.backupEligible ||\n (!credential.backupEligible &&\n (credential.counter > 0 || auth.counter > 0) &&\n auth.counter <= credential.counter)\n )\n return yield* rejected();\n const publicKey = yield* bytes(credential.publicKey);\n\n yield* checkKey(publicKey, profile.algorithms, credential.algorithm);\n\n const result = yield* maintained(() =>\n verifyAuthenticationResponse({\n response: {\n ...wire,\n response: {\n ...assertionResponse,\n ...(userHandle === undefined ? {} : { userHandle }),\n },\n clientExtensionResults: {},\n },\n expectedChallenge: ceremony.challenge,\n expectedOrigin: [...profile.origins],\n expectedRPID: profile.rpId,\n expectedType: \"webauthn.get\",\n requireUserVerification: profile.userVerification === \"required\",\n credential: {\n id: credential.protocolCredentialId,\n publicKey,\n counter: credential.backupEligible ? 0 : credential.counter,\n },\n }),\n );\n\n if (!result.verified) return yield* rejected();\n const info = result.authenticationInfo;\n\n if (\n info.credentialID !== wire.id ||\n info.origin !== client.origin ||\n info.rpID !== profile.rpId ||\n info.newCounter !== auth.counter ||\n info.userVerified !== auth.flags.uv ||\n (info.credentialDeviceType === \"multiDevice\") !== credential.backupEligible ||\n info.credentialBackedUp !== auth.flags.bs\n )\n return yield* rejected();\n yield* fresh(ceremony);\n\n return yield* capture(\n PasskeyAssertionVerified,\n yield* decode(PasskeyAssertionVerified, {\n protocolCredentialId: info.credentialID,\n ...(userHandle === undefined ? {} : { userHandle }),\n counter: info.newCounter,\n userVerified: info.userVerified,\n backupEligible: info.credentialDeviceType === \"multiDevice\",\n backupState: info.credentialBackedUp,\n }),\n );\n },\n unexpected,\n );\n\n return PasskeyProtocol.of({\n prepareAuthentication,\n prepareRegistration,\n verifyRegistration,\n verifyAuthentication,\n });\n },\n);\n\n/** Requires the same host profiles as the strategy. */\nexport const layerSimpleWebAuthnPasskeyProtocol = Layer.effect(\n PasskeyProtocol,\n Effect.flatMap(PasskeyConfig, makeSimpleWebAuthnPasskeyProtocol),\n);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@yielded/auth-simplewebauthn","version":"0.1.0-beta.
|
|
1
|
+
{"name":"@yielded/auth-simplewebauthn","version":"0.1.0-beta.26","dependencies":{"@yielded/auth":"0.1.0-beta.26","tldts":"7.4.11"},"devDependencies":{"@simplewebauthn/browser":"14.0.0","@simplewebauthn/server":"14.0.1","effect":"4.0.0","typescript":"7.0.2","vite-plus":"0.3.3"},"peerDependencies":{"@simplewebauthn/browser":">=14.0.0 <15","@simplewebauthn/server":">=14.0.1 <15","effect":"^4.0.0"},"exports":{"./Browser":{"types":"./dist/Browser.d.mts","default":"./dist/Browser.mjs"},"./Server":{"types":"./dist/Server.d.mts","default":"./dist/Server.mjs"}},"description":"SimpleWebAuthn browser ceremonies and server verification for Yielded Auth.","homepage":"https://yielded.dev/auth/","bugs":{"url":"https://github.com/yielded-dev/auth/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/yielded-dev/auth.git","directory":"packages/auth-simplewebauthn"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test --passWithNoTests"},"peerDependenciesMeta":{"@simplewebauthn/browser":{"optional":true},"@simplewebauthn/server":{"optional":true}}}
|
|
@@ -11,6 +11,70 @@ import { parse } from "tldts";
|
|
|
11
11
|
|
|
12
12
|
import { SimpleWebAuthnPasskeyProtocolOptions } from "./models";
|
|
13
13
|
|
|
14
|
+
/** Validate an immutable profile selected at issue. Verification intentionally
|
|
15
|
+
* accepts that persisted selection through its ceremony lifetime during deploys. */
|
|
16
|
+
export const validateSimpleWebAuthnProfile = Effect.fn("validateSimpleWebAuthnProfile")(function* (
|
|
17
|
+
input: PasskeyProfile,
|
|
18
|
+
) {
|
|
19
|
+
const profile = yield* snapshotPasskey(PasskeyProfile, input).pipe(
|
|
20
|
+
Effect.mapError(() => PasskeyConfigurationError.make({})),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (
|
|
24
|
+
new Set(profile.origins).size !== profile.origins.length ||
|
|
25
|
+
new Set(profile.algorithms).size !== profile.algorithms.length ||
|
|
26
|
+
(profile.primarySignIn &&
|
|
27
|
+
(profile.residentKey !== "required" || profile.userVerification !== "required"))
|
|
28
|
+
) {
|
|
29
|
+
return yield* PasskeyConfigurationError.make({});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const valid = yield* Effect.try({
|
|
33
|
+
try: () => {
|
|
34
|
+
const host = new URL(`https://${profile.rpId}`).hostname;
|
|
35
|
+
|
|
36
|
+
if (host !== profile.rpId) return false;
|
|
37
|
+
|
|
38
|
+
const domain = parse(host, {
|
|
39
|
+
allowIcannDomains: true,
|
|
40
|
+
allowPrivateDomains: true,
|
|
41
|
+
extractHostname: false,
|
|
42
|
+
validateHostname: true,
|
|
43
|
+
detectIp: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
host === "localhost"
|
|
48
|
+
? !profile.developmentLocalhost
|
|
49
|
+
: domain.isIp || domain.domain === null || domain.publicSuffix === host
|
|
50
|
+
)
|
|
51
|
+
return false;
|
|
52
|
+
|
|
53
|
+
return profile.origins.every((origin) => {
|
|
54
|
+
const url = new URL(origin);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
url.origin === origin &&
|
|
58
|
+
url.username === "" &&
|
|
59
|
+
url.password === "" &&
|
|
60
|
+
(url.protocol === "https:" ||
|
|
61
|
+
(profile.developmentLocalhost &&
|
|
62
|
+
profile.rpId === "localhost" &&
|
|
63
|
+
url.hostname === "localhost" &&
|
|
64
|
+
url.protocol === "http:")) &&
|
|
65
|
+
(url.hostname === profile.rpId ||
|
|
66
|
+
(profile.rpId !== "localhost" && url.hostname.endsWith(`.${profile.rpId}`)))
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
catch: () => PasskeyConfigurationError.make({}),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!valid) return yield* PasskeyConfigurationError.make({});
|
|
74
|
+
|
|
75
|
+
return profile;
|
|
76
|
+
});
|
|
77
|
+
|
|
14
78
|
export const captureSimpleWebAuthnProfiles = Effect.fn("captureSimpleWebAuthnProfiles")(
|
|
15
79
|
function* (options: SimpleWebAuthnPasskeyProtocolOptions) {
|
|
16
80
|
const captured = yield* snapshotPasskey(SimpleWebAuthnPasskeyProtocolOptions, options).pipe(
|
|
@@ -20,69 +84,16 @@ export const captureSimpleWebAuthnProfiles = Effect.fn("captureSimpleWebAuthnPro
|
|
|
20
84
|
const keys = new Set<string>();
|
|
21
85
|
|
|
22
86
|
for (const profile of captured.profiles) {
|
|
23
|
-
const key = `${profile.rpId}/${profile.profileId}
|
|
87
|
+
const key = `${profile.rpId}/${profile.profileId}`;
|
|
24
88
|
|
|
25
|
-
if (
|
|
26
|
-
keys.has(key) ||
|
|
27
|
-
new Set(profile.origins).size !== profile.origins.length ||
|
|
28
|
-
new Set(profile.algorithms).size !== profile.algorithms.length ||
|
|
29
|
-
(profile.primarySignIn &&
|
|
30
|
-
(profile.residentKey !== "required" || profile.userVerification !== "required"))
|
|
31
|
-
) {
|
|
32
|
-
return yield* PasskeyConfigurationError.make({});
|
|
33
|
-
}
|
|
89
|
+
if (keys.has(key)) return yield* PasskeyConfigurationError.make({});
|
|
34
90
|
keys.add(key);
|
|
35
|
-
|
|
36
|
-
const valid = yield* Effect.try({
|
|
37
|
-
try: () => {
|
|
38
|
-
const host = new URL(`https://${profile.rpId}`).hostname;
|
|
39
|
-
|
|
40
|
-
if (host !== profile.rpId) return false;
|
|
41
|
-
|
|
42
|
-
const domain = parse(host, {
|
|
43
|
-
allowIcannDomains: true,
|
|
44
|
-
allowPrivateDomains: true,
|
|
45
|
-
extractHostname: false,
|
|
46
|
-
validateHostname: true,
|
|
47
|
-
detectIp: true,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
host === "localhost"
|
|
52
|
-
? !profile.developmentLocalhost
|
|
53
|
-
: domain.isIp || domain.domain === null || domain.publicSuffix === host
|
|
54
|
-
)
|
|
55
|
-
return false;
|
|
56
|
-
|
|
57
|
-
return profile.origins.every((origin) => {
|
|
58
|
-
const url = new URL(origin);
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
url.origin === origin &&
|
|
62
|
-
url.username === "" &&
|
|
63
|
-
url.password === "" &&
|
|
64
|
-
(url.protocol === "https:" ||
|
|
65
|
-
(profile.developmentLocalhost &&
|
|
66
|
-
profile.rpId === "localhost" &&
|
|
67
|
-
url.hostname === "localhost" &&
|
|
68
|
-
url.protocol === "http:")) &&
|
|
69
|
-
(url.hostname === profile.rpId ||
|
|
70
|
-
(profile.rpId !== "localhost" && url.hostname.endsWith(`.${profile.rpId}`)))
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
},
|
|
74
|
-
catch: () => PasskeyConfigurationError.make({}),
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
if (!valid) return yield* PasskeyConfigurationError.make({});
|
|
91
|
+
yield* validateSimpleWebAuthnProfile(profile);
|
|
78
92
|
}
|
|
79
93
|
|
|
80
94
|
return Effect.fn("selectSimpleWebAuthnProfile")(function* (input: PasskeyProfile) {
|
|
81
95
|
const profile = captured.profiles.find(
|
|
82
|
-
(value) =>
|
|
83
|
-
value.rpId === input.rpId &&
|
|
84
|
-
value.profileId === input.profileId &&
|
|
85
|
-
value.generation === input.generation,
|
|
96
|
+
(value) => value.rpId === input.rpId && value.profileId === input.profileId,
|
|
86
97
|
);
|
|
87
98
|
|
|
88
99
|
if (profile === undefined || !(yield* samePasskey(PasskeyProfile, profile, input)))
|
|
@@ -40,7 +40,7 @@ import type { Context } from "effect";
|
|
|
40
40
|
import { Cause, DateTime, Effect, Layer, Redacted, Schema } from "effect";
|
|
41
41
|
import { Base64Url } from "effect/encoding";
|
|
42
42
|
|
|
43
|
-
import { captureSimpleWebAuthnProfiles } from "./configuration";
|
|
43
|
+
import { captureSimpleWebAuthnProfiles, validateSimpleWebAuthnProfile } from "./configuration";
|
|
44
44
|
import type { SimpleWebAuthnPasskeyProtocolOptions } from "./models";
|
|
45
45
|
|
|
46
46
|
type Protocol = Context.Service.Shape<typeof PasskeyProtocol>;
|
|
@@ -511,7 +511,9 @@ export const makeSimpleWebAuthnPasskeyProtocol = Effect.fn("makeSimpleWebAuthnPa
|
|
|
511
511
|
const fixed = yield* capture(registrationInput, input),
|
|
512
512
|
ceremony = fixed.ceremony;
|
|
513
513
|
|
|
514
|
-
const profile = yield*
|
|
514
|
+
const profile = yield* validateSimpleWebAuthnProfile(ceremony.profile).pipe(
|
|
515
|
+
Effect.mapError(rejected),
|
|
516
|
+
);
|
|
515
517
|
|
|
516
518
|
if (
|
|
517
519
|
(ceremony.purpose !== "registration" || ceremony.context._tag !== "Registration") &&
|
|
@@ -610,8 +612,12 @@ export const makeSimpleWebAuthnPasskeyProtocol = Effect.fn("makeSimpleWebAuthnPa
|
|
|
610
612
|
ceremony = fixed.ceremony,
|
|
611
613
|
credential = fixed.credential;
|
|
612
614
|
|
|
613
|
-
const profile = yield*
|
|
614
|
-
|
|
615
|
+
const profile = yield* validateSimpleWebAuthnProfile(ceremony.profile).pipe(
|
|
616
|
+
Effect.mapError(rejected),
|
|
617
|
+
),
|
|
618
|
+
original = yield* validateSimpleWebAuthnProfile(credential.profile).pipe(
|
|
619
|
+
Effect.mapError(rejected),
|
|
620
|
+
);
|
|
615
621
|
|
|
616
622
|
const contexts = {
|
|
617
623
|
"sign-in": "SignIn",
|