ComponentsEnterprise SSO
SSO Config Card
Configure OIDC and SAML 2.0 SSO providers for enterprise authentication
Overview
Comprehensive interface for configuring enterprise Single Sign-On (SSO) using OIDC (OpenID Connect) or SAML 2.0 protocols.
Features
- ✅ OIDC Support - Full OpenID Connect provider configuration
- ✅ SAML 2.0 Support - Complete SAML setup with metadata
- ✅ Attribute Mapping - Map claims/attributes to user model
- ✅ Auto-fetch - Load provider data from Better Auth
- ✅ Advanced Settings - PKCE, signature algorithms, certificate validation
Installation
npm install @bettercone/uiUsage
import { SSOConfigCard } from '@bettercone/ui'
export default function SSOSettings() {
return (
<SSOConfigCard
providerId="acme-corp-okta"
onSuccess={(provider) => console.log('Saved:', provider)}
showActions={true}
allowDelete={true}
/>
)
}import { SSOConfigCard } from '@bettercone/ui'
import { authClient } from '@/lib/auth-client'
export default function SSOSettings() {
return (
<SSOConfigCard
authClient={authClient}
providerId="enterprise-azure-ad"
onSuccess={(provider) => console.log('Saved:', provider)}
/>
)
}import { SSOConfigCard } from '@bettercone/ui'
const provider = {
id: "sso_okta_001",
providerId: "acme-corp-okta",
issuer: "https://dev-12345678.okta.com",
domain: "acmecorp.com",
oidcConfig: {
clientId: "0oa2a3b4c5d6e7f8g9h0",
clientSecret: "your-client-secret",
authorizationEndpoint: "https://dev-12345678.okta.com/oauth2/v1/authorize",
tokenEndpoint: "https://dev-12345678.okta.com/oauth2/v1/token",
jwksEndpoint: "https://dev-12345678.okta.com/oauth2/v1/keys",
scopes: ["openid", "email", "profile"],
pkce: true,
},
}
<SSOConfigCard data={provider} />OIDC Configuration
For Google, Okta, Auth0, Azure AD with OIDC, etc.
<SSOConfigCard
data={{
id: "sso_google_001",
providerId: "workspace-google",
issuer: "https://accounts.google.com",
oidcConfig: {
clientId: "123456...apps.googleusercontent.com",
clientSecret: "GOCSPX-...",
authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
tokenEndpoint: "https://oauth2.googleapis.com/token",
jwksEndpoint: "https://www.googleapis.com/oauth2/v3/certs",
scopes: ["openid", "email", "profile"],
pkce: true,
mapping: {
id: "sub",
email: "email",
name: "name",
image: "picture",
},
},
}}
/>OIDC Fields
clientId- OAuth 2.0 client ID from IdPclientSecret- OAuth 2.0 client secretauthorizationEndpoint- Authorization URLtokenEndpoint- Token exchange endpointjwksEndpoint- JSON Web Key Set URLdiscoveryEndpoint- .well-known/openid-configurationscopes- Requested scopes (openid, email, profile)pkce- Enable PKCE (recommended)
SAML 2.0 Configuration
For Azure AD SAML, Okta SAML, OneLogin, etc.
<SSOConfigCard
data={{
id: "sso_azure_002",
providerId: "enterprise-azure-ad",
samlConfig: {
entryPoint: "https://login.microsoftonline.com/tenant-id/saml2",
cert: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
callbackUrl: "https://yourapp.com/api/auth/sso/saml2/callback",
audience: "https://yourapp.com",
wantAssertionsSigned: true,
signatureAlgorithm: "sha256",
mapping: {
id: "nameID",
email: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
name: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
},
},
}}
/>SAML Fields
entryPoint- IdP's SAML SSO URLcert- X.509 certificate (PEM format)callbackUrl- Your ACS URLaudience- Expected audience / entity IDwantAssertionsSigned- Require signed assertionssignatureAlgorithm- sha1 | sha256 | sha512identifierFormat- NameID format
Attribute Mapping
Map claim/attribute names, not values. E.g., "email" → "email" claim in ID token.
Standard Fields
id- Unique identifier (OIDC: "sub", SAML: "nameID")email- Email addressemailVerified- Email verification statusname- Full namefirstName/lastName- Given/family namesimage- Profile picture URL
Custom Fields
mapping: {
id: "sub",
email: "email",
extraFields: {
department: "department",
employeeId: "employee_id",
companyId: "https://acme.com/claims/company",
}
}Props
interface SSOConfigCardProps {
authClient?: object
providerId?: string
data?: SSOProvider
onSuccess?: (provider: SSOProvider) => void
onError?: (error: Error) => void
showActions?: boolean
allowDelete?: boolean
className?: string
}Types
interface SSOProvider {
id: string
providerId: string
issuer?: string
domain?: string
organizationId?: string
oidcConfig?: OIDCConfig
samlConfig?: SAMLConfig
createdAt?: Date
updatedAt?: Date
}
interface OIDCConfig {
clientId: string
clientSecret?: string
authorizationEndpoint: string
tokenEndpoint: string
jwksEndpoint?: string
scopes?: string[]
pkce?: boolean
mapping?: AttributeMapping
}
interface SAMLConfig {
entryPoint: string
cert: string
callbackUrl: string
audience?: string
wantAssertionsSigned?: boolean
signatureAlgorithm?: 'sha1' | 'sha256' | 'sha512'
identifierFormat?: string
mapping?: AttributeMapping
}Integration Modes
Auto-fetch Mode
<SSOConfigCard providerId="my-provider" />Custom authClient Mode
import { authClient } from '@/lib/auth-client'
<SSOConfigCard authClient={authClient} providerId="my-provider" />Data Prop Mode
<SSOConfigCard
data={myProviderData}
onSuccess={async (updated) => {
await fetch('/api/sso/providers', {
method: 'PUT',
body: JSON.stringify(updated),
})
}}
/>Better Auth Setup
import { betterAuth } from 'better-auth'
import { sso } from 'better-auth/plugins'
export const auth = betterAuth({
plugins: [
sso({
providers: {
// Providers registered dynamically
},
}),
],
})import { createAuthClient } from 'better-auth/client'
import { ssoClient } from 'better-auth/client/plugins'
export const authClient = createAuthClient({
plugins: [ssoClient()],
})Common Patterns
Admin SSO Management
export default function AdminSSOPage() {
const [providers, setProviders] = useState<string[]>([])
return (
<div className="space-y-6">
{providers.map((providerId) => (
<SSOConfigCard
key={providerId}
providerId={providerId}
allowDelete={true}
/>
))}
</div>
)
}Organization-scoped SSO
<SSOConfigCard
data={{
...providerData,
organizationId: currentOrg.id,
}}
onSuccess={async (provider) => {
await fetch(`/api/orgs/${currentOrg.id}/sso`, {
method: 'PUT',
body: JSON.stringify(provider),
})
}}
/>Read-only View
<SSOConfigCard
data={existingProvider}
showActions={false}
allowDelete={false}
/>Security Notes
Never expose client secrets in client-side code! Use environment variables or secret management services.
Best Practices:
- Store client secrets securely
- Validate SAML certificates
- Always enable PKCE for OIDC
- Use HTTPS for all endpoints
- Verify SAML audience matches entity ID
Troubleshooting
Provider data doesn't load
- Verify
providerIdexists - Check Better Auth SSO plugin is configured
- Ensure authClient has access
OIDC authentication fails
- Verify endpoints (authorization, token, jwks)
- Include "openid" in scopes
- Match redirect URI to callback URL
- Enable PKCE
SAML authentication fails
- Validate certificate PEM format
- Match signature/digest algorithms with IdP
- Verify callback URL = ACS endpoint
- Check NameID format matches IdP
Related Components
- SAMLSetupWizard - Guided SAML setup
- OIDCProviderCard - OAuth2/OIDC provider management