@foldrun/core 9.4.0 → 10.0.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/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/src/oauth-assertion.d.ts +77 -0
- package/dist/src/oauth-assertion.js +223 -0
- package/dist/src/oauth-capabilities.d.ts +22 -0
- package/dist/src/oauth-capabilities.js +178 -0
- package/dist/src/oauth-token.d.ts +16 -2
- package/dist/src/oauth-token.js +24 -3
- package/dist/src/secrets.d.ts +8 -0
- package/dist/src/secrets.js +9 -5
- package/index.ts +1 -0
- package/package.json +1 -1
- package/src/oauth-assertion.ts +234 -0
- package/src/oauth-capabilities.ts +204 -0
- package/src/oauth-token.ts +31 -4
- package/src/secrets.ts +17 -5
package/src/secrets.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { platform } from "./platform.ts";
|
|
|
26
26
|
import { fetchUntrusted } from "./untrusted-fetch.ts";
|
|
27
27
|
import { oauthTokenRequest, readTokenResponse, tokenAuthProblem, type TokenAuth } from "./oauth-token.ts";
|
|
28
28
|
import { isSecureUrl } from "./oauth-discovery.ts";
|
|
29
|
+
import type { AssertionAlg } from "./oauth-assertion.ts";
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* GCM's auth tag, pinned to its full 16 bytes at every cipher call. Without
|
|
@@ -945,6 +946,13 @@ export interface OAuth2Config {
|
|
|
945
946
|
/** The provider's revocation endpoint (RFC 7009): deleting or replacing
|
|
946
947
|
* this secret revokes its tokens there (revokeOAuth2). */
|
|
947
948
|
revocation_url?: string;
|
|
949
|
+
/** token_auth private_key_jwt: the private key (PEM or JWK) the client
|
|
950
|
+
* signs its assertions with, sealed with the rest of the recipe; its kid,
|
|
951
|
+
* alg, and certificate when the provider names keys by it (Entra). */
|
|
952
|
+
client_assertion_key?: string;
|
|
953
|
+
client_assertion_kid?: string;
|
|
954
|
+
client_assertion_alg?: AssertionAlg;
|
|
955
|
+
client_assertion_cert?: string;
|
|
948
956
|
}
|
|
949
957
|
|
|
950
958
|
const OAUTH2_PREFIX = "@oauth2 ";
|
|
@@ -970,8 +978,8 @@ export function setOAuth2Secret(
|
|
|
970
978
|
}
|
|
971
979
|
// A public client (PKCE, no secret) refreshes with its client id alone;
|
|
972
980
|
// client credentials are nothing without the secret.
|
|
973
|
-
if (config.grant_type === "client_credentials" && !config.client_secret?.trim()) {
|
|
974
|
-
throw new Error("oauth2 secret needs client_secret for client_credentials");
|
|
981
|
+
if (config.grant_type === "client_credentials" && !config.client_secret?.trim() && config.token_auth !== "private_key_jwt") {
|
|
982
|
+
throw new Error("oauth2 secret needs client_secret for client_credentials (or token_auth private_key_jwt with a key)");
|
|
975
983
|
}
|
|
976
984
|
if (config.grant_type !== "client_credentials" && !config.refresh_token?.trim()) {
|
|
977
985
|
throw new Error("oauth2 secret needs refresh_token (or grant_type: client_credentials)");
|
|
@@ -1068,11 +1076,14 @@ async function exchange(config: OAuth2Config, cacheKey: string): Promise<OAuthLi
|
|
|
1068
1076
|
const flight = (async () => {
|
|
1069
1077
|
// One builder for every token request (oauth-token.ts): the client's
|
|
1070
1078
|
// identity goes where its token_auth says, and an `extra` cannot set it.
|
|
1071
|
-
|
|
1079
|
+
// Built per try: a private_key_jwt assertion's jti is good once, so a
|
|
1080
|
+
// retry after a lost answer must carry a new one (Codex review).
|
|
1081
|
+
const build = () => oauthTokenRequest(config, {
|
|
1072
1082
|
grant_type: config.grant_type ?? "refresh_token",
|
|
1073
1083
|
...(config.grant_type === "client_credentials" ? {} : { refresh_token: config.refresh_token! }),
|
|
1074
1084
|
...(config.extra ?? {}),
|
|
1075
|
-
});
|
|
1085
|
+
}, { audience: config.token_url });
|
|
1086
|
+
build(); // a config that cannot make a request says so before any network
|
|
1076
1087
|
// A network blip is retried; a provider's answer is not. "fetch failed"
|
|
1077
1088
|
// (a reset, a DNS hiccup, a timeout) failed whole steps on the first try
|
|
1078
1089
|
// — gbp-desk's Thursday posts, 7 Oct 2026. A 4xx (revoked, wrong client)
|
|
@@ -1083,6 +1094,7 @@ async function exchange(config: OAuth2Config, cacheKey: string): Promise<OAuthLi
|
|
|
1083
1094
|
for (let attempt = 0; attempt < OAUTH_REFRESH_TRIES; attempt++) {
|
|
1084
1095
|
if (attempt) await new Promise((r) => setTimeout(r, oauthRetryDelayMs(attempt)));
|
|
1085
1096
|
try {
|
|
1097
|
+
const request = build();
|
|
1086
1098
|
res = await fetchUntrusted(config.token_url, {
|
|
1087
1099
|
method: "POST",
|
|
1088
1100
|
headers: request.headers,
|
|
@@ -1231,7 +1243,7 @@ export async function revokeOAuth2Value(
|
|
|
1231
1243
|
const out: OAuthRevokeResult = { revoked: [] };
|
|
1232
1244
|
for (const [hint, token] of tokens) {
|
|
1233
1245
|
try {
|
|
1234
|
-
const request = oauthTokenRequest(config, { token, token_type_hint: hint });
|
|
1246
|
+
const request = oauthTokenRequest(config, { token, token_type_hint: hint }, { audience: config.token_url });
|
|
1235
1247
|
const res = await fetcher(config.revocation_url, { method: "POST", headers: request.headers, body: request.body, redirect: "error", signal: AbortSignal.timeout(10_000) });
|
|
1236
1248
|
if (res.ok) {
|
|
1237
1249
|
await res.body?.cancel().catch(() => {});
|