Skip to content

OAuth / SSO

Postmill supports generic OpenID Connect (OIDC) providers for user authentication. This lets self-hosters integrate with Authentik, Keycloak, Authelia, Dex, Supabase, or any OIDC-compliant identity provider. SAML is not supported — OIDC only.

Admin-managed login providers

Login providers are configured in the separate administration app (a distinct repository — this repo ships no /admin frontend and no login-provider write API), stored platform-wide in the AuthProviderConfig table with client ID/secret encrypted at rest (one config per provider: Google, GitHub, or generic OIDC). For each provider you can set:

  • Enabled — whether the provider's button appears on the login page
  • Client ID / Client Secret — encrypted; never returned to the browser after save
  • OIDC endpoints (generic provider only) — authorization, token, and userinfo URLs
  • Scopes — defaults to openid profile email
  • Display name — the login button label (e.g. "Sign in with Supabase")

This repo only reads AuthProviderConfig. The login page reads GET /auth/providers, which merges the env-derived provider list with the enabled DB configs: email/password (LOCAL) is always present, env-configured providers stay listed, and a DB config wins per provider key (its display name and credentials take precedence over the env ones for that provider only).

Env fallback (the chicken-and-egg): when no enabled DB config exists for a provider, the env vars below are used instead — so a fresh deployment can offer SSO logins before anything is configured in the database. Email/password (LOCAL) login is always available regardless of provider config (subject to DISABLE_REGISTRATION).

OAuth logins also import the user's display name and avatar from the provider; accounts without a provider picture fall back to a Gravatar generated from their email, then to initials.

Generic OIDC setup

1. Create an OIDC application in your provider

Create a new OIDC client in your identity provider. You will need:

  • Client ID and Client Secret — generated by your provider
  • Redirect URIhttps://<your-postmill-domain>/settings
  • Scopes — typically openid profile email

Example redirect URI: https://postmill.example.com/settings

2. Configure the provider

Preferred: in the administration app, enable the Generic OIDC provider and enter the client ID/secret, the three endpoints, scopes, and a display name. No redeploy needed.

Or via env vars (bootstrap fallback) — set these in your .env file or Docker Compose environment; they apply only while no enabled DB config exists for the provider:

yaml
# Required for any self-hosted deployment
IS_GENERAL: 'true'

# Enable generic OIDC
POSTMILL_GENERIC_OAUTH: 'true'

# Provider endpoints
POSTMILL_OAUTH_AUTH_URL: 'https://auth.example.com/application/o/authorize'
POSTMILL_OAUTH_TOKEN_URL: 'https://auth.example.com/application/o/token'
POSTMILL_OAUTH_USERINFO_URL: 'https://auth.example.com/application/o/userinfo'

# Client credentials
POSTMILL_OAUTH_CLIENT_ID: 'your-client-id'
POSTMILL_OAUTH_CLIENT_SECRET: 'your-client-secret'

# Login button appearance
NEXT_PUBLIC_POSTMILL_OAUTH_DISPLAY_NAME: 'Authentik'
NEXT_PUBLIC_POSTMILL_OAUTH_LOGO_URL: 'https://raw.githubusercontent.com/walkxcode/dashboard-icons/master/png/authentik.png'

3. Optional: Override OIDC scopes

The default scopes are openid profile email. Override if your provider requires different or additional scopes:

yaml
POSTMILL_OAUTH_SCOPE: 'openid profile email groups'

4. Redeploy

Restart the application after setting these variables. The login page will show a button with NEXT_PUBLIC_POSTMILL_OAUTH_DISPLAY_NAME and NEXT_PUBLIC_POSTMILL_OAUTH_LOGO_URL.

Required env vars

VariableDefaultPurpose
IS_GENERALMust be true for self-hosted deployments
POSTMILL_GENERIC_OAUTHfalseSet to true to enable generic OIDC login
POSTMILL_OAUTH_AUTH_URLAuthorization endpoint
POSTMILL_OAUTH_TOKEN_URLToken endpoint
POSTMILL_OAUTH_USERINFO_URLUserinfo endpoint
POSTMILL_OAUTH_CLIENT_IDClient ID from your OIDC provider
POSTMILL_OAUTH_CLIENT_SECRETClient secret from your OIDC provider
NEXT_PUBLIC_POSTMILL_OAUTH_DISPLAY_NAMELogin button label
NEXT_PUBLIC_POSTMILL_OAUTH_LOGO_URLLogin button icon URL

Optional env vars

VariableDefaultPurpose
POSTMILL_OAUTH_SCOPEopenid profile emailOIDC scopes to request

Provider-specific examples

Authentik

yaml
IS_GENERAL: 'true'
POSTMILL_GENERIC_OAUTH: 'true'
POSTMILL_OAUTH_AUTH_URL: 'https://authentik.example.com/application/o/authorize/'
POSTMILL_OAUTH_TOKEN_URL: 'https://authentik.example.com/application/o/token/'
POSTMILL_OAUTH_USERINFO_URL: 'https://authentik.example.com/application/o/userinfo/'
POSTMILL_OAUTH_CLIENT_ID: '<your-client-id>'
POSTMILL_OAUTH_CLIENT_SECRET: '<your-client-secret>'
NEXT_PUBLIC_POSTMILL_OAUTH_DISPLAY_NAME: 'Authentik'
NEXT_PUBLIC_POSTMILL_OAUTH_LOGO_URL: 'https://raw.githubusercontent.com/walkxcode/dashboard-icons/master/png/authentik.png'

In Authentik:

  1. Create a new Application with type OAuth2/OpenID
  2. Set the redirect URI to https://<your-domain>/settings
  3. Under the Provider settings, note the client ID and secret
  4. Under Advanced protocol settings, ensure the signing key and subject mode are set

Keycloak

yaml
POSTMILL_OAUTH_AUTH_URL: 'https://keycloak.example.com/realms/<realm>/protocol/openid-connect/auth'
POSTMILL_OAUTH_TOKEN_URL: 'https://keycloak.example.com/realms/<realm>/protocol/openid-connect/token'
POSTMILL_OAUTH_USERINFO_URL: 'https://keycloak.example.com/realms/<realm>/protocol/openid-connect/userinfo'

Authelia

yaml
POSTMILL_OAUTH_AUTH_URL: 'https://auth.example.com/api/oidc/authorization'
POSTMILL_OAUTH_TOKEN_URL: 'https://auth.example.com/api/oidc/token'
POSTMILL_OAUTH_USERINFO_URL: 'https://auth.example.com/api/oidc/userinfo'

Dex

yaml
POSTMILL_OAUTH_AUTH_URL: 'https://dex.example.com/auth'
POSTMILL_OAUTH_TOKEN_URL: 'https://dex.example.com/token'
POSTMILL_OAUTH_USERINFO_URL: 'https://dex.example.com/userinfo'

GitHub login

In addition to generic OIDC, Postmill supports GitHub OAuth as a login provider. Configure it in the administration app (preferred), or via the bootstrap env vars:

yaml
GITHUB_CLIENT_ID: '<your-github-oauth-app-client-id>'
GITHUB_CLIENT_SECRET: '<your-github-oauth-app-client-secret>'

Create a GitHub OAuth App at Settings -> Developer settings -> OAuth Apps with the callback URL set to https://<your-domain>/settings (the adapter requests the user:email scope).

Google login

Google OAuth login is also supported. Configure it in the administration app (preferred), or use YOUTUBE_CLIENT_ID / YOUTUBE_CLIENT_SECRET as the bootstrap env credentials. These variables are dual-use: they are also the platform channel app for YouTube posting — setting them gives every organization one-click YouTube Connect (per-org Google/YouTube/GMB apps via Settings → Channels still win for channel posting). Setup details: Platform Channel Apps → Google — YouTube.

Facebook login

Facebook OAuth login reuses the platform channel app — there is no separate login credential set. Set the flag and the channel credentials:

yaml
FACEBOOK_SSO_ENABLED: 'true'
FACEBOOK_APP_ID: '<your-meta-app-id>'
FACEBOOK_APP_SECRET: '<your-meta-app-secret>'

The Meta app needs the consumer Facebook Login product for the login button to work — the "Facebook Login for Business" Configuration-ID flow is Pages-only and cannot log users in. Login requests only public_profile,email; if the user denies the email permission, Postmill mints a synthetic address (fb_<id>@facebook.login.postmill.local) that is skipped by newsletter/welcome emails. Callback to register: https://<your-domain>/integrations/social/facebook.

X login

X OAuth login also reuses the platform channel app, but over a separate OAuth 2.0 + PKCE flow (channel posting is OAuth 1.0a — enable both in the app's User authentication settings):

yaml
X_SSO_ENABLED: 'true'
X_API_KEY: '<your-api-key>'
X_API_SECRET: '<your-api-secret>'

Login requests only the users.read scope, which returns no email address — every X SSO account gets a synthetic address (x_<id>@x.login.postmill.local), skipped by newsletter/welcome emails. Callback to register: https://<your-domain>/integrations/social/x.

LinkedIn login

LinkedIn OAuth login reuses the platform channel app via LinkedIn's OpenID Connect flow (openid profile email against /v2/userinfo):

yaml
LINKEDIN_SSO_ENABLED: 'true'
LINKEDIN_CLIENT_ID: '<your-client-id>'
LINKEDIN_CLIENT_SECRET: '<your-client-secret>'

The app needs the Sign In with LinkedIn using OpenID Connect product enabled (the channel flow already requires it). Callback to register: https://<your-domain>/integrations/social/linkedin.

No account linking (all SSO providers): logins match users by (provider, provider-user-id) only. The same person signing in via two providers — even with the same real email — gets separate User + Org accounts; identities are never merged.

Login callback routes

OAuth callbacks do not land on a single /auth/callback endpoint. Each login provider redirects back to its own path carrying state=login, and the frontend proxy (apps/frontend/src/proxy.ts) forwards it to /auth?…&provider=<NAME>:

ProviderCallback path to register
Googlehttps://<your-domain>/integrations/social/youtube?state=login (register the path without the query)
Facebookhttps://<your-domain>/integrations/social/facebook
Xhttps://<your-domain>/integrations/social/x
LinkedInhttps://<your-domain>/integrations/social/linkedin
GitHubhttps://<your-domain>/settings
Generic OIDChttps://<your-domain>/settings

The dual-use paths are shared with channel OAuth: channel-connect callbacks hit the same /integrations/social/<provider> paths without state=login and pass straight through to the channel flow. Ensure your reverse proxy passes these paths (and /settings) through to the frontend without modification.

Disable registration

To restrict who can sign up, set DISABLE_REGISTRATION=true. New users cannot self-register via LOCAL (email/password) or any OAuth provider (Google, GitHub, Facebook, X, LinkedIn) — with two exceptions: the very first user of an empty instance can always register (so the operator can bootstrap), and GENERIC OIDC sign-ins are exempt (users authenticated by your identity provider still provision). Existing users can still log in. Create new users through the Teams settings page.

  • Configuration — full env var reference including IS_GENERAL, GitHub, and SSO vars
  • Platform Channel Apps — the shared channel-OAuth apps the Facebook/X/LinkedIn/Google logins dual-use
  • Security — OAuth 2.0 / PKCE hardening, open-redirect allowlisting

Verified against v1.0.0

The AI-native social media management platform — postmill.ai