@amalgm/core 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/docs/authorization.md +9 -0
- package/package.json +1 -1
|
@@ -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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"}
|
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