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