mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 20:27:44 +00:00
feat(sso): defaultSSO options and ACS endpoint (#3660)
Co-authored-by: Bereket Engida <Bekacru@gmail.com> Co-authored-by: Bereket Engida <86073083+Bekacru@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
a208c09894
commit
b3ead859e6
@@ -2166,6 +2166,21 @@ C0.7,239.6,62.1,0.5,62.2,0.4c0,0,54,13.8,119.9,30.8S302.1,62,302.2,62c0.2,0,0.2,
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "SAML SSO with Okta",
|
||||
href: "/docs/guides/saml-sso-with-okta",
|
||||
icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="m140.844 1.778l-5.266 64.853a66 66 0 0 0-7.542-.427c-3.203 0-6.334.214-9.393.712l-2.99-31.432a1.72 1.72 0 0 1 1.709-1.848h5.337l-2.562-31.787C120.066.853 120.848 0 121.774 0h17.434c.996 0 1.779.853 1.636 1.849zm-43.976 3.2c-.285-.925-1.281-1.494-2.206-1.138L78.295 9.813c-.925.356-1.352 1.423-.925 2.276l13.307 29.013l-5.052 1.85c-.926.355-1.352 1.421-.926 2.275l13.592 28.515a61 61 0 0 1 15.868-6.044L96.94 4.978zM56.734 23.04l37.643 53.049c-4.768 3.129-9.108 6.827-12.809 11.093L59.011 64.996a1.72 1.72 0 0 1 .071-2.49l4.127-3.413L40.794 36.41c-.711-.711-.64-1.849.142-2.489l13.307-11.164c.783-.64 1.85-.498 2.42.284zM25.139 53.76c-.783-.569-1.921-.284-2.42.569l-8.68 15.075c-.499.854-.143 1.92.71 2.347L43.64 85.404l-2.704 4.623c-.498.853-.142 1.99.783 2.346l28.749 13.156a60.2 60.2 0 0 1 8.254-14.791zM3.862 94.72c.143-.996 1.139-1.564 2.064-1.351l62.976 16.427a62.3 62.3 0 0 0-2.704 16.782l-31.524-2.56a1.642 1.642 0 0 1-1.494-1.991l.925-5.263l-31.808-2.986c-.996-.071-1.637-.996-1.495-1.991l2.99-17.138zm-2.348 42.524c-.996.072-1.637.996-1.494 1.992l3.06 17.137c.142.996 1.138 1.565 2.063 1.351l30.883-8.035l.925 5.262c.143.996 1.139 1.565 2.064 1.351l30.456-8.39c-1.779-5.263-2.917-10.88-3.202-16.64l-64.826 5.972zM11.62 182.33c-.498-.853-.143-1.92.711-2.347l58.778-27.875c2.206 5.262 5.195 10.169 8.753 14.577L54.1 185.031c-.783.569-1.921.356-2.42-.498l-2.704-4.693l-26.257 18.133c-.783.57-1.922.285-2.42-.569l-8.752-15.075zm71.23-12.231L37.094 216.39c-.712.711-.64 1.849.142 2.489l13.378 11.164c.783.64 1.85.498 2.42-.284l18.501-26.027l4.127 3.485c.783.64 1.922.498 2.49-.356l17.933-26.026c-4.839-2.987-9.322-6.614-13.165-10.738zm-9.037 74.31c-.925-.355-1.352-1.421-.925-2.275L100 182.97c4.98 2.56 10.389 4.48 16.01 5.547l-7.97 30.577c-.213.925-1.28 1.494-2.205 1.138l-5.052-1.849l-8.468 30.791c-.285.925-1.281 1.494-2.206 1.138l-16.367-5.973zm46.68-55.11l-5.265 64.853c-.071.996.711 1.849 1.637 1.849h17.434c.996 0 1.779-.853 1.636-1.849l-2.561-31.787h5.336a1.72 1.72 0 0 0 1.708-1.848l-2.988-31.432c-3.06.498-6.191.712-9.393.712c-2.562 0-5.053-.143-7.543-.498m62.763-175.574c.427-.924 0-1.92-.925-2.275l-16.366-5.973c-.926-.356-1.922.213-2.206 1.137l-8.468 30.791l-5.053-1.848c-.925-.356-1.921.213-2.206 1.137l-7.97 30.578c5.693 1.138 11.03 3.058 16.011 5.547zm35.722 25.814L173.222 85.83a62 62 0 0 0-13.165-10.738l17.933-26.026c.569-.783 1.707-.996 2.49-.356l4.127 3.485l18.502-26.027c.57-.782 1.708-.925 2.42-.285l13.377 11.165c.783.64.783 1.778.143 2.489zm24.764 36.409c.925-.427 1.21-1.494.711-2.347L235.7 58.524c-.498-.853-1.637-1.066-2.42-.568l-26.257 18.133l-2.704-4.622c-.499-.854-1.637-1.138-2.42-.498l-25.76 18.347c3.558 4.408 6.476 9.315 8.753 14.577l58.778-27.875zm9.25 23.609l2.99 17.137c.142.996-.499 1.85-1.495 1.991l-64.826 6.045c-.285-5.831-1.424-11.378-3.203-16.64l30.457-8.391c.925-.285 1.921.355 2.063 1.35l.925 5.263l30.884-8.035c.925-.214 1.92.355 2.063 1.35zm-2.917 62.933c.925.213 1.921-.356 2.064-1.351L255.126 144c.143-.996-.498-1.849-1.494-1.991l-31.808-2.987l.925-5.262c.142-.996-.498-1.849-1.495-1.991l-31.523-2.56a62.3 62.3 0 0 1-2.704 16.782l62.976 16.427zM233.28 201.6c-.498.853-1.636 1.067-2.419.569l-53.583-36.978a60.2 60.2 0 0 0 8.254-14.791l28.749 13.156c.925.426 1.28 1.493.783 2.346l-2.704 4.622l28.89 13.654c.854.426 1.21 1.493.712 2.346zm-71.657-21.831l37.643 53.049c.57.782 1.708.924 2.42.284l13.306-11.164c.783-.64.783-1.778.143-2.49l-22.415-22.684l4.127-3.413c.783-.64.783-1.778.07-2.489l-22.557-22.186c-3.771 4.266-8.04 8.035-12.808 11.093zm-.356 72.249c-.925.355-1.921-.214-2.206-1.138l-17.22-62.72a61 61 0 0 0 15.868-6.044l13.592 28.515c.426.925 0 1.991-.926 2.276l-5.052 1.849l13.307 29.013c.427.924 0 1.92-.925 2.275l-16.367 5.974z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Optimize for Performance",
|
||||
href: "/docs/guides/optimizing-for-performance",
|
||||
|
||||
174
docs/content/docs/guides/saml-sso-with-okta.mdx
Normal file
174
docs/content/docs/guides/saml-sso-with-okta.mdx
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: SAML SSO with Okta
|
||||
description: A guide to integrating SAML Single Sign-On (SSO) with Better Auth, featuring Okta
|
||||
---
|
||||
|
||||
This guide walks you through setting up SAML Single Sign-On (SSO) with your Identity Provider (IdP), using Okta as an example. For advanced configuration details and the full API reference, check out the [SSO Plugin Documentation](/docs/plugins/sso).
|
||||
|
||||
## What is SAML?
|
||||
|
||||
SAML (Security Assertion Markup Language) is an XML-based standard for exchanging authentication and authorization data between an Identity Provider (IdP) (e.g., Okta, Azure AD, OneLogin) and a Service Provider (SP) (in this case, Better Auth).
|
||||
|
||||
In this setup:
|
||||
|
||||
- **IdP (Okta)**: Authenticates users and sends assertions about their identity.
|
||||
- **SP (Better Auth)**: Validates assertions and logs the user in.up.
|
||||
|
||||
### Step 1: Create a SAML Application in Okta
|
||||
|
||||
1. Log in to your Okta Admin Console
|
||||
2. Navigate to Applications > Applications
|
||||
3. Click "Create App Integration"
|
||||
4. Select "SAML 2.0" as the Sign-in method
|
||||
5. Configure the following settings:
|
||||
|
||||
- **Single Sign-on URL**: Your Better Auth ACS endpoint (e.g., `http://localhost:3000/api/auth/sso/saml2/sp/acs/sso`). while `sso` being your providerId
|
||||
- **Audience URI (SP Entity ID)**: Your Better Auth metadata URL (e.g., `http://localhost:3000/api/auth/sso/saml2/sp/metadata`)
|
||||
- **Name ID format**: Email Address or any of your choice.
|
||||
|
||||
6. Download the IdP metadata XML file and certificate
|
||||
|
||||
### Step 2: Configure Better Auth
|
||||
|
||||
Here’s an example configuration for Okta in a dev environment:
|
||||
|
||||
```typescript
|
||||
const ssoConfig = {
|
||||
defaultSSO: [{
|
||||
domain: "localhost:3000", // Your domain
|
||||
providerId: "sso",
|
||||
samlConfig: {
|
||||
// SP Configuration
|
||||
issuer: "http://localhost:3000/api/auth/sso/saml2/sp/metadata",
|
||||
entryPoint: "https://trial-1076874.okta.com/app/trial-1076874_samltest_1/exktofb0a62hqLAUL697/sso/saml",
|
||||
callbackUrl: "/dashboard", // Redirect after successful authentication
|
||||
|
||||
// IdP Configuration
|
||||
idpMetadata: {
|
||||
entityID: "https://trial-1076874.okta.com/app/exktofb0a62hqLAUL697/sso/saml/metadata",
|
||||
singleSignOnService: [{
|
||||
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
|
||||
Location: "https://trial-1076874.okta.com/app/trial-1076874_samltest_1/exktofb0a62hqLAUL697/sso/saml"
|
||||
}],
|
||||
cert: `-----BEGIN CERTIFICATE-----
|
||||
MIIDqjCCApKgAwIBAgIGAZhVGMeUMA0GCSqGSIb3DQEBCwUAMIGVMQswCQYDVQQGEwJVUzETMBEG
|
||||
...
|
||||
[Your Okta Certificate]
|
||||
...
|
||||
-----END CERTIFICATE-----`
|
||||
},
|
||||
|
||||
// SP Metadata
|
||||
spMetadata: {
|
||||
metadata: `<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
entityID="http://localhost:3000/api/sso/saml2/sp/metadata">
|
||||
...
|
||||
[Your SP Metadata XML]
|
||||
...
|
||||
</md:EntityDescriptor>`
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Multiple Default Providers (Optional)
|
||||
|
||||
You can configure multiple SAML providers for different domains:
|
||||
|
||||
```typescript
|
||||
const ssoConfig = {
|
||||
defaultSSO: [
|
||||
{
|
||||
domain: "company.com",
|
||||
providerId: "company-okta",
|
||||
samlConfig: {
|
||||
// Okta SAML configuration for company.com
|
||||
}
|
||||
},
|
||||
{
|
||||
domain: "partner.com",
|
||||
providerId: "partner-adfs",
|
||||
samlConfig: {
|
||||
// ADFS SAML configuration for partner.com
|
||||
}
|
||||
},
|
||||
{
|
||||
domain: "contractor.org",
|
||||
providerId: "contractor-azure",
|
||||
samlConfig: {
|
||||
// Azure AD SAML configuration for contractor.org
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
**Explicit**: Pass providerId directly when signing in.
|
||||
**Domain fallback:** Matches based on the user’s email domain. e.g. user@company.com → matches `company-okta` provider.
|
||||
</Callout>
|
||||
|
||||
|
||||
### Step 4: Initiating Sign-In
|
||||
|
||||
You can start an SSO flow in three ways:
|
||||
|
||||
**1. Explicitly by `providerId` (recommended):**
|
||||
|
||||
```typescript
|
||||
// Explicitly specify which provider to use
|
||||
await authClient.signIn.sso({
|
||||
providerId: "company-okta",
|
||||
callbackURL: "/dashboard"
|
||||
});
|
||||
```
|
||||
|
||||
**2. By email domain matching:**
|
||||
|
||||
```typescript
|
||||
// Automatically matches provider based on email domain
|
||||
await authClient.signIn.sso({
|
||||
email: "user@company.com",
|
||||
callbackURL: "/dashboard"
|
||||
});
|
||||
```
|
||||
|
||||
**3. By specifying domain:**
|
||||
|
||||
```typescript
|
||||
// Explicitly specify domain for matching
|
||||
await authClient.signIn.sso({
|
||||
domain: "partner.com",
|
||||
callbackURL: "/dashboard"
|
||||
});
|
||||
```
|
||||
|
||||
**Important Notes**:
|
||||
- DummyIDP should ONLY be used for development and testing
|
||||
- Never use these certificates in production
|
||||
- The example uses `localhost:3000` - adjust URLs for your environment
|
||||
- For production, always use proper IdP providers like Okta, Azure AD, or OneLogin
|
||||
|
||||
### Step 5: Dynamically Registering SAML Providers
|
||||
|
||||
For dynamic registration, you should register SAML providers using the API. See the [SSO Plugin Documentation](/docs/plugins/sso#register-a-saml-provider) for detailed registration instructions.
|
||||
|
||||
Example registration:
|
||||
|
||||
```typescript
|
||||
await authClient.sso.register({
|
||||
providerId: "okta-prod",
|
||||
issuer: "https://your-domain.com",
|
||||
domain: "your-domain.com",
|
||||
samlConfig: {
|
||||
// Your production SAML configuration
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [SSO Plugin Documentation](/docs/plugins/sso)
|
||||
- [Okta SAML Documentation](https://developer.okta.com/docs/concepts/saml/)
|
||||
- [SAML 2.0 Specification](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf)
|
||||
@@ -7,11 +7,6 @@ description: Integrate Single Sign-On (SSO) with your application.
|
||||
|
||||
Single Sign-On (SSO) allows users to authenticate with multiple applications using a single set of credentials. This plugin supports OpenID Connect (OIDC), OAuth2 providers, and SAML 2.0.
|
||||
|
||||
|
||||
<Callout type="warn">
|
||||
This plugin is in active development and may not be suitable for production use. Please report any issues or bugs on [GitHub](https://github.com/better-auth/better-auth) and any security concerns on [security@better-auth.com](mailto:security@better-auth.com).
|
||||
</Callout>
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
@@ -100,14 +95,18 @@ await authClient.sso.register({
|
||||
discoveryEndpoint: "https://idp.example.com/.well-known/openid-configuration",
|
||||
scopes: ["openid", "email", "profile"],
|
||||
pkce: true,
|
||||
},
|
||||
mapping: {
|
||||
id: "sub",
|
||||
email: "email",
|
||||
emailVerified: "email_verified",
|
||||
name: "name",
|
||||
image: "picture",
|
||||
},
|
||||
mapping: {
|
||||
id: "sub",
|
||||
email: "email",
|
||||
emailVerified: "email_verified",
|
||||
name: "name",
|
||||
image: "picture",
|
||||
extraFields: {
|
||||
department: "department",
|
||||
role: "role"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
@@ -129,14 +128,18 @@ await auth.api.registerSSOProvider({
|
||||
discoveryEndpoint: "https://idp.example.com/.well-known/openid-configuration",
|
||||
scopes: ["openid", "email", "profile"],
|
||||
pkce: true,
|
||||
},
|
||||
mapping: {
|
||||
id: "sub",
|
||||
email: "email",
|
||||
emailVerified: "email_verified",
|
||||
name: "name",
|
||||
image: "picture",
|
||||
},
|
||||
mapping: {
|
||||
id: "sub",
|
||||
email: "email",
|
||||
emailVerified: "email_verified",
|
||||
name: "name",
|
||||
image: "picture",
|
||||
extraFields: {
|
||||
department: "department",
|
||||
role: "role"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headers,
|
||||
});
|
||||
@@ -183,19 +186,20 @@ await authClient.sso.register({
|
||||
isAssertionEncrypted: true,
|
||||
encPrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
|
||||
encPrivateKeyPass: "your-sp-encryption-key-password"
|
||||
},
|
||||
mapping: {
|
||||
id: "nameID",
|
||||
email: "email",
|
||||
name: "displayName",
|
||||
firstName: "givenName",
|
||||
lastName: "surname",
|
||||
emailVerified: "email_verified",
|
||||
extraFields: {
|
||||
department: "department",
|
||||
role: "role"
|
||||
}
|
||||
}
|
||||
},
|
||||
mapping: {
|
||||
id: "nameID",
|
||||
email: "email",
|
||||
name: "displayName",
|
||||
firstName: "givenName",
|
||||
lastName: "surname",
|
||||
extraFields: {
|
||||
department: "department",
|
||||
role: "role"
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
@@ -233,19 +237,20 @@ await auth.api.registerSSOProvider({
|
||||
isAssertionEncrypted: true,
|
||||
encPrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
|
||||
encPrivateKeyPass: "your-sp-encryption-key-password"
|
||||
},
|
||||
mapping: {
|
||||
id: "nameID",
|
||||
email: "email",
|
||||
name: "displayName",
|
||||
firstName: "givenName",
|
||||
lastName: "surname",
|
||||
emailVerified: "email_verified",
|
||||
extraFields: {
|
||||
department: "department",
|
||||
role: "role"
|
||||
}
|
||||
}
|
||||
},
|
||||
mapping: {
|
||||
id: "nameID",
|
||||
email: "email",
|
||||
name: "displayName",
|
||||
firstName: "givenName",
|
||||
lastName: "surname",
|
||||
extraFields: {
|
||||
department: "department",
|
||||
role: "role"
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
headers,
|
||||
});
|
||||
@@ -611,6 +616,36 @@ organizationProvisioning: {
|
||||
|
||||
## SAML Configuration
|
||||
|
||||
|
||||
### Default SSO Provider
|
||||
|
||||
```ts title="auth.ts"
|
||||
const auth = betterAuth({
|
||||
plugins: [
|
||||
sso({
|
||||
defaultSSO: {
|
||||
providerId: "default-saml", // Provider ID for the default provider
|
||||
samlConfig: {
|
||||
issuer: "https://your-app.com",
|
||||
entryPoint: "https://idp.example.com/sso",
|
||||
cert: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
|
||||
callbackUrl: "http://localhost:3000/api/auth/sso/saml2/sp/acs",
|
||||
spMetadata: {
|
||||
entityID: "http://localhost:3000/api/auth/sso/saml2/sp/metadata",
|
||||
metadata: "<!-- Your SP Metadata XML -->",
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
The defaultSSO provider will be used when:
|
||||
1. No matching provider is found in the database
|
||||
|
||||
This allows you to test SAML authentication without setting up providers in the database. The defaultSSO provider supports all the same configuration options as regular SAML providers.
|
||||
|
||||
### Service Provider Configuration
|
||||
|
||||
When registering a SAML provider, you need to provide Service Provider (SP) metadata configuration:
|
||||
@@ -679,6 +714,8 @@ The plugin requires additional fields in the `ssoProvider` table to store the pr
|
||||
]}
|
||||
/>
|
||||
|
||||
For a detailed guide on setting up SAML SSO with examples for Okta and testing with DummyIDP, see our [SAML SSO Setup Guide](/docs/guides/sso-saml-guide).
|
||||
|
||||
## Options
|
||||
|
||||
### Server
|
||||
@@ -735,5 +772,31 @@ The plugin requires additional fields in the `ssoProvider` table to store the pr
|
||||
type: "number | function",
|
||||
default: 10,
|
||||
},
|
||||
defaultSSO: {
|
||||
description: "Configure a default SSO provider for testing and development. This provider will be used when no matching provider is found in the database.",
|
||||
type: "object",
|
||||
properties: {
|
||||
domain: {
|
||||
description: "The domain to match for this default provider.",
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
providerId: {
|
||||
description: "The provider ID to use for the default provider.",
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
samlConfig: {
|
||||
description: "SAML configuration for the default provider.",
|
||||
type: "SAMLConfig",
|
||||
required: false,
|
||||
},
|
||||
oidcConfig: {
|
||||
description: "OIDC configuration for the default provider.",
|
||||
type: "OIDCConfig",
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user