UNPKG

@scalar/api-client

Version:

the open source API testing client

129 lines (128 loc) 5.87 kB
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 };