| title | Social & Enterprise SSO |
|---|---|
| description | Enable Google sign-in in open source, and extend auth providers through packages. |
ObjectStack open source ships two built-in social sign-in implementations, both wired by os serve from deployment env vars:
- Google OAuth: configure in Setup → Authentication, or from
GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETdeployment env vars - GitHub OAuth: configure from
GITHUB_CLIENT_IDandGITHUB_CLIENT_SECRETdeployment env vars
Additional providers should be contributed by product or enterprise packages through
the auth:configure hook. Those packages can add better-auth socialProviders
or OIDC/generic OAuth providers without forking @objectstack/plugin-auth.
The Studio login and registration pages automatically render Continue with ...
buttons for every enabled provider returned by /api/v1/auth/config.
- Go to Google Cloud Console → APIs & Services → Credentials.
- Click Create Credentials → OAuth 2.0 Client ID → Web application.
- Add an Authorized redirect URI:
For local development:
https://<your-domain>/api/v1/auth/callback/googlehttp://localhost:3000/api/v1/auth/callback/google - Copy the Client ID and Client Secret.
Then either save the values in Setup → Authentication → Social sign-in or provide them as deployment environment variables:
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# Optional settings env override. UI settings are lower precedence than env.
# OS_AUTH_GOOGLE_ENABLED=false- Go to GitHub Developer Settings → OAuth Apps → New OAuth App.
- Set Authorization callback URL:
https://<your-domain>/api/v1/auth/callback/github - Copy the Client ID and generate a Client Secret.
Provide them as deployment environment variables:
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secretThe providers below are examples for enterprise or product packages, not built-in
open-source settings. A package can register them by listening to auth:configure
and mutating the draft auth config.
The env-var names shown below (e.g. MICROSOFT_CLIENT_ID, APPLE_CLIENT_ID) are
illustrative for an extension package's own reader code — unlike GOOGLE_* and
GITHUB_*, the framework does not read them out of the box, so an extension
must wire them in its auth:configure handler.
- Go to Azure Portal → Azure Active Directory → App registrations → New registration.
- Add a redirect URI (Web):
https://<your-domain>/api/v1/auth/callback/microsoft - Under Certificates & secrets, create a new client secret.
MICROSOFT_CLIENT_ID=your-azure-app-client-id
MICROSOFT_CLIENT_SECRET=your-azure-client-secret
# Optional: restrict to a single tenant (default: "common" — all Microsoft accounts)
# MICROSOFT_TENANT_ID=your-tenant-id- Go to Apple Developer Console → Certificates, IDs & Profiles → Identifiers → register a Services ID.
- Enable Sign In with Apple and configure the redirect URL:
https://<your-domain>/api/v1/auth/callback/apple - Generate a private key under Keys.
APPLE_CLIENT_ID=your-apple-services-id
APPLE_CLIENT_SECRET=your-apple-private-key-jwtApple requires the client secret to be a signed JWT derived from the private key. See better-auth Apple docs for details.
Enterprise packages can pass oidcProviders into @objectstack/plugin-auth
or contribute them through auth:configure. The open-source package does not
ship a generic OIDC settings UI.
Per-environment external IdP (ADR-0069). Recent releases add a per-environment external-IdP path built on
@better-auth/sso(a generic OIDC relying party) and surface anssoflag in the public/auth/config(features.sso) so a client can show an enterprise-login button when SSO is configured. TheoidcProvidersextension below remains the in-process path.
const oidcProviders = [
{
"providerId": "okta",
"name": "Okta",
"discoveryUrl": "https://your-org.okta.com/.well-known/openid-configuration",
"clientId": "your-okta-client-id",
"clientSecret": "your-okta-client-secret",
"scopes": ["openid", "email", "profile"]
}
];const oidcProviders = [
{
"providerId": "azure-ad",
"name": "Azure AD",
"discoveryUrl": "https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration",
"clientId": "your-azure-client-id",
"clientSecret": "your-azure-client-secret"
}
];const oidcProviders = [
{
"providerId": "keycloak",
"name": "Keycloak",
"discoveryUrl": "https://keycloak.example.com/realms/your-realm/.well-known/openid-configuration",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"pkce": true
}
];const oidcProviders = [
{
"providerId": "okta",
"name": "Okta SSO",
"discoveryUrl": "https://your-org.okta.com/.well-known/openid-configuration",
"clientId": "...",
"clientSecret": "..."
},
{
"providerId": "azure-ad",
"name": "Microsoft SSO",
"discoveryUrl": "https://login.microsoftonline.com/<tenant>/v2.0/.well-known/openid-configuration",
"clientId": "...",
"clientSecret": "..."
}
];| Field | Required | Description |
|---|---|---|
providerId |
✅ | Unique identifier used internally (e.g. okta, azure-ad) |
name |
— | Display name shown on the button (defaults to providerId) |
discoveryUrl |
✅* | OIDC discovery URL — auto-fetches all endpoints |
authorizationUrl |
✅* | OAuth2 authorization endpoint (if no discoveryUrl) |
tokenUrl |
✅* | OAuth2 token endpoint (if no discoveryUrl) |
userInfoUrl |
— | OAuth2 userinfo endpoint |
issuer |
— | Expected issuer for token validation |
clientId |
✅ | OAuth2 client ID |
clientSecret |
✅ | OAuth2 client secret |
scopes |
— | Requested scopes (default: openid email profile) |
pkce |
— | Enable PKCE — recommended for public clients |
*Either discoveryUrl or authorizationUrl + tokenUrl must be provided.
Restart the server (pnpm dev), then:
curl http://localhost:3000/api/v1/auth/configExpected response (abbreviated — getPublicConfig() also returns disableSignUp/requireEmailVerification on emailPassword and additional features keys such as multiOrgEnabled, oidcProvider, deviceAuthorization, and admin):
{
"emailPassword": { "enabled": true },
"socialProviders": [
{ "id": "google", "name": "Google", "enabled": true, "type": "social" },
{ "id": "github", "name": "GitHub", "enabled": true, "type": "social" },
{ "id": "okta", "name": "Okta SSO", "enabled": true, "type": "oidc" }
],
"features": { "twoFactor": false, "passkeys": false, "magicLink": false, "organization": true }
}The Studio /login and /register pages will now show a button for each enabled provider.
Social providers (Google, GitHub, Microsoft, Apple):
- User clicks Continue with Google (or similar).
- Client calls
POST /api/v1/auth/sign-in/socialwith{ provider: "google", callbackURL }. - Browser redirects to the provider's consent screen.
- Provider redirects to
/api/v1/auth/callback/google. - better-auth creates a session and redirects to
callbackURL.
OIDC/enterprise providers:
- User clicks Continue with Okta SSO.
- Client calls
POST /api/v1/auth/sign-in/oauth2with{ providerId: "okta", callbackURL }. - Browser redirects to the provider's authorization endpoint.
- Provider redirects back; better-auth validates the OIDC token and creates a session.
OS_AUTH_SECRETmust be set to a random string in production (a long random value of 32+ characters is recommended).AUTH_SECRETandBETTER_AUTH_SECRETare accepted as deprecated legacy aliases and emit a deprecation warning. If no secret is set, the auth plugin is skipped in production.OS_AUTH_URL(orOS_BASE_URLas a fallback) must match the domain registered with each provider; it determines the base URL used to build OAuth callback URLs.- Never commit client secrets to source control — use a secrets manager in production.
- The redirect URI registered with each provider must match exactly:
https://<your-domain>/api/v1/auth/callback/<provider-id>.