Skip to content

Single Sign-On (SSO) Configuration

Configure Google and Microsoft SSO for your AuthVital instance.

Overview

AuthVital supports SSO with: - Google - Google Workspace and personal accounts - Microsoft - Azure AD, Microsoft 365, personal Microsoft accounts

SSO can be configured at two levels: 1. Instance-level: Default SSO for all tenants 2. Tenant-level: Custom SSO per tenant (overrides instance defaults)

Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                           User clicks "Sign in with Google"                  │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│  AuthVital checks tenant SSO config                                          │
│  ├─ Tenant has custom config? → Use tenant credentials                       │
│  └─ No custom config? → Use instance-level credentials                       │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│  Redirect to Google/Microsoft OAuth                                          │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│  User authenticates with Google/Microsoft                                    │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│  AuthVital receives OAuth callback                                           │
│  ├─ User exists? → Log them in                                               │
│  ├─ Email matches existing user? → Link accounts (if autoLinkExisting)       │
│  └─ New user? → Create account (if autoCreateUser)                           │
└─────────────────────────────────────────────────────────────────────────────┘

Google SSO Setup

1. Create Google Cloud Project

  1. Go to Google Cloud Console
  2. Create a new project or select existing
  3. Enable the Google+ API (for profile info)
  1. Go to APIs & ServicesOAuth consent screen
  2. Choose user type:
  3. Internal: Only your Google Workspace users
  4. External: Any Google account
  5. Fill in app information:
  6. App name: Your app name
  7. User support email: Your email
  8. Authorized domains: yourapp.com, yourauthvital.com
  9. Add scopes:
  10. openid
  11. email
  12. profile

3. Create OAuth Credentials

  1. Go to APIs & ServicesCredentials
  2. Click Create CredentialsOAuth client ID
  3. Application type: Web application
  4. Add authorized redirect URIs:
    https://auth.yourapp.com/api/sso/google/callback
    
  5. Save the Client ID and Client Secret

4. Configure in AuthVital

Via Admin Panel:

  1. Go to SettingsSSO
  2. Select Google
  3. Enter:
  4. Client ID
  5. Client Secret
  6. Allowed domains (optional, e.g., yourcompany.com)
  7. Enable and save

Via API:

await authvital.admin.configureSso({
  provider: 'GOOGLE',
  enabled: true,
  clientId: 'your-google-client-id',
  clientSecret: 'your-google-client-secret',
  scopes: ['openid', 'email', 'profile'],
  allowedDomains: ['yourcompany.com'], // Optional: restrict to domain
  autoCreateUser: true,   // Create new users automatically
  autoLinkExisting: true, // Link to existing accounts with same email
});

Microsoft SSO Setup

1. Register App in Azure AD

  1. Go to Azure Portal
  2. Navigate to Azure Active DirectoryApp registrations
  3. Click New registration
  4. Configure:
  5. Name: Your app name
  6. Supported account types:
    • Single tenant: Only your organization
    • Multitenant: Any Azure AD organization
    • Multitenant + personal: Azure AD + personal Microsoft accounts
  7. Redirect URI: https://auth.yourapp.com/api/sso/microsoft/callback
  8. Click Register

2. Configure Authentication

  1. Go to Authentication
  2. Under Platform configurations, verify Web redirect URI
  3. Enable ID tokens and Access tokens

3. Create Client Secret

  1. Go to Certificates & secrets
  2. Click New client secret
  3. Set description and expiration
  4. Copy the Value immediately (shown only once!)

4. Note Your IDs

From Overview tab, note: - Application (client) ID - Directory (tenant) ID (if single-tenant)

5. Configure in AuthVital

Via Admin Panel:

  1. Go to SettingsSSO
  2. Select Microsoft
  3. Enter:
  4. Client ID (Application ID)
  5. Client Secret
  6. Tenant ID (optional, for single-tenant)
  7. Allowed domains (optional)
  8. Enable and save

Via API:

await authvital.admin.configureSso({
  provider: 'MICROSOFT',
  enabled: true,
  clientId: 'your-azure-app-id',
  clientSecret: 'your-azure-client-secret',
  // For multi-tenant, use 'common' or 'organizations' or 'consumers'
  // For single-tenant, use your directory/tenant ID
  scopes: ['openid', 'email', 'profile', 'User.Read'],
  allowedDomains: ['yourcompany.com'],
  autoCreateUser: true,
  autoLinkExisting: true,
});

Tenant-Level SSO

Tenants can configure their own SSO credentials:

Use Case

  • Enterprise customers with their own Azure AD
  • Organizations wanting isolated identity management
  • Different SSO providers per tenant

Configuration

Via Tenant Admin Panel:

  1. Tenant admin goes to SettingsSSO
  2. Configures their own OAuth credentials
  3. Can optionally enforce SSO (disable password login)

Via API:

await authvital.tenants.configureSso('tenant-id', {
  provider: 'MICROSOFT',
  enabled: true,
  clientId: 'tenant-specific-azure-app-id',
  clientSecret: 'tenant-specific-secret',
  enforced: true, // Disable password login for this tenant
  allowedDomains: ['tenant-company.com'],
});

Enforcement

When SSO is enforced for a tenant: - Password login is disabled - Users must use SSO - Password reset is disabled - Only SSO-linked users can access

// Check if tenant has enforced SSO
const ssoConfig = await authvital.tenants.getSsoConfig('tenant-id', 'MICROSOFT');
if (ssoConfig.enforced) {
  // Hide password login, show only SSO button
}

Domain Restrictions

Restrict SSO to specific email domains:

// Instance-level: only allow company emails
await authvital.admin.configureSso({
  provider: 'GOOGLE',
  allowedDomains: ['yourcompany.com', 'subsidiary.com'],
  // ...
});

// Tenant-level: restrict to tenant's domain
await authvital.tenants.configureSso('tenant-id', {
  provider: 'MICROSOFT',
  allowedDomains: ['tenant-company.com'],
  // ...
});

If a user tries to SSO with an email outside allowed domains, they'll see an error.

Account Linking

⚠️ Security Warning: Auto-linking is now opt-in

The autoLinkExisting option now defaults to false for security reasons. Enabling auto-linking can expose your application to account takeover attacks: if an attacker controls an SSO account (e.g., Google or Microsoft) with the same email address as an unverified AuthVital account, they could gain access to that account by simply signing in via SSO. Only enable autoLinkExisting: true if you trust your SSO provider's email verification AND require email verification on AuthVital accounts.

When autoLinkExisting: true (opt-in):

  1. User signs in with Google/Microsoft
  2. AuthVital checks for existing user with same email
  3. If found, links the SSO identity to existing account
  4. User can now sign in via password OR SSO

Manual Linking

Users can link SSO accounts from their profile:

// Get available SSO providers
const providers = await authvital.sso.getAvailableProviders();
// [{ provider: 'GOOGLE', enabled: true }, { provider: 'MICROSOFT', enabled: true }]

// Initiate linking (returns OAuth URL)
const { url } = await authvital.sso.initiateLink(req, {
  provider: 'GOOGLE',
  redirectUri: 'https://yourapp.com/settings/account',
});

// Redirect user to url

Unlinking SSO

// Remove SSO link (user must have password set)
await authvital.sso.unlink(req, 'GOOGLE');
// Or for Microsoft:
await authvital.sso.unlink(req, 'MICROSOFT');

SSO Buttons

Important: SSO provider information should come from your backend. The available providers are fetched server-side using the AuthVital SDK.

Instance SSO Buttons

Show SSO options on login page:

import { useState, useEffect } from 'react';
import { useAuth } from '@authvital/sdk/client';

// NOTE: SSO provider information comes from your backend.
// Server-side (your API route):
//   const providers = await authvital.sso.getAvailableProviders();
//
// For SSO login, redirect to the provider URL:
//   const loginUrl = await authvital.sso.getLoginUrl(provider, { redirectUri, ... });

function LoginPage() {
  const [ssoProviders, setSsoProviders] = useState([]);

  useEffect(() => {
    // Fetch available SSO providers from YOUR backend
    fetch('/api/sso/providers')
      .then(res => res.json())
      .then(data => setSsoProviders(data.providers));
  }, []);

  return (
    <div>
      <LoginForm />

      {ssoProviders.length > 0 && (
        <>
          <Divider>or continue with</Divider>
          <SsoButtons providers={ssoProviders} />
        </>
      )}
    </div>
  );
}

function SsoButtons({ providers }) {
  const handleSsoLogin = async (provider: string) => {
    // Get SSO login URL from YOUR backend
    const response = await fetch(`/api/sso/login-url?provider=${provider}`);
    const { loginUrl } = await response.json();
    window.location.href = loginUrl;
  };

  return (
    <div className="sso-buttons">
      {providers.includes('GOOGLE') && (
        <button onClick={() => handleSsoLogin('google')}>
          <GoogleIcon /> Sign in with Google
        </button>
      )}
      {providers.includes('MICROSOFT') && (
        <button onClick={() => handleSsoLogin('microsoft')}>
          <MicrosoftIcon /> Sign in with Microsoft
        </button>
      )}
    </div>
  );
}

Tenant-Specific SSO

For tenant-scoped login pages, you'll need a backend endpoint to fetch SSO config:

Backend (using SDK):

import { createAuthVital } from '@authvital/sdk/server';

const authvital = createAuthVital({ /* config */ });

// Public endpoint to get tenant SSO info
app.get('/api/tenants/:slug/sso-info', async (req, res) => {
  try {
    const providers = await authvital.sso.getProvidersForTenant(req.params.slug);
    res.json(providers);
  } catch (error) {
    res.status(404).json({ error: 'Tenant not found' });
  }
});

Frontend:

import { useState, useEffect } from 'react';

function TenantLoginPage({ tenantSlug }: { tenantSlug: string }) {
  const [ssoProviders, setSsoProviders] = useState<Array<{ provider: string; enforced: boolean }>>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchSsoConfig = async () => {
      try {
        const response = await fetch(`/api/tenants/${tenantSlug}/sso-info`);
        if (!response.ok) throw new Error('Failed to fetch SSO config');
        const providers = await response.json();
        setSsoProviders(providers);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error');
      } finally {
        setLoading(false);
      }
    };
    fetchSsoConfig();
  }, [tenantSlug]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  const enforcedProvider = ssoProviders.find(p => p.enforced);

  // If SSO is enforced, show only SSO button
  if (enforcedProvider) {
    return (
      <div>
        <h1>Sign in to {tenantSlug}</h1>
        <button onClick={() => loginWithSso(enforcedProvider.provider.toLowerCase(), tenantSlug)}>
          Sign in with {enforcedProvider.provider}
        </button>
      </div>
    );
  }

  // Otherwise show both options
  return (
    <div>
      <LoginForm />
      {ssoProviders.map(({ provider }) => (
        <button 
          key={provider}
          onClick={() => loginWithSso(provider.toLowerCase(), tenantSlug)}
        >
          Sign in with {provider}
        </button>
      ))}
    </div>
  );
}

Security Considerations

✅ Best Practices

  1. Use domain restrictions - Only allow expected email domains
  2. Enable auto-link carefully - Consider security implications
  3. Rotate client secrets - Especially for Microsoft (they expire)
  4. Use internal consent - For enterprise Google Workspace
  5. Monitor SSO usage - Track failed attempts

❌ Avoid

  1. Don't share credentials - Each tenant should have own credentials if needed
  2. Don't skip domain validation - Prevents account takeover
  3. Don't disable MFA - SSO doesn't replace MFA

Troubleshooting

"Invalid redirect URI"

  1. Check redirect URI matches exactly (including trailing slashes)
  2. Ensure protocol matches (https vs http)
  3. For Google: Verify in Cloud Console → Credentials
  4. For Microsoft: Verify in Azure → Authentication

"Account not found"

  1. Check autoCreateUser is enabled
  2. Verify email domain is allowed
  3. Check if user already exists with different email
  1. User must have password set to unlink SSO
  2. Check for existing SSO link for same provider
  3. Verify email matches between accounts

Microsoft: "AADSTS50011"

Reply URL mismatch. Ensure Azure AD registration has exact redirect URI.

Google: "Access blocked"

  1. OAuth consent screen not configured
  2. App not verified (for external users)
  3. Scopes not approved