UNPKG

@scalar/api-client

Version:

the open source API testing client

140 lines (139 loc) 6.27 kB
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 };