Home Realm Discovery

Email-based SSO routing for automatic identity provider detection

Updated Apr 12, 2026 Edit this page

Home Realm Discovery (HRD)

Home Realm Discovery enables automatic routing of users to their organization’s identity provider based on their email domain.

Overview

When a user enters their email address, HRD checks if the email domain is associated with a configured upstream identity provider. If a match is found, the user is redirected to their organization’s SSO provider instead of seeing the password login form.

sequenceDiagram
    participant User
    participant Client
    participant AuthOS
    participant IdP

    User->>Client: Enter email
    Client->>AuthOS: POST /api/auth/lookup-email
    AuthOS-->>Client: {auth_method: "upstream", connection_id: "..."}
    Client->>AuthOS: GET /auth/github?org=acme&service=app&connection_id=...
    AuthOS-->>IdP: Redirect to IdP
    IdP-->>AuthOS: OAuth callback
    AuthOS-->>Client: Tokens

Endpoints

POST /api/auth/lookup-email

Look up email domain to determine authentication method.

Synopsis

Property Value
Authentication Public
Rate Limit 1 request/second (Burst: 20)
Idempotent Yes

Request Body

Field Type Required Description
email string Yes User’s email address

Example Request

curl -X POST https://sso.example.com/api/auth/lookup-email \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@acme.com"
  }'

Response - Upstream SSO Required (200 OK)

{
  "auth_method": "upstream",
  "connection_id": "con_okta_123",
  "provider_name": "Acme Okta",
  "domain_verified": true
}

Response - Fallback to Password/OAuth (200 OK)

{
  "auth_method": "password",
  "connection_id": null,
  "provider_name": null,
  "domain_verified": true
}

Response Fields

Field Type Description
auth_method string "upstream", "password", or "oauth"
connection_id string The ID of the upstream connection to use
provider_name string Display name of the provider
domain_verified boolean Whether the domain ownership has been verified

Errors

Status Code Condition
400 BAD_REQUEST Invalid email format
404 NOT_FOUND Organization or service not found

Configuring HRD

Step 1: Verify Domain Ownership

Before enabling HRD for a domain, you must verify ownership:

curl -X POST https://sso.example.com/api/organizations/acme-corp/domains \
  -H "Authorization: Bearer {jwt}" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "acme.com"
  }'

Response:

{
  "domain": "acme.com",
  "verification_token": "authos-verify=abc123def456",
  "verified": false
}

Add the verification token as a TXT record:

_authos-verify.acme.com TXT "authos-verify=abc123def456"

Step 2: Verify the Domain

curl -X POST https://sso.example.com/api/organizations/acme-corp/domains/acme.com/verify \
  -H "Authorization: Bearer {jwt}"

Step 3: Configure Upstream Provider

curl -X POST https://sso.example.com/api/organizations/acme-corp/upstream-providers \
  -H "Authorization: Bearer {jwt}" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "saml",
    "name": "Okta SSO",
    "domains": ["acme.com"],
    "config": {
      "idp_metadata_url": "https://acme.okta.com/app/.../sso/saml/metadata",
      "sp_entity_id": "https://sso.example.com/saml/acme-corp"
    }
  }'

Supported Providers

Provider Type Configuration
GitHub OAuth 2.0 Client ID, Client Secret
Google OAuth 2.0 Client ID, Client Secret
Microsoft OAuth 2.0 Client ID, Client Secret, Tenant ID
SAML SAML 2.0 IDP Metadata URL or manual config

SSO Enforcement

When HRD is configured for a domain, you can optionally enforce SSO:

curl -X PATCH https://sso.example.com/api/organizations/acme-corp/upstream-providers/{id} \
  -H "Authorization: Bearer {jwt}" \
  -H "Content-Type: application/json" \
  -d '{
    "enforce_sso": true
  }'

With enforcement enabled:

  • Users with matching email domains must authenticate via SSO
  • Password login is disabled for these users
  • Password reset is disabled for these users

Client Integration

React Example

import { useState } from 'react';

function LoginForm() {
  const [email, setEmail] = useState('');
  const [showPassword, setShowPassword] = useState(false);

  async function handleEmailSubmit(e) {
    e.preventDefault();
    
    const response = await fetch('/api/auth/lookup-email', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email,
        org_slug: 'acme-corp',
        service_slug: 'main-app'
      })
    });
    
    const result = await response.json();
    
    if (result.auth_method === 'upstream') {
      // Redirect to SSO provider with connection_id
      window.location.href = `/auth/github?org=acme-corp&service=main-app&connection_id=${result.connection_id}`;
    } else {
      // Show password field or default OAuth
      setShowPassword(true);
    }
  }

  return (
    <form onSubmit={handleEmailSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
      />
      {showPassword && (
        <input type="password" placeholder="Password" />
      )}
      <button type="submit">
        {showPassword ? 'Sign In' : 'Continue'}
      </button>
    </form>
  );
}

Security Considerations

  1. Domain Verification Required - HRD only routes verified domains
  2. No Email Enumeration - Always returns a response regardless of user existence
  3. Rate Limited - Prevents domain probing attacks
  4. HTTPS Required - All HRD endpoints require HTTPS