@softacus-software/elwis-mcp-core 0.1.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/README.md +120 -0
- package/dist/cjs/app-scope.js +81 -0
- package/dist/cjs/app-scope.js.map +1 -0
- package/dist/cjs/conformance/host.conformance.js +98 -0
- package/dist/cjs/conformance/host.conformance.js.map +1 -0
- package/dist/cjs/conformance/read-port.conformance.js +131 -0
- package/dist/cjs/conformance/read-port.conformance.js.map +1 -0
- package/dist/cjs/errors.js +74 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/host/elwis-jwt-verifier.js +40 -0
- package/dist/cjs/host/elwis-jwt-verifier.js.map +1 -0
- package/dist/cjs/host/file-link-store.js +60 -0
- package/dist/cjs/host/file-link-store.js.map +1 -0
- package/dist/cjs/host/identity.js +3 -0
- package/dist/cjs/host/identity.js.map +1 -0
- package/dist/cjs/host/index.js +24 -0
- package/dist/cjs/host/index.js.map +1 -0
- package/dist/cjs/host/link-ticket.js +59 -0
- package/dist/cjs/host/link-ticket.js.map +1 -0
- package/dist/cjs/host/oidc-token.js +96 -0
- package/dist/cjs/host/oidc-token.js.map +1 -0
- package/dist/cjs/host/oidc-verifier.js +67 -0
- package/dist/cjs/host/oidc-verifier.js.map +1 -0
- package/dist/cjs/host/serve-http.js +282 -0
- package/dist/cjs/host/serve-http.js.map +1 -0
- package/dist/cjs/host/session-broker.js +84 -0
- package/dist/cjs/host/session-broker.js.map +1 -0
- package/dist/cjs/index.js +43 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plan/expand.js +297 -0
- package/dist/cjs/plan/expand.js.map +1 -0
- package/dist/cjs/plan/resolve-existing.js +58 -0
- package/dist/cjs/plan/resolve-existing.js.map +1 -0
- package/dist/cjs/plan/split.js +92 -0
- package/dist/cjs/plan/split.js.map +1 -0
- package/dist/cjs/plan/target-state.js +120 -0
- package/dist/cjs/plan/target-state.js.map +1 -0
- package/dist/cjs/ports/elwis-api.port.js +15 -0
- package/dist/cjs/ports/elwis-api.port.js.map +1 -0
- package/dist/cjs/resolve.js +89 -0
- package/dist/cjs/resolve.js.map +1 -0
- package/dist/esm/app-scope.d.ts +26 -0
- package/dist/esm/app-scope.d.ts.map +1 -0
- package/dist/esm/app-scope.js +76 -0
- package/dist/esm/app-scope.js.map +1 -0
- package/dist/esm/conformance/host.conformance.d.ts +3 -0
- package/dist/esm/conformance/host.conformance.d.ts.map +1 -0
- package/dist/esm/conformance/host.conformance.js +95 -0
- package/dist/esm/conformance/host.conformance.js.map +1 -0
- package/dist/esm/conformance/read-port.conformance.d.ts +24 -0
- package/dist/esm/conformance/read-port.conformance.d.ts.map +1 -0
- package/dist/esm/conformance/read-port.conformance.js +127 -0
- package/dist/esm/conformance/read-port.conformance.js.map +1 -0
- package/dist/esm/errors.d.ts +40 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +63 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/host/elwis-jwt-verifier.d.ts +9 -0
- package/dist/esm/host/elwis-jwt-verifier.d.ts.map +1 -0
- package/dist/esm/host/elwis-jwt-verifier.js +37 -0
- package/dist/esm/host/elwis-jwt-verifier.js.map +1 -0
- package/dist/esm/host/file-link-store.d.ts +13 -0
- package/dist/esm/host/file-link-store.d.ts.map +1 -0
- package/dist/esm/host/file-link-store.js +56 -0
- package/dist/esm/host/file-link-store.js.map +1 -0
- package/dist/esm/host/identity.d.ts +13 -0
- package/dist/esm/host/identity.d.ts.map +1 -0
- package/dist/esm/host/identity.js +2 -0
- package/dist/esm/host/identity.js.map +1 -0
- package/dist/esm/host/index.d.ts +15 -0
- package/dist/esm/host/index.d.ts.map +1 -0
- package/dist/esm/host/index.js +9 -0
- package/dist/esm/host/index.js.map +1 -0
- package/dist/esm/host/link-ticket.d.ts +17 -0
- package/dist/esm/host/link-ticket.d.ts.map +1 -0
- package/dist/esm/host/link-ticket.js +54 -0
- package/dist/esm/host/link-ticket.js.map +1 -0
- package/dist/esm/host/oidc-token.d.ts +20 -0
- package/dist/esm/host/oidc-token.d.ts.map +1 -0
- package/dist/esm/host/oidc-token.js +93 -0
- package/dist/esm/host/oidc-token.js.map +1 -0
- package/dist/esm/host/oidc-verifier.d.ts +14 -0
- package/dist/esm/host/oidc-verifier.d.ts.map +1 -0
- package/dist/esm/host/oidc-verifier.js +63 -0
- package/dist/esm/host/oidc-verifier.js.map +1 -0
- package/dist/esm/host/serve-http.d.ts +42 -0
- package/dist/esm/host/serve-http.d.ts.map +1 -0
- package/dist/esm/host/serve-http.js +278 -0
- package/dist/esm/host/serve-http.js.map +1 -0
- package/dist/esm/host/session-broker.d.ts +45 -0
- package/dist/esm/host/session-broker.d.ts.map +1 -0
- package/dist/esm/host/session-broker.js +77 -0
- package/dist/esm/host/session-broker.js.map +1 -0
- package/dist/esm/index.d.ts +18 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/plan/expand.d.ts +37 -0
- package/dist/esm/plan/expand.d.ts.map +1 -0
- package/dist/esm/plan/expand.js +294 -0
- package/dist/esm/plan/expand.js.map +1 -0
- package/dist/esm/plan/resolve-existing.d.ts +16 -0
- package/dist/esm/plan/resolve-existing.d.ts.map +1 -0
- package/dist/esm/plan/resolve-existing.js +55 -0
- package/dist/esm/plan/resolve-existing.js.map +1 -0
- package/dist/esm/plan/split.d.ts +14 -0
- package/dist/esm/plan/split.d.ts.map +1 -0
- package/dist/esm/plan/split.js +88 -0
- package/dist/esm/plan/split.js.map +1 -0
- package/dist/esm/plan/target-state.d.ts +27 -0
- package/dist/esm/plan/target-state.d.ts.map +1 -0
- package/dist/esm/plan/target-state.js +114 -0
- package/dist/esm/plan/target-state.js.map +1 -0
- package/dist/esm/ports/elwis-api.port.d.ts +56 -0
- package/dist/esm/ports/elwis-api.port.d.ts.map +1 -0
- package/dist/esm/ports/elwis-api.port.js +12 -0
- package/dist/esm/ports/elwis-api.port.js.map +1 -0
- package/dist/esm/resolve.d.ts +23 -0
- package/dist/esm/resolve.d.ts.map +1 -0
- package/dist/esm/resolve.js +79 -0
- package/dist/esm/resolve.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare class ElwisApiError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly body: unknown;
|
|
5
|
+
constructor(status: number, path: string, body: unknown);
|
|
6
|
+
}
|
|
7
|
+
export declare function IsElwisApiError(error: unknown): error is ElwisApiError;
|
|
8
|
+
export declare function ErrorStatus(error: unknown): number | undefined;
|
|
9
|
+
export declare class AmbiguityError extends Error {
|
|
10
|
+
readonly candidates: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
}>;
|
|
14
|
+
constructor(message: string, candidates: Array<{
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
}>);
|
|
18
|
+
}
|
|
19
|
+
export declare class NotFoundError extends Error {
|
|
20
|
+
readonly kind: string;
|
|
21
|
+
readonly nameOrAlias: string;
|
|
22
|
+
readonly candidates: Array<{
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
}>;
|
|
26
|
+
constructor(kind: string, nameOrAlias: string, candidates?: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
}>);
|
|
30
|
+
}
|
|
31
|
+
export declare class AppScopeError extends Error {
|
|
32
|
+
constructor(ref: string);
|
|
33
|
+
}
|
|
34
|
+
export declare class TenantMismatchError extends Error {
|
|
35
|
+
constructor(expected: string, actual: string | null);
|
|
36
|
+
}
|
|
37
|
+
export declare class NoTenantError extends Error {
|
|
38
|
+
constructor();
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBAEX,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAOxD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAStE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE9D;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;gBAE7C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAK7E;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;gBAE7C,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAM;CAOpG;AAED,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAMpD;AAED,qBAAa,aAAc,SAAQ,KAAK;;CAKvC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export class ElwisApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
path;
|
|
4
|
+
body;
|
|
5
|
+
constructor(status, path, body) {
|
|
6
|
+
super(`ELWIS API ${status} for ${path}`);
|
|
7
|
+
this.name = 'ElwisApiError';
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.path = path;
|
|
10
|
+
this.body = body;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function IsElwisApiError(error) {
|
|
14
|
+
if (error instanceof ElwisApiError)
|
|
15
|
+
return true;
|
|
16
|
+
return (typeof error === 'object' &&
|
|
17
|
+
error !== null &&
|
|
18
|
+
typeof error.status === 'number' &&
|
|
19
|
+
typeof error.name === 'string' &&
|
|
20
|
+
/^Elwis(Api|Http)Error$/.test(error.name));
|
|
21
|
+
}
|
|
22
|
+
export function ErrorStatus(error) {
|
|
23
|
+
return IsElwisApiError(error) ? error.status : undefined;
|
|
24
|
+
}
|
|
25
|
+
export class AmbiguityError extends Error {
|
|
26
|
+
candidates;
|
|
27
|
+
constructor(message, candidates) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'AmbiguityError';
|
|
30
|
+
this.candidates = candidates;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class NotFoundError extends Error {
|
|
34
|
+
kind;
|
|
35
|
+
nameOrAlias;
|
|
36
|
+
candidates;
|
|
37
|
+
constructor(kind, nameOrAlias, candidates = []) {
|
|
38
|
+
super(`No active ${kind} named "${nameOrAlias}" in this application`);
|
|
39
|
+
this.name = 'NotFoundError';
|
|
40
|
+
this.kind = kind;
|
|
41
|
+
this.nameOrAlias = nameOrAlias;
|
|
42
|
+
this.candidates = candidates;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class AppScopeError extends Error {
|
|
46
|
+
constructor(ref) {
|
|
47
|
+
super(`Application "${ref}" is outside the configured application allowlist`);
|
|
48
|
+
this.name = 'AppScopeError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export class TenantMismatchError extends Error {
|
|
52
|
+
constructor(expected, actual) {
|
|
53
|
+
super(`No data was read or written. This connection is locked to tenant ${expected}, but the access token belongs to a different tenant (${actual ?? 'unknown'}).`);
|
|
54
|
+
this.name = 'TenantMismatchError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export class NoTenantError extends Error {
|
|
58
|
+
constructor() {
|
|
59
|
+
super('No tenant is set, so there is no way to know which ELWIS environment to work in.');
|
|
60
|
+
this.name = 'NoTenantError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAS;IACf,IAAI,CAAS;IACb,IAAI,CAAU;IAEvB,YAAY,MAAc,EAAE,IAAY,EAAE,IAAa;QACrD,KAAK,CAAC,aAAa,MAAM,QAAQ,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,YAAY,aAAa;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAQ,KAA8B,CAAC,MAAM,KAAK,QAAQ;QAC1D,OAAQ,KAA4B,CAAC,IAAI,KAAK,QAAQ;QACtD,wBAAwB,CAAC,IAAI,CAAE,KAA0B,CAAC,IAAI,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,UAAU,CAAsC;IAEzD,YAAY,OAAe,EAAE,UAA+C;QAC1E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAS;IACb,WAAW,CAAS;IACpB,UAAU,CAAsC;IAEzD,YAAY,IAAY,EAAE,WAAmB,EAAE,aAAkD,EAAE;QACjG,KAAK,CAAC,aAAa,IAAI,WAAW,WAAW,uBAAuB,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,GAAW;QACrB,KAAK,CAAC,gBAAgB,GAAG,mDAAmD,CAAC,CAAC;QAC9E,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,QAAgB,EAAE,MAAqB;QACjD,KAAK,CACH,oEAAoE,QAAQ,yDAAyD,MAAM,IAAI,SAAS,IAAI,CAC7J,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC;QACE,KAAK,CAAC,kFAAkF,CAAC,CAAC;QAC1F,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TokenVerifier } from './identity.js';
|
|
2
|
+
export type ResolveTokenIdentity = (token: string) => Promise<{
|
|
3
|
+
tenantId: string | null;
|
|
4
|
+
}>;
|
|
5
|
+
export declare function ElwisJwtVerifier(options: {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
resolveIdentity: ResolveTokenIdentity;
|
|
8
|
+
}): TokenVerifier;
|
|
9
|
+
//# sourceMappingURL=elwis-jwt-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elwis-jwt-verifier.d.ts","sourceRoot":"","sources":["../../../src/host/elwis-jwt-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,eAAe,CAAC;AAiBrE,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC;AAE3F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,oBAAoB,CAAC;CACvC,GAAG,aAAa,CAuBhB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const TENANT_ID_PATTERN = /^[a-f0-9]{24}$/i;
|
|
2
|
+
function AssertLooksLikeAToken(token) {
|
|
3
|
+
const parts = token.split('.');
|
|
4
|
+
const payload = parts[1];
|
|
5
|
+
if (parts.length !== 3 || payload === undefined) {
|
|
6
|
+
throw new Error('the bearer value is not a valid token');
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error('the bearer value is not a valid token');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function ElwisJwtVerifier(options) {
|
|
16
|
+
return {
|
|
17
|
+
async verify(token) {
|
|
18
|
+
AssertLooksLikeAToken(token);
|
|
19
|
+
let resolved;
|
|
20
|
+
try {
|
|
21
|
+
resolved = await options.resolveIdentity(token);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
throw new Error(`could not resolve the bearer token against ELWIS: ${e instanceof Error ? e.message : String(e)}`);
|
|
25
|
+
}
|
|
26
|
+
if (resolved.tenantId === null || !TENANT_ID_PATTERN.test(resolved.tenantId)) {
|
|
27
|
+
throw new Error(`the resolved identity has no valid tenant id (got: ${resolved.tenantId ?? 'null'})`);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
token,
|
|
31
|
+
baseUrl: options.baseUrl,
|
|
32
|
+
tenantId: resolved.tenantId,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=elwis-jwt-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elwis-jwt-verifier.js","sourceRoot":"","sources":["../../../src/host/elwis-jwt-verifier.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAE5C,SAAS,qBAAqB,CAAC,KAAa;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAID,MAAM,UAAU,gBAAgB,CAAC,OAGhC;IACC,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAa;YACxB,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAE7B,IAAI,QAAqC,CAAC;YAC1C,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YAClD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACrH,CAAC;YAED,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7E,MAAM,IAAI,KAAK,CAAC,sDAAsD,QAAQ,CAAC,QAAQ,IAAI,MAAM,GAAG,CAAC,CAAC;YACxG,CAAC;YAED,OAAO;gBACL,KAAK;gBACL,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;aAC5B,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LinkedSession, LinkStore } from './session-broker.js';
|
|
2
|
+
export declare class FileLinkStore implements LinkStore {
|
|
3
|
+
private readonly path;
|
|
4
|
+
private entries;
|
|
5
|
+
private writing;
|
|
6
|
+
constructor(path: string);
|
|
7
|
+
private Load;
|
|
8
|
+
private Flush;
|
|
9
|
+
get(key: string): Promise<LinkedSession | null>;
|
|
10
|
+
put(key: string, session: LinkedSession): Promise<void>;
|
|
11
|
+
delete(key: string): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=file-link-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-link-store.d.ts","sourceRoot":"","sources":["../../../src/host/file-link-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKpE,qBAAa,aAAc,YAAW,SAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,OAAO,CAAoC;gBAEvC,IAAI,EAAE,MAAM;IAKxB,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,KAAK;IAWP,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAI/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMzC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
const FILE_MODE = 0o600;
|
|
4
|
+
const DIRECTORY_MODE = 0o700;
|
|
5
|
+
export class FileLinkStore {
|
|
6
|
+
path;
|
|
7
|
+
entries;
|
|
8
|
+
writing = Promise.resolve();
|
|
9
|
+
constructor(path) {
|
|
10
|
+
this.path = path;
|
|
11
|
+
this.entries = this.Load();
|
|
12
|
+
}
|
|
13
|
+
Load() {
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
raw = readFileSync(this.path, 'utf8');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
24
|
+
return {};
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
Flush() {
|
|
32
|
+
const snapshot = JSON.stringify(this.entries);
|
|
33
|
+
this.writing = this.writing.then(() => {
|
|
34
|
+
mkdirSync(dirname(this.path), { recursive: true, mode: DIRECTORY_MODE });
|
|
35
|
+
const temporary = `${this.path}.${process.pid}.tmp`;
|
|
36
|
+
writeFileSync(temporary, snapshot, { mode: FILE_MODE });
|
|
37
|
+
renameSync(temporary, this.path);
|
|
38
|
+
}, () => { });
|
|
39
|
+
return this.writing;
|
|
40
|
+
}
|
|
41
|
+
async get(key) {
|
|
42
|
+
return this.entries[key] ?? null;
|
|
43
|
+
}
|
|
44
|
+
async put(key, session) {
|
|
45
|
+
this.entries = { ...this.entries, [key]: session };
|
|
46
|
+
await this.Flush();
|
|
47
|
+
}
|
|
48
|
+
async delete(key) {
|
|
49
|
+
const { [key]: removed, ...rest } = this.entries;
|
|
50
|
+
if (removed === undefined)
|
|
51
|
+
return;
|
|
52
|
+
this.entries = rest;
|
|
53
|
+
await this.Flush();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=file-link-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-link-store.js","sourceRoot":"","sources":["../../../src/host/file-link-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,SAAS,GAAG,KAAK,CAAC;AACxB,MAAM,cAAc,GAAG,KAAK,CAAC;AAE7B,MAAM,OAAO,aAAa;IACP,IAAI,CAAS;IACtB,OAAO,CAAgC;IACvC,OAAO,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEnD,YAAY,IAAY;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAEO,IAAI;QACV,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;YAC1C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,CAAC;YACtF,OAAO,MAAuC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,KAAK;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;YACpC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;YACzE,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;YACpD,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;YACxD,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACb,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,OAAsB;QAC3C,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC;QACnD,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACjD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ResolvedIdentity {
|
|
2
|
+
token: string;
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
tenantId?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TokenVerifier {
|
|
7
|
+
verify(token: string, resource: string): Promise<ResolvedIdentity>;
|
|
8
|
+
}
|
|
9
|
+
export interface McpServerLike {
|
|
10
|
+
connect(transport: unknown): Promise<void>;
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../src/host/identity.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../src/host/identity.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type { McpServerLike, ResolvedIdentity, TokenVerifier } from './identity.js';
|
|
2
|
+
export type { ServeOptions, ServeHandle, McpEndpoint, LinkOptions, LinkOutcome, LinkRequest } from './serve-http.js';
|
|
3
|
+
export { CreateLinkTicket, ReadLinkTicket } from './link-ticket.js';
|
|
4
|
+
export type { LinkTicketClaims } from './link-ticket.js';
|
|
5
|
+
export { ServeMcpHttp } from './serve-http.js';
|
|
6
|
+
export { ElwisJwtVerifier } from './elwis-jwt-verifier.js';
|
|
7
|
+
export { VerifyOidcToken } from './oidc-token.js';
|
|
8
|
+
export type { OidcClaims, JsonWebKeySet } from './oidc-token.js';
|
|
9
|
+
export { OidcVerifier } from './oidc-verifier.js';
|
|
10
|
+
export { ElwisSessionBroker, MemoryLinkStore, AccountNotLinkedError, LinkKey } from './session-broker.js';
|
|
11
|
+
export { FileLinkStore } from './file-link-store.js';
|
|
12
|
+
export type { LinkStore, LinkedSession, SessionBroker, SubjectClaims } from './session-broker.js';
|
|
13
|
+
export type { ResolveTokenIdentity } from './elwis-jwt-verifier.js';
|
|
14
|
+
export { RunHostConformance } from '../conformance/host.conformance.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/host/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACpF,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACrH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC1G,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAElG,YAAY,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { CreateLinkTicket, ReadLinkTicket } from './link-ticket.js';
|
|
2
|
+
export { ServeMcpHttp } from './serve-http.js';
|
|
3
|
+
export { ElwisJwtVerifier } from './elwis-jwt-verifier.js';
|
|
4
|
+
export { VerifyOidcToken } from './oidc-token.js';
|
|
5
|
+
export { OidcVerifier } from './oidc-verifier.js';
|
|
6
|
+
export { ElwisSessionBroker, MemoryLinkStore, AccountNotLinkedError, LinkKey } from './session-broker.js';
|
|
7
|
+
export { FileLinkStore } from './file-link-store.js';
|
|
8
|
+
export { RunHostConformance } from '../conformance/host.conformance.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/host/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC1G,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAKrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface LinkTicketClaims {
|
|
2
|
+
iss: string;
|
|
3
|
+
sub: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
resource?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LinkTicketOptions {
|
|
8
|
+
secret: string;
|
|
9
|
+
now?: number;
|
|
10
|
+
ttlSeconds?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const DEFAULT_LINK_TICKET_TTL_SECONDS = 600;
|
|
13
|
+
export declare function CreateLinkTicket(claims: LinkTicketClaims, options: LinkTicketOptions): string;
|
|
14
|
+
export declare function ReadLinkTicket(ticket: string, options: LinkTicketOptions): LinkTicketClaims & {
|
|
15
|
+
exp: number;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=link-ticket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-ticket.d.ts","sourceRoot":"","sources":["../../../src/host/link-ticket.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAgBnD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAW7F;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CA8B7G"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
export const DEFAULT_LINK_TICKET_TTL_SECONDS = 600;
|
|
3
|
+
function Sign(body, secret) {
|
|
4
|
+
return createHmac('sha256', secret).update(body).digest('base64url');
|
|
5
|
+
}
|
|
6
|
+
function Matches(a, b) {
|
|
7
|
+
const left = Buffer.from(a);
|
|
8
|
+
const right = Buffer.from(b);
|
|
9
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
10
|
+
}
|
|
11
|
+
export function CreateLinkTicket(claims, options) {
|
|
12
|
+
const now = options.now ?? Math.floor(Date.now() / 1000);
|
|
13
|
+
const body = {
|
|
14
|
+
iss: claims.iss,
|
|
15
|
+
sub: claims.sub,
|
|
16
|
+
...(claims.email ? { email: claims.email } : {}),
|
|
17
|
+
...(claims.resource ? { resource: claims.resource } : {}),
|
|
18
|
+
exp: now + (options.ttlSeconds ?? DEFAULT_LINK_TICKET_TTL_SECONDS),
|
|
19
|
+
};
|
|
20
|
+
const encoded = Buffer.from(JSON.stringify(body)).toString('base64url');
|
|
21
|
+
return `${encoded}.${Sign(encoded, options.secret)}`;
|
|
22
|
+
}
|
|
23
|
+
export function ReadLinkTicket(ticket, options) {
|
|
24
|
+
const parts = ticket.split('.');
|
|
25
|
+
const [encoded, signature] = parts;
|
|
26
|
+
if (parts.length !== 2 || !encoded || !signature) {
|
|
27
|
+
throw new Error('the link ticket is malformed');
|
|
28
|
+
}
|
|
29
|
+
if (!Matches(signature, Sign(encoded, options.secret))) {
|
|
30
|
+
throw new Error('the link ticket signature does not verify');
|
|
31
|
+
}
|
|
32
|
+
let body;
|
|
33
|
+
try {
|
|
34
|
+
body = JSON.parse(Buffer.from(encoded, 'base64url').toString('utf8'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error('the link ticket payload is not valid JSON');
|
|
38
|
+
}
|
|
39
|
+
const parsed = body;
|
|
40
|
+
if (typeof parsed.iss !== 'string' || typeof parsed.sub !== 'string' || typeof parsed.exp !== 'number') {
|
|
41
|
+
throw new Error('the link ticket payload is incomplete');
|
|
42
|
+
}
|
|
43
|
+
if (parsed.exp <= (options.now ?? Math.floor(Date.now() / 1000))) {
|
|
44
|
+
throw new Error('the link ticket has expired');
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
iss: parsed.iss,
|
|
48
|
+
sub: parsed.sub,
|
|
49
|
+
...(typeof parsed.email === 'string' ? { email: parsed.email } : {}),
|
|
50
|
+
...(typeof parsed.resource === 'string' ? { resource: parsed.resource } : {}),
|
|
51
|
+
exp: parsed.exp,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=link-ticket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-ticket.js","sourceRoot":"","sources":["../../../src/host/link-ticket.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface JsonWebKeySet {
|
|
2
|
+
keys: Array<Record<string, unknown>>;
|
|
3
|
+
}
|
|
4
|
+
export interface OidcClaims {
|
|
5
|
+
sub: string;
|
|
6
|
+
iss: string;
|
|
7
|
+
aud: string[];
|
|
8
|
+
exp: number;
|
|
9
|
+
email?: string;
|
|
10
|
+
[claim: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface VerifyOidcTokenOptions {
|
|
13
|
+
issuer: string;
|
|
14
|
+
resource: string;
|
|
15
|
+
jwks: JsonWebKeySet;
|
|
16
|
+
now?: number;
|
|
17
|
+
clockSkewSeconds?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function VerifyOidcToken(token: string, options: VerifyOidcTokenOptions): Promise<OidcClaims>;
|
|
20
|
+
//# sourceMappingURL=oidc-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-token.d.ts","sourceRoot":"","sources":["../../../src/host/oidc-token.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,aAAa,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAoCD,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiEzG"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { webcrypto } from 'node:crypto';
|
|
2
|
+
const DEFAULT_CLOCK_SKEW_SECONDS = 60;
|
|
3
|
+
const ALGORITHMS = {
|
|
4
|
+
RS256: { importParams: { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, verifyParams: { name: 'RSASSA-PKCS1-v1_5' } },
|
|
5
|
+
RS384: { importParams: { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-384' }, verifyParams: { name: 'RSASSA-PKCS1-v1_5' } },
|
|
6
|
+
RS512: { importParams: { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-512' }, verifyParams: { name: 'RSASSA-PKCS1-v1_5' } },
|
|
7
|
+
ES256: { importParams: { name: 'ECDSA', namedCurve: 'P-256' }, verifyParams: { name: 'ECDSA', hash: 'SHA-256' } },
|
|
8
|
+
ES384: { importParams: { name: 'ECDSA', namedCurve: 'P-384' }, verifyParams: { name: 'ECDSA', hash: 'SHA-384' } },
|
|
9
|
+
ES512: { importParams: { name: 'ECDSA', namedCurve: 'P-521' }, verifyParams: { name: 'ECDSA', hash: 'SHA-512' } },
|
|
10
|
+
};
|
|
11
|
+
function DecodeSegment(segment, what) {
|
|
12
|
+
let parsed;
|
|
13
|
+
try {
|
|
14
|
+
parsed = JSON.parse(Buffer.from(segment, 'base64url').toString('utf8'));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error(`the token ${what} is not valid JSON`);
|
|
18
|
+
}
|
|
19
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
20
|
+
throw new Error(`the token ${what} is not an object`);
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
function Audiences(claim) {
|
|
25
|
+
if (typeof claim === 'string')
|
|
26
|
+
return [claim];
|
|
27
|
+
if (Array.isArray(claim))
|
|
28
|
+
return claim.filter((entry) => typeof entry === 'string');
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
function SameResource(a, b) {
|
|
32
|
+
return a.replace(/\/+$/, '') === b.replace(/\/+$/, '');
|
|
33
|
+
}
|
|
34
|
+
export async function VerifyOidcToken(token, options) {
|
|
35
|
+
const parts = token.split('.');
|
|
36
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
37
|
+
if (parts.length !== 3 || !encodedHeader || !encodedPayload || encodedSignature === undefined) {
|
|
38
|
+
throw new Error('the bearer value is not a JWS compact serialization');
|
|
39
|
+
}
|
|
40
|
+
const header = DecodeSegment(encodedHeader, 'header');
|
|
41
|
+
const algorithm = typeof header.alg === 'string' ? ALGORITHMS[header.alg] : undefined;
|
|
42
|
+
if (!algorithm) {
|
|
43
|
+
throw new Error(`unsupported token signing algorithm: ${String(header.alg)}`);
|
|
44
|
+
}
|
|
45
|
+
const kid = typeof header.kid === 'string' ? header.kid : null;
|
|
46
|
+
const candidates = options.jwks.keys.filter(key => kid === null || key.kid === kid);
|
|
47
|
+
if (candidates.length === 0) {
|
|
48
|
+
throw new Error(`no key in the issuer key set matches kid ${kid ?? '(absent)'}`);
|
|
49
|
+
}
|
|
50
|
+
const signed = Buffer.from(`${encodedHeader}.${encodedPayload}`, 'utf8');
|
|
51
|
+
const signature = Buffer.from(encodedSignature, 'base64url');
|
|
52
|
+
let verified = false;
|
|
53
|
+
for (const jwk of candidates) {
|
|
54
|
+
let key;
|
|
55
|
+
try {
|
|
56
|
+
key = await webcrypto.subtle.importKey('jwk', jwk, algorithm.importParams, false, ['verify']);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (await webcrypto.subtle.verify(algorithm.verifyParams, key, signature, signed)) {
|
|
62
|
+
verified = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!verified) {
|
|
67
|
+
throw new Error('the token signature does not verify against the issuer key set');
|
|
68
|
+
}
|
|
69
|
+
const payload = DecodeSegment(encodedPayload, 'payload');
|
|
70
|
+
if (payload.iss !== options.issuer) {
|
|
71
|
+
throw new Error(`the token issuer ${String(payload.iss)} is not the configured issuer`);
|
|
72
|
+
}
|
|
73
|
+
const audiences = Audiences(payload.aud);
|
|
74
|
+
if (!audiences.some(entry => SameResource(entry, options.resource))) {
|
|
75
|
+
throw new Error(`the token audience does not include this resource (${options.resource})`);
|
|
76
|
+
}
|
|
77
|
+
const now = options.now ?? Math.floor(Date.now() / 1000);
|
|
78
|
+
const skew = options.clockSkewSeconds ?? DEFAULT_CLOCK_SKEW_SECONDS;
|
|
79
|
+
if (typeof payload.exp !== 'number') {
|
|
80
|
+
throw new Error('the token has no exp claim');
|
|
81
|
+
}
|
|
82
|
+
if (payload.exp <= now) {
|
|
83
|
+
throw new Error('the token has expired');
|
|
84
|
+
}
|
|
85
|
+
if (typeof payload.nbf === 'number' && payload.nbf - skew > now) {
|
|
86
|
+
throw new Error('the token is not valid yet');
|
|
87
|
+
}
|
|
88
|
+
if (typeof payload.sub !== 'string' || payload.sub.length === 0) {
|
|
89
|
+
throw new Error('the token has no sub claim');
|
|
90
|
+
}
|
|
91
|
+
return { ...payload, sub: payload.sub, iss: options.issuer, aud: audiences, exp: payload.exp };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=oidc-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-token.js","sourceRoot":"","sources":["../../../src/host/oidc-token.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TokenVerifier } from './identity.js';
|
|
2
|
+
import type { SessionBroker } from './session-broker.js';
|
|
3
|
+
export type FetchJson = (url: string) => Promise<unknown>;
|
|
4
|
+
export interface OidcVerifierOptions {
|
|
5
|
+
issuer: string;
|
|
6
|
+
broker: SessionBroker;
|
|
7
|
+
fetchJson?: FetchJson;
|
|
8
|
+
clockSkewSeconds?: number;
|
|
9
|
+
keyCacheSeconds?: number;
|
|
10
|
+
now?: () => number;
|
|
11
|
+
}
|
|
12
|
+
export declare const DEFAULT_KEY_CACHE_SECONDS = 600;
|
|
13
|
+
export declare function OidcVerifier(options: OidcVerifierOptions): TokenVerifier;
|
|
14
|
+
//# sourceMappingURL=oidc-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-verifier.d.ts","sourceRoot":"","sources":["../../../src/host/oidc-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAyB7C,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,aAAa,CAsCxE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { VerifyOidcToken } from './oidc-token.js';
|
|
2
|
+
export const DEFAULT_KEY_CACHE_SECONDS = 600;
|
|
3
|
+
const DISCOVERY_SUFFIX = '/.well-known/openid-configuration';
|
|
4
|
+
async function DefaultFetchJson(url) {
|
|
5
|
+
const res = await fetch(url);
|
|
6
|
+
if (!res.ok)
|
|
7
|
+
throw new Error(`${url} answered ${res.status}`);
|
|
8
|
+
return res.json();
|
|
9
|
+
}
|
|
10
|
+
function KeyIds(jwks) {
|
|
11
|
+
return new Set(jwks.keys.map(key => (typeof key.kid === 'string' ? key.kid : '')));
|
|
12
|
+
}
|
|
13
|
+
function TokenKeyId(token) {
|
|
14
|
+
const header = token.split('.')[0];
|
|
15
|
+
if (!header)
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(Buffer.from(header, 'base64url').toString('utf8'));
|
|
19
|
+
return typeof parsed.kid === 'string' ? parsed.kid : null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function OidcVerifier(options) {
|
|
26
|
+
const fetchJson = options.fetchJson ?? DefaultFetchJson;
|
|
27
|
+
const now = options.now ?? Date.now;
|
|
28
|
+
const cacheMs = (options.keyCacheSeconds ?? DEFAULT_KEY_CACHE_SECONDS) * 1000;
|
|
29
|
+
let jwksUri = null;
|
|
30
|
+
let cached = null;
|
|
31
|
+
let cachedAt = 0;
|
|
32
|
+
async function KeySet(refresh) {
|
|
33
|
+
if (cached && !refresh && now() - cachedAt < cacheMs)
|
|
34
|
+
return cached;
|
|
35
|
+
if (jwksUri === null) {
|
|
36
|
+
const discovery = await fetchJson(`${options.issuer.replace(/\/+$/, '')}${DISCOVERY_SUFFIX}`);
|
|
37
|
+
if (typeof discovery.jwks_uri !== 'string') {
|
|
38
|
+
throw new Error(`the issuer ${options.issuer} published no jwks_uri`);
|
|
39
|
+
}
|
|
40
|
+
jwksUri = discovery.jwks_uri;
|
|
41
|
+
}
|
|
42
|
+
cached = await fetchJson(jwksUri);
|
|
43
|
+
cachedAt = now();
|
|
44
|
+
return cached;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
async verify(token, resource) {
|
|
48
|
+
let jwks = await KeySet(false);
|
|
49
|
+
const kid = TokenKeyId(token);
|
|
50
|
+
if (kid !== null && !KeyIds(jwks).has(kid)) {
|
|
51
|
+
jwks = await KeySet(true);
|
|
52
|
+
}
|
|
53
|
+
const claims = await VerifyOidcToken(token, {
|
|
54
|
+
issuer: options.issuer,
|
|
55
|
+
resource,
|
|
56
|
+
jwks,
|
|
57
|
+
clockSkewSeconds: options.clockSkewSeconds,
|
|
58
|
+
});
|
|
59
|
+
return options.broker.resolve(claims);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=oidc-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-verifier.js","sourceRoot":"","sources":["../../../src/host/oidc-verifier.ts"],"names":[],"mappings":"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"}
|