@spine-event-engine/auth 2.0.0-snapshot.10
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 +201 -0
- package/README.md +71 -0
- package/REFERENCE.md +94 -0
- package/dist/gateway/dynamic-subscription-creator.d.ts +52 -0
- package/dist/gateway/dynamic-subscription-creator.d.ts.map +1 -0
- package/dist/gateway/dynamic-subscription-creator.js +71 -0
- package/dist/gateway/dynamic-subscription-creator.js.map +1 -0
- package/dist/gateway/dynamic-unary-forwarder.d.ts +130 -0
- package/dist/gateway/dynamic-unary-forwarder.d.ts.map +1 -0
- package/dist/gateway/dynamic-unary-forwarder.js +185 -0
- package/dist/gateway/dynamic-unary-forwarder.js.map +1 -0
- package/dist/gateway/index.d.ts +164 -0
- package/dist/gateway/index.d.ts.map +1 -0
- package/dist/gateway/index.js +195 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/index.d.ts +433 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/native/index.d.ts +182 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +463 -0
- package/dist/native/index.js.map +1 -0
- package/dist/oidc/contracts.d.ts +334 -0
- package/dist/oidc/contracts.d.ts.map +1 -0
- package/dist/oidc/contracts.js +15 -0
- package/dist/oidc/contracts.js.map +1 -0
- package/dist/oidc/index.d.ts +41 -0
- package/dist/oidc/index.d.ts.map +1 -0
- package/dist/oidc/index.js +825 -0
- package/dist/oidc/index.js.map +1 -0
- package/dist/providers/index.d.ts +154 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +574 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/request/index.d.ts +10 -0
- package/dist/request/index.d.ts.map +1 -0
- package/dist/request/index.js +81 -0
- package/dist/request/index.js.map +1 -0
- package/dist/sessions/cookies.d.ts +104 -0
- package/dist/sessions/cookies.d.ts.map +1 -0
- package/dist/sessions/cookies.js +295 -0
- package/dist/sessions/cookies.js.map +1 -0
- package/dist/sessions/opaque.d.ts +163 -0
- package/dist/sessions/opaque.d.ts.map +1 -0
- package/dist/sessions/opaque.js +268 -0
- package/dist/sessions/opaque.js.map +1 -0
- package/dist/sessions/signed.d.ts +245 -0
- package/dist/sessions/signed.d.ts.map +1 -0
- package/dist/sessions/signed.js +534 -0
- package/dist/sessions/signed.js.map +1 -0
- package/dist/subscriptions/index.d.ts +463 -0
- package/dist/subscriptions/index.d.ts.map +1 -0
- package/dist/subscriptions/index.js +814 -0
- package/dist/subscriptions/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, CodeMatters. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
5
|
+
* in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
10
|
+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
11
|
+
* or implied. See the License for the specific language governing permissions and limitations under
|
|
12
|
+
* the License.
|
|
13
|
+
*/
|
|
14
|
+
import { create, fromBinary } from "@bufbuild/protobuf";
|
|
15
|
+
import { ActorContextSchema, CommandSchema } from "@spine-event-engine/proto";
|
|
16
|
+
import { QuerySchema, SubscriptionSchema, TargetSchema, TopicSchema, } from "@spine-event-engine/proto/client";
|
|
17
|
+
import { AnyMessages } from "@spine-event-engine/core";
|
|
18
|
+
/**
|
|
19
|
+
* Decodes the public request envelopes accepted by authentication gateways.
|
|
20
|
+
*/
|
|
21
|
+
export const IncomingRequests = Object.freeze({
|
|
22
|
+
// prettier-ignore
|
|
23
|
+
/**
|
|
24
|
+
* Decodes one known wire envelope and omits malformed data.
|
|
25
|
+
* @param input Supplies the wire envelope and its transport context.
|
|
26
|
+
* @returns Returns decoded request facts or `undefined` for malformed input.
|
|
27
|
+
*/
|
|
28
|
+
decode(input) {
|
|
29
|
+
try {
|
|
30
|
+
switch (input.kind) {
|
|
31
|
+
case "command": {
|
|
32
|
+
const command = fromBinary(CommandSchema, input.value);
|
|
33
|
+
return {
|
|
34
|
+
kind: input.kind,
|
|
35
|
+
command,
|
|
36
|
+
message: command.message === undefined || input.registry === undefined
|
|
37
|
+
? undefined
|
|
38
|
+
: AnyMessages.unpackUsing(input.registry, command.message),
|
|
39
|
+
messageType: command.message?.typeUrl ?? "",
|
|
40
|
+
requestedContext: command.context?.actorContext ?? create(ActorContextSchema),
|
|
41
|
+
transport: input.transport,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
case "query": {
|
|
45
|
+
const query = fromBinary(QuerySchema, input.value);
|
|
46
|
+
return {
|
|
47
|
+
kind: input.kind,
|
|
48
|
+
query,
|
|
49
|
+
target: query.target ?? create(TargetSchema),
|
|
50
|
+
requestedContext: query.context ?? create(ActorContextSchema),
|
|
51
|
+
transport: input.transport,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
case "subscribe": {
|
|
55
|
+
const topic = fromBinary(TopicSchema, input.value);
|
|
56
|
+
return {
|
|
57
|
+
kind: input.kind,
|
|
58
|
+
topic,
|
|
59
|
+
target: topic.target ?? create(TargetSchema),
|
|
60
|
+
requestedContext: topic.context ?? create(ActorContextSchema),
|
|
61
|
+
transport: input.transport,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
case "activate":
|
|
65
|
+
case "cancel": {
|
|
66
|
+
const subscription = fromBinary(SubscriptionSchema, input.value);
|
|
67
|
+
return {
|
|
68
|
+
kind: input.kind,
|
|
69
|
+
subscription,
|
|
70
|
+
requestedContext: subscription.topic?.context ?? create(ActorContextSchema),
|
|
71
|
+
transport: input.transport,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/request/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,WAAW,GACZ,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAOvD;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAqC,MAAM,CAAC,MAAM,CAAC;IAC9E,kBAAkB;IAElB;;;;OAIG;IACH,MAAM,CAAC,KAA2B;QAChC,IAAI,CAAC;YACH,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;gBACnB,KAAK,SAAS,CAAC,CAAC,CAAC;oBACf,MAAM,OAAO,GAAG,UAAU,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBACvD,OAAO;wBACL,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,OAAO;wBACP,OAAO,EACL,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;4BAC3D,CAAC,CAAC,SAAS;4BACX,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC;wBAC9D,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE;wBAC3C,gBAAgB,EAAE,OAAO,CAAC,OAAO,EAAE,YAAY,IAAI,MAAM,CAAC,kBAAkB,CAAC;wBAC7E,SAAS,EAAE,KAAK,CAAC,SAAS;qBAC3B,CAAC;gBACJ,CAAC;gBACD,KAAK,OAAO,CAAC,CAAC,CAAC;oBACb,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBACnD,OAAO;wBACL,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,KAAK;wBACL,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC;wBAC5C,gBAAgB,EAAE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,kBAAkB,CAAC;wBAC7D,SAAS,EAAE,KAAK,CAAC,SAAS;qBAC3B,CAAC;gBACJ,CAAC;gBACD,KAAK,WAAW,CAAC,CAAC,CAAC;oBACjB,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBACnD,OAAO;wBACL,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,KAAK;wBACL,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC;wBAC5C,gBAAgB,EAAE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,kBAAkB,CAAC;wBAC7D,SAAS,EAAE,KAAK,CAAC,SAAS;qBAC3B,CAAC;gBACJ,CAAC;gBACD,KAAK,UAAU,CAAC;gBAChB,KAAK,QAAQ,CAAC,CAAC,CAAC;oBACd,MAAM,YAAY,GAAG,UAAU,CAAC,kBAAkB,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBACjE,OAAO;wBACL,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,YAAY;wBACZ,gBAAgB,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,IAAI,MAAM,CAAC,kBAAkB,CAAC;wBAC3E,SAAS,EAAE,KAAK,CAAC,SAAS;qBAC3B,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { RequestCredential } from "../index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Header input whose own-property arrays preserve observable duplicate values.
|
|
4
|
+
*/
|
|
5
|
+
export type OpaqueSessionHeaders = Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
6
|
+
/**
|
|
7
|
+
* Construction options for strict opaque-session browser cookie handling.
|
|
8
|
+
*/
|
|
9
|
+
export interface OpaqueSessionCookiesOptions {
|
|
10
|
+
/**
|
|
11
|
+
* HMAC key copied on construction; it needs at least 32 bytes and the owned copy is zeroed on `close()`.
|
|
12
|
+
*/
|
|
13
|
+
readonly csrfSecret: Uint8Array;
|
|
14
|
+
/**
|
|
15
|
+
* One or more non-empty canonical Origins matched byte-for-byte on cookie requests.
|
|
16
|
+
*/
|
|
17
|
+
readonly origins: readonly string[];
|
|
18
|
+
/**
|
|
19
|
+
* Distinct valid `__Host-` session-cookie name; defaults to `__Host-spine-session`.
|
|
20
|
+
*/
|
|
21
|
+
readonly sessionCookieName?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Distinct valid `__Host-` CSRF-cookie name; defaults to `__Host-spine-csrf`.
|
|
24
|
+
*/
|
|
25
|
+
readonly csrfCookieName?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Maximum observable own header fields and array values. Defaults to 32.
|
|
28
|
+
*/
|
|
29
|
+
readonly maxHeaderValues?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum total header characters. Defaults to 16,384.
|
|
32
|
+
*/
|
|
33
|
+
readonly maxHeaderCharacters?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Maximum cookie pairs. Defaults to 64.
|
|
36
|
+
*/
|
|
37
|
+
readonly maxCookiePairs?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Rejection result from strict browser credential extraction.
|
|
41
|
+
*/
|
|
42
|
+
export interface OpaqueCredentialRejection {
|
|
43
|
+
/**
|
|
44
|
+
* Identifies the rejected extraction result.
|
|
45
|
+
*/
|
|
46
|
+
readonly kind: "rejected";
|
|
47
|
+
/**
|
|
48
|
+
* Explains why the credential was rejected.
|
|
49
|
+
*/
|
|
50
|
+
readonly reason: "missing-credential" | "duplicate-authorization" | "malformed-authorization" | "malformed-cookie" | "ambiguous-cookie" | "missing-origin" | "duplicate-origin" | "forbidden-origin" | "missing-csrf" | "duplicate-csrf" | "csrf-mismatch" | "request-too-large" | "closed";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Result of extracting a bearer or CSRF-protected opaque-cookie credential.
|
|
54
|
+
*/
|
|
55
|
+
export type OpaqueCredentialExtraction = RequestCredential | OpaqueCredentialRejection;
|
|
56
|
+
/**
|
|
57
|
+
* Strict, framework-neutral helper whose `close()` zeroes the copied CSRF secret.
|
|
58
|
+
*/
|
|
59
|
+
export declare class OpaqueSessionCookies {
|
|
60
|
+
private readonly secret;
|
|
61
|
+
private readonly origins;
|
|
62
|
+
private readonly sessionName;
|
|
63
|
+
private readonly csrfName;
|
|
64
|
+
private readonly maxHeaderValues;
|
|
65
|
+
private readonly maxHeaderCharacters;
|
|
66
|
+
private readonly maxCookiePairs;
|
|
67
|
+
private closed;
|
|
68
|
+
/**
|
|
69
|
+
* Creates strict cookie extraction with copied CSRF key material.
|
|
70
|
+
* @param options The cookie names, trusted origins, bounds, and CSRF secret.
|
|
71
|
+
*/
|
|
72
|
+
constructor(options: OpaqueSessionCookiesOptions);
|
|
73
|
+
/**
|
|
74
|
+
* Creates the fixed-length unpadded base64url CSRF value for one session ID.
|
|
75
|
+
* @param sessionId The session identifier protected by the CSRF value.
|
|
76
|
+
* @returns The derived CSRF value.
|
|
77
|
+
*/
|
|
78
|
+
csrf(sessionId: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Creates the immutable pair of host-only cookies for a newly issued session.
|
|
81
|
+
* @param sessionId The issued opaque session identifier.
|
|
82
|
+
* @returns The session and CSRF Set-Cookie values.
|
|
83
|
+
*/
|
|
84
|
+
issue(sessionId: string): readonly string[];
|
|
85
|
+
/**
|
|
86
|
+
* Creates the immutable pair of host-only cookie removals.
|
|
87
|
+
* @returns The expired session and CSRF Set-Cookie values.
|
|
88
|
+
*/
|
|
89
|
+
clear(): readonly string[];
|
|
90
|
+
/**
|
|
91
|
+
* Reads bearer credentials first, otherwise a fully checked cookie credential.
|
|
92
|
+
* @param headers The request headers to inspect.
|
|
93
|
+
* @returns The admitted credential or its rejection reason.
|
|
94
|
+
*/
|
|
95
|
+
extract(headers: OpaqueSessionHeaders): OpaqueCredentialExtraction;
|
|
96
|
+
/**
|
|
97
|
+
* Closes the extractor, zeroes its HMAC secret, and rejects later operations.
|
|
98
|
+
* @returns Completes after the secret is cleared.
|
|
99
|
+
*/
|
|
100
|
+
close(): Promise<void>;
|
|
101
|
+
private cookieCredential;
|
|
102
|
+
private open;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=cookies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/sessions/cookies.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;AAEpG;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAG1C;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAEhC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAElC;;OAEG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAEtC;;OAEG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IAGxC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,MAAM,EACX,oBAAoB,GACpB,yBAAyB,GACzB,yBAAyB,GACzB,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,GAClB,kBAAkB,GAClB,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,mBAAmB,GACnB,QAAQ,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG,iBAAiB,GAAG,yBAAyB,CAAC;AAEvF;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,MAAM,CAAS;IAEvB;;;OAGG;gBACS,OAAO,EAAE,2BAA2B;IA8BhD;;;;OAIG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAQ/B;;;;OAIG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE;IAQ3C;;;OAGG;IACH,KAAK,IAAI,SAAS,MAAM,EAAE;IAQ1B;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,0BAA0B;IAclE;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,IAAI;CAGb"}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, CodeMatters. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
5
|
+
* in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
10
|
+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
11
|
+
* or implied. See the License for the specific language governing permissions and limitations under
|
|
12
|
+
* the License.
|
|
13
|
+
*/
|
|
14
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
15
|
+
/**
|
|
16
|
+
* Strict, framework-neutral helper whose `close()` zeroes the copied CSRF secret.
|
|
17
|
+
*/
|
|
18
|
+
export class OpaqueSessionCookies {
|
|
19
|
+
secret;
|
|
20
|
+
origins;
|
|
21
|
+
sessionName;
|
|
22
|
+
csrfName;
|
|
23
|
+
maxHeaderValues;
|
|
24
|
+
maxHeaderCharacters;
|
|
25
|
+
maxCookiePairs;
|
|
26
|
+
closed = false;
|
|
27
|
+
/**
|
|
28
|
+
* Creates strict cookie extraction with copied CSRF key material.
|
|
29
|
+
* @param options The cookie names, trusted origins, bounds, and CSRF secret.
|
|
30
|
+
*/
|
|
31
|
+
constructor(options) {
|
|
32
|
+
if (options.csrfSecret.byteLength < 32)
|
|
33
|
+
throw new Error("csrfSecret must contain at least 32 bytes");
|
|
34
|
+
this.secret = new Uint8Array(options.csrfSecret);
|
|
35
|
+
this.origins = new Set(options.origins.map(CookieValues.validOrigin));
|
|
36
|
+
if (this.origins.size === 0)
|
|
37
|
+
throw new Error("origins must contain at least one valid Origin");
|
|
38
|
+
this.sessionName = CookieValues.validCookieName(options.sessionCookieName ?? "__Host-spine-session", "sessionCookieName");
|
|
39
|
+
this.csrfName = CookieValues.validCookieName(options.csrfCookieName ?? "__Host-spine-csrf", "csrfCookieName");
|
|
40
|
+
if (this.sessionName === this.csrfName)
|
|
41
|
+
throw new Error("sessionCookieName and csrfCookieName must be distinct");
|
|
42
|
+
this.maxHeaderValues = CookieValues.positiveSafeInteger(options.maxHeaderValues ?? 32, "maxHeaderValues");
|
|
43
|
+
this.maxHeaderCharacters = CookieValues.positiveSafeInteger(options.maxHeaderCharacters ?? 16_384, "maxHeaderCharacters");
|
|
44
|
+
this.maxCookiePairs = CookieValues.positiveSafeInteger(options.maxCookiePairs ?? 64, "maxCookiePairs");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates the fixed-length unpadded base64url CSRF value for one session ID.
|
|
48
|
+
* @param sessionId The session identifier protected by the CSRF value.
|
|
49
|
+
* @returns The derived CSRF value.
|
|
50
|
+
*/
|
|
51
|
+
csrf(sessionId) {
|
|
52
|
+
this.open();
|
|
53
|
+
if (!CookieValues.validSessionId(sessionId)) {
|
|
54
|
+
throw new Error("sessionId must be a 43-character unpadded base64url session ID");
|
|
55
|
+
}
|
|
56
|
+
return createHmac("sha256", this.secret).update(sessionId, "utf8").digest("base64url");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates the immutable pair of host-only cookies for a newly issued session.
|
|
60
|
+
* @param sessionId The issued opaque session identifier.
|
|
61
|
+
* @returns The session and CSRF Set-Cookie values.
|
|
62
|
+
*/
|
|
63
|
+
issue(sessionId) {
|
|
64
|
+
const csrf = this.csrf(sessionId);
|
|
65
|
+
return Object.freeze([
|
|
66
|
+
`${this.sessionName}=${sessionId}; Path=/; Secure; HttpOnly; SameSite=Lax`,
|
|
67
|
+
`${this.csrfName}=${csrf}; Path=/; Secure; SameSite=Lax`,
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Creates the immutable pair of host-only cookie removals.
|
|
72
|
+
* @returns The expired session and CSRF Set-Cookie values.
|
|
73
|
+
*/
|
|
74
|
+
clear() {
|
|
75
|
+
this.open();
|
|
76
|
+
return Object.freeze([
|
|
77
|
+
`${this.sessionName}=; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=0`,
|
|
78
|
+
`${this.csrfName}=; Path=/; Secure; SameSite=Lax; Max-Age=0`,
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Reads bearer credentials first, otherwise a fully checked cookie credential.
|
|
83
|
+
* @param headers The request headers to inspect.
|
|
84
|
+
* @returns The admitted credential or its rejection reason.
|
|
85
|
+
*/
|
|
86
|
+
extract(headers) {
|
|
87
|
+
if (this.closed)
|
|
88
|
+
return CookieValues.rejection("closed");
|
|
89
|
+
const values = CookieValues.headerValues(headers, this.maxHeaderValues, this.maxHeaderCharacters);
|
|
90
|
+
if (values === undefined)
|
|
91
|
+
return CookieValues.rejection("request-too-large");
|
|
92
|
+
const authorization = values.get("authorization") ?? [];
|
|
93
|
+
if (authorization.length > 1)
|
|
94
|
+
return CookieValues.rejection("duplicate-authorization");
|
|
95
|
+
if (authorization.length === 1)
|
|
96
|
+
return CookieValues.bearer(CookieValues.only(authorization));
|
|
97
|
+
return this.cookieCredential(values);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Closes the extractor, zeroes its HMAC secret, and rejects later operations.
|
|
101
|
+
* @returns Completes after the secret is cleared.
|
|
102
|
+
*/
|
|
103
|
+
close() {
|
|
104
|
+
if (this.closed)
|
|
105
|
+
return Promise.resolve();
|
|
106
|
+
this.closed = true;
|
|
107
|
+
this.secret.fill(0);
|
|
108
|
+
return Promise.resolve();
|
|
109
|
+
}
|
|
110
|
+
cookieCredential(values) {
|
|
111
|
+
const cookies = values.get("cookie") ?? [];
|
|
112
|
+
const parsed = CookieValues.parseCookies(cookies, this.sessionName, this.csrfName, this.maxCookiePairs);
|
|
113
|
+
if ("reason" in parsed)
|
|
114
|
+
return CookieValues.rejection(parsed.reason);
|
|
115
|
+
const origin = CookieValues.one(values, "origin", "missing-origin", "duplicate-origin");
|
|
116
|
+
if ("reason" in origin)
|
|
117
|
+
return CookieValues.rejection(origin.reason);
|
|
118
|
+
if (!this.origins.has(origin.value))
|
|
119
|
+
return CookieValues.rejection("forbidden-origin");
|
|
120
|
+
const supplied = CookieValues.one(values, "x-spine-csrf", "missing-csrf", "duplicate-csrf");
|
|
121
|
+
if ("reason" in supplied)
|
|
122
|
+
return CookieValues.rejection(supplied.reason);
|
|
123
|
+
const expected = this.csrf(parsed.session);
|
|
124
|
+
if (!CookieValues.validCsrf(supplied.value) ||
|
|
125
|
+
!CookieValues.sameCsrf(expected, supplied.value) ||
|
|
126
|
+
!CookieValues.sameCsrf(expected, parsed.csrf)) {
|
|
127
|
+
return CookieValues.rejection("csrf-mismatch");
|
|
128
|
+
}
|
|
129
|
+
return Object.freeze({ kind: "cookie", value: parsed.session });
|
|
130
|
+
}
|
|
131
|
+
open() {
|
|
132
|
+
if (this.closed)
|
|
133
|
+
throw new Error("OpaqueSessionCookies is closed");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Parses and validates cookie values used by {@link OpaqueSessionCookies}.
|
|
138
|
+
*/
|
|
139
|
+
const CookieValues = Object.freeze({
|
|
140
|
+
headerValues(headers, maxValues, maxCharacters) {
|
|
141
|
+
const result = new Map();
|
|
142
|
+
let count = 0;
|
|
143
|
+
let characters = 0;
|
|
144
|
+
for (const name in headers) {
|
|
145
|
+
if (!Object.prototype.hasOwnProperty.call(headers, name))
|
|
146
|
+
continue;
|
|
147
|
+
if (count === maxValues || characters + name.length > maxCharacters)
|
|
148
|
+
return undefined;
|
|
149
|
+
count += 1;
|
|
150
|
+
characters += name.length;
|
|
151
|
+
const supplied = headers[name];
|
|
152
|
+
if (supplied === undefined)
|
|
153
|
+
continue;
|
|
154
|
+
const key = name.toLowerCase();
|
|
155
|
+
const current = result.get(key) ?? [];
|
|
156
|
+
if (typeof supplied === "string") {
|
|
157
|
+
if (characters + supplied.length > maxCharacters)
|
|
158
|
+
return undefined;
|
|
159
|
+
characters += supplied.length;
|
|
160
|
+
current.push(supplied);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
for (const [index, value] of supplied.entries()) {
|
|
164
|
+
if (index > 0 && count === maxValues)
|
|
165
|
+
return undefined;
|
|
166
|
+
if (characters + value.length > maxCharacters)
|
|
167
|
+
return undefined;
|
|
168
|
+
if (index > 0)
|
|
169
|
+
count += 1;
|
|
170
|
+
characters += value.length;
|
|
171
|
+
current.push(value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
result.set(key, current);
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
},
|
|
178
|
+
bearer(value) {
|
|
179
|
+
const match = /^bearer ([\x21-\x7e]+)$/i.exec(value);
|
|
180
|
+
return match === null
|
|
181
|
+
? CookieValues.rejection("malformed-authorization")
|
|
182
|
+
: Object.freeze({ kind: "bearer", value: CookieValues.only(match.slice(1)) });
|
|
183
|
+
},
|
|
184
|
+
parseCookies(headers, sessionName, csrfName, maxPairs) {
|
|
185
|
+
const found = new Map();
|
|
186
|
+
let count = 0;
|
|
187
|
+
for (const header of headers) {
|
|
188
|
+
if (CookieValues.hasForbiddenCookieCharacter(header))
|
|
189
|
+
return { reason: "malformed-cookie" };
|
|
190
|
+
for (const pair of header.split(";")) {
|
|
191
|
+
count += 1;
|
|
192
|
+
if (count > maxPairs)
|
|
193
|
+
return { reason: "request-too-large" };
|
|
194
|
+
const index = pair.indexOf("=");
|
|
195
|
+
if (index <= 0)
|
|
196
|
+
return { reason: "malformed-cookie" };
|
|
197
|
+
const name = pair.slice(0, index).trim();
|
|
198
|
+
const value = pair.slice(index + 1).trim();
|
|
199
|
+
if (!CookieValues.validCookiePair(name, value))
|
|
200
|
+
return { reason: "malformed-cookie" };
|
|
201
|
+
if (name === sessionName || name === csrfName) {
|
|
202
|
+
if (found.has(name))
|
|
203
|
+
return { reason: "ambiguous-cookie" };
|
|
204
|
+
found.set(name, [value]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const session = found.get(sessionName) ?? [];
|
|
209
|
+
const csrf = found.get(csrfName) ?? [];
|
|
210
|
+
if (session.length === 0 && csrf.length === 0)
|
|
211
|
+
return { reason: "missing-credential" };
|
|
212
|
+
if (session.length > 1 || csrf.length > 1)
|
|
213
|
+
return { reason: "ambiguous-cookie" };
|
|
214
|
+
if (session.length !== 1 ||
|
|
215
|
+
csrf.length !== 1 ||
|
|
216
|
+
!CookieValues.validSessionId(CookieValues.only(session)) ||
|
|
217
|
+
!CookieValues.validCsrf(CookieValues.only(csrf))) {
|
|
218
|
+
return { reason: "malformed-cookie" };
|
|
219
|
+
}
|
|
220
|
+
return { session: CookieValues.only(session), csrf: CookieValues.only(csrf) };
|
|
221
|
+
},
|
|
222
|
+
one(values, name, missing, duplicate) {
|
|
223
|
+
const entries = values.get(name) ?? [];
|
|
224
|
+
if (entries.length === 0)
|
|
225
|
+
return { reason: missing };
|
|
226
|
+
if (entries.length !== 1)
|
|
227
|
+
return { reason: duplicate };
|
|
228
|
+
return { value: CookieValues.only(entries) };
|
|
229
|
+
},
|
|
230
|
+
only(values) {
|
|
231
|
+
const value = values[0];
|
|
232
|
+
if (value === undefined)
|
|
233
|
+
throw new Error("expected one header value");
|
|
234
|
+
return value;
|
|
235
|
+
},
|
|
236
|
+
hasForbiddenCookieCharacter(value) {
|
|
237
|
+
for (const character of value) {
|
|
238
|
+
const code = character.charCodeAt(0);
|
|
239
|
+
if (code <= 31 || code === 127 || character === ",")
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
return false;
|
|
243
|
+
},
|
|
244
|
+
validOrigin(origin) {
|
|
245
|
+
try {
|
|
246
|
+
const url = new URL(origin);
|
|
247
|
+
if ((url.protocol !== "https:" && url.protocol !== "http:") ||
|
|
248
|
+
url.username !== "" ||
|
|
249
|
+
url.password !== "" ||
|
|
250
|
+
url.pathname !== "/" ||
|
|
251
|
+
url.search !== "" ||
|
|
252
|
+
url.hash !== "" ||
|
|
253
|
+
url.origin !== origin) {
|
|
254
|
+
throw new Error();
|
|
255
|
+
}
|
|
256
|
+
return origin;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
throw new Error("origins must contain only canonical Origins");
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
validCookieName(value, name) {
|
|
263
|
+
if (!value.startsWith("__Host-") || !/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(value)) {
|
|
264
|
+
throw new Error(`${name} must be a valid __Host- cookie name`);
|
|
265
|
+
}
|
|
266
|
+
return value;
|
|
267
|
+
},
|
|
268
|
+
positiveSafeInteger(value, name) {
|
|
269
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
270
|
+
throw new Error(`${name} must be a positive safe integer`);
|
|
271
|
+
}
|
|
272
|
+
return value;
|
|
273
|
+
},
|
|
274
|
+
validCookiePair(name, value) {
|
|
275
|
+
return /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name) && /^[\x21-\x7e]+$/.test(value);
|
|
276
|
+
},
|
|
277
|
+
base64url(value) {
|
|
278
|
+
return /^[A-Za-z0-9_-]+$/.test(value) && value.length % 4 !== 1;
|
|
279
|
+
},
|
|
280
|
+
validCsrf(value) {
|
|
281
|
+
return value.length === 43 && CookieValues.base64url(value);
|
|
282
|
+
},
|
|
283
|
+
validSessionId(value) {
|
|
284
|
+
return value.length === 43 && CookieValues.base64url(value);
|
|
285
|
+
},
|
|
286
|
+
sameCsrf(expected, supplied) {
|
|
287
|
+
if (expected.length !== supplied.length)
|
|
288
|
+
return false;
|
|
289
|
+
return timingSafeEqual(Buffer.from(expected), Buffer.from(supplied));
|
|
290
|
+
},
|
|
291
|
+
rejection(reason) {
|
|
292
|
+
return Object.freeze({ kind: "rejected", reason });
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
//# sourceMappingURL=cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/sessions/cookies.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { AuthenticatedPrincipal, CookieCredential, RequestCredential, ResolvedSession, SessionResolver } from "../index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Clock used to evaluate opaque-session expiry.
|
|
4
|
+
*/
|
|
5
|
+
export interface OpaqueSessionClock {
|
|
6
|
+
/**
|
|
7
|
+
* Returns Unix epoch milliseconds as a safe integer in the Protobuf Timestamp range.
|
|
8
|
+
* @returns The current milliseconds.
|
|
9
|
+
*/
|
|
10
|
+
now(): number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates random bytes for an opaque session identifier.
|
|
14
|
+
* @param length The required byte length.
|
|
15
|
+
* @returns The generated bytes.
|
|
16
|
+
*/
|
|
17
|
+
export type OpaqueSessionRandom = (length: number) => Uint8Array;
|
|
18
|
+
/**
|
|
19
|
+
* Construction options for the bounded in-memory opaque-session resolver.
|
|
20
|
+
*/
|
|
21
|
+
export interface OpaqueSessionsOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Millisecond clock; each value must be a safe integer in Timestamp range.
|
|
24
|
+
*/
|
|
25
|
+
readonly clock?: OpaqueSessionClock;
|
|
26
|
+
/**
|
|
27
|
+
* Receives exactly `32` and returns exactly 32 random bytes per call.
|
|
28
|
+
* Throwing or wrong-length results consume a bounded collision attempt, and
|
|
29
|
+
* every returned mutable buffer is zeroed before this resolver returns.
|
|
30
|
+
*/
|
|
31
|
+
readonly randomBytes?: OpaqueSessionRandom;
|
|
32
|
+
/**
|
|
33
|
+
* Positive safe session lifetime in milliseconds; defaults to eight hours.
|
|
34
|
+
*/
|
|
35
|
+
readonly ttlMilliseconds?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Positive safe retained-session bound; defaults to 10,000.
|
|
38
|
+
*/
|
|
39
|
+
readonly maxSessions?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Positive safe ID-generation attempt bound; defaults to three.
|
|
42
|
+
*/
|
|
43
|
+
readonly collisionAttempts?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Result of creating an opaque session.
|
|
47
|
+
*/
|
|
48
|
+
export type OpaqueSessionCreateResult = {
|
|
49
|
+
/**
|
|
50
|
+
* Identifies successful session creation.
|
|
51
|
+
*/
|
|
52
|
+
readonly kind: "created";
|
|
53
|
+
/**
|
|
54
|
+
* Provides the cookie credential for the created session.
|
|
55
|
+
*/
|
|
56
|
+
readonly credential: CookieCredential;
|
|
57
|
+
/**
|
|
58
|
+
* Provides a defensive copy of the created session.
|
|
59
|
+
*/
|
|
60
|
+
readonly session: ResolvedSession;
|
|
61
|
+
} | {
|
|
62
|
+
/**
|
|
63
|
+
* Identifies rejected session creation.
|
|
64
|
+
*/
|
|
65
|
+
readonly kind: "rejected";
|
|
66
|
+
/**
|
|
67
|
+
* Explains why the session could not be created.
|
|
68
|
+
*/
|
|
69
|
+
readonly reason: "capacity-exceeded" | "entropy-exhausted" | "clock-failure" | "closed";
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Result of rotating an opaque session.
|
|
73
|
+
*/
|
|
74
|
+
export type OpaqueSessionRotateResult = {
|
|
75
|
+
/**
|
|
76
|
+
* Identifies successful session rotation.
|
|
77
|
+
*/
|
|
78
|
+
readonly kind: "rotated";
|
|
79
|
+
/**
|
|
80
|
+
* Provides the replacement cookie credential.
|
|
81
|
+
*/
|
|
82
|
+
readonly credential: CookieCredential;
|
|
83
|
+
/**
|
|
84
|
+
* Provides a defensive copy of the replacement session.
|
|
85
|
+
*/
|
|
86
|
+
readonly session: ResolvedSession;
|
|
87
|
+
} | {
|
|
88
|
+
/**
|
|
89
|
+
* Identifies rejected session rotation.
|
|
90
|
+
*/
|
|
91
|
+
readonly kind: "rejected";
|
|
92
|
+
/**
|
|
93
|
+
* Explains why the session could not be rotated.
|
|
94
|
+
*/
|
|
95
|
+
readonly reason: "not-found" | "expired" | "unsupported-credential" | "entropy-exhausted" | "clock-failure" | "closed";
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Enumeration-safe acknowledgement of an opaque-session logout request.
|
|
99
|
+
*/
|
|
100
|
+
export interface OpaqueSessionLogoutResult {
|
|
101
|
+
/**
|
|
102
|
+
* Confirms that the logout request was accepted.
|
|
103
|
+
*/
|
|
104
|
+
readonly kind: "logged-out";
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* A bounded, terminal in-memory store of random opaque application sessions.
|
|
108
|
+
*
|
|
109
|
+
* It is intentionally process-local: applications needing durable or shared
|
|
110
|
+
* sessions provide another `SessionResolver` implementation.
|
|
111
|
+
*/
|
|
112
|
+
export declare class OpaqueSessions implements SessionResolver {
|
|
113
|
+
private readonly records;
|
|
114
|
+
private readonly clock;
|
|
115
|
+
private readonly random;
|
|
116
|
+
private readonly ttlMilliseconds;
|
|
117
|
+
private readonly maxSessions;
|
|
118
|
+
private readonly collisionAttempts;
|
|
119
|
+
private closed;
|
|
120
|
+
/**
|
|
121
|
+
* Creates a bounded in-memory session store.
|
|
122
|
+
* @param options The optional clock, entropy, lifetime, and capacity settings.
|
|
123
|
+
*/
|
|
124
|
+
constructor(options?: OpaqueSessionsOptions);
|
|
125
|
+
/**
|
|
126
|
+
* Creates a new session unless the terminal or bounded store state rejects it.
|
|
127
|
+
* @param principal The authenticated principal to retain.
|
|
128
|
+
* @returns The creation outcome and, when accepted, credential and session.
|
|
129
|
+
*/
|
|
130
|
+
create(principal: AuthenticatedPrincipal): Promise<OpaqueSessionCreateResult>;
|
|
131
|
+
/**
|
|
132
|
+
* Resolves one currently live cookie credential as a defensive copy.
|
|
133
|
+
* @param credentialInput The credential to resolve.
|
|
134
|
+
* @returns The live session, or undefined when it is invalid or expired.
|
|
135
|
+
*/
|
|
136
|
+
resolve(credentialInput: RequestCredential): Promise<ResolvedSession | undefined>;
|
|
137
|
+
/**
|
|
138
|
+
* Updates a live opaque credential without ever retaining both values.
|
|
139
|
+
* @param credentialInput The credential to replace.
|
|
140
|
+
* @returns The rotation outcome and, when accepted, the replacement values.
|
|
141
|
+
*/
|
|
142
|
+
rotate(credentialInput: RequestCredential): Promise<OpaqueSessionRotateResult>;
|
|
143
|
+
/**
|
|
144
|
+
* Deletes a cookie session when present without revealing whether it existed.
|
|
145
|
+
* @param credentialInput The credential to invalidate.
|
|
146
|
+
* @returns An enumeration-safe logout acknowledgement.
|
|
147
|
+
*/
|
|
148
|
+
logout(credentialInput: RequestCredential): Promise<OpaqueSessionLogoutResult>;
|
|
149
|
+
/**
|
|
150
|
+
* Closes the store and drops retained session identity and record references.
|
|
151
|
+
* @returns Completes after retained records are dropped.
|
|
152
|
+
*/
|
|
153
|
+
close(): Promise<void>;
|
|
154
|
+
private isClosed;
|
|
155
|
+
private record;
|
|
156
|
+
private nextId;
|
|
157
|
+
private live;
|
|
158
|
+
private sweepExpired;
|
|
159
|
+
private expired;
|
|
160
|
+
private now;
|
|
161
|
+
private failClosed;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=opaque.d.ts.map
|