@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
498 lines (497 loc) • 14.8 kB
JavaScript
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