@knpkv/browser-pairing 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 knpkv
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # @knpkv/browser-pairing
2
+
3
+ Typed, redacted browser-pairing credentials and transport primitives. Product
4
+ packages keep their own persistence, authorization, and route policy.
5
+
6
+ `PairingCode`, `SessionToken`, and `CsrfToken` are distinct branded roles over
7
+ the same validated credential encoding. Consumers must issue and decode the
8
+ role they need; the generic `BrowserCredential` is only a pre-role transport
9
+ value.
10
+
11
+ Typed cookie serialization accepts only a redacted `SessionToken`; JavaScript
12
+ callers still reach the runtime credential and attribute validation boundary.
@@ -0,0 +1,28 @@
1
+ import { Crypto, Effect, Redacted, Schema } from "effect";
2
+ import { CredentialDigest } from "./schema.js";
3
+ declare const BrowserPairingError_base: Schema.Class<BrowserPairingError, Schema.TaggedStruct<"BrowserPairingError", {
4
+ readonly reason: Schema.Literals<readonly ["credential-rejected", "crypto-failed", "invalid-lifetime"]>;
5
+ }>, import("effect/Cause").YieldableError>;
6
+ /** Shared failure class; applications map it to their local HTTP/domain errors. */
7
+ export declare class BrowserPairingError extends BrowserPairingError_base {
8
+ }
9
+ /** Produce a fresh generic browser credential using the caller's platform crypto service. */
10
+ export declare const issueCredential: () => Effect.Effect<Redacted.Redacted<string & import("effect/Brand").Brand<"BrowserCredential">>, BrowserPairingError, Crypto.Crypto>;
11
+ /** Produce a pairing credential while preserving its role brand. */
12
+ export declare const issuePairingCode: () => Effect.Effect<Redacted.Redacted<string & import("effect/Brand").Brand<"PairingCode">>, BrowserPairingError, Crypto.Crypto>;
13
+ /** Produce a session credential while preserving its role brand. */
14
+ export declare const issueSessionToken: () => Effect.Effect<Redacted.Redacted<string & import("effect/Brand").Brand<"SessionToken">>, BrowserPairingError, Crypto.Crypto>;
15
+ /** Produce a CSRF credential while preserving its role brand. */
16
+ export declare const issueCsrfToken: () => Effect.Effect<Redacted.Redacted<string & import("effect/Brand").Brand<"CsrfToken">>, BrowserPairingError, Crypto.Crypto>;
17
+ /** Hash a credential for durable persistence without retaining its plaintext. */
18
+ export declare const hashCredential: (credential: Redacted.Redacted<string>) => Effect.Effect<string & import("effect/Brand").Brand<"CredentialDigest">, BrowserPairingError, Crypto.Crypto>;
19
+ /** Verify a supplied credential against a stored digest in constant time. */
20
+ export declare const verifyCredentialDigest: (credential: Redacted.Redacted<string>, expectedDigest: string) => Effect.Effect<undefined, BrowserPairingError, Crypto.Crypto>;
21
+ /** Compare validated digests without an early-exit string comparison. */
22
+ export declare const credentialDigestsEqual: (left: CredentialDigest, right: CredentialDigest) => boolean;
23
+ /** Compare two untrusted credential strings without exposing length or content. */
24
+ export declare const credentialValuesEqual: (left: string, right: string) => boolean;
25
+ /** Validate and calculate a bounded expiry at the edge of an issuance operation. */
26
+ export declare const expiresAt: (now: number, lifetimeMs: number) => Effect.Effect<number, BrowserPairingError, never>;
27
+ export {};
28
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEjE,OAAO,EAAqB,gBAAgB,EAAwC,MAAM,aAAa,CAAA;;;;AAIvG,mFAAmF;AACnF,qBAAa,mBAAoB,SAAQ,wBAEvC;CAAG;AAgBL,6FAA6F;AAC7F,eAAO,MAAM,eAAe,wIAE1B,CAAA;AAEF,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,kIAE3B,CAAA;AAEF,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,mIAE5B,CAAA;AAEF,iEAAiE;AACjE,eAAO,MAAM,cAAc,gIAEzB,CAAA;AAEF,iFAAiF;AACjF,eAAO,MAAM,cAAc,yJAczB,CAAA;AAWF,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,iIAiBjC,CAAA;AAEF,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,SAAU,gBAAgB,SAAS,gBAAgB,KAAG,OAKxF,CAAA;AAED,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,SAAU,MAAM,SAAS,MAAM,KAAG,OAQnE,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,SAAS,wFAOpB,CAAA"}
package/dist/crypto.js ADDED
@@ -0,0 +1,86 @@
1
+ import { Crypto, Effect, Redacted, Result, Schema } from "effect";
2
+ import { Hex } from "effect/encoding";
3
+ import { BrowserCredential, CredentialDigest, CsrfToken, PairingCode, SessionToken } from "./schema.js";
4
+ const CREDENTIAL_BYTES = 32;
5
+ /** Shared failure class; applications map it to their local HTTP/domain errors. */
6
+ export class BrowserPairingError extends Schema.TaggedError()("BrowserPairingError", {
7
+ reason: Schema.Literals(["credential-rejected", "crypto-failed", "invalid-lifetime"])
8
+ }) {
9
+ }
10
+ const issueCredentialFor = (decode) => Effect.gen(function* () {
11
+ const cryptoService = yield* Crypto.Crypto;
12
+ const bytes = yield* cryptoService.randomBytes(CREDENTIAL_BYTES).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "crypto-failed" })));
13
+ const decoded = yield* decode(Hex.encode(bytes)).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "crypto-failed" })));
14
+ return Redacted.make(decoded);
15
+ });
16
+ /** Produce a fresh generic browser credential using the caller's platform crypto service. */
17
+ export const issueCredential = Effect.fn("BrowserPairing.issueCredential")(function* () {
18
+ return yield* issueCredentialFor(Schema.decodeUnknownEffect(BrowserCredential));
19
+ });
20
+ /** Produce a pairing credential while preserving its role brand. */
21
+ export const issuePairingCode = Effect.fn("BrowserPairing.issuePairingCode")(function* () {
22
+ return yield* issueCredentialFor(Schema.decodeUnknownEffect(PairingCode));
23
+ });
24
+ /** Produce a session credential while preserving its role brand. */
25
+ export const issueSessionToken = Effect.fn("BrowserPairing.issueSessionToken")(function* () {
26
+ return yield* issueCredentialFor(Schema.decodeUnknownEffect(SessionToken));
27
+ });
28
+ /** Produce a CSRF credential while preserving its role brand. */
29
+ export const issueCsrfToken = Effect.fn("BrowserPairing.issueCsrfToken")(function* () {
30
+ return yield* issueCredentialFor(Schema.decodeUnknownEffect(CsrfToken));
31
+ });
32
+ /** Hash a credential for durable persistence without retaining its plaintext. */
33
+ export const hashCredential = Effect.fn("BrowserPairing.hashCredential")(function* (credential) {
34
+ const decoded = yield* Schema.decodeUnknownEffect(BrowserCredential)(Redacted.value(credential)).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "credential-rejected" })));
35
+ const bytes = yield* Effect.fromResult(Hex.decode(decoded)).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "credential-rejected" })));
36
+ const cryptoService = yield* Crypto.Crypto;
37
+ const digest = yield* cryptoService.digest("SHA-256", bytes).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "crypto-failed" })));
38
+ return CredentialDigest.make(Hex.encode(digest));
39
+ });
40
+ const fixedTimeEqual = (left, right) => {
41
+ let difference = left.byteLength ^ right.byteLength;
42
+ const length = Math.max(left.byteLength, right.byteLength);
43
+ for (let index = 0; index < length; index += 1) {
44
+ difference |= (left[index] ?? 0) ^ (right[index] ?? 0);
45
+ }
46
+ return difference === 0;
47
+ };
48
+ /** Verify a supplied credential against a stored digest in constant time. */
49
+ export const verifyCredentialDigest = Effect.fn("BrowserPairing.verifyCredentialDigest")(function* (credential, expectedDigest) {
50
+ const expected = yield* Schema.decodeUnknownEffect(CredentialDigest)(expectedDigest).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "credential-rejected" })));
51
+ const actual = yield* hashCredential(credential);
52
+ const actualBytes = yield* Effect.fromResult(Hex.decode(actual)).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "credential-rejected" })));
53
+ const expectedBytes = yield* Effect.fromResult(Hex.decode(expected)).pipe(Effect.mapError(() => new BrowserPairingError({ reason: "credential-rejected" })));
54
+ if (!fixedTimeEqual(actualBytes, expectedBytes)) {
55
+ return yield* new BrowserPairingError({ reason: "credential-rejected" });
56
+ }
57
+ });
58
+ /** Compare validated digests without an early-exit string comparison. */
59
+ export const credentialDigestsEqual = (left, right) => {
60
+ const leftBytes = Hex.decode(left);
61
+ const rightBytes = Hex.decode(right);
62
+ return Result.isSuccess(leftBytes) && Result.isSuccess(rightBytes) &&
63
+ fixedTimeEqual(leftBytes.success, rightBytes.success);
64
+ };
65
+ /** Compare two untrusted credential strings without exposing length or content. */
66
+ export const credentialValuesEqual = (left, right) => {
67
+ const decodedLeft = Schema.decodeUnknownResult(BrowserCredential)(left);
68
+ const decodedRight = Schema.decodeUnknownResult(BrowserCredential)(right);
69
+ if (Result.isFailure(decodedLeft) || Result.isFailure(decodedRight))
70
+ return false;
71
+ const leftBytes = Hex.decode(decodedLeft.success);
72
+ const rightBytes = Hex.decode(decodedRight.success);
73
+ return Result.isSuccess(leftBytes) && Result.isSuccess(rightBytes) &&
74
+ fixedTimeEqual(leftBytes.success, rightBytes.success);
75
+ };
76
+ /** Validate and calculate a bounded expiry at the edge of an issuance operation. */
77
+ export const expiresAt = Effect.fn("BrowserPairing.expiresAt")(function* (now, lifetimeMs) {
78
+ if (!Number.isSafeInteger(now) || !Number.isSafeInteger(lifetimeMs) || lifetimeMs <= 0) {
79
+ return yield* new BrowserPairingError({ reason: "invalid-lifetime" });
80
+ }
81
+ const expiry = now + lifetimeMs;
82
+ if (!Number.isSafeInteger(expiry))
83
+ return yield* new BrowserPairingError({ reason: "invalid-lifetime" });
84
+ return expiry;
85
+ });
86
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACjE,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEvG,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAE3B,mFAAmF;AACnF,MAAM,OAAO,mBAAoB,SAAQ,MAAM,CAAC,WAAW,EAAuB,CAAC,qBAAqB,EAAE;IACxG,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;CACtF,CAAC;CAAG;AAEL,MAAM,kBAAkB,GAAG,CACzB,MAA+E,EAC/E,EAAE,CACF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,IAAI,CACnE,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC,CAC5E,CAAA;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CACnD,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC,CAC5E,CAAA;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AAC/B,CAAC,CAAC,CAAA;AAEJ,6FAA6F;AAC7F,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,gCAAgC,CAAC,CAAC,QAAQ,CAAC;IAClF,OAAO,KAAK,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC,CAAA;AACjF,CAAC,CAAC,CAAA;AAEF,oEAAoE;AACpE,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,iCAAiC,CAAC,CAAC,QAAQ,CAAC;IACpF,OAAO,KAAK,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAA;AAC3E,CAAC,CAAC,CAAA;AAEF,oEAAoE;AACpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,kCAAkC,CAAC,CAAC,QAAQ,CAAC;IACtF,OAAO,KAAK,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAA;AAC5E,CAAC,CAAC,CAAA;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC,QAAQ,CAAC;IAChF,OAAO,KAAK,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAA;AACzE,CAAC,CAAC,CAAA;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC,QAAQ,CAAC,EAChF,UAAqC;IAErC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CACnG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAClF,CAAA;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAC9D,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAClF,CAAA;IACD,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,IAAI,CAC/D,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC,CAC5E,CAAA;IACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;AAClD,CAAC,CAAC,CAAA;AAEF,MAAM,cAAc,GAAG,CAAC,IAAgB,EAAE,KAAiB,EAAW,EAAE;IACtE,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IAC1D,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC/C,UAAU,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,UAAU,KAAK,CAAC,CAAA;AACzB,CAAC,CAAA;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,EAAE,CAAC,uCAAuC,CAAC,CAAC,QAAQ,CAAC,EAChG,UAAqC,EACrC,cAAsB;IAEtB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAAC,cAAc,CAAC,CAAC,IAAI,CACvF,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAClF,CAAA;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAA;IAChD,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CACnE,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAClF,CAAA;IACD,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CACvE,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAClF,CAAA;IACD,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAA;IAC1E,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,IAAsB,EAAE,KAAuB,EAAW,EAAE;IACjG,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAClC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACpC,OAAO,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;QAChE,cAAc,CAAC,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;AACzD,CAAC,CAAA;AAED,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAAE,KAAa,EAAW,EAAE;IAC5E,MAAM,WAAW,GAAG,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAA;IACvE,MAAM,YAAY,GAAG,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,CAAA;IACzE,IAAI,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC;QAAE,OAAO,KAAK,CAAA;IACjF,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;IACnD,OAAO,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;QAChE,cAAc,CAAC,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;AACzD,CAAC,CAAA;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EAAC,GAAW,EAAE,UAAkB;IACtG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACvF,OAAO,KAAK,CAAC,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAA;IACvE,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,GAAG,UAAU,CAAA;IAC/B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,IAAI,mBAAmB,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAA;IACxG,OAAO,MAAM,CAAA;AACf,CAAC,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from "./crypto.js";
2
+ export * from "./schema.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./crypto.js";
2
+ export * from "./schema.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
@@ -0,0 +1,75 @@
1
+ import { Redacted, Schema } from "effect";
2
+ /** A 256-bit browser credential. Keep values redacted except at a transport boundary. */
3
+ export declare const BrowserCredential: Schema.brand<Schema.String, "BrowserCredential">;
4
+ export type BrowserCredential = typeof BrowserCredential.Type;
5
+ /** Pairing code accepted by an unauthenticated confirmation route. */
6
+ export declare const PairingCode: Schema.brand<Schema.String, "PairingCode">;
7
+ export type PairingCode = typeof PairingCode.Type;
8
+ /** Opaque session credential normally delivered through an HttpOnly cookie. */
9
+ export declare const SessionToken: Schema.brand<Schema.String, "SessionToken">;
10
+ export type SessionToken = typeof SessionToken.Type;
11
+ /** Independent mutation proof sent in a request header. */
12
+ export declare const CsrfToken: Schema.brand<Schema.String, "CsrfToken">;
13
+ export type CsrfToken = typeof CsrfToken.Type;
14
+ /** SHA-256 digest persisted instead of a browser credential. */
15
+ export declare const CredentialDigest: Schema.brand<Schema.String, "CredentialDigest">;
16
+ export type CredentialDigest = typeof CredentialDigest.Type;
17
+ /** Credential-bearing confirmation payload containing the one-time pairing proof. */
18
+ export declare const PairingConfirmationRequest: Schema.Struct<{
19
+ readonly pairingCode: Schema.brand<Schema.String, "PairingCode">;
20
+ }>;
21
+ export type PairingConfirmationRequest = typeof PairingConfirmationRequest.Type;
22
+ /** Credential-bearing response containing the CSRF mutation proof. */
23
+ export declare const PairingConfirmationResponse: Schema.Struct<{
24
+ readonly csrfToken: Schema.brand<Schema.String, "CsrfToken">;
25
+ }>;
26
+ export type PairingConfirmationResponse = typeof PairingConfirmationResponse.Type;
27
+ export declare const OneTimeCredentialState: Schema.Struct<{
28
+ readonly expiresAt: Schema.Number;
29
+ readonly consumedAt: Schema.NullOr<Schema.Number>;
30
+ readonly revokedAt: Schema.NullOr<Schema.Number>;
31
+ }>;
32
+ export type OneTimeCredentialState = typeof OneTimeCredentialState.Type;
33
+ export type OneTimeCredentialDecision = "accepted" | "expired" | "consumed" | "revoked" | "invalid";
34
+ /** Expiry is inclusive: a credential at its deadline is no longer usable. */
35
+ export declare const isExpired: (now: number, expiry: number) => boolean;
36
+ /** Pure state transition guard reusable by durable and in-memory stores. */
37
+ export declare const decideOneTimeCredential: (state: OneTimeCredentialState, now: number) => OneTimeCredentialDecision;
38
+ declare const CredentialCookieError_base: Schema.Class<CredentialCookieError, Schema.TaggedStruct<"CredentialCookieError", {
39
+ readonly reason: Schema.Literals<readonly ["invalid-credential", "invalid-attribute", "invalid-max-age"]>;
40
+ }>, import("effect/Cause").YieldableError>;
41
+ /** A cookie value or attribute contained a delimiter, control character, or invalid credential. */
42
+ export declare class CredentialCookieError extends CredentialCookieError_base {
43
+ }
44
+ interface CredentialCookieOptionsBase {
45
+ readonly name: string;
46
+ readonly path: string;
47
+ readonly httpOnly: boolean;
48
+ readonly sameSite: "strict" | "lax" | "none";
49
+ readonly maxAge?: number;
50
+ }
51
+ /** Cookie policy with an origin required whenever the Secure attribute is enabled. */
52
+ export type CredentialCookieOptions = (CredentialCookieOptionsBase & {
53
+ readonly secure: true;
54
+ readonly sourceOrigin: string;
55
+ }) | (CredentialCookieOptionsBase & {
56
+ readonly secure: false;
57
+ readonly sourceOrigin?: string;
58
+ });
59
+ /** Serialize a validated credential cookie without allowing header injection or invalid lifetimes. */
60
+ export declare const serializeCredentialCookie: (credential: Redacted.Redacted<SessionToken>, options: CredentialCookieOptions) => string;
61
+ /** A credential-bearing bootstrap result; the present token remains redacted until transport. */
62
+ export type BootstrapTokenRead = {
63
+ readonly _tag: "missing";
64
+ } | {
65
+ readonly _tag: "malformed";
66
+ } | {
67
+ readonly _tag: "present";
68
+ readonly token: Redacted.Redacted<PairingCode>;
69
+ };
70
+ /** Read a bootstrap token without silently accepting malformed URL fragments. */
71
+ export declare const readBootstrapToken: (hash: string) => BootstrapTokenRead;
72
+ /** Remove a bootstrap fragment while retaining the current route and query. */
73
+ export declare const bootstrapRouteWithoutToken: (pathname: string, search: string) => string;
74
+ export {};
75
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/dist/schema.js ADDED
@@ -0,0 +1,158 @@
1
+ import { Redacted, Result, Schema } from "effect";
2
+ const CREDENTIAL_PATTERN = /^[0-9a-f]{64}$/u;
3
+ const COOKIE_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/u;
4
+ const CredentialValue = Schema.String.check(Schema.isPattern(CREDENTIAL_PATTERN, { expected: "a lowercase 256-bit browser credential" }));
5
+ /** A 256-bit browser credential. Keep values redacted except at a transport boundary. */
6
+ export const BrowserCredential = CredentialValue.pipe(Schema.brand("BrowserCredential"));
7
+ /** Pairing code accepted by an unauthenticated confirmation route. */
8
+ export const PairingCode = CredentialValue.pipe(Schema.brand("PairingCode"));
9
+ /** Opaque session credential normally delivered through an HttpOnly cookie. */
10
+ export const SessionToken = CredentialValue.pipe(Schema.brand("SessionToken"));
11
+ /** Independent mutation proof sent in a request header. */
12
+ export const CsrfToken = CredentialValue.pipe(Schema.brand("CsrfToken"));
13
+ /** SHA-256 digest persisted instead of a browser credential. */
14
+ export const CredentialDigest = Schema.String.check(Schema.isPattern(CREDENTIAL_PATTERN, { expected: "a lowercase SHA-256 credential digest" })).pipe(Schema.brand("CredentialDigest"));
15
+ /** Credential-bearing confirmation payload containing the one-time pairing proof. */
16
+ export const PairingConfirmationRequest = Schema.Struct({ pairingCode: PairingCode });
17
+ /** Credential-bearing response containing the CSRF mutation proof. */
18
+ export const PairingConfirmationResponse = Schema.Struct({ csrfToken: CsrfToken });
19
+ export const OneTimeCredentialState = Schema.Struct({
20
+ expiresAt: Schema.Number.check(Schema.isInt()),
21
+ consumedAt: Schema.NullOr(Schema.Number.check(Schema.isInt())),
22
+ revokedAt: Schema.NullOr(Schema.Number.check(Schema.isInt()))
23
+ });
24
+ /** Expiry is inclusive: a credential at its deadline is no longer usable. */
25
+ export const isExpired = (now, expiry) => !Number.isSafeInteger(now) || !Number.isSafeInteger(expiry) || now >= expiry;
26
+ /** Pure state transition guard reusable by durable and in-memory stores. */
27
+ export const decideOneTimeCredential = (state, now) => {
28
+ if (!Number.isSafeInteger(now) ||
29
+ !Number.isSafeInteger(state.expiresAt) ||
30
+ (state.consumedAt !== null && !Number.isSafeInteger(state.consumedAt)) ||
31
+ (state.revokedAt !== null && !Number.isSafeInteger(state.revokedAt)))
32
+ return "invalid";
33
+ if (state.revokedAt !== null)
34
+ return "revoked";
35
+ if (state.consumedAt !== null)
36
+ return "consumed";
37
+ if (isExpired(now, state.expiresAt))
38
+ return "expired";
39
+ return "accepted";
40
+ };
41
+ /** A cookie value or attribute contained a delimiter, control character, or invalid credential. */
42
+ export class CredentialCookieError extends Schema.TaggedError()("CredentialCookieError", { reason: Schema.Literals(["invalid-credential", "invalid-attribute", "invalid-max-age"]) }) {
43
+ }
44
+ const CredentialCookieOptionsSchema = Schema.Union([
45
+ Schema.Struct({
46
+ name: Schema.String,
47
+ path: Schema.String,
48
+ httpOnly: Schema.Boolean,
49
+ sameSite: Schema.Literals(["strict", "lax", "none"]),
50
+ secure: Schema.Literal(true),
51
+ maxAge: Schema.optionalKey(Schema.Number),
52
+ sourceOrigin: Schema.String
53
+ }),
54
+ Schema.Struct({
55
+ name: Schema.String,
56
+ path: Schema.String,
57
+ httpOnly: Schema.Boolean,
58
+ sameSite: Schema.Literals(["strict", "lax", "none"]),
59
+ secure: Schema.Literal(false),
60
+ maxAge: Schema.optionalKey(Schema.Number),
61
+ sourceOrigin: Schema.optionalKey(Schema.String)
62
+ })
63
+ ]);
64
+ const isLoopbackIpv4 = (hostname) => {
65
+ const octets = hostname.split(".");
66
+ return octets.length === 4 &&
67
+ octets[0] === "127" &&
68
+ octets.slice(1).every((octet) => /^\d{1,3}$/u.test(octet) && Number(octet) <= 255);
69
+ };
70
+ const isChromiumLocalhost = (hostname) => {
71
+ const normalized = hostname.toLowerCase();
72
+ return isLoopbackIpv4(normalized) ||
73
+ normalized === "[::1]" ||
74
+ normalized === "localhost" ||
75
+ normalized === "localhost." ||
76
+ normalized.endsWith(".localhost") ||
77
+ normalized.endsWith(".localhost.");
78
+ };
79
+ /** Serialize a validated credential cookie without allowing header injection or invalid lifetimes. */
80
+ export const serializeCredentialCookie = (credential, options) => {
81
+ const value = Redacted.isRedacted(credential) ? Redacted.value(credential) : String(credential);
82
+ const decoded = Schema.decodeUnknownResult(BrowserCredential)(value);
83
+ if (Result.isFailure(decoded))
84
+ throw new CredentialCookieError({ reason: "invalid-credential" });
85
+ const decodedOptions = Schema.decodeUnknownResult(CredentialCookieOptionsSchema)(options);
86
+ if (Result.isFailure(decodedOptions))
87
+ throw new CredentialCookieError({ reason: "invalid-attribute" });
88
+ const validatedOptions = decodedOptions.success;
89
+ const invalidPath = (value) => value.length === 0 || Array.from(value).some((character) => {
90
+ const code = character.charCodeAt(0);
91
+ return !((code >= 0x20 && code <= 0x3a) || (code >= 0x3c && code <= 0x7e));
92
+ });
93
+ const normalizedName = validatedOptions.name.toLowerCase();
94
+ const hasRecognizedPrefix = normalizedName.startsWith("__host-http-") ||
95
+ normalizedName.startsWith("__http-") ||
96
+ normalizedName.startsWith("__host-") ||
97
+ normalizedName.startsWith("__secure-");
98
+ const sourceUrl = validatedOptions.sourceOrigin !== undefined && URL.canParse(validatedOptions.sourceOrigin)
99
+ ? new URL(validatedOptions.sourceOrigin)
100
+ : undefined;
101
+ const sourceOriginIsTrustworthy = sourceUrl !== undefined &&
102
+ (sourceUrl.protocol === "https:" ||
103
+ (sourceUrl.protocol === "http:" && isChromiumLocalhost(sourceUrl.hostname)));
104
+ const invalidSecureSource = validatedOptions.secure &&
105
+ (validatedOptions.sourceOrigin === undefined || !sourceOriginIsTrustworthy);
106
+ const invalidReservedPrefix = (normalizedName.startsWith("__host-http-") &&
107
+ (!validatedOptions.secure || !validatedOptions.httpOnly || validatedOptions.path !== "/")) ||
108
+ (normalizedName.startsWith("__http-") && (!validatedOptions.secure || !validatedOptions.httpOnly)) ||
109
+ (normalizedName.startsWith("__host-") && (!validatedOptions.secure || validatedOptions.path !== "/")) ||
110
+ (normalizedName.startsWith("__secure-") && !validatedOptions.secure) ||
111
+ (hasRecognizedPrefix && !sourceOriginIsTrustworthy);
112
+ if (!COOKIE_NAME_PATTERN.test(validatedOptions.name) ||
113
+ invalidPath(validatedOptions.path) ||
114
+ !validatedOptions.path.startsWith("/") ||
115
+ invalidReservedPrefix ||
116
+ invalidSecureSource ||
117
+ (validatedOptions.sourceOrigin !== undefined && sourceUrl === undefined)) {
118
+ throw new CredentialCookieError({ reason: "invalid-attribute" });
119
+ }
120
+ if (validatedOptions.maxAge !== undefined &&
121
+ (!Number.isSafeInteger(validatedOptions.maxAge) || validatedOptions.maxAge < 0)) {
122
+ throw new CredentialCookieError({ reason: "invalid-max-age" });
123
+ }
124
+ if (validatedOptions.sameSite === "none" && !validatedOptions.secure) {
125
+ throw new CredentialCookieError({ reason: "invalid-attribute" });
126
+ }
127
+ const sameSite = validatedOptions.sameSite === "strict"
128
+ ? "Strict"
129
+ : validatedOptions.sameSite === "lax"
130
+ ? "Lax"
131
+ : validatedOptions.sameSite === "none"
132
+ ? "None"
133
+ : undefined;
134
+ if (sameSite === undefined)
135
+ throw new CredentialCookieError({ reason: "invalid-attribute" });
136
+ const parts = [
137
+ `${validatedOptions.name}=${decoded.success}`,
138
+ ...(validatedOptions.httpOnly ? ["HttpOnly"] : []),
139
+ `Path=${validatedOptions.path}`,
140
+ `SameSite=${sameSite}`,
141
+ ...(validatedOptions.secure ? ["Secure"] : []),
142
+ ...(validatedOptions.maxAge === undefined ? [] : [`Max-Age=${String(validatedOptions.maxAge)}`])
143
+ ];
144
+ return parts.join("; ");
145
+ };
146
+ /** Read a bootstrap token without silently accepting malformed URL fragments. */
147
+ export const readBootstrapToken = (hash) => {
148
+ const value = new URLSearchParams(hash.startsWith("#") ? hash.slice(1) : hash).get("bootstrap_token");
149
+ if (value === null)
150
+ return { _tag: "missing" };
151
+ const decoded = Schema.decodeUnknownResult(PairingCode)(value);
152
+ return Result.isSuccess(decoded)
153
+ ? { _tag: "present", token: Redacted.make(decoded.success) }
154
+ : { _tag: "malformed" };
155
+ };
156
+ /** Remove a bootstrap fragment while retaining the current route and query. */
157
+ export const bootstrapRouteWithoutToken = (pathname, search) => `${pathname}${search}`;
158
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@knpkv/browser-pairing",
3
+ "version": "0.2.0",
4
+ "description": "Typed browser pairing credentials and transport primitives",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/knpkv/npm.git",
9
+ "directory": "packages/browser-pairing"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": false,
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./schema": {
21
+ "types": "./dist/schema.d.ts",
22
+ "import": "./dist/schema.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "peerDependencies": {
33
+ "effect": "4.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@effect/vitest": "4.0.0",
37
+ "@types/node": "^26.6.4",
38
+ "effect": "4.0.0",
39
+ "rimraf": "^6.1.3",
40
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
41
+ "vitest": "^5.0.3"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc -b tsconfig.build.json --force",
45
+ "check": "tsc -b tsconfig.json && tsc -p tsconfig.roles.json --noEmit",
46
+ "lint": "eslint src test",
47
+ "test": "vitest run"
48
+ }
49
+ }