UNPKG

@scalar/api-client

Version:

the open source API testing client

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