UNPKG

@poppinss/oauth-client

Version:

A framework agnostic package to implement "Login with" flow using OAuth compliant authorization servers.

280 lines (279 loc) 5.99 kB
import { Exception, createError } from "@poppinss/exception"; import ky from "ky"; import { debuglog } from "node:util"; //#region \0rolldown/runtime.js var __defProp = Object.defineProperty; var __exportAll = (all, no_symbols) => { let target = {}; for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" }); return target; }; //#endregion //#region src/errors.ts var errors_exports = /* @__PURE__ */ __exportAll({ E_OAUTH_MISSING_TOKEN: () => E_OAUTH_MISSING_TOKEN, E_OAUTH_STATE_MISMATCH: () => E_OAUTH_STATE_MISMATCH }); /** * Raised when unable to get access token for oauth2 or oauth token and secret * for oauth1 */ const E_OAUTH_MISSING_TOKEN = class MissingTokenException extends Exception { static status = 400; static code = "E_OAUTH_MISSING_TOKEN"; static oauth2Message = "Invalid oauth2 response. Missing \"access_token\""; static oauth1Message = "Invalid oauth1 response. Missing \"oauth_token\" and \"oauth_token_secret\""; }; /** * Raised when unable to verify the CSRF state post redirect */ const E_OAUTH_STATE_MISMATCH = createError("Unable to verify re-redirect state", "E_OAUTH_STATE_MISMATCH", 400); //#endregion //#region src/debug.ts var debug_default = debuglog("oauth_client"); //#endregion //#region src/http_client.ts /** * An HTTP client abstraction we need for making OAuth requests */ var HttpClient = class { #baseUrl; /** * Request string params */ #params = {}; /** * Form fields to send */ #fields = {}; /** * Headers to send */ #headers = {}; /** * Oauth1 params */ #oauth1Params = {}; /** * The request body type. * * - The content type will be set to `application/json` * for a json request type. * - The content type will be set to `application/x-www-form-urlencoded` * for a urlencoded request type. */ #requestType = "urlencoded"; /** * Expected response body type. */ #responseType = "text"; constructor(baseUrl) { this.#baseUrl = baseUrl; } /** * Returns the got options for the request */ #getRequestOptions() { const hasBody = Object.keys(this.#fields).length > 0; const hasParams = Object.keys(this.#params).length > 0; return { ...hasBody ? this.#requestType === "json" ? { json: this.#fields } : { body: new URLSearchParams(this.#fields) } : {}, ...hasParams ? { searchParams: this.#params } : {}, headers: this.#headers }; } /** * Returns the response body of the got instance */ #getResponseBody(request) { if (this.#responseType === "json") return request.json(); return request.text(); } /** * Get access to request query params */ getParams() { return this.#params; } /** * Get access to request headers */ getHeaders() { return this.#headers; } /** * Get access to request body fields */ getFields() { return this.#fields; } /** * Get access to request oAuth1 params */ getOauth1Params() { return this.#oauth1Params; } /** * Get the request type */ getRequestType() { return this.#requestType; } /** * Get the type of the desired response type */ getResponseType() { return this.#responseType; } /** * Define query string param */ param(key, value) { this.#params[key] = value; return this; } /** * Remove a named param */ clearParam(key) { delete this.#params[key]; return this; } /** * Define an oauth1 param that makes it way to the Authorization * header */ oauth1Param(key, value) { this.#oauth1Params[key] = value; return this; } /** * Remove a named oauth1Param */ clearOauth1Param(key) { delete this.#oauth1Params[key]; return this; } /** * Define request body */ field(key, value) { this.#fields[key] = value; return this; } /** * Remove a field by its name */ clearField(key) { delete this.#fields[key]; return this; } /** * Define request header */ header(key, value) { this.#headers[key] = value; return this; } /** * Remove a header by its name */ clearHeader(key) { delete this.#headers[key]; return this; } /** * Set the request content type. */ sendAs(type) { this.#requestType = type; return this; } /** * Define how to parse the response */ parseAs(type) { this.#responseType = type; return this; } /** * Reset the client state */ clear() { this.#requestType = "urlencoded"; this.#responseType = "text"; this.#params = {}; this.#fields = {}; this.#headers = {}; this.#oauth1Params = {}; return this; } /** * Make a post request */ async post() { const options = this.#getRequestOptions(); debug_default("making POST request url: \"%s\" options: %O", this.#baseUrl, options); return this.#getResponseBody(ky.post(this.#baseUrl, options)); } /** * Make a get request */ async get() { const options = this.#getRequestOptions(); debug_default("making GET request url: \"%s\" options:%O", this.#baseUrl, options); return this.#getResponseBody(ky.get(this.#baseUrl, options)); } }; //#endregion //#region src/url_builder.ts /** * Fluent API to constructor a URL with query string */ var UrlBuilder = class { #url; constructor(baseUrl) { this.#url = new URL(baseUrl); } /** * Returns URL params as an object */ getParams() { const params = {}; for (const [key, value] of this.#url.searchParams.entries()) params[key] = value; return params; } /** * Define the request param */ param(key, value) { this.#url.searchParams.append(key, value); return this; } /** * Clear a specific param */ clearParam(key) { this.#url.searchParams.delete(key); return this; } /** * Clear all params */ clear() { Array.from(this.#url.searchParams.keys()).forEach((key) => this.clearParam(key)); return this; } /** * Returns the url */ makeUrl() { return this.#url.toString(); } }; //#endregion export { E_OAUTH_STATE_MISMATCH as a, E_OAUTH_MISSING_TOKEN as i, HttpClient as n, errors_exports as o, debug_default as r, UrlBuilder as t };