> ## Documentation Index
> Fetch the complete documentation index at: https://autopilot.docs.xano.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dashboard Auth — Google OAuth

> Configure the Autopilot dashboard to sign users in with Google OAuth/OIDC.

This guide configures the Autopilot dashboard to sign users in with **Google OAuth/OIDC**
instead of Xano. It is an alternative to the default Xano method; exactly one method is
active per cluster. Authorization (who can do what) is unchanged — a Google-authenticated
user is resolved against the same `AccessPolicy` by email.

Auth is gated by `auth.enabled` (default **false**). Enabling Google on a cluster is a
deliberate, per-cluster opt-in. Rollback is instant: flip the method back to `xano` or set
`auth.enabled=false` and redeploy.

## Prerequisites

* A Google Cloud project where you can create OAuth credentials.
* The dashboard's external base URL (the dashboard hostname), e.g. `https://ops.xano.com`. The OAuth redirect URI is always `<base>/api/v1/auth/callback`.
* A Google Workspace domain to enforce (e.g. `xano.com`) — strongly recommended.

## 1. Create the OAuth client

In the GCP Console → **APIs & Services → Credentials → Create credentials → OAuth client ID**:

* **Application type:** Web application
* **Authorized redirect URIs:** add `<base>/api/v1/auth/callback` for **every** hostname that will use Google on this cluster, e.g. `https://ops.xano.com/api/v1/auth/callback`. (The path is always `/api/v1/auth/callback`.)

Note the generated **Client ID** and **Client secret**.

## 2. Create the client-secret Secret

The client secret is injected from a Kubernetes Secret (read by the kubelet) — it is
**never** placed in Helm values or git.

```bash theme={null}
kubectl --context=<cluster> create secret generic ops-ai-google-oauth \
  -n ops-ai --from-literal=client-secret='<the-client-secret>'
```

The key name (`client-secret`) is the Helm default (`auth.google.clientSecret.secretKey`).

## 3. Set Helm values

In the cluster's env file (`deploy/envs/<tier>/<cluster>.yaml`) or a values override:

```yaml theme={null}
auth:
  enabled: true
  method: google
  dashboardBaseURL: "https://ops.xano.com"   # the external base; callback is <base>/api/v1/auth/callback
  superAdmins:                               # bootstrap admins (unkillable, never edited via the API)
    - you@xano.com
  google:
    clientId: "<client-id>.apps.googleusercontent.com"
    allowedDomain: "xano.com"                # enforced Google Workspace hd
    allowAnyDomain: false                    # keep false to enforce the domain (recommended)
    clientSecret:
      secretName: ops-ai-google-oauth        # the Secret created in step 2
      secretKey: client-secret
```

<Warning>
  **Hosted-domain enforcement is on by default.** Either set `allowedDomain`, or explicitly
  set `allowAnyDomain: true` to accept any Google account (including consumer `@gmail.com`,
  which carry no `hd` claim). The controller refuses to start a `method=google` cluster
  unless one of these is satisfied (and `clientId` + the secret are present) — a fail-fast
  startup error, not a silent lockout.
</Warning>

## 4. Deploy and verify

Deploy the cluster as usual:

```bash theme={null}
scripts/deploy-cluster.sh --context=<cluster>
```

Verify:

1. Open the dashboard → you should be redirected to `/login`, which now shows **"Sign in with Google"** with the Google glyph.
2. Click it → Google's account chooser appears (scoped to the allowed domain via the `hd` hint) → after consent you land back on `/app/`.
3. A first-time user with no grant lands on the access-request screen; an admin approves them on the **Access Control → Pending Requests** page.

**Lockout insurance:** set `superAdmins` (e.g. `you@xano.com`) — a super-admin resolves to
`admin` even with no `AccessPolicy` CRD present, so admin-gated features (terminal, logs)
work immediately. Every other user is resolved against the cluster's `AccessPolicy` and needs
a matching grant.

## Notes & limitations

* **Session length** is bounded by the Google ID token's expiry (\~1 hour), so users re-authenticate roughly hourly even though `AUTH_COOKIE_MAX_AGE` caps the upper bound at 12h.
* **In-flight logins** (the \~10-minute window between clicking sign-in and completing the callback) are invalidated if the controller restarts — the user simply clicks sign-in again. This is expected for the single-replica controller (the handshake key is ephemeral/in-memory by design).
* **Rollback:** set `auth.method: xano` (with `auth.xano.accountOrigin`) or `auth.enabled: false` and redeploy. The Google code is dormant unless `method=google`.
