@scalar/api-client
Version:
the open source API testing client
114 lines (113 loc) • 5.59 kB
JavaScript
import { shouldUseProxy as U } from "@scalar/oas-utils/helpers";
import { encode as A, fromUint8Array as S } from "js-base64";
const C = () => {
const a = new Uint8Array(32);
return crypto.getRandomValues(a), S(a, !0);
}, E = async (a, t) => {
if (t === "plain")
return a;
if (typeof crypto?.subtle?.digest != "function")
return console.warn("SHA-256 is only supported when using https, using a plain text code challenge instead."), a;
const f = new TextEncoder().encode(a), h = await crypto.subtle.digest("SHA-256", f);
return S(new Uint8Array(h), !0);
}, z = async (a, t, m, f, h) => {
const i = a[t];
try {
if (!i)
return [new Error("Flow not found"), null];
const e = m.join(" ");
if (t === "clientCredentials" || t === "password")
return k(a, t, e, {
proxyUrl: h
});
const r = (Math.random() + 1).toString(36).substring(2, 10), c = new URL(a[t].authorizationUrl);
let o = null;
if (t === "implicit")
c.searchParams.set("response_type", "token");
else if (t === "authorizationCode") {
const s = a[t];
if (c.searchParams.set("response_type", "code"), s["x-usePkce"] !== "no") {
const n = C(), l = await E(n, s["x-usePkce"]);
o = {
codeVerifier: n,
codeChallenge: l,
codeChallengeMethod: s["x-usePkce"] === "SHA-256" ? "S256" : "plain"
}, c.searchParams.set("code_challenge", l), c.searchParams.set("code_challenge_method", o.codeChallengeMethod);
}
}
const w = a[t];
if (w["x-scalar-secret-redirect-uri"].startsWith("/")) {
const s = f?.url || window.location.origin + window.location.pathname, n = new URL(w["x-scalar-secret-redirect-uri"], s).toString();
c.searchParams.set("redirect_uri", n);
} else
c.searchParams.set("redirect_uri", w["x-scalar-secret-redirect-uri"]);
i["x-scalar-security-query"] && Object.keys(i["x-scalar-security-query"]).forEach((s) => {
const n = i["x-scalar-security-query"]?.[s];
n && c.searchParams.set(s, n);
}), c.searchParams.set("client_id", i["x-scalar-secret-client-id"]), c.searchParams.set("state", r), e && c.searchParams.set("scope", e);
const d = window.open(c, "openAuth2Window", "left=100,top=100,width=800,height=600");
return d ? new Promise((s) => {
const n = setInterval(() => {
let l = null, g = null, x = null, p = null;
try {
const u = new URL(d.location.href).searchParams, y = i["x-tokenName"] || "access_token";
l = u.get(y), g = u.get("code"), x = u.get("error"), p = u.get("error_description");
const _ = new URLSearchParams(d.location.href.split("#")[1]);
l ||= _.get(y), g ||= _.get("code"), x ||= _.get("error"), p ||= _.get("error_description");
} catch {
}
if (d.closed || l || g || x)
if (clearInterval(n), d.close(), x)
s([new Error(`OAuth error: ${x}${p ? ` (${p})` : ""}`), null]);
else if (l) {
const u = d.location.href.match(/state=([^&]*)/)?.[1];
s(u === r ? [null, l] : [new Error("State mismatch"), null]);
} else g && t === "authorizationCode" ? new URL(d.location.href).searchParams.get("state") === r ? k(a, t, e, {
code: g,
pkce: o,
proxyUrl: h
}).then(s) : s([new Error("State mismatch"), null]) : (clearInterval(n), s([new Error("Window was closed without granting authorization"), null]));
}, 200);
}) : [new Error("Failed to open auth window"), null];
} catch {
return [new Error("Failed to authorize oauth2 flow"), null];
}
}, k = async (a, t, m, {
code: f,
pkce: h,
proxyUrl: i
} = {}) => {
const e = a[t];
if (!e)
return [new Error("OAuth2 flow was not defined"), null];
const r = new URLSearchParams();
if (r.set("client_id", e["x-scalar-secret-client-id"]), m && (t === "clientCredentials" || t === "password") && r.set("scope", m), e["x-scalar-secret-client-secret"] && (!e["x-scalar-credentials-location"] || e["x-scalar-credentials-location"] === "body") && r.set("client_secret", e["x-scalar-secret-client-secret"]), "x-scalar-secret-redirect-uri" in e && e["x-scalar-secret-redirect-uri"] && r.set("redirect_uri", e["x-scalar-secret-redirect-uri"]), f)
r.set("code", f), r.set("grant_type", "authorization_code"), h && r.set("code_verifier", h.codeVerifier);
else if (t === "password") {
const o = a[t];
r.set("grant_type", "password"), r.set("username", o["x-scalar-secret-username"]), r.set("password", o["x-scalar-secret-password"]);
} else
r.set("grant_type", "client_credentials");
e["x-scalar-security-body"] && Object.entries(e["x-scalar-security-body"]).forEach(([o, w]) => {
w && r.set(o, w);
});
try {
const o = {
"Content-Type": "application/x-www-form-urlencoded"
};
e["x-scalar-secret-client-secret"] && (!e["x-scalar-credentials-location"] || e["x-scalar-credentials-location"] === "header") && (o.Authorization = `Basic ${A(`${e["x-scalar-secret-client-id"]}:${e["x-scalar-secret-client-secret"]}`)}`);
const P = U(i, e.tokenUrl) ? `${i}?${new URLSearchParams([["scalar_url", e.tokenUrl]]).toString()}` : e.tokenUrl, s = await (await fetch(P, {
method: "POST",
headers: o,
body: r
})).json(), n = e["x-tokenName"] || "access_token";
return [null, s[n]];
} catch {
return [new Error("Failed to get an access token. Please check your credentials."), null];
}
};
export {
z as authorizeOauth2,
k as authorizeServers,
E as generateCodeChallenge
};