@amalgm/core 0.4.1 → 0.4.3
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/PURPOSE.md +2 -0
- package/dist/identity/authorization/index.d.ts +1 -0
- package/dist/identity/authorization/index.d.ts.map +1 -1
- package/dist/identity/authorization/index.js +1 -0
- package/dist/identity/authorization/index.js.map +1 -1
- package/dist/identity/authorization/policy.d.ts +2 -0
- package/dist/identity/authorization/policy.d.ts.map +1 -1
- package/dist/identity/authorization/policy.js +10 -0
- package/dist/identity/authorization/policy.js.map +1 -1
- package/dist/identity/authorization/resource.d.ts +39 -0
- package/dist/identity/authorization/resource.d.ts.map +1 -0
- package/dist/identity/authorization/resource.js +132 -0
- package/dist/identity/authorization/resource.js.map +1 -0
- package/dist/transport/shipped-routes.d.ts +5 -3
- package/dist/transport/shipped-routes.d.ts.map +1 -1
- package/dist/transport/shipped-routes.js +5 -4
- package/dist/transport/shipped-routes.js.map +1 -1
- package/docs/authorization.md +9 -0
- package/package.json +1 -1
package/PURPOSE.md
CHANGED
|
@@ -100,6 +100,8 @@ that composes an exact Core release with the other product releases.
|
|
|
100
100
|
17. **Retirement is explicit subtraction.** Every retired Engine prefix stays
|
|
101
101
|
named as evidence but is absent from the active route table, so dead
|
|
102
102
|
compatibility surfaces cannot masquerade as unfinished product work.
|
|
103
|
+
The retired `/email` compatibility door is owned by neither Core nor
|
|
104
|
+
Shell; current-user delivery belongs to the standalone Channels product.
|
|
103
105
|
18. **Machine setup is one trust rail with two entrances.** Browser polling and
|
|
104
106
|
an approved setup code converge before the same registration, credential
|
|
105
107
|
split, installation, canonical-runtime, and readiness path;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export declare const AUTHORIZATION_POLICY: Readonly<{
|
|
3
3
|
readonly 'amalgm-api-proxy': readonly string[];
|
|
4
4
|
readonly 'amalgm-apps': readonly string[];
|
|
5
|
+
readonly 'amalgm-automations': readonly string[];
|
|
6
|
+
readonly 'amalgm-channels': readonly string[];
|
|
5
7
|
}>;
|
|
6
8
|
export type AuthorizationAudience = keyof typeof AUTHORIZATION_POLICY;
|
|
7
9
|
export type AuthorizationScope = (typeof AUTHORIZATION_POLICY)[AuthorizationAudience][number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB;;;;;EAuBtB,CAAC;AAEZ,MAAM,MAAM,qBAAqB,GAAG,MAAM,OAAO,oBAAoB,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9F,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAEtF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,kBAAkB,EAAE,CAEhG;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,EAAE,SAAS,MAAM,EAAE,GACxB,SAAS,kBAAkB,EAAE,CAQ/B;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,CAAC;gBAE3C,IAAI,EAAE,iBAAiB,GAAG,mBAAmB;CAO1D"}
|
|
@@ -12,6 +12,16 @@ export const AUTHORIZATION_POLICY = Object.freeze({
|
|
|
12
12
|
'apps:read',
|
|
13
13
|
'apps:run',
|
|
14
14
|
]),
|
|
15
|
+
'amalgm-automations': Object.freeze([
|
|
16
|
+
'automations:read',
|
|
17
|
+
'automations:write',
|
|
18
|
+
'runs:execute',
|
|
19
|
+
'runs:read',
|
|
20
|
+
]),
|
|
21
|
+
'amalgm-channels': Object.freeze([
|
|
22
|
+
'channels:read',
|
|
23
|
+
'channels:send',
|
|
24
|
+
]),
|
|
15
25
|
});
|
|
16
26
|
export function isAuthorizationAudience(value) {
|
|
17
27
|
return typeof value === 'string' && value in AUTHORIZATION_POLICY;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IAChD,kBAAkB,EAAE,MAAM,CAAC,MAAM,CAAC;QAChC,eAAe;QACf,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,SAAS;KACV,CAAC;IACF,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC;QAC3B,aAAa;QACb,WAAW;QACX,UAAU;KACX,CAAC;CACM,CAAC,CAAC;AAKZ,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,oBAAoB,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAA+B;IAC/D,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAA+B,EAC/B,MAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,wBAAwB,CAAC,iBAAiB,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,wBAAwB,CAAC,mBAAmB,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAkC,CAAC;AACvE,CAAC;AAED,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,IAAI,CAA0C;IAEvD,YAAY,IAA6C;QACvD,KAAK,CAAC,IAAI,KAAK,iBAAiB;YAC9B,CAAC,CAAC,8CAA8C;YAChD,CAAC,CAAC,sDAAsD,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IAChD,kBAAkB,EAAE,MAAM,CAAC,MAAM,CAAC;QAChC,eAAe;QACf,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,SAAS;KACV,CAAC;IACF,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC;QAC3B,aAAa;QACb,WAAW;QACX,UAAU;KACX,CAAC;IACF,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC;QAClC,kBAAkB;QAClB,mBAAmB;QACnB,cAAc;QACd,WAAW;KACZ,CAAC;IACF,iBAAiB,EAAE,MAAM,CAAC,MAAM,CAAC;QAC/B,eAAe;QACf,eAAe;KAChB,CAAC;CACM,CAAC,CAAC;AAKZ,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,oBAAoB,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAA+B;IAC/D,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAA+B,EAC/B,MAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,wBAAwB,CAAC,iBAAiB,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,wBAAwB,CAAC,mBAAmB,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAkC,CAAC;AACvE,CAAC;AAED,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,IAAI,CAA0C;IAEvD,YAAY,IAA6C;QACvD,KAAK,CAAC,IAAI,KAAK,iBAAiB;YAC9B,CAAC,CAAC,8CAA8C;YAChD,CAAC,CAAC,sDAAsD,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type AuthorizationAudience, type AuthorizationScope } from './policy.js';
|
|
2
|
+
export interface VerifiedJwt {
|
|
3
|
+
readonly header: Readonly<Record<string, unknown>>;
|
|
4
|
+
readonly claims: Readonly<Record<string, unknown>>;
|
|
5
|
+
}
|
|
6
|
+
export interface MachineResourceVerifierPorts {
|
|
7
|
+
verifyAccessToken(token: string): Promise<VerifiedJwt>;
|
|
8
|
+
verifyDpopProof(proof: string): Promise<VerifiedJwt>;
|
|
9
|
+
jwkThumbprint(jwk: Readonly<Record<string, unknown>>): Promise<string>;
|
|
10
|
+
accessTokenHash(token: string): Promise<string>;
|
|
11
|
+
consumeProof(keyThumbprint: string, jti: string): Promise<boolean>;
|
|
12
|
+
nowSeconds(): number;
|
|
13
|
+
}
|
|
14
|
+
export interface MachineResourceRequest {
|
|
15
|
+
readonly authorization: string | null | undefined;
|
|
16
|
+
readonly dpop: string | null | undefined;
|
|
17
|
+
readonly method: string;
|
|
18
|
+
readonly url: string;
|
|
19
|
+
readonly issuer: string;
|
|
20
|
+
readonly audience: AuthorizationAudience;
|
|
21
|
+
readonly requiredScopes: readonly AuthorizationScope[];
|
|
22
|
+
}
|
|
23
|
+
export interface AuthorizedMachinePrincipal {
|
|
24
|
+
readonly userId: string;
|
|
25
|
+
readonly computerId: string;
|
|
26
|
+
readonly clientId: 'amalgm-shell';
|
|
27
|
+
readonly scopes: readonly AuthorizationScope[];
|
|
28
|
+
readonly keyThumbprint: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The one resource-server decision for every hosted Amalgm product. Crypto
|
|
32
|
+
* and shared replay persistence are injected; claims and DPoP law stay here.
|
|
33
|
+
*/
|
|
34
|
+
export declare function authorizeMachineResource(request: MachineResourceRequest, ports: MachineResourceVerifierPorts): Promise<AuthorizedMachinePrincipal>;
|
|
35
|
+
export declare class MachineResourceAuthorizationError extends Error {
|
|
36
|
+
readonly code: string;
|
|
37
|
+
constructor(code: string);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/resource.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,4BAA4B;IAC3C,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACvD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrD,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,UAAU,IAAI,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,QAAQ,CAAC,cAAc,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACxD;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,0BAA0B,CAAC,CAwErC;AA8DD,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;CAKzB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { ACCESS_TOKEN_CLOCK_SKEW_SECONDS, ACCESS_TOKEN_TTL_SECONDS, DPOP_PROOF_MAX_AGE_SECONDS, scopesFromClaim, } from './claims.js';
|
|
2
|
+
import { normalizeDpopHtu, normalizeDpopMethod } from './dpop.js';
|
|
3
|
+
import { canonicalScopes, } from './policy.js';
|
|
4
|
+
/**
|
|
5
|
+
* The one resource-server decision for every hosted Amalgm product. Crypto
|
|
6
|
+
* and shared replay persistence are injected; claims and DPoP law stay here.
|
|
7
|
+
*/
|
|
8
|
+
export async function authorizeMachineResource(request, ports) {
|
|
9
|
+
const token = dpopAccessToken(request.authorization);
|
|
10
|
+
const proofValue = requiredText(request.dpop, 'dpop_required');
|
|
11
|
+
const now = ports.nowSeconds();
|
|
12
|
+
if (!Number.isInteger(now))
|
|
13
|
+
throw new MachineResourceAuthorizationError('invalid_clock');
|
|
14
|
+
const access = await verified(() => ports.verifyAccessToken(token), 'invalid_access_token');
|
|
15
|
+
assertHeader(access.header, 'at+jwt', false);
|
|
16
|
+
const claims = access.claims;
|
|
17
|
+
if (claims.iss !== request.issuer || claims.aud !== request.audience) {
|
|
18
|
+
throw new MachineResourceAuthorizationError('access_target_mismatch');
|
|
19
|
+
}
|
|
20
|
+
const issuedAt = integerClaim(claims.iat, 'invalid_access_claims');
|
|
21
|
+
const expiresAt = integerClaim(claims.exp, 'invalid_access_claims');
|
|
22
|
+
if (issuedAt > now + ACCESS_TOKEN_CLOCK_SKEW_SECONDS
|
|
23
|
+
|| expiresAt <= now - ACCESS_TOKEN_CLOCK_SKEW_SECONDS
|
|
24
|
+
|| expiresAt - issuedAt > ACCESS_TOKEN_TTL_SECONDS)
|
|
25
|
+
throw new MachineResourceAuthorizationError('invalid_access_lifetime');
|
|
26
|
+
const userId = claimText(claims.sub, 'invalid_access_claims');
|
|
27
|
+
const computerId = claimText(claims.computer_id, 'invalid_access_claims');
|
|
28
|
+
if (claims.client_id !== 'amalgm-shell') {
|
|
29
|
+
throw new MachineResourceAuthorizationError('invalid_client');
|
|
30
|
+
}
|
|
31
|
+
claimText(claims.jti, 'invalid_access_claims');
|
|
32
|
+
const confirmation = record(claims.cnf, 'invalid_access_claims');
|
|
33
|
+
const keyThumbprint = claimText(confirmation.jkt, 'invalid_access_claims');
|
|
34
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(keyThumbprint)) {
|
|
35
|
+
throw new MachineResourceAuthorizationError('invalid_access_claims');
|
|
36
|
+
}
|
|
37
|
+
const rawScopes = scopesFromClaim(claims.scope);
|
|
38
|
+
let scopes;
|
|
39
|
+
try {
|
|
40
|
+
scopes = canonicalScopes(request.audience, rawScopes);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new MachineResourceAuthorizationError('invalid_scope');
|
|
44
|
+
}
|
|
45
|
+
const requiredScopes = canonicalScopes(request.audience, request.requiredScopes);
|
|
46
|
+
if (requiredScopes.some((scope) => !scopes.includes(scope))) {
|
|
47
|
+
throw new MachineResourceAuthorizationError('insufficient_scope');
|
|
48
|
+
}
|
|
49
|
+
const proof = await verified(() => ports.verifyDpopProof(proofValue), 'invalid_dpop_proof');
|
|
50
|
+
const publicJwk = assertHeader(proof.header, 'dpop+jwt', true);
|
|
51
|
+
const proofClaims = proof.claims;
|
|
52
|
+
if (proofClaims.htm !== normalizeDpopMethod(request.method)
|
|
53
|
+
|| proofClaims.htu !== normalizeDpopHtu(request.url)
|
|
54
|
+
|| proofClaims.ath !== await ports.accessTokenHash(token))
|
|
55
|
+
throw new MachineResourceAuthorizationError('dpop_target_mismatch');
|
|
56
|
+
const proofIat = integerClaim(proofClaims.iat, 'invalid_dpop_claims');
|
|
57
|
+
const proofJti = claimText(proofClaims.jti, 'invalid_dpop_claims');
|
|
58
|
+
if (Math.abs(now - proofIat) > DPOP_PROOF_MAX_AGE_SECONDS
|
|
59
|
+
|| proofJti.length > 200)
|
|
60
|
+
throw new MachineResourceAuthorizationError('invalid_dpop_claims');
|
|
61
|
+
if (await ports.jwkThumbprint(publicJwk) !== keyThumbprint) {
|
|
62
|
+
throw new MachineResourceAuthorizationError('dpop_key_mismatch');
|
|
63
|
+
}
|
|
64
|
+
if (!await ports.consumeProof(keyThumbprint, proofJti)) {
|
|
65
|
+
throw new MachineResourceAuthorizationError('dpop_replayed');
|
|
66
|
+
}
|
|
67
|
+
return Object.freeze({
|
|
68
|
+
userId,
|
|
69
|
+
computerId,
|
|
70
|
+
clientId: 'amalgm-shell',
|
|
71
|
+
scopes,
|
|
72
|
+
keyThumbprint,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function dpopAccessToken(value) {
|
|
76
|
+
const match = value?.match(/^DPoP\s+(\S+)$/);
|
|
77
|
+
if (!match?.[1])
|
|
78
|
+
throw new MachineResourceAuthorizationError('dpop_access_required');
|
|
79
|
+
return match[1];
|
|
80
|
+
}
|
|
81
|
+
function assertHeader(header, type, requiresJwk) {
|
|
82
|
+
if (header.typ !== type || header.alg !== 'ES256') {
|
|
83
|
+
throw new MachineResourceAuthorizationError('invalid_jwt_header');
|
|
84
|
+
}
|
|
85
|
+
if (!requiresJwk)
|
|
86
|
+
return Object.freeze({});
|
|
87
|
+
const jwk = record(header.jwk, 'invalid_jwt_header');
|
|
88
|
+
if (jwk.kty !== 'EC'
|
|
89
|
+
|| jwk.crv !== 'P-256'
|
|
90
|
+
|| typeof jwk.x !== 'string'
|
|
91
|
+
|| typeof jwk.y !== 'string'
|
|
92
|
+
|| 'd' in jwk)
|
|
93
|
+
throw new MachineResourceAuthorizationError('invalid_jwt_header');
|
|
94
|
+
return jwk;
|
|
95
|
+
}
|
|
96
|
+
async function verified(operation, code) {
|
|
97
|
+
try {
|
|
98
|
+
return await operation();
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
throw new MachineResourceAuthorizationError(code);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function requiredText(value, code) {
|
|
105
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
106
|
+
throw new MachineResourceAuthorizationError(code);
|
|
107
|
+
}
|
|
108
|
+
return value.trim();
|
|
109
|
+
}
|
|
110
|
+
function claimText(value, code) {
|
|
111
|
+
return requiredText(value, code);
|
|
112
|
+
}
|
|
113
|
+
function integerClaim(value, code) {
|
|
114
|
+
if (!Number.isInteger(value))
|
|
115
|
+
throw new MachineResourceAuthorizationError(code);
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function record(value, code) {
|
|
119
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
120
|
+
throw new MachineResourceAuthorizationError(code);
|
|
121
|
+
}
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
export class MachineResourceAuthorizationError extends Error {
|
|
125
|
+
code;
|
|
126
|
+
constructor(code) {
|
|
127
|
+
super(code);
|
|
128
|
+
this.name = 'MachineResourceAuthorizationError';
|
|
129
|
+
this.code = code;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../../src/identity/authorization/resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* under its own '/pty' handling).
|
|
10
10
|
*
|
|
11
11
|
* Counts, pinned by tests: 3 gateway prefixes, 5 gateway exact paths,
|
|
12
|
-
* 3 port-monitor prefixes, 1 chat prefix,
|
|
12
|
+
* 3 port-monitor prefixes, 1 chat prefix, 21 active MCP prefixes.
|
|
13
13
|
*
|
|
14
14
|
* This is product configuration riding in the kernel repo for the
|
|
15
15
|
* cutover: the table SHAPE is core's; these CONTENTS belong to the
|
|
@@ -23,8 +23,10 @@ import type { RouteTable } from './routing.js';
|
|
|
23
23
|
* `/tasks` is the superseded SQLite scheduler facade; `/mcp-connections` is
|
|
24
24
|
* the superseded second registry now represented by Tools connection records
|
|
25
25
|
* plus credential references resolved by Core's machine-local vault; `/github`
|
|
26
|
-
* is superseded by the next cloud integration and credential collection model
|
|
26
|
+
* is superseded by the next cloud integration and credential collection model;
|
|
27
|
+
* `/email` was the in-process Notifications compatibility door and is replaced
|
|
28
|
+
* by the independently deployed Channels product.
|
|
27
29
|
*/
|
|
28
|
-
export declare const retiredEngineRoutePrefixes: readonly ["/tasks", "/mcp-connections", "/github"];
|
|
30
|
+
export declare const retiredEngineRoutePrefixes: readonly ["/tasks", "/mcp-connections", "/github", "/email"];
|
|
29
31
|
export declare const shippedRouteTable: RouteTable;
|
|
30
32
|
//# sourceMappingURL=shipped-routes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shipped-routes.d.ts","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C
|
|
1
|
+
{"version":3,"file":"shipped-routes.d.ts","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,8DAA+D,CAAC;AAEvG,eAAO,MAAM,iBAAiB,EAAE,UA4B/B,CAAC"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* under its own '/pty' handling).
|
|
10
10
|
*
|
|
11
11
|
* Counts, pinned by tests: 3 gateway prefixes, 5 gateway exact paths,
|
|
12
|
-
* 3 port-monitor prefixes, 1 chat prefix,
|
|
12
|
+
* 3 port-monitor prefixes, 1 chat prefix, 21 active MCP prefixes.
|
|
13
13
|
*
|
|
14
14
|
* This is product configuration riding in the kernel repo for the
|
|
15
15
|
* cutover: the table SHAPE is core's; these CONTENTS belong to the
|
|
@@ -22,9 +22,11 @@
|
|
|
22
22
|
* `/tasks` is the superseded SQLite scheduler facade; `/mcp-connections` is
|
|
23
23
|
* the superseded second registry now represented by Tools connection records
|
|
24
24
|
* plus credential references resolved by Core's machine-local vault; `/github`
|
|
25
|
-
* is superseded by the next cloud integration and credential collection model
|
|
25
|
+
* is superseded by the next cloud integration and credential collection model;
|
|
26
|
+
* `/email` was the in-process Notifications compatibility door and is replaced
|
|
27
|
+
* by the independently deployed Channels product.
|
|
26
28
|
*/
|
|
27
|
-
export const retiredEngineRoutePrefixes = ['/tasks', '/mcp-connections', '/github'];
|
|
29
|
+
export const retiredEngineRoutePrefixes = ['/tasks', '/mcp-connections', '/github', '/email'];
|
|
28
30
|
export const shippedRouteTable = {
|
|
29
31
|
gatewayPrefixes: ['/__amalgm', '/diagnostics', '/pty'],
|
|
30
32
|
gatewayExactPaths: ['/', '/healthz', '/runtime-state', '/pty/session', '/pty/resize'],
|
|
@@ -42,7 +44,6 @@ export const shippedRouteTable = {
|
|
|
42
44
|
'/state',
|
|
43
45
|
'/local',
|
|
44
46
|
'/credentials',
|
|
45
|
-
'/email',
|
|
46
47
|
'/slack',
|
|
47
48
|
'/user-api-keys',
|
|
48
49
|
'/apps',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shipped-routes.js","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH
|
|
1
|
+
{"version":3,"file":"shipped-routes.js","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,QAAQ,EAAE,kBAAkB,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAEvG,MAAM,CAAC,MAAM,iBAAiB,GAAe;IAC3C,eAAe,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC;IACtD,iBAAiB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,CAAC;IACrF,mBAAmB,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,eAAe,CAAC;IAC9D,YAAY,EAAE,CAAC,OAAO,CAAC;IACvB,WAAW,EAAE;QACX,MAAM;QACN,SAAS;QACT,cAAc;QACd,iBAAiB;QACjB,YAAY;QACZ,kBAAkB;QAClB,KAAK;QACL,UAAU;QACV,QAAQ;QACR,QAAQ;QACR,cAAc;QACd,QAAQ;QACR,gBAAgB;QAChB,OAAO;QACP,YAAY;QACZ,SAAS;QACT,eAAe;QACf,gBAAgB;QAChB,SAAS;QACT,mBAAmB;QACnB,UAAU;KACX;CACF,CAAC"}
|
package/docs/authorization.md
CHANGED
|
@@ -36,6 +36,15 @@ Import `@amalgm/core/authorization` for:
|
|
|
36
36
|
- URL and method normalization;
|
|
37
37
|
- the closed initial-pin, ordinary-reuse, and explicit-recovery transition;
|
|
38
38
|
- `CredentialBroker`, whose issuer and signer effects are injected by Shell.
|
|
39
|
+
- `authorizeMachineResource`, whose crypto and replay effects are injected by
|
|
40
|
+
a hosted product while Core makes the one claims-and-DPoP decision.
|
|
41
|
+
|
|
42
|
+
The closed service grants are:
|
|
43
|
+
|
|
44
|
+
- `amalgm-api-proxy`: one scope per supported LLM provider;
|
|
45
|
+
- `amalgm-apps`: read, run, and manage apps;
|
|
46
|
+
- `amalgm-automations`: read/write definitions and read/execute runs;
|
|
47
|
+
- `amalgm-channels`: read delivery history and send to the current user.
|
|
39
48
|
|
|
40
49
|
The default access lifetime is 15 minutes. Cached credentials refresh at least
|
|
41
50
|
two minutes early, with up to 30 seconds of jitter. DPoP proofs are one-request
|
package/package.json
CHANGED