UNPKG

@open-condo/miniapp-utils

Version:

A set of helper functions / components / hooks used to build new condo apps fast

244 lines (239 loc) 9.81 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/helpers/oidc.ts var oidc_exports = {}; __export(oidc_exports, { OIDCMiddleware: () => OIDCMiddleware }); module.exports = __toCommonJS(oidc_exports); var import_openid_client = require("openid-client"); var import_zod = require("zod"); // src/helpers/ip/utils.ts var v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; var v4Str = `(${v4Seg}[.]){3}${v4Seg}`; var IPv4Reg = new RegExp(`^${v4Str}$`); var v6Seg = "(?:[0-9a-fA-F]{1,4})"; var IPv6Reg = new RegExp( `^((?:${v6Seg}:){7}(?:${v6Seg}|:)|(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:)))(%[0-9a-zA-Z]{1,})?$` ); // src/helpers/urls.ts var REGEXP_ESCAPE_CHARS = /[\\^$.*+?()[\]{}|]/g; var WILDCARD_REGEXP_PART = "([a-zA-Z0-9-]{1,63})"; var WILDCARD_REGEXP_PART_ESCAPED = _escapeRegexp(WILDCARD_REGEXP_PART); function isSafeUrl(url) { if (!url || typeof url !== "string") return false; let decodedUrl; try { decodedUrl = decodeURI(url); } catch (error) { return false; } const normalizedUrl = decodedUrl.replace(/[\u0000-\u001F\s]/g, "").toLowerCase(); return !normalizedUrl.includes("javascript:"); } function _escapeRegexp(source) { return source.replace(REGEXP_ESCAPE_CHARS, "\\$&"); } // src/helpers/uuid.ts var import_crypto = require("crypto"); function generateUUIDv4() { let randomValues; if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { return crypto.randomUUID(); } else if (typeof window !== "undefined" && window.crypto && window.crypto.getRandomValues) { randomValues = new Uint8Array(16); window.crypto.getRandomValues(randomValues); } else { randomValues = (0, import_crypto.randomBytes)(16); } randomValues[6] = randomValues[6] & 15 | 64; randomValues[8] = randomValues[8] & 63 | 128; return [...randomValues].map((value, index) => { const hex = value.toString(16).padStart(2, "0"); if (index === 4 || index === 6 || index === 8 || index === 10) { return `-${hex}`; } return hex; }).join(""); } // src/helpers/oidc.ts var _OIDCMiddleware = class _OIDCMiddleware { static getQueryParams(req) { return new URL(req.url || "/", "https://_").searchParams; } constructor({ getSession, oidcConfig, redirectUri, logger, onAuthSuccess, onError, middlewareOptions }) { this.getSession = getSession; const { serverUrl, clientId, clientSecret, clientOptions, issuerOptions, scope } = oidcConfig; const issuer = new import_openid_client.Issuer({ authorization_endpoint: `${serverUrl}/oidc/auth`, token_endpoint: `${serverUrl}/oidc/token`, end_session_endpoint: `${serverUrl}/oidc/session/end`, jwks_uri: `${serverUrl}/oidc/jwks`, revocation_endpoint: `${serverUrl}/oidc/token/revocation`, userinfo_endpoint: `${serverUrl}/oidc/me`, issuer: serverUrl, ...issuerOptions || {} }); this.redirectUris = Array.isArray(redirectUri) ? redirectUri : [redirectUri]; this.middlewareOptions = middlewareOptions; this.onAuthSuccess = onAuthSuccess; this.onError = onError; this.scope = scope || "openid"; this.client = new issuer.Client({ client_id: clientId, client_secret: clientSecret, redirect_uris: this.redirectUris, response_types: ["code"], token_endpoint_auth_method: "client_secret_basic", ...clientOptions || {} }); this.logger = logger || console; this.sendError = this.sendError.bind(this); this.getAuthHandler = this.getAuthHandler.bind(this); this.getCallbackHandler = this.getCallbackHandler.bind(this); this.prepareMiddleware = this.prepareMiddleware.bind(this); } sendError(err, req, res, next) { if (next && this.onError) { this.onError(err, req, res, next); return; } if (next) { next(err); return; } const errId = generateUUIDv4(); this.logger.error({ msg: "oidc auth error", errId, err }); res.writeHead(500, { "Content-Type": "text/plain" }); res.end(`OIDC auth error: ${errId}`); } getAuthHandler() { const sessionGetter = this.getSession; const client = this.client; const sendError = this.sendError; const scope = this.scope; const redirectUris = this.redirectUris; return async function authHandler(req, res, next) { const session = await sessionGetter(req, res); try { const query = _OIDCMiddleware.getQueryParams(req); const queryRedirectUri = query.get("redirect_uri"); const next2 = query.get("next"); if (next2 && isSafeUrl(next2)) { session[_OIDCMiddleware.OIDC_NEXT_URL_KEY] = next2; } else { delete session[_OIDCMiddleware.OIDC_NEXT_URL_KEY]; } const redirectUri = redirectUris.find((uri) => uri === queryRedirectUri) ?? redirectUris[0]; const checks = { nonce: import_openid_client.generators.nonce(), state: import_openid_client.generators.state() }; session[_OIDCMiddleware.OIDC_CHECKS_KEY] = { ...checks }; session[_OIDCMiddleware.OIDC_REDIRECT_URI_KEY] = redirectUri; await session.save(); const authUrl = client.authorizationUrl({ scope, redirect_uri: redirectUri, ...checks }); res.writeHead(302, { Location: authUrl }); res.end(); } catch (err) { delete session[_OIDCMiddleware.OIDC_CHECKS_KEY]; delete session[_OIDCMiddleware.OIDC_NEXT_URL_KEY]; await session.save(); return sendError(err, req, res, next); } }; } getCallbackHandler() { const sessionGetter = this.getSession; const sendError = this.sendError; const client = this.client; const onAuthSuccess = this.onAuthSuccess; const redirectUris = this.redirectUris; return async function callbackHandler(req, res, next) { let session = await sessionGetter(req, res); try { const { success, data: checks } = _OIDCMiddleware.CHECK_SCHEMA.safeParse(session[_OIDCMiddleware.OIDC_CHECKS_KEY]); const nextUrl = session[_OIDCMiddleware.OIDC_NEXT_URL_KEY]; const redirectUri = session[_OIDCMiddleware.OIDC_REDIRECT_URI_KEY]; if (typeof redirectUri !== "string" || !redirectUris.includes(redirectUri)) { return sendError(new Error("Invalid redirect URI"), req, res, next); } if (!success) { return sendError(new Error("Invalid nonce or state"), req, res, next); } const params = client.callbackParams(req); const { access_token: accessToken, refresh_token: refreshToken, id_token: idToken } = await client.callback(redirectUri, params, checks); let userInfo; if (accessToken) { userInfo = await client.userinfo(accessToken); } delete session[_OIDCMiddleware.OIDC_CHECKS_KEY]; delete session[_OIDCMiddleware.OIDC_NEXT_URL_KEY]; await session.save(); if (onAuthSuccess) { await onAuthSuccess(req, res, { accessToken, refreshToken, idToken, userInfo }); session = await sessionGetter(req, res); } session[_OIDCMiddleware.OIDC_ID_TOKEN_KEY] = idToken; session[_OIDCMiddleware.OIDC_ACCESS_TOKEN_KEY] = accessToken; session[_OIDCMiddleware.OIDC_REFRESH_TOKEN_KEY] = refreshToken; await session.save(); const location = typeof nextUrl === "string" && isSafeUrl(nextUrl) ? nextUrl : "/"; res.writeHead(302, { Location: location }); res.end(); } catch (err) { delete session[_OIDCMiddleware.OIDC_CHECKS_KEY]; delete session[_OIDCMiddleware.OIDC_NEXT_URL_KEY]; await session.save(); return sendError(err, req, res, next); } }; } prepareMiddleware() { if (!this.middlewareOptions) { return null; } const { app, apiPrefix = "/api/oidc" } = this.middlewareOptions; app.get(`${apiPrefix}/auth`, this.getAuthHandler()); app.get(`${apiPrefix}/callback`, this.getCallbackHandler()); return app; } }; _OIDCMiddleware.OIDC_ID_TOKEN_KEY = "oidcIdToken"; _OIDCMiddleware.OIDC_ACCESS_TOKEN_KEY = "oidcAccessToken"; _OIDCMiddleware.OIDC_REFRESH_TOKEN_KEY = "oidcRefreshToken"; _OIDCMiddleware.OIDC_NEXT_URL_KEY = "oidcNextUrl"; _OIDCMiddleware.OIDC_CHECKS_KEY = "oidcChecks"; _OIDCMiddleware.OIDC_REDIRECT_URI_KEY = "oidcRedirectUri"; _OIDCMiddleware.CHECK_SCHEMA = import_zod.z.object({ nonce: import_zod.z.string(), state: import_zod.z.string() }); var OIDCMiddleware = _OIDCMiddleware; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { OIDCMiddleware }); //# sourceMappingURL=oidc.js.map