Skip to content

Authentication

Add Google or any OIDC identity provider to your Pug instance with PUG_CONFIG_FILE.

Updated View as Markdown

Password and magic-link sign-in work out of the box - no configuration beyond a working email provider. This page is about external sign-in providers: Google, Okta, Keycloak, Entra ID, Auth0, or anything else that speaks OpenID Connect.

There is no Google-specific integration. Every external provider, Google included, goes through the same generic OIDC path.

The config file

Providers are the one setting that can’t be a flat environment variable (an instance can have several, each with its own fields), so they live in a versioned JSON file that the server reads at startup. Point PUG_CONFIG_FILE at it:

services:
  server:
    environment:
      PUG_CONFIG_FILE: /etc/pug/config.json
    volumes:
      - ./config.json:/etc/pug/config.json:ro
config.jsonjson
{
  "version": 1,
  "auth": {
    "providers": [
      {
        "id": "google",
        "type": "oidc",
        "displayName": "Google",
        "issuerUrl": "https://accounts.google.com",
        "clientId": "YOUR_GOOGLE_CLIENT_ID",
        "clientSecret": "YOUR_GOOGLE_CLIENT_SECRET",
        "scopes": ["openid", "profile", "email"]
      },
      {
        "id": "company_sso",
        "type": "oidc",
        "displayName": "Company SSO",
        "issuerUrl": "https://login.example.com/realms/main",
        "clientId": "pug",
        "scopes": ["openid", "profile", "email"]
      }
    ]
  }
}
Field Required? Description
id Yes Unique lowercase identifier (^[a-z][a-z0-9_-]*$, max 63 chars). Permanent - see below.
type Yes oidc is the only supported value.
displayName Yes 1-64 characters, rendered on the sign-in button. Change this, not id, when you want different wording.
clientId Yes OAuth client ID from the provider.
clientSecret No Only for confidential clients. Stays server-side - see Secrets never reach the browser.
issuerUrl Yes The OIDC issuer. HTTPS, no credentials, query, or fragment. http://localhost is allowed for development.
scopes No Defaults to ["openid", "profile", "email"]. openid and email are always required.

The file is validated strictly at startup, and any problem stops the server: an unknown field, an unsupported version, a duplicate id, a missing required value, or a non-HTTPS remote issuer. That’s deliberate - a half-applied config that boots “successfully” with sign-in quietly broken is worse than a crash your orchestrator will retry and surface.

Set up a provider

Create an OIDC client at the identity provider

Enable the Authorization Code flow with PKCE (S256). Register exactly this redirect URI, using your dashboard’s origin:

https://YOUR_PUG_DASHBOARD/oauth/callback

Check the ID token claims

The provider must expose standard OIDC discovery metadata (/.well-known/openid-configuration) and return these claims:

  • sub
  • email
  • email_verified: true

name and picture are optional. Pug verifies the ID token’s signature, issuer, audience, expiry, nonce, and verified-email claim on the server.

Add it to config.json and restart

Add an entry to auth.providers and restart the server. It logs the configured provider IDs at startup - and warns when the list is empty, so a half-finished rollout doesn’t look like a healthy boot.

Discovery runs on the first sign-in, not at startup. An issuer that’s unreachable when the server boots doesn’t stop it from starting, and starts working on its own once it recovers; sign-ins against it fail with OAUTH_PROVIDER_UNAVAILABLE in the meantime.

Redirect URI rules

Pug requires the redirect_uri to use the path /oauth/callback with no query or fragment, over HTTPS except on localhost / 127.0.0.1 / ::1. When the browser sends an Origin header it must be same-origin with that URI. The host is pinned by the redirect URI you register at the identity provider, not by Pug.

Secrets never reach the browser

The dashboard reads only the non-secret fields - id, type, display name, client ID, issuer, scopes. clientSecret is never returned by the API, and the authorization-code exchange happens server-side, so a confidential client stays confidential even though the flow starts in a browser.

Set clientSecret only when your provider requires a confidential client. Public clients (a typical Keycloak SPA client, for instance) omit it and rely on PKCE alone.

Google

Configure Google like any other OIDC provider: issuer https://accounts.google.com, plus its client ID and secret from the Google Cloud console. Register the same /oauth/callback redirect URI on the Google OAuth client.

Name the entry "id": "google". That’s the value existing Google accounts were already stored under, so they resolve directly with no migration.

Accounts and linking

A verified email from an identity provider links to an existing Pug account with the same address - including one that already has a password or uses magic links. Linking doesn’t clear an existing password, so a user can keep signing in either way, and the sign-in marks that account’s email verified.

An address that isn’t already a Pug account gets one. The sign-in provisions a customer, an organization with that user as its admin, and a default project.

Not in this release

Pug does not request or retain an external refresh token, map provider groups to Pug roles, or initiate provider-wide logout. Signing out of Pug ends the Pug session only.

Further reading

  • Configuration: PUG_CONFIG_FILE and every other backend variable
  • Dashboard: build and serve the UI these buttons appear on
Navigation

Type to search...

↑↓ navigate↵ selectEsc close