Skip to content

OAuth Providers

Fluxbase supports OAuth 2.0 and OpenID Connect (OIDC) authentication with multiple providers. Integrate with identity providers like Google, GitHub, Microsoft, Apple, and any OIDC-compliant provider.

OAuth authentication enables:

  • Social login - Users sign in with existing accounts (Google, GitHub, etc.)
  • Enterprise SSO - Integrate with corporate identity providers
  • Custom OIDC - Connect any OIDC-compliant provider (Keycloak, Auth0, Authelia)
  • Automatic provisioning - Create users on first login

Fluxbase includes built-in support for well-known providers and custom OIDC providers:

Provider Type Auto-Discovery
Google Well-known Yes
GitHub Well-known Yes
Microsoft/Azure AD Well-known Yes
Apple Well-known Yes
Facebook Well-known Yes
Twitter (X) Well-known Yes
LinkedIn Well-known Yes
GitLab Well-known Yes
Bitbucket Well-known Yes
Keycloak Custom OIDC Requires issuer_url
Auth0 Custom OIDC Requires issuer_url
Authelia Custom OIDC Requires issuer_url
Any OIDC Provider Custom OIDC Requires issuer_url

Each OAuth provider has different requirements for scopes and refresh tokens:

Provider OIDC Refresh Token Mechanism Required Scopes
Google Yes access_type=offline + prompt=consent (auto) openid, email, profile
GitHub No Different mechanism (GitHub Apps) read:user, user:email
Microsoft Yes OIDC authorization code flow openid, email, profile, offline_access
GitLab Yes OIDC authorization code flow read_user, openid, email, offline_access
Apple No Short-lived tokens only email, name
Facebook No Long-lived tokens via API (no refresh) email, public_profile
Twitter (X) No OAuth 2.0 PKCE flow offline.access, users.read, tweet.read
LinkedIn No Auto-issued refresh_token openid, profile, email
Bitbucket Yes Rotating refresh tokens account, email
OIDC Providers Yes OIDC authorization code flow openid, email, profile, offline_access

Notes:

  • Google: Fluxbase automatically adds access_type=offline and prompt=consent parameters
  • GitHub: Not fully OIDC-compliant; refresh tokens require GitHub Apps setup
  • Apple: Tokens are short-lived; users must re-authenticate periodically
  • Facebook: No standard refresh tokens; consider implementing long-lived token exchange
  • Twitter (X): Note the scope uses a dot (offline.access), not underscore

Configure OAuth providers in your fluxbase.yaml:

auth:
oauth_providers:
# Well-known providers (issuer URL auto-detected)
- name: google
enabled: true
client_id: "YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
display_name: "Google"
- name: github
enabled: true
client_id: "YOUR_GITHUB_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [user:email]
display_name: "GitHub"
- name: microsoft
enabled: true
client_id: "YOUR_AZURE_AD_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile, offline_access]
display_name: "Microsoft"
- name: apple
enabled: true
client_id: "com.yourapp.service"
client_secret: "YOUR_APPLE_SECRET"
scopes: [openid, email, name]
display_name: "Apple"
# Custom OIDC providers (issuer_url required)
- name: keycloak
enabled: true
issuer_url: "https://auth.example.com/realms/main"
client_id: "fluxbase-client"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile, offline_access]
display_name: "Corporate SSO"
- name: auth0
enabled: true
issuer_url: "https://your-tenant.auth0.com"
client_id: "YOUR_AUTH0_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile, offline_access]
display_name: "Auth0"
Option Description Required
name Provider identifier (lowercase, e.g., “google”, “keycloak”) Yes
enabled Enable this provider Yes
client_id OAuth client ID from the provider Yes
client_secret OAuth client secret Yes (except Apple)
issuer_url OIDC issuer URL for discovery Required for custom providers
scopes OAuth scopes to request No (defaults provided)
display_name Human-friendly name for UI No

Additional options (via Admin API or database):

Option Description Default
authorization_url Custom authorization endpoint Auto-discovered
token_url Custom token endpoint Auto-discovered
user_info_url Custom userinfo endpoint Auto-discovered
allow_dashboard_login Allow for admin dashboard SSO false
allow_app_login Allow for app user authentication true

You can also configure OAuth providers through the admin UI at Authentication → OAuth Providers:

Add OAuth Provider in Admin Dashboard

The admin dashboard supports:

  • Auto-discovery from OpenID Discovery URLs
  • Manual endpoint configuration for non-standard providers
  • Provider testing before enabling
  • Real-time validation of client credentials

For deployment, use environment variables with the pattern FLUXBASE_AUTH_OAUTH_PROVIDERS_*:

Terminal window
# First provider (index 0)
FLUXBASE_AUTH_OAUTH_PROVIDERS_0_NAME=google
FLUXBASE_AUTH_OAUTH_PROVIDERS_0_ENABLED=true
FLUXBASE_AUTH_OAUTH_PROVIDERS_0_CLIENT_ID=your-google-client-id
FLUXBASE_AUTH_OAUTH_PROVIDERS_0_CLIENT_SECRET=your-google-secret
FLUXBASE_AUTH_OAUTH_PROVIDERS_0_SCOPES=openid,email,profile
FLUXBASE_AUTH_OAUTH_PROVIDERS_0_DISPLAY_NAME=Google
# Second provider (index 1)
FLUXBASE_AUTH_OAUTH_PROVIDERS_1_NAME=github
FLUXBASE_AUTH_OAUTH_PROVIDERS_1_ENABLED=true
FLUXBASE_AUTH_OAUTH_PROVIDERS_1_CLIENT_ID=your-github-client-id
FLUXBASE_AUTH_OAUTH_PROVIDERS_1_CLIENT_SECRET=your-github-secret
FLUXBASE_AUTH_OAUTH_PROVIDERS_1_SCOPES=user:email
# Custom OIDC provider (index 2)
FLUXBASE_AUTH_OAUTH_PROVIDERS_2_NAME=keycloak
FLUXBASE_AUTH_OAUTH_PROVIDERS_2_ENABLED=true
FLUXBASE_AUTH_OAUTH_PROVIDERS_2_ISSUER_URL=https://auth.example.com/realms/main
FLUXBASE_AUTH_OAUTH_PROVIDERS_2_CLIENT_ID=fluxbase-client
FLUXBASE_AUTH_OAUTH_PROVIDERS_2_CLIENT_SECRET=your-keycloak-secret
Endpoint Method Description
/api/v1/auth/oauth/providers GET List available OAuth providers (public)
/api/v1/auth/oauth/:provider/authorize GET Initiate OAuth flow (redirects to provider)
/api/v1/auth/oauth/:provider/callback GET OAuth callback handler
/api/v1/auth/oauth/:provider/token GET Retrieve the provider access token (authenticated)
/api/v1/auth/oauth/:provider/logout POST Initiate provider-side logout (OIDC/RP)
/api/v1/auth/oauth/:provider/logout/callback GET OAuth logout callback
/api/v1/admin/oauth/providers GET List all providers (admin)
/api/v1/admin/oauth/providers POST Create new provider (admin)
/api/v1/admin/oauth/providers/:id PUT Update provider (admin)
/api/v1/admin/oauth/providers/:id DELETE Delete provider (admin)

Add your OAuth provider configuration:

auth:
oauth_providers:
- name: google
enabled: true
client_id: "YOUR_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]

Register your application with the OAuth provider and set the redirect URI:

https://your-domain.com/api/v1/auth/oauth/{provider}/callback

For example:

  • Google: https://your-domain.com/api/v1/auth/oauth/google/callback
  • GitHub: https://your-domain.com/api/v1/auth/oauth/github/callback
Terminal window
# Open in browser to initiate OAuth flow
open "https://your-domain.com/api/v1/auth/oauth/google/authorize?redirect_to=https://your-domain.com/dashboard"
  1. Go to Google Cloud ConsoleAPIs & ServicesCredentials
  2. Click Create CredentialsOAuth 2.0 Client ID
  3. Select Web application
  4. Add authorized redirect URI: https://your-domain.com/api/v1/auth/oauth/google/callback
  5. Copy the Client ID and Client Secret
auth:
oauth_providers:
- name: google
enabled: true
client_id: "YOUR_CLIENT_ID.apps.googleusercontent.com"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
  1. Go to GitHub Developer SettingsOAuth AppsNew OAuth App
  2. Set Authorization callback URL: https://your-domain.com/api/v1/auth/oauth/github/callback
  3. Copy the Client ID and generate a Client Secret
auth:
oauth_providers:
- name: github
enabled: true
client_id: "YOUR_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [user:email]
  1. Go to Azure PortalAzure Active DirectoryApp registrations
  2. Click New registration
  3. Add redirect URI: https://your-domain.com/api/v1/auth/oauth/microsoft/callback
  4. Go to Certificates & secretsNew client secret
auth:
oauth_providers:
- name: microsoft
enabled: true
client_id: "YOUR_APPLICATION_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
  1. Go to Apple Developer PortalCertificates, Identifiers & Profiles
  2. Create a Services ID with Sign in with Apple enabled
  3. Configure the return URL: https://your-domain.com/api/v1/auth/oauth/apple/callback
  4. Create a Key for Sign in with Apple
auth:
oauth_providers:
- name: apple
enabled: true
client_id: "com.yourapp.service"
client_secret: "YOUR_APPLE_SECRET" # Generated JWT
scopes: [openid, email, name]

For Keycloak, Auth0, Authelia, or any OIDC-compliant provider:

Keycloak:

auth:
oauth_providers:
- name: keycloak
enabled: true
issuer_url: "https://keycloak.example.com/realms/myrealm"
client_id: "fluxbase"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
display_name: "Corporate SSO"

Auth0:

auth:
oauth_providers:
- name: auth0
enabled: true
issuer_url: "https://your-tenant.auth0.com"
client_id: "YOUR_AUTH0_CLIENT_ID"
client_secret: "YOUR_AUTH0_CLIENT_SECRET"
scopes: [openid, email, profile]
display_name: "Auth0"

Authelia:

auth:
oauth_providers:
- name: authelia
enabled: true
issuer_url: "https://auth.example.com"
client_id: "fluxbase"
client_secret: "YOUR_AUTHELIA_SECRET"
scopes: [openid, email, profile]
display_name: "Authelia"

Initiate OAuth flow:

import { createClient } from "@nimbleflux/fluxbase-sdk";
const client = createClient('http://localhost:8080', 'your-anon-key')
// Get available OAuth providers
const { data: providers } = await client.auth.getOAuthProviders();
// [{ name: 'google', display_name: 'Google' }, ...]
// Get OAuth authorization URL
const { data } = await client.auth.signInWithOAuth("google", {
redirectTo: `${window.location.origin}/auth/callback`,
});
// Redirect to provider
window.location.href = data.url;

Handle callback:

// In your /auth/callback route
const code = searchParams.get("code");
const state = searchParams.get("state");
const { data, error } = await client.auth.exchangeCodeForSession(code, state);
if (error) throw error;
const { user, session } = data;
// User is now authenticated
console.log("Logged in as:", user.email);
import { useSession } from "@nimbleflux/fluxbase-sdk-react";
function OAuthLoginButtons() {
const { data: providers } = await client.auth.getOAuthProviders();
return (
<div>
{providers?.map((provider) => (
<button
key={provider.name}
onClick={() => {
client.auth.signInWithOAuth(provider.name, {
redirectTo: "/dashboard",
});
}}
>
Sign in with {provider.display_name || provider.name}
</button>
))}
</div>
);
}

Handling callback:

pages/auth/callback.tsx
import { useEffect } from "react";
import { useSession } from "@nimbleflux/fluxbase-sdk-react";
import { useNavigate } from "react-router-dom";
function OAuthCallback() {
const { data: session, isLoading } = useSession();
const navigate = useNavigate();
useEffect(() => {
if (!isLoading && session) {
navigate("/dashboard");
}
}, [session, isLoading]);
return <div>Completing sign in...</div>;
}
Terminal window
GET /api/v1/auth/oauth/providers

Response:

{
"providers": [
{
"name": "google",
"display_name": "Google",
"enabled": true
},
{
"name": "github",
"display_name": "GitHub",
"enabled": true
}
]
}
Terminal window
GET /api/v1/auth/oauth/:provider/authorize?redirect_to=https://myapp.example.com/dashboard

Redirects to provider’s authorization page.

Terminal window
GET /api/v1/admin/oauth/providers
Authorization: Bearer <admin-token>

Response:

{
"providers": [
{
"id": "uuid",
"provider_name": "google",
"display_name": "Google",
"client_id": "xxx.apps.googleusercontent.com",
"enabled": true,
"scopes": ["openid", "email", "profile"],
"allow_dashboard_login": false,
"allow_app_login": true,
"created_at": "2024-01-01T00:00:00Z"
}
]
}
Terminal window
POST /api/v1/admin/oauth/providers
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"provider_name": "okta",
"display_name": "Okta SSO",
"enabled": true,
"client_id": "YOUR_OKTA_CLIENT_ID",
"client_secret": "YOUR_OKTA_SECRET",
"is_custom": true,
"authorization_url": "https://your-org.okta.com/oauth2/v1/authorize",
"token_url": "https://your-org.okta.com/oauth2/v1/token",
"user_info_url": "https://your-org.okta.com/oauth2/v1/userinfo",
"scopes": ["openid", "email", "profile"],
"allow_dashboard_login": true,
"allow_app_login": true
}
Terminal window
PUT /api/v1/admin/oauth/providers/:id
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"enabled": false,
"display_name": "Updated Name"
}
Terminal window
DELETE /api/v1/admin/oauth/providers/:id
Authorization: Bearer <admin-token>

OAuth tokens are encrypted at rest using AES-256-GCM. Configure an encryption key:

Terminal window
# Must be exactly 32 bytes
FLUXBASE_ENCRYPTION_KEY=your-32-byte-encryption-key-here

Fluxbase automatically generates and validates state tokens to prevent CSRF attacks:

  • State tokens expire after 10 minutes
  • Tokens are cryptographically random
  • Validation is enforced on callback
Practice Description
Use HTTPS in production OAuth requires secure connections
Protect client secrets Store in environment variables, never in code
Use PKCE Fluxbase supports PKCE for enhanced security
Limit scopes Only request necessary permissions
Rotate secrets Periodically rotate client secrets
Monitor OAuth usage Watch for unusual patterns and failed attempts
Validate redirect URIs Use exact match, avoid wildcards

Control which users can authenticate based on claims in their OAuth ID token (roles, groups, permissions, etc.).

Restrict OAuth authentication based on ID token claims:

auth:
oauth_providers:
- name: google
enabled: true
client_id: "YOUR_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
allow_dashboard_login: true
# RBAC configuration
required_claims:
roles:
- "admin" # User must have at least ONE of these role values
- "editor"
department:
- "IT"
- "Engineering"
denied_claims:
status:
- "suspended" # Reject users with ANY of these status values
- "inactive"

Two types of claim validation rules:

Rule Logic Example Use Case
required_claims User must have at least ONE matching value per claim Require admin OR editor role
denied_claims Reject if ANY value matches Block suspended or inactive users

Execution order: Denied claims are checked first (highest priority), then required claims.

OAuth claims can be strings or arrays. The validation handles both:

{
"roles": "admin", // Single string value
"groups": ["admins", "IT"], // Array of strings
"level": 5 // Number (converted to string)
}

Dashboard admin access (Google Workspace)

Only users with admin role can access the dashboard:

auth:
oauth_providers:
- name: google
enabled: true
client_id: "YOUR_CLIENT_ID.apps.googleusercontent.com"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
allow_dashboard_login: true
allow_app_login: false
required_claims:
hd: ["company.com"] # Google Workspace domain
role: ["admin", "superuser"]

Azure AD group-based access

Restrict access based on Azure AD group membership:

auth:
oauth_providers:
- name: microsoft
enabled: true
client_id: "YOUR_AZURE_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
allow_dashboard_login: true
required_claims:
groups:
- "FluxbaseAdmins" # Azure AD group name
- "ApplicationAdmins"
denied_claims:
groups:
- "Contractors"

Auth0 with custom claims

Use Auth0 app_metadata or custom claims:

auth:
oauth_providers:
- name: auth0
enabled: true
issuer_url: "https://your-tenant.auth0.com"
client_id: "YOUR_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]
allow_dashboard_login: true
required_claims:
"https://myapp.com/roles":
- "admin"
"https://myapp.com/subscription":
- "premium"
- "enterprise"

Keycloak realm roles

Filter by Keycloak realm or client roles:

auth:
oauth_providers:
- name: keycloak
enabled: true
issuer_url: "https://auth.company.com/realms/main"
client_id: "fluxbase-client"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile, roles]
allow_dashboard_login: true
required_claims:
realm_access.roles:
- "fluxbase-admin"
denied_claims:
account_status:
- "locked"
- "suspended"

When claim validation fails, users see clear error messages:

  • "Access denied: claim 'roles' has restricted value 'contractor'" - User has denied claim value
  • "Access denied: missing required claim 'roles'" - Required claim not in ID token
  • "Access denied: claim 'roles' must have one of: [admin, editor]" - User doesn’t have any allowed values

Fluxbase validates claims from the OAuth ID token (JWT). The ID token is returned during the token exchange and contains user identity and claims.

For custom claims:

  1. Configure your IdP to include claims in ID token (not just userinfo endpoint)
  2. Request appropriate scopes to receive the claims
  3. Some providers require custom scopes for custom claims

“Claims not being found”

Check the ID token structure from your provider:

Terminal window
# Use jwt.io to decode your ID token and inspect claims
# Or check browser dev tools Network tab during OAuth callback

“Azure AD groups not working”

Azure AD requires specific configuration to include groups in ID token:

  1. In Azure Portal, go to App registrations → Your app
  2. Go to Token configuration
  3. Click Add groups claim
  4. Select Security groups and Group ID (or sAMAccountName for names)
  5. Check ID under token type

“Keycloak roles not in token”

Enable role claims in Keycloak:

  1. Go to Clients → Your client → Mappers
  2. Add realm roles mapper
  3. Set Token Claim Name to match your config (e.g., realm_access.roles)
  4. Enable Add to ID token

“Auth0 custom claims not appearing”

Auth0 custom claims must use namespaced format:

// Auth0 Rule/Action to add custom claims
function(user, context, callback) {
const namespace = 'https://myapp.com/';
context.idToken[namespace + 'roles'] = user.app_metadata.roles;
callback(null, user, context);
}

“Claim values are case-sensitive”

Claim values are matched exactly:

  • "Admin""admin"
  • "IT-Team""IT Team"

Check exact values from your IdP.

OAuth providers can be used for dashboard admin authentication, enabling SSO-only mode.

Configure a provider for dashboard login:

auth:
oauth_providers:
- name: google
enabled: true
client_id: "YOUR_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
scopes: [openid, email, profile]

Then via Admin API, enable dashboard login:

Terminal window
PUT /api/v1/admin/oauth/providers/:id
{
"allow_dashboard_login": true
}

Once SSO is configured for dashboard login:

  1. Go to AuthenticationAuth Settings in the dashboard
  2. Enable Disable Password Login
  3. Save settings

When enabled:

  • Login page shows only SSO buttons
  • Password form is hidden
  • Backend rejects password attempts

With password login disabled, use SSO for CLI authentication:

Terminal window
# SSO login (opens browser)
fluxbase auth login --server https://api.example.com --sso
# Or use an API token
fluxbase auth login --server https://api.example.com --token your-api-token

If locked out due to misconfigured SSO, use the admin dashboard to temporarily re-enable password login.

// User must be authenticated
const { url } = await client.auth.linkIdentity({ provider: "github" });
window.location.href = url;
await client.auth.unlinkIdentity({ identity: { id: "identity-id" } });
Issue Solution
Redirect URI mismatch Ensure redirect URI in Fluxbase config exactly matches provider registration (include protocol, no trailing slashes)
Invalid state parameter Enable cookies (state stored in cookie), verify cross-site cookie settings
Client secret invalid Verify secret hasn’t expired, regenerate if needed
Users can’t sign in Check provider returns email, verify scopes include necessary permissions
Custom provider not working Verify issuer_url is accessible, check OIDC discovery endpoint
Token encryption errors Ensure FLUXBASE_ENCRYPTION_KEY is exactly 32 bytes
CORS errors Configure allowed origins in Fluxbase CORS settings
Dev vs prod URLs Use environment variables for redirect URLs

If users need to re-authenticate frequently or sessions don’t persist, check provider-specific requirements:

  • Mechanism: access_type=offline parameter + prompt=consent
  • Automatic: Fluxbase adds both parameters automatically
  • Scopes: [openid, email, profile] - NO offline_access
scopes: [openid, email, profile] # ✅ Correct
  • Mechanism: Standard OIDC with offline_access scope
  • Scopes: [openid, email, profile, offline_access]
scopes: [openid, email, profile, offline_access] # ✅ Correct
  • Mechanism: Not OIDC-compliant; uses GitHub App tokens
  • Note: Standard OAuth may not provide refresh tokens; consider GitHub Apps
  • Scopes: [read:user, user:email] - NO offline_access
scopes: [read:user, user:email] # ✅ Correct
  • Mechanism: OAuth 2.0 PKCE flow
  • Important: Uses dot not underscore!
  • Scopes: [tweet.read, users.read, offline.access]
scopes: [tweet.read, users.read, offline.access] # ✅ Note the dot!
  • Mechanism: Standard OAuth 2.0
  • Note: Issues refresh tokens automatically
  • Scopes: [r_liteprofile, r_emailaddress]
scopes: [r_liteprofile, r_emailaddress] # ✅ Correct
  • Mechanism: OAuth 2.0 with rotating refresh tokens
  • Scopes: [account, email]
scopes: [account, email] # ✅ Correct
  • Mechanism: Short-lived tokens only
  • Note: Users must re-authenticate periodically (Apple design)
  • Scopes: [email, name] - No refresh tokens
scopes: [email, name] # ✅ Correct (no refresh token support)
  • Mechanism: Long-lived tokens via separate API exchange
  • Note: Standard OAuth does not provide refresh tokens
  • Scopes: [email, public_profile]
scopes: [email, public_profile] # ✅ Standard (no refresh tokens)

Custom OIDC (Keycloak, Auth0, Authelia, etc.)

Section titled “Custom OIDC (Keycloak, Auth0, Authelia, etc.)”
  • Mechanism: Standard OIDC authorization code flow
  • Scopes: [openid, email, profile, offline_access]
scopes: [openid, email, profile, offline_access] # ✅ Correct

Why refresh tokens matter:

  • Enable long-lived sessions without requiring re-authentication
  • Allow automatic token refresh in the background
  • Essential for production applications with persistent login

How Fluxbase handles it:

  • Automatically adds access_type=offline for all providers
  • Adds prompt=consent for Google (ensures refresh tokens on repeat logins)
  • Stores refresh tokens securely in the database (encrypted if key configured)

Enable debug logging to troubleshoot OAuth:

debug: true

Check logs for:

  • OAuth request/response details
  • Token exchange errors
  • User info extraction