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