UNPKG

@entityauth/auth-client

Version:

TypeScript/JavaScript client SDK for Entity Auth - complete authentication infrastructure with organizations, sessions, and real-time security

510 lines (509 loc) 16.8 kB
"use strict"; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; 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/index.ts var index_exports = {}; __export(index_exports, { EntityAuthClient: () => EntityAuthClient, SDK: () => SDK, init: () => init }); module.exports = __toCommonJS(index_exports); var TRAILING_SLASH_REGEX = /\/$/; var HTTP_UNAUTHORIZED = 401; var PROCESS_ENV = typeof process !== "undefined" && process.env ? process.env : void 0; var ENV_BASE_URL = PROCESS_ENV ? PROCESS_ENV.NEXT_PUBLIC_ENTITY_AUTH_URL || PROCESS_ENV.ENTITY_AUTH_URL || PROCESS_ENV.NEXT_PUBLIC_EA_BASE_URL || PROCESS_ENV.EA_BASE_URL || PROCESS_ENV.NEXT_PUBLIC_EA_URL || "" : ""; var DEFAULT_BASE_URL = ENV_BASE_URL || "https://entity-auth.com"; var SDK_CONFIG = { baseURL: DEFAULT_BASE_URL, tenantId: null }; function init(options) { if (!(options == null ? void 0 : options.tenantId)) { throw new Error( "EntityAuth.init({ tenantId }) is required. Never ask users for tenantId in forms; configure it once from env." ); } SDK_CONFIG.tenantId = options.tenantId; const base = options.baseURL || resolveBaseURL(); if (base) { SDK_CONFIG.baseURL = base.replace(TRAILING_SLASH_REGEX, ""); } } function resolveTenantId() { const tid = SDK_CONFIG.tenantId || (PROCESS_ENV ? PROCESS_ENV.NEXT_PUBLIC_ENTITY_AUTH_TENANT_ID || PROCESS_ENV.ENTITY_AUTH_TENANT_ID || PROCESS_ENV.NEXT_PUBLIC_EA_TENANT_ID || PROCESS_ENV.EA_TENANT_ID || "" : ""); if (!tid) { throw new Error( "Missing tenantId. Call EntityAuth.init({ tenantId }) or set NEXT_PUBLIC_ENTITY_AUTH_TENANT_ID." ); } return tid; } function resolveBaseURL() { const base = SDK_CONFIG.baseURL || ENV_BASE_URL; return base ? base.replace(TRAILING_SLASH_REGEX, "") : DEFAULT_BASE_URL; } function getDeprecatedTenantId(value) { if (!value || typeof value !== "object") return null; if (!("tenantId" in value)) return null; const tid = value.tenantId; return typeof tid === "string" && tid.length > 0 ? tid : null; } function join(baseURL, path) { const url = baseURL || DEFAULT_BASE_URL; const base = url.replace(TRAILING_SLASH_REGEX, ""); const p = path.startsWith("/") ? path : `/${path}`; return `${base}${p}`; } async function request(path, init2) { const url = join(init2 == null ? void 0 : init2.baseURL, path); const _a = init2 || {}, { baseURL: _ignored } = _a, rest = __objRest(_a, ["baseURL"]); const res = await fetch(url, __spreadProps(__spreadValues({}, rest), { credentials: "include" })); if (!res.ok) { const text = await res.text(); throw new Error(text || `HTTP ${res.status}`); } return res.json(); } var EntityAuthClient = class { constructor(options) { this.accessToken = null; this.tokenListeners = /* @__PURE__ */ new Set(); this.refreshInFlight = null; const configuredBase = (options == null ? void 0 : options.baseURL) || resolveBaseURL(); this.baseURL = configuredBase.replace(TRAILING_SLASH_REGEX, ""); } // Token management getAccessToken() { return this.accessToken; } onTokenChange(listener) { this.tokenListeners.add(listener); return () => { this.tokenListeners.delete(listener); }; } setAccessToken(token) { this.accessToken = token; for (const l of this.tokenListeners) { try { l(token); } catch (e) { } } } // Allow external flows (e.g. passkey verify) to apply a new access token applyAccessToken(token) { this.setAccessToken(token || null); } // Auth-aware fetch with auto-refresh via cookie (web) async fetch(input, init2) { const url = join(this.baseURL, input); const headers = new Headers((init2 == null ? void 0 : init2.headers) || {}); const token = this.getAccessToken(); if (token) { headers.set("authorization", `Bearer ${token}`); } let res = await globalThis.fetch(url, __spreadProps(__spreadValues({}, init2), { headers })); if (res.status === HTTP_UNAUTHORIZED) { try { await this.refreshSingleFlight(); } catch (e) { this.setAccessToken(null); return res; } const retryHeaders = new Headers((init2 == null ? void 0 : init2.headers) || {}); const newToken = this.getAccessToken(); if (newToken) { retryHeaders.set("authorization", `Bearer ${newToken}`); } res = await globalThis.fetch(url, __spreadProps(__spreadValues({}, init2), { headers: retryHeaders })); } return res; } refreshSingleFlight() { if (!this.refreshInFlight) { this.refreshInFlight = this.refresh().then((data) => { const access = data == null ? void 0 : data.accessToken; if (!access) { throw new Error("No access token after refresh"); } return access; }).finally(() => { this.refreshInFlight = null; }); } return this.refreshInFlight; } register(input) { if (getDeprecatedTenantId(input)) { console.warn( "[EntityAuth] Passing tenantId to register() is deprecated. Configure it once via init({ tenantId })." ); } const tenantId = input.tenantId || resolveTenantId(); return request("/api/auth/register", { baseURL: this.baseURL, method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email: input.email, password: input.password, tenantId }) }); } login(input) { const headers = { "content-type": "application/json" }; return request("/api/auth/login", { baseURL: this.baseURL, method: "POST", headers, body: JSON.stringify({ email: input.email, password: input.password, // Back-compat: accept input.tenantId if present but prefer configured tenant tenantId: (() => { const deprecated = getDeprecatedTenantId(input); if (deprecated) { console.warn( "[EntityAuth] Passing tenantId to login() is deprecated. Configure it once via init({ tenantId })." ); return deprecated; } return resolveTenantId(); })() }) }).then((out) => { const access = out.accessToken; if (access) { this.setAccessToken(access); } else { this.setAccessToken(null); } return out; }); } refresh(headerRefreshToken) { return request("/api/auth/refresh", { baseURL: this.baseURL, method: "POST", headers: headerRefreshToken ? { "x-refresh-token": headerRefreshToken } : void 0 }).then((out) => { const access = out.accessToken; if (access) { this.setAccessToken(access); } return out; }); } logout() { return request("/api/auth/logout", { baseURL: this.baseURL, method: "POST" }).finally(() => { this.setAccessToken(null); }); } // OpenAPI metadata discovery getOpenAPI() { return request("/api/openapi", { baseURL: this.baseURL, method: "GET" }); } // GraphQL helper async graphql(query, variables) { var _a; const res = await this.fetch("/api/graphql", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ query, variables }) }); const json = await res.json(); if (json.errors) { throw new Error("GraphQL error"); } return (_a = json.data) != null ? _a : {}; } createOrg(input) { var _a; const headers = new Headers({ "content-type": "application/json" }); return this.fetch("/api/org/create", { method: "POST", headers, body: JSON.stringify({ tenantId: (_a = input.tenantId) != null ? _a : resolveTenantId(), name: input.name, slug: input.slug, ownerId: input.ownerId }) }).then((r) => r.json()); } addMember(input) { const headers = new Headers({ "content-type": "application/json" }); return this.fetch("/api/org/add-member", { method: "POST", headers, body: JSON.stringify(input) }).then((r) => r.json()); } switchOrg(input) { const headers = new Headers({ "content-type": "application/json" }); return this.fetch("/api/org/switch", { method: "POST", headers, body: JSON.stringify(input) }).then((r) => r.json()); } getUserOrganizations() { return this.fetch("/api/org/list", { method: "GET" }).then((r) => r.json()); } getCurrentTenantId() { return this.fetch("/api/user/me", { method: "GET" }).then((r) => r.json()).then((data) => data.tenantId); } // Users setUsername(input) { const headers = new Headers({ "content-type": "application/json" }); return this.fetch("/api/user/username/set", { method: "POST", headers, body: JSON.stringify(input) }).then((r) => r.json()); } checkUsername(input) { const query = new URLSearchParams({ value: input.value }).toString(); return this.fetch(`/api/user/username/check?${query}`, { method: "GET" }).then((r) => r.json()); } getUserMe() { return this.fetch("/api/user/me", { method: "GET" }).then((r) => r.json()); } userByUsername(input) { const query = new URLSearchParams({ username: input.username }).toString(); return this.fetch(`/api/user/by-username?${query}`, { method: "GET" }).then( (r) => r.json() ); } userByEmail(input) { const headers = new Headers({ "content-type": "application/json" }); return this.fetch("/api/user/by-email", { method: "POST", headers, body: JSON.stringify(input) }).then((r) => r.json()); } // Misc getConvexConfig() { return this.fetch("/api/convex", { method: "GET" }).then((r) => r.json()); } openapi() { return request("/api/openapi", { baseURL: this.baseURL, method: "GET" }); } // Sessions getCurrentSession() { return this.fetch("/api/session/current", { method: "GET" }).then( (r) => r.json() ); } listSessions(options) { const params = new URLSearchParams(); if (options == null ? void 0 : options.includeRevoked) params.set("includeRevoked", "true"); const path = `/api/session/list${params.toString() ? `?${params.toString()}` : ""}`; return this.fetch(path, { method: "GET" }).then((r) => r.json()); } getSessionById(input) { const params = new URLSearchParams({ id: input.id }).toString(); return this.fetch(`/api/session/by-id?${params}`, { method: "GET" }).then((r) => r.json()); } revokeSession(input) { const headers = new Headers({ "content-type": "application/json" }); return this.fetch("/api/session/revoke", { method: "POST", headers, body: JSON.stringify(input) }).then((r) => r.json()); } revokeSessionsByUser(input) { const headers = new Headers({ "content-type": "application/json" }); return this.fetch("/api/session/revoke-by-user", { method: "POST", headers, body: JSON.stringify(input) }).then((r) => r.json()); } }; var SDK = { // Back-compat static helpers defaulting to same-origin register(input) { const tenantId = (() => { const deprecated = getDeprecatedTenantId(input); if (deprecated) { console.warn( "[EntityAuth] Passing tenantId to SDK.register() is deprecated. Configure it once via init({ tenantId })." ); return deprecated; } return resolveTenantId(); })(); if (getDeprecatedTenantId(input)) { console.warn("[EntityAuth] tenantId on SDK.register() is deprecated."); } return request("/api/auth/register", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email: input.email, password: input.password, tenantId }) }); }, login(input) { const headers = { "content-type": "application/json" }; return request("/api/auth/login", { method: "POST", headers, body: JSON.stringify({ email: input.email, password: input.password, tenantId: (() => { const deprecated = getDeprecatedTenantId(input); if (deprecated) { console.warn( "[EntityAuth] Passing tenantId to SDK.login() is deprecated. Configure it once via init({ tenantId })." ); return deprecated; } return resolveTenantId(); })() }) }); }, refresh(headerRefreshToken) { return request("/api/auth/refresh", { method: "POST", headers: headerRefreshToken ? { "x-refresh-token": headerRefreshToken } : void 0 }); }, logout() { return request("/api/auth/logout", { method: "POST" }); }, createOrg(input) { var _a; return request("/api/org/create", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ tenantId: (_a = input.tenantId) != null ? _a : resolveTenantId(), name: input.name, slug: input.slug, ownerId: input.ownerId }) }); }, addMember(input) { return request("/api/org/add-member", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input) }); }, switchOrg(input) { return request("/api/org/switch", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input) }); }, getUserOrganizations() { return request("/api/org/list", { method: "GET" }); }, getCurrentTenantId() { return request("/api/user/me", { method: "GET" }).then((data) => data.tenantId); }, openapi() { return request("/api/openapi", { method: "GET" }); }, // Sessions (static helpers) getCurrentSession() { return request("/api/session/current", { method: "GET" }); }, listSessions(options) { const params = new URLSearchParams(); if (options == null ? void 0 : options.includeRevoked) params.set("includeRevoked", "true"); const path = `/api/session/list${params.toString() ? `?${params.toString()}` : ""}`; return request(path, { method: "GET" }); }, getSessionById(input) { const params = new URLSearchParams({ id: input.id }).toString(); return request(`/api/session/by-id?${params}`, { method: "GET" }); }, revokeSession(input) { return request("/api/session/revoke", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input) }); }, revokeSessionsByUser(input) { return request("/api/session/revoke-by-user", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input) }); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { EntityAuthClient, SDK, init }); //# sourceMappingURL=index.cjs.map