UNPKG

@randajan/oauth2-client

Version:

Lightweight Node.js helper that streamlines OAuth 2.0 and service-account authentication for all Google APIs, giving downstream packages hassle-free token acquisition and refresh

192 lines (185 loc) 5.78 kB
import { extendURL, formatCredentials, isValidURL, objFromBase64, objToBase64, validateFn, validateURL } from "./chunk-RGLWNWDM.js"; // src/consts.js var vault = /* @__PURE__ */ new WeakMap(); // src/class/OAuth2Client.js import { virtuals } from "@randajan/props"; var OAuth2Client = class { constructor(Grant, options = {}) { const grant = new Grant(this, options); virtuals(this, { name: (_) => grant.name, uidKey: (_) => grant.uidKey }); vault.set(this, grant); } account(credentials, ...args) { const grant = vault.get(this); const c = grant.getCredentials ? grant.getCredentials(credentials, ...args) : credentials; return c instanceof Promise ? c.then((cr) => grant.createAccount(cr)) : grant.createAccount(c); } getInitAuthURL(options = {}) { const grant = vault.get(this); try { return grant.getInitAuthURL(options); } catch (err) { return grant.fallbackRedirect(1, err); } } getExitAuthURL({ code, state }, context) { const grant = vault.get(this); try { return grant.getExitAuthURL({ code, state }, context); } catch (err) { return grant.fallbackRedirect(2, err); } } }; // src/class/OAuth2Account.js import { solids } from "@randajan/props"; var OAuth2Account = class { constructor(client, credentials = {}) { credentials = formatCredentials(credentials); solids(this, { client, credentials }, false); } async uid() { const profile = await this.profile(); const { name, uidKey } = this.client; const id = profile?.[uidKey]; if (id) { return `${name}:${id}`; } } async profile() { return {}; } async tokens() { return {}; } async scopes(scopes) { const grant = vault.get(this.client); return grant.effaceScopes(scopes); } }; // src/errors.js var RedirectError = class _RedirectError extends Error { static is(any) { return any instanceof _RedirectError; } constructor(code, message, options) { super(message, options); this.code = code; } }; // src/class/OAuth2Grant.js var OAuth2Grant = class { static name = ""; static uidKey = ""; static scopePrefix = ""; static scopesCommon = []; static scopesNoPrefix = []; static Account = OAuth2Account; constructor(client, opt = {}) { const { name, uidKey, scopesCommon } = this.constructor; this.client = client; this.name = opt.name || name; this.uidKey = opt.uidKey || uidKey; this.isOffline = !!opt.isOffline; this.clientId = opt.clientId; this.clientSecret = opt.clientSecret; this.redirectUri = validateURL(true, opt.redirectUri, "options.redirectUri"); this.fallbackUri = validateURL(true, opt.fallbackUri, "options.fallbackUri"); this.landingUri = validateURL(false, opt.landingUri, "options.landingUri"); this.onAuth = validateFn(true, opt.onAuth, "options.onAuth"); this.onRenew = validateFn(true, opt.onRenew, "options.onRenew"); this.getCredentials = validateFn(false, opt.getCredentials, "options.getCredentials"); this.optExtra = opt.extra || {}; this.scopesRequired = this.effaceScopes([...scopesCommon, ...opt.scopes || []]); } createAccount(credentials) { const { client, constructor: { Account } } = this; return new Account(client, credentials); } effaceScope(scope, withPrefix = false) { const { scopePrefix, scopesNoPrefix } = this.constructor; if (scope == null) { return; } scope = String(scope).replace(/[\s\n\r]+/g, " ").trim().toLowerCase(); if (!scopePrefix || scopesNoPrefix.includes(scope)) { return scope; } const sw = scope.startsWith(scopePrefix); return sw === withPrefix ? scope : withPrefix ? scopePrefix + scope : scope.substring(scopePrefix.length); } effaceScopes(scopes, withPrefix = false, withRequired = false) { const { scopesRequired } = this; if (typeof scopes === "string") { scopes = scopes.split(" "); } if (!Array.isArray(scopes)) { scopes = []; } if (withRequired) { scopes = scopes.concat(scopesRequired); } const r = /* @__PURE__ */ new Set(); for (let scope of scopes) { if (scope) { scope = this.effaceScope(scope, withPrefix); } if (scope) { r.add(scope); } } return [...r]; } generateAuthUrl(scope, state, extra = {}) { } getInitAuthURL(options = {}) { const { landingUri, state: stateObj, scopes, extra } = options; if ((landingUri || !this.landingUri) && !isValidURL(landingUri)) { throw new RedirectError(1, "Bad request. Missing valid 'landingUri'"); } const scope = this.effaceScopes(scopes, true, true); const state = objToBase64([landingUri || this.landingUri, stateObj]); return this.generateAuthUrl(scope, state, extra || {}); } async swapCodeForTokens(code) { } async getExitAuthURL({ code, state }, context) { if (!code) { throw new RedirectError(201, "Bad request. Missing 'code'"); } const [landingUri, stateObj] = objFromBase64(state); if (!isValidURL(landingUri)) { throw new RedirectError(202, "Bad request. Missing valid 'state'"); } const tokens = await this.swapCodeForTokens(code); const account = this.createAccount(tokens); const customUri = await this.onAuth(account, { context, landingUri, state: stateObj }); return customUri || landingUri; } fallbackRedirect(majorCode, err) { const c = RedirectError.is(err) ? err.code : 0; return extendURL(this.fallbackUri, { errorCode: majorCode * 100 + c, errorMessage: err.message }); } }; export { vault, OAuth2Client, OAuth2Account, RedirectError, OAuth2Grant }; //# sourceMappingURL=chunk-27ATWD3Y.js.map