Cross-domain SSO

One IdP login across app-a.com and app-b.fr, with one provider per domain.

This example puts two unrelated domains behind one IdP session. Each domain keeps its own Gate One cookie; no cookie crosses the TLD boundary.

Shared group, per-domain providers

{
  "ssoGroups": [{
    "id": "company",
    "issuer": "https://idp.example.com/realms/company",
    "clientId": "gate-one",
    "clientSecretEnv": "OIDC_CLIENT_SECRET",
    "sessionKeyEnv": "OIDC_SESSION_KEY",
    "sessionStore": {
      "type": "redis",
      "url": "redis://redis:6379/0",
      "prefix": "cladone:auth:v1:"
    },
    "providers": [
      {
        "id": "app-a", "loginBaseUrl": "https://login.app-a.com",
        "cookieDomain": "app-a.com", "defaultAppUrl": "https://app-a.com/"
      },
      {
        "id": "app-b", "loginBaseUrl": "https://login.app-b.fr",
        "cookieDomain": "app-b.fr", "defaultAppUrl": "https://app-b.fr/"
      }
    ]
  }],
  "defaultAuthProvider": "app-a",
  "routes": [{
    "hosts": ["app-a.com", "app-b.fr"],
    "path": "/*",
    "frontend": true,
    "backend": "http://app:8080",
    "auth": {}
  }]
}

issuer, clientId, sessionStore and sessionKeyEnv are written once. Each provider entry only carries its domain, login host and optional cookie name. The route is written once; Gate One expands it to app-a.com and app-b.fr and binds each one to its provider.

Run it

The packaged demo runs two Gate One instances and Redis; it is the same shared-state shape (Docker Compose):

cd gate-one-src/deploy/local/redis
BIN_DIR=~/gate-one-bin ./run.sh

The script prints the URLs and stops with ./run.sh down. See [deploy/local/redis](https://github.com/clad-one/gate-one/tree/main/deploy/local/redis) for the stack and its README.

To run it by hand instead:

export OIDC_CLIENT_SECRET=...
export OIDC_SESSION_KEY="$(openssl rand -base64 32)"
export REDIS_PASSWORD=...
gate-one -check -config routes.json
gate-one -config routes.json

Register both callbacks at the IdP:

What to verify

  1. Log in on app-a.com once.

  2. Visit app-b.fr with the same browser. Gate One redirects to the same IdP and returns without a login form.

  3. Log out on app-a.com; the next request on app-b.fr must be refused (back-channel logout or shared Redis revocation).

  4. Expire/clear the IdP session; the next visit must show the normal login form, not a loop.

Note
  • This is zero form, not zero redirect.

  • Both domains must use the same issuer and the same user identity.

  • Use cookieDomain instead when the applications share a parent domain.