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