UNPKG

@scalar/api-client

Version:

the open source API testing client

114 lines (113 loc) 5.51 kB
import { makeUrlAbsolute as x } from "@scalar/helpers/url/make-url-absolute"; import { shouldUseProxy as U } from "@scalar/oas-utils/helpers"; import { fromUint8Array as S, encode as w } from "js-base64"; const A = () => { const e = new Uint8Array(32); return crypto.getRandomValues(e), S(e, !0); }, b = async (e, c) => { if (c === "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 n = new TextEncoder().encode(e), o = await crypto.subtle.digest("SHA-256", n); return S(new Uint8Array(o), !0); }, T = async (e, c, u) => { try { if (!e) return [new Error("Flow not found"), null]; const n = e.selectedScopes.join(" "); if (e.type === "clientCredentials" || e.type === "password") return k( e, n, { proxyUrl: u }, c ); const o = (Math.random() + 1).toString(36).substring(2, 10), _ = x(e.authorizationUrl, { baseUrl: c?.url }), 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 = A(), a = await b(r, e["x-usePkce"]); y = { codeVerifier: r, codeChallenge: a, codeChallengeMethod: e["x-usePkce"] === "SHA-256" ? "S256" : "plain" }, t.searchParams.set("code_challenge", a), t.searchParams.set("code_challenge_method", y.codeChallengeMethod); } if (e["x-scalar-redirect-uri"].startsWith("/")) { const r = c?.url || window.location.origin + window.location.pathname, a = x(e["x-scalar-redirect-uri"], { baseUrl: r }); t.searchParams.set("redirect_uri", a); } 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 a = e["x-scalar-security-query"]?.[r]; a && t.searchParams.set(r, a); }), t.searchParams.set("client_id", e["x-scalar-client-id"]), t.searchParams.set("state", o), n && t.searchParams.set("scope", n); const s = window.open(t, "openAuth2Window", "left=100,top=100,width=800,height=600"); return s ? new Promise((r) => { const a = setInterval(() => { let h = null, l = null, d = null, m = null; try { const i = new URL(s.location.href).searchParams, P = e["x-tokenName"] || "access_token"; h = i.get(P), l = i.get("code"), d = i.get("error"), m = i.get("error_description"); const g = new URLSearchParams(s.location.href.split("#")[1]); h ||= g.get(P), l ||= g.get("code"), d ||= g.get("error"), m ||= g.get("error_description"); } catch { } if (s.closed || h || l || d) if (clearInterval(a), s.close(), d) r([new Error(`OAuth error: ${d}${m ? ` (${m})` : ""}`), null]); else if (h) { const i = s.location.href.match(/state=([^&]*)/)?.[1]; r(i === o ? [null, h] : [new Error("State mismatch"), null]); } else l ? new URL(s.location.href).searchParams.get("state") === o ? k( e, n, { code: l, pkce: y, proxyUrl: u }, c ).then(r) : r([new Error("State mismatch"), null]) : (clearInterval(a), 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]; } }, k = async (e, c, { code: u, pkce: n, 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"]), c && (e.type === "clientCredentials" || e.type === "password") && t.set("scope", c), 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"), n && t.set("code_verifier", n.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, s]) => { s && t.set(p, s); }); 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 ${w(`${e["x-scalar-client-id"]}:${e.clientSecret}`)}`); const r = x(e.tokenUrl, { baseUrl: _?.url }), a = U(o, r) ? `${o}?${new URLSearchParams([["scalar_url", r]]).toString()}` : r, l = await (await fetch(a, { method: "POST", headers: p, body: t })).json(), d = e["x-tokenName"] || "access_token"; return [null, l[d]]; } catch { return [new Error("Failed to get an access token. Please check your credentials."), null]; } }; export { T as authorizeOauth2, k as authorizeServers, b as generateCodeChallenge };