@astrale-os/kernel-client 0.6.0-beta.75 → 0.6.0-beta.76

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.
@@ -11,8 +11,8 @@ type IssuerDependencies = Pick<ExchangeDependencies, 'kernel' | 'fetch' | 'polic
11
11
  export declare function tokenEndpoint(dependencies: IssuerDependencies, domain: IssuerId, operation: SessionOperation): Promise<string>;
12
12
  /**
13
13
  * Present a delegation at a Domain's token endpoint and admit the returned credential as provider
14
- * evidence: its issuer must be that Domain, its audience the source Kernel, and its signed expiry
15
- * the reported one.
14
+ * evidence: it must be the exchange representation served with `Cache-Control: no-store`, its
15
+ * issuer that Domain, its audience the source Kernel, and its signed expiry the reported one.
16
16
  */
17
17
  export declare function redeem(dependencies: IssuerDependencies, domain: IssuerId, endpoint: string, delegation: MintedCredential, operation: SessionOperation): Promise<SessionCredential>;
18
18
  export {};
@@ -23,11 +23,14 @@ export async function tokenEndpoint(dependencies, domain, operation) {
23
23
  }
24
24
  /**
25
25
  * Present a delegation at a Domain's token endpoint and admit the returned credential as provider
26
- * evidence: its issuer must be that Domain, its audience the source Kernel, and its signed expiry
27
- * the reported one.
26
+ * evidence: it must be the exchange representation served with `Cache-Control: no-store`, its
27
+ * issuer that Domain, its audience the source Kernel, and its signed expiry the reported one.
28
28
  */
29
29
  export async function redeem(dependencies, domain, endpoint, delegation, operation) {
30
- const exchanged = await fetchAdmitted(dependencies, TOKEN_EXCHANGE, endpoint, operation, (body) => issuer.exchange.acceptResponse(body), delegation);
30
+ const exchanged = await fetchAdmitted(dependencies, TOKEN_EXCHANGE, endpoint, operation, (body, headers) => {
31
+ requireCredentialHeaders(headers);
32
+ return issuer.exchange.acceptResponse(body);
33
+ }, delegation);
31
34
  let claims;
32
35
  try {
33
36
  claims = inspect(exchanged.token);
@@ -78,7 +81,7 @@ async function fetchAdmitted(dependencies, leg, url, operation, admit, credentia
78
81
  requireExactResponse(response, url, 'Domain token exchange');
79
82
  if (!response.ok)
80
83
  throw await refusal(response, leg, signal);
81
- return admit(codecs.json.decode(await boundedResponse(response, dependencies.maximumResponseBytes, signal)));
84
+ return admit(codecs.json.decode(await boundedResponse(response, dependencies.maximumResponseBytes, signal)), response.headers);
82
85
  }
83
86
  catch (cause) {
84
87
  throw signal.aborted ? cause : unadmitted(cause, leg);
@@ -124,6 +127,20 @@ function unadmitted(cause, leg) {
124
127
  }
125
128
  return cause;
126
129
  }
130
+ /** A credential is served as the exchange representation and never stored by an HTTP cache. */
131
+ function requireCredentialHeaders(headers) {
132
+ const mediaType = headers.get('content-type')?.split(';', 1)[0].trim().toLowerCase();
133
+ if (mediaType !== issuer.exchange.MEDIA_TYPE) {
134
+ throw invalidResponse('Domain token exchange returned an unsupported Content-Type.');
135
+ }
136
+ const directives = headers
137
+ .get('cache-control')
138
+ ?.split(',')
139
+ .map((directive) => directive.trim().toLowerCase()) ?? [];
140
+ if (!directives.includes('no-store')) {
141
+ throw invalidResponse('Domain token exchange response is missing Cache-Control: no-store.');
142
+ }
143
+ }
127
144
  function invalidResponse(message, cause) {
128
145
  return new ExchangeError(message, {
129
146
  failure: 'invalid-response',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-client",
3
- "version": "0.6.0-beta.75",
3
+ "version": "0.6.0-beta.76",
4
4
  "description": "Universal Client — protocol-first, transport-agnostic",
5
5
  "keywords": [
6
6
  "astrale",