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{
"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/callbackCheck the ID token claims
The provider must expose standard OIDC discovery metadata (/.well-known/openid-configuration) and return these claims:
subemailemail_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.
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_FILEand every other backend variable - Dashboard: build and serve the UI these buttons appear on