mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-13 19:10:39 +00:00
72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
export type ScopeTokenProvider = {
|
|
getAccessToken: (scope: string) => Promise<string>;
|
|
};
|
|
|
|
function isAuthFailureStatus(status: number): boolean {
|
|
return status === 401 || status === 403;
|
|
}
|
|
|
|
export async function fetchWithBearerAuthScopeFallback(params: {
|
|
url: string;
|
|
scopes: readonly string[];
|
|
tokenProvider?: ScopeTokenProvider;
|
|
fetchFn?: typeof fetch;
|
|
requestInit?: RequestInit;
|
|
requireHttps?: boolean;
|
|
shouldAttachAuth?: (url: string) => boolean;
|
|
shouldRetry?: (response: Response) => boolean;
|
|
}): Promise<Response> {
|
|
const fetchFn = params.fetchFn ?? fetch;
|
|
let parsedUrl: URL;
|
|
try {
|
|
parsedUrl = new URL(params.url);
|
|
} catch {
|
|
throw new Error(`Invalid URL: ${params.url}`);
|
|
}
|
|
if (params.requireHttps === true && parsedUrl.protocol !== "https:") {
|
|
throw new Error(`URL must use HTTPS: ${params.url}`);
|
|
}
|
|
|
|
const fetchOnce = (headers?: Headers): Promise<Response> =>
|
|
fetchFn(params.url, {
|
|
...params.requestInit,
|
|
...(headers ? { headers } : {}),
|
|
});
|
|
|
|
const firstAttempt = await fetchOnce();
|
|
if (firstAttempt.ok) {
|
|
return firstAttempt;
|
|
}
|
|
if (!params.tokenProvider) {
|
|
return firstAttempt;
|
|
}
|
|
|
|
const shouldRetry =
|
|
params.shouldRetry ?? ((response: Response) => isAuthFailureStatus(response.status));
|
|
if (!shouldRetry(firstAttempt)) {
|
|
return firstAttempt;
|
|
}
|
|
if (params.shouldAttachAuth && !params.shouldAttachAuth(params.url)) {
|
|
return firstAttempt;
|
|
}
|
|
|
|
for (const scope of params.scopes) {
|
|
try {
|
|
const token = await params.tokenProvider.getAccessToken(scope);
|
|
const authHeaders = new Headers(params.requestInit?.headers);
|
|
authHeaders.set("Authorization", `Bearer ${token}`);
|
|
const authAttempt = await fetchOnce(authHeaders);
|
|
if (authAttempt.ok) {
|
|
return authAttempt;
|
|
}
|
|
if (!shouldRetry(authAttempt)) {
|
|
continue;
|
|
}
|
|
} catch {
|
|
// Ignore token/fetch errors and continue trying remaining scopes.
|
|
}
|
|
}
|
|
|
|
return firstAttempt;
|
|
}
|