White-label SaaS for end-customers
Your B2B customers ship Olympus auth to THEIR customers under their brand
You're a B2B SaaS that resells / embeds Olympus. Your customers want to brand the auth as theirs, not yours, when their end-users see it.
Layers
B2B customer = your direct customer.
End-user = your customer's customer.
B2B customer brands login page → end-user sees the B2B customer's brand.Pattern A: per-tenant CIAM stack
Each B2B customer gets their own Olympus deployment (separate domain, separate config):
auth.acme-corp.com(their brand) → Acme's Olympus instance.auth.bigcorp.com(their brand) → BigCorp's Olympus instance.
You operate but they brand. Highest isolation, highest cost.
See Multi-tenant hard isolation.
Pattern B: one CIAM, per-tenant subdomain
acme.your-domain.com → tenant=acme, branded as Acme.
bigcorp.your-domain.com → tenant=bigcorp, branded as BigCorp.Shared infrastructure, per-tenant config.
Routing
Caddy:
*.your-domain.com {
reverse_proxy hera:3000
}Hera reads subdomain via host header:
// hera middleware
const subdomain = req.headers.host?.split(".")[0];
const tenant = await getTenantBySubdomain(subdomain);
if (!tenant) return notFound();
req.tenant = tenant;Branding
In each page, use tenant context for theme:
export async function generateMetadata({ params }) {
const tenant = await getTenant(params.subdomain);
return {
title: `Sign in - ${tenant.name}`,
icons: { icon: tenant.favicon },
};
}
export default function Login() {
const tenant = useTenant();
return (
<div style={{ backgroundColor: tenant.primaryColor }}>
<img src={tenant.logo} />
<h1>Sign in to {tenant.name}</h1>
...
</div>
);
}CSS variables:
:root {
--color-primary: var(--tenant-primary, #4F46E5);
--color-bg: var(--tenant-bg, #FFFFFF);
}Set via inline style on <html> with tenant values.
Pattern C: full white-label, no Olympus mention
For deepest white-label: end-users never see "Olympus" anywhere:
- No "Powered by Olympus" footer.
- No links to Olympus docs.
- No Olympus error messages.
- Custom user-facing domain.
This requires:
- Custom Hera build per tenant (or runtime config).
- Stripped branding.
- Tenant's privacy policy / TOS displayed.
- Customer support routes to tenant's support, not yours.
Custom domain
Customer wants auth.acme.com (their domain) not acme.your-domain.com.
DNS: customer adds CNAME → your Caddy.
Caddy auto-provisions Let's Encrypt cert for that domain.
auth.acme.com {
reverse_proxy hera:3000
}
auth.bigcorp.com {
reverse_proxy hera:3000
}Or wildcard with SNI-based routing.
Cert provisioning
For dozens of customer domains, Caddy can use on-demand TLS:
{
on_demand_tls {
ask http://your-backend/check-domain
interval 2m
burst 5
}
}
:443 {
tls {
on_demand
}
reverse_proxy hera:3000
}When a request comes in for an unfamiliar domain, Caddy asks your backend "should I issue a cert?", backend looks up if it's a registered customer domain. Yes → Caddy provisions.
End-user privacy policy
End-user signs up at auth.acme.com. The privacy policy they're agreeing to is Acme's, not yours (typically).
Display:
- Tenant's privacy policy at registration.
- Tenant's TOS.
Athena lets each tenant configure these URLs.
Data ownership
Critical: end-user data belongs to the B2B customer (Acme), NOT to you.
Your TOS with B2B customers should clarify:
- B2B customer is the "data controller."
- You're the "data processor."
- Data belongs to B2B customer.
- Acme can export at any time.
- On contract end, you delete or return.
DPA (Data Processing Agreement) signed by both parties.
Identifying tenant in audit
Every audit event scoped to tenant:
INSERT INTO security_audit (tenant_id, identity_id, event_type, ...)
VALUES ('acme', $user, 'login', ...);When investigating, scoped by tenant.
Cross-tenant ops
Your internal ops (you = the SaaS operator):
- Can access any tenant's data (for support).
- Logs every cross-tenant action.
- Customer can audit (you provide audit log to them on request).
Beyond support, NO cross-tenant operations. Don't aggregate end-user data across tenants for analytics, privacy violation.
Disaster recovery per tenant
If one tenant's data is corrupted, restore that tenant's portion without affecting others. Per-tenant backups (or tagged within shared DB).
End-user vs B2B billing
End-users don't pay you (they pay the B2B customer if at all). B2B customer pays you (based on MAU, features, etc.).
Stripe integration: customer side only.
Onboarding new tenants
UI for B2B customer onboarding:
- Sign up (you, as direct customer, have your own auth).
- Create tenant.
- Configure branding (upload logo, set colors).
- Configure their subdomain or custom domain.
- Verify domain (DNS TXT).
- Test login.
- Go live.
Self-service for low-touch, sales-led for enterprise.