response_mode=form_post for Apple. Pylon handles the provider-specific details; you set two env vars per provider.
Built-in providers
For any other OIDC-compliant IdP, use the generic OIDC adapter — see Generic OIDC.
Set up credentials
Register your app with the provider, then set:<PROVIDER> with GOOGLE, GITHUB, APPLE, etc. (the uppercase form of the table id above).
If a provider’s CLIENT_ID or CLIENT_SECRET is unset, Pylon disables that provider. /api/auth/providers does not list it.
On Pylon Cloud, set these in Settings → Environment per workspace.
Apple
Apple is different. Itsclient_secret is a JWT you sign with your developer ES256 key, not a static string. Pylon creates a fresh JWT on every token exchange, so you do not manage rotation.
response_mode=form_post) instead of the usual GET. Pylon’s callback handler accepts both. The user’s identity comes from the id_token JWT in the token response, not a userinfo endpoint.
Note: Apple sends the user’s name only on the first sign-in for an app. Later logins returnsubanddisplayName.
Microsoft
SetPYLON_OAUTH_MICROSOFT_TENANT for single-tenant apps:
common (any account — work, school, personal).
Twitter / X
Twitter requires PKCE. Pylon generates the verifier and challenge automatically and stores them in the OAuth state record. No extra config is needed. Twitter’s userinfo does not include email by default, so Pylon synthesizes<username>@x.invalid to give account rows a value. Apps that require a real email should reject these accounts.
Generic OIDC
For any OpenID Connect provider — Auth0, Okta, Keycloak, Cognito, Logto, Authentik, Zitadel, your homegrown IdP — set:auth0 here). Pylon fetches <issuer>/.well-known/openid-configuration on first use and caches the discovered endpoints. The discovery doc’s token_endpoint_auth_methods_supported decides whether pylon uses Basic auth (the OIDC default) or a client_secret-in-body POST.
How it works
Profile pictures
Pylon captures the provider’s profile picture (OIDCpicture claim, GitHub
avatar_url) on every OAuth login and stores it on the account link. It
refreshes each sign-in, so rotated provider CDN URLs stay current. It appears
as avatar_url on:
GET /api/auth/meandGET /api/auth/session— for the signed-in user’s own account chip (useSession()exposes it assession.avatarUrl);GET /api/auth/orgs/:id/members— so team rosters can render real photos.
null for password and magic-link users (no provider picture),
so render an initials fallback. Pylon stores only https:// URLs. When a user
has several linked providers, the most recently used one wins.
Two ways to invoke
Browser flow (302 redirect)
GET /api/auth/login/<provider>?callback=<url>&redirect=1 returns a 302 directly to the provider. The callback handler sets a cookie and 302s to the callback URL you supplied:
callback (and the optional error_callback) MUST have an origin listed in manifest.auth.trustedOrigins (or PYLON_TRUSTED_ORIGINS). Loopback (http://localhost, 127.0.0.1, [::1], any port) is always auto-trusted, so pylon dev works without config. See Security plugins → Unified trustedOrigins.
JSON flow (manual)
For SPAs or native apps that want to control the redirect themselves:redirect, the user comes back to your app’s ?code=...&state=xyz URL, and you POST it to the callback:
CSRF protection
Every OAuth start mints a random state token (256 bits, prefixedpylon_) that expires after 10 minutes and is single-use. The callback rejects the request if:
- The state is missing
- The state has expired
- The state was already used (replay)
- The state was minted for a different provider (e.g. Google state on a GitHub callback)
What gets created
On first OAuth sign-in for a given email, Pylon creates aUser row:
(provider, provider_account_id), not by email, so a user who renamed their email keeps their account.
Email verification is implicit. The OAuth provider already verified the email, so Pylon stamps emailVerified immediately. Your User entity should have:
Discovering configured providers
Error responses
Pylon sanitizes provider error messages. It redacts
client_secret, code_verifier, refresh_token, id_token, and access_token values before they reach oauth_error_message redirect URLs or server logs.