Entity Auth

Single Sign-On (SSO)

OAuth/OIDC sign-in with automatic cross-domain support

EntityAuth provides shared SSO - one OAuth app (Google, GitHub) works for all tenants automatically. No per-tenant configuration needed!

Shared SSO Setup (One-Time)

As the EntityAuth admin, configure these environment variables once and all tenants get SSO:

# Google OAuth (create at https://console.cloud.google.com)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# GitHub OAuth (create at https://github.com/settings/developers)
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

# Token encryption
EA_TOKEN_ENC_KEY=your-32-byte-hex-key
EA_TOKEN_ENC_KEY_VERSION=1

OAuth Redirect URIs:

  • Google: https://your-entity-auth-domain.com/api/auth/sso/callback
  • GitHub: https://your-entity-auth-domain.com/api/auth/sso/callback

That's it! All workspaces now have Google and GitHub SSO.

Same-Domain SSO (Easiest)

If your app is on the same domain as EntityAuth (e.g., both on entity-auth.com):

Use the AuthForm component

import { AuthForm } from '@entity-auth/components';

<AuthForm 
  client={ea} 
  workspaceTenantId="your-tenant"
  redirectTo="/dashboard"
/>

The user clicks "Sign in with Google", completes OAuth, and is redirected back with a session cookie. It just works!

Cross-Domain SSO (Different Domain)

If your app is on a different domain (e.g., your-app.com using EntityAuth on entity-auth.com):

EntityAuth uses server-side ticket exchange via Next.js middleware. This ensures clean authentication flow with zero page reloads.

Add Entity Auth middleware

For Next.js 13+ apps, create middleware.ts in your app root:

import { entityAuthMiddleware } from '@entityauth/nextjs/middleware';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  return entityAuthMiddleware(request, {
    entityAuthUrl: process.env.NEXT_PUBLIC_ENTITY_AUTH_URL || 'https://entity-auth.com',
    workspaceTenantId: process.env.NEXT_PUBLIC_ENTITY_AUTH_WORKSPACE_TENANT_ID || '',
    protectedRoutes: ['/dashboard/*', '/profile'],
    publicRoutes: ['/'],
    loginUrl: '/auth',
  });
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

What this does:

Detects SSO redirect with __ea_ticket parameter, exchanges ticket for refresh token server-side (before page loads), sets cookie on your domain, redirects to clean URL, protects authenticated routes. Zero client-side complexity!

Update your AuthProvider

Ensure your auth provider seeds the refresh token from the cookie:

import { EntityAuthClient } from '@entityauth/auth-client';

const ea = new EntityAuthClient({
  baseURL: process.env.NEXT_PUBLIC_ENTITY_AUTH_URL,
});

useEffect(() => {
  // Seed refresh token from cookie (set by middleware)
  const cookieMatch = document.cookie.match(/(?:^|;\s*)ea_refresh=([^;]+)/);
  const cookieToken = cookieMatch?.[1];
  if (cookieToken) {
    localStorage.setItem("ea_refresh_token", cookieToken);
    ea.setRefreshToken(cookieToken);
  }
}, []);

Add SSO buttons to your login page

async function loginWithGoogle() {
  try {
    const { authorizationUrl } = await client.startSSO({
      provider: "google",
      returnTo: "/dashboard",
    });
    window.location.href = authorizationUrl;
  } catch (e) {
    const error = e as Error;
    alert(`Google sign-in error: ${error.message}`);
  }
}

async function loginWithGitHub() {
  try {
    const { authorizationUrl } = await client.startSSO({
      provider: "github",
      returnTo: "/dashboard",
    });
    window.location.href = authorizationUrl;
  } catch (e) {
    const error = e as Error;
    alert(`GitHub sign-in error: ${error.message}`);
  }
}

Flow:

  1. User clicks "Sign in with Google"
  2. client.startSSO() calls Entity Auth service (uses configured baseURL)
  3. Completes OAuth on EntityAuth
  4. EntityAuth redirects to https://your-app.com/dashboard?__ea_ticket=abc123
  5. TicketExchangeHandler detects the ticket and exchanges it for a refresh token
  6. Refresh token is stored and user is authenticated!

Mobile SSO (iOS/Swift)

Mobile apps use the same ticket-based flow. See the Swift SDK docs for details.

let sso = EntityAuthSSO(baseURL: URL(string: "https://entity-auth.com")!)
let result = try await sso.signIn(
  provider: "github", 
  returnTo: URL(string: "yourapp://auth")!,
  workspaceTenantId: "your-tenant"
)
// result = (accessToken, refreshToken, sessionId, userId)

Security

  • PKCE enforced (S256 method)
  • State validation with tenant prefix
  • Nonce validation for OpenID Connect
  • Tickets expire in 60 seconds
  • Single-use tickets (can't be replayed)
  • Workspace-scoped tickets

Supported Providers

  • Google (OpenID Connect)
  • GitHub (OAuth 2.0)

Want to add more? Implement a provider adapter with buildAuthUrl, exchangeCode, and getUserProfile in src/lib/sso/providers/.