UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

498 lines (497 loc) 14.8 kB
import { DEFAULT_ORGANIZATION_CONFIG as o } from "./defaults.js"; import { ThemeManager as u } from "./theme.js"; import { AppearanceManager as l } from "./appearance.js"; var c = Object.defineProperty, f = (s, e, t) => e in s ? c(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t, n = (s, e, t) => f(s, typeof e != "symbol" ? e + "" : e, t); class m { constructor(e, t, i) { n(this, "config"), n(this, "themeManager"), n(this, "appearanceManager"), n(this, "listeners", /* @__PURE__ */ new Set()), this.config = this.mergeWithDefaults(e), this.themeManager = t || new u(), this.appearanceManager = i || new l(), this.applyOrganizationBranding(); } /** * Get current organization configuration */ getConfig() { return { ...this.config }; } /** * Update organization configuration */ updateConfig(e) { this.config = { ...this.config, ...e, settings: { ...this.config.settings, ...e.settings }, features: { ...this.config.features, ...e.features }, limits: { ...this.config.limits, ...e.limits }, compliance: { ...this.config.compliance, ...e.compliance }, usage: { ...this.config.usage, ...e.usage } }, e.settings?.branding && this.applyOrganizationBranding(), this.notifyListeners(); } /** * Check if a feature is enabled */ isFeatureEnabled(e) { const t = e.split("."); let i = this.config.features; for (const r of t) { if (i?.[r] === void 0) return !1; i = i[r]; } return !!i; } /** * Check if a user type is allowed */ isUserTypeAllowed(e) { switch (e) { case "internal": return this.config.tier === "enterprise"; case "external": return this.isFeatureEnabled("userManagement.userProfiles"); case "end_user": return !0; // Always allowed default: return !1; } } /** * Get user limits for a specific user type */ getUserLimits(e) { switch (e) { case "internal": return this.config.limits.users.maxInternalUsers; case "external": return this.config.limits.users.maxExternalUsers; case "end_user": return this.config.limits.users.maxEndUsers; default: return 0; } } /** * Check if organization is within limits */ checkLimits() { const e = []; return this.config.usage.currentUsers > this.config.limits.users.maxUsers && e.push({ type: "users", current: this.config.usage.currentUsers, limit: this.config.limits.users.maxUsers }), this.config.usage.currentEndUsers > this.config.limits.users.maxEndUsers && e.push({ type: "endUsers", current: this.config.usage.currentEndUsers, limit: this.config.limits.users.maxEndUsers }), this.config.usage.monthlyApiRequests > this.config.limits.api.monthlyRequestLimit && e.push({ type: "apiRequests", current: this.config.usage.monthlyApiRequests, limit: this.config.limits.api.monthlyRequestLimit }), { withinLimits: e.length === 0, violations: e }; } /** * Get organization tier configuration */ getTierConfig() { return { free: { name: "Free", features: [ "Basic authentication", "Up to 100 users", "Email support" ], limits: { users: 100, apiRequests: 1e3, sessions: 5 } }, starter: { name: "Starter", price: "$29/month", features: [ "Everything in Free", "Up to 1,000 users", "MFA support", "Basic branding", "Priority support" ], limits: { users: 1e3, apiRequests: 1e4, sessions: 10 } }, professional: { name: "Professional", price: "$99/month", features: [ "Everything in Starter", "Up to 10,000 users", "SSO integration", "Advanced branding", "API access", "Webhooks", "Audit logs" ], limits: { users: 1e4, apiRequests: 1e5, sessions: 25 } }, enterprise: { name: "Enterprise", price: "Custom", features: [ "Everything in Professional", "Unlimited users", "SAML/LDAP integration", "Custom domain", "Advanced security", "Compliance features", "Dedicated support" ], limits: { users: -1, // Unlimited apiRequests: -1, // Unlimited sessions: -1 // Unlimited } } }[this.config.tier]; } /** * Generate UI configuration based on organization settings */ generateUIConfig() { const e = { projectId: this.config.id, organization: this.config, features: { signUp: this.isFeatureEnabled("authentication.signUp"), signIn: this.isFeatureEnabled("authentication.signIn"), passwordReset: this.isFeatureEnabled("authentication.passwordReset"), mfa: this.isFeatureEnabled("security.mfa"), sso: this.isFeatureEnabled("security.sso"), organizationManagement: this.isFeatureEnabled("organization.memberManagement"), userProfile: this.isFeatureEnabled("userManagement.userProfiles"), sessionManagement: this.isFeatureEnabled("security.sessionManagement") } }; return this.isFeatureEnabled("ui.customThemes") && (e.theme = this.generateCustomTheme()), this.isFeatureEnabled("ui.customThemes") && (e.appearance = this.generateCustomAppearance()), this.isFeatureEnabled("ui.localization") && (e.localization = this.generateLocalizationConfig()), e; } /** * Subscribe to configuration changes */ subscribe(e) { return this.listeners.add(e), () => { this.listeners.delete(e); }; } /** * Validate organization configuration */ validateConfig() { const e = [], t = []; this.config.id || e.push("Organization ID is required"), this.config.name || e.push("Organization name is required"); const { withinLimits: i, violations: r } = this.checkLimits(); return i || r.forEach((a) => { t.push(`${a.type} limit exceeded: ${a.current}/${a.limit}`); }), this.config.settings.branding?.logo && !this.isValidUrl(this.config.settings.branding.logo) && e.push("Invalid logo URL"), this.isFeatureEnabled("security.mfaRequired") && !this.isFeatureEnabled("security.mfa") && e.push("MFA must be enabled if MFA is required"), this.isFeatureEnabled("organization.customDomain") && this.config.tier === "free" && t.push("Custom domain requires paid plan"), { isValid: e.length === 0, errors: e, warnings: t }; } // Private methods mergeWithDefaults(e) { const t = { authentication: { signUp: !0, signIn: !0, passwordReset: !0, emailVerification: !0, phoneVerification: !1, socialAuth: !1, magicLink: !1, passkeys: !1 }, security: { mfa: !1, mfaRequired: !1, sso: !1, sessionManagement: !0, auditLogs: !1, ipWhitelist: !1, deviceTrust: !1, riskAssessment: !1 }, userManagement: { userProfiles: !0, userRoles: !1, userPermissions: !1, userInvitations: !0, userSuspension: !1, userDeletion: !1, bulkUserOperations: !1 }, organization: { memberManagement: !0, roleManagement: !1, invitations: !0, customBranding: !1, customDomain: !1, webhooks: !1, apiAccess: !1, analytics: !1 }, ui: { darkMode: !0, customThemes: !1, localization: !0, customCSS: !1, logoUpload: !1, colorCustomization: !1, layoutCustomization: !1 }, integrations: { saml: !1, oidc: !1, ldap: !1, scim: !1, slack: !1, microsoft: !1, google: !1, github: !1 } }, i = { users: { maxUsers: 100, maxEndUsers: 1e3, maxExternalUsers: 50, maxInternalUsers: 5 }, sessions: { maxSessionsPerUser: 5, maxConcurrentSessions: 100, sessionTimeout: 1800, // 30 minutes maxSessionDuration: 86400 // 24 hours }, api: { monthlyRequestLimit: 1e3, rateLimit: 100, // per minute burstLimit: 200, maxWebhooks: 3 }, storage: { maxLogoSize: 1024 * 1024, // 1MB maxCustomCSSSize: 50 * 1024, // 50KB auditLogRetention: 90, // days maxCustomFields: 10 }, features: { maxRoles: 10, maxPermissions: 50, maxIntegrations: 5, maxDomains: 1 } }, r = { dataRetention: { userDataRetention: 365, auditLogRetention: 90, sessionLogRetention: 30, automaticDeletion: !1 }, privacy: { gdprCompliant: !1, ccpaCompliant: !1, hipaaCompliant: !1, soc2Compliant: !1, dataProcessingAgreement: !1 }, security: { encryptionAtRest: !0, encryptionInTransit: !0, keyRotation: !1, backupEncryption: !1, accessLogging: !0 }, reporting: { complianceReports: !1, auditReports: !1, securityReports: !1, dataExport: !1, rightToBeForgotten: !1 } }; return { ...o, ...e, features: { ...t, ...e.features }, limits: { ...i, ...e.limits }, compliance: { ...r, ...e.compliance }, tier: e.tier || "free", isActive: e.isActive ?? !0, subscriptionStatus: e.subscriptionStatus || "active", usage: { currentUsers: 0, currentEndUsers: 0, monthlyApiRequests: 0, storageUsed: 0, lastActivityAt: /* @__PURE__ */ new Date(), ...e.usage } }; } applyOrganizationBranding() { this.config.settings.branding && (this.themeManager.applyBranding({ logo: { url: this.config.settings.branding.logo, alt: this.config.name }, colors: { primary: this.config.settings.branding.primaryColor || "#3b82f6", secondary: this.config.settings.branding.secondaryColor || "#64748b" }, fonts: { primary: "Inter, ui-sans-serif, system-ui, sans-serif" }, customCSS: this.config.settings.branding.customCSS }), this.appearanceManager.applyOrganizationBranding(this.config)); } generateCustomTheme() { return this.isFeatureEnabled("ui.customThemes") ? this.themeManager.getTheme() : {}; } generateCustomAppearance() { return this.isFeatureEnabled("ui.customThemes") ? this.appearanceManager.getConfig() : {}; } generateLocalizationConfig() { return this.isFeatureEnabled("ui.localization") ? { defaultLocale: "en", supportedLocales: ["en", "es", "fr"] } : {}; } isValidUrl(e) { try { return new URL(e), !0; } catch { return !1; } } notifyListeners() { this.listeners.forEach((e) => e(this.config)); } } function p(s, e, t) { return new m(s, e, t); } function y(s) { return { id: s.id, name: s.name, slug: s.slug, settings: { allowPublicSignup: s.allowPublicSignup, requireEmailVerification: s.requireEmailVerification, requirePhoneVerification: s.requirePhoneVerification, allowedDomains: s.allowedDomains, mfaRequired: s.mfaSettings?.required, allowedMfaMethods: s.mfaSettings?.allowedMethods || [], passwordPolicy: s.passwordPolicy, sessionSettings: s.sessionSettings, branding: s.branding, customFields: s.customFields }, features: { // Map API features to UI feature flags authentication: { signUp: s.allowPublicSignup, signIn: !0, passwordReset: !0, emailVerification: s.requireEmailVerification, phoneVerification: s.requirePhoneVerification, socialAuth: s.ssoEnabled, magicLink: s.features?.magicLink || !1, passkeys: s.features?.passkeys || !1 }, security: { mfa: s.mfaSettings?.enabled || !1, mfaRequired: s.mfaSettings?.required || !1, sso: s.ssoEnabled || !1, sessionManagement: !0, auditLogs: s.features?.auditLogs || !1, ipWhitelist: s.features?.ipWhitelist || !1, deviceTrust: s.features?.deviceTrust || !1, riskAssessment: s.features?.riskAssessment || !1 } // ... other feature mappings }, tier: s.plan?.tier || "free", isActive: s.active, subscriptionStatus: s.subscription?.status || "active", usage: { currentUsers: s.stats?.currentUsers || 0, currentEndUsers: s.stats?.currentEndUsers || 0, monthlyApiRequests: s.stats?.monthlyApiRequests || 0, storageUsed: s.stats?.storageUsed || 0, lastActivityAt: new Date(s.stats?.lastActivityAt || Date.now()) } }; } function b(s) { const e = { free: { authentication: { signUp: !0, signIn: !0, passwordReset: !0 }, security: { sessionManagement: !0 }, userManagement: { userProfiles: !0, userInvitations: !0 }, organization: { memberManagement: !0, invitations: !0 }, ui: { darkMode: !0, localization: !0 } }, starter: { // All free features plus: security: { mfa: !0 }, ui: { customThemes: !0, logoUpload: !0 }, organization: { customBranding: !0 } }, professional: { // All starter features plus: security: { sso: !0, auditLogs: !0 }, organization: { webhooks: !0, apiAccess: !0, analytics: !0 }, integrations: { saml: !0, oidc: !0 }, ui: { customCSS: !0, colorCustomization: !0 } }, enterprise: { // All professional features plus: security: { ipWhitelist: !0, deviceTrust: !0, riskAssessment: !0 }, organization: { customDomain: !0 }, integrations: { ldap: !0, scim: !0 }, userManagement: { bulkUserOperations: !0 }, ui: { layoutCustomization: !0 } } }, t = ["free", "starter", "professional", "enterprise"], i = t.indexOf(s); let r = {}; for (let a = 0; a <= i; a++) r = { ...r, ...e[t[a]] }; return r; } export { o as DEFAULT_ORGANIZATION_CONFIG, m as OrganizationConfigManager, p as createOrganizationConfigManager, b as getFeaturesByTier, y as transformOrganizationSettings }; //# sourceMappingURL=organization.js.map