feat(auth): add generic OpenID Connect provider for SSO login - #395
Open
dexion wants to merge 1 commit into
Open
feat(auth): add generic OpenID Connect provider for SSO login#395dexion wants to merge 1 commit into
dexion wants to merge 1 commit into
Conversation
dexion
marked this pull request as draft
August 25, 2026 16:09
dexion
force-pushed
the
feat/generic-oidc-sso
branch
from
August 25, 2026 16:50
1f148a1 to
5fdb3f0
Compare
dexion
marked this pull request as ready for review
August 25, 2026 16:51
PentAGI can delegate login to Google or GitHub. A self-hosted installation sitting behind a corporate identity provider — Keycloak, Authentik, Okta, Entra ID, Auth0 — has no way to use it, even though all of them speak plain OpenID Connect and OAuthClient is already an abstraction over a provider. Writing the provider surfaced a smaller problem worth fixing along the way. The authorization request was built for Google and applied to everyone: every provider received response_mode=form_post and response_type=code id_token, and the SameSite mode of the temporary state and nonce cookies was decided by comparing the provider name to the string "google" in two separate places. GitHub tolerates the extra parameters, but a standards-compliant OIDC provider reads response_type=code id_token as a hybrid flow request and refuses it unless the client is allowed to use the implicit flow. Keycloak answers such a request with "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client." Both decisions now belong to the provider: - AuthCodeOptions(nonce) returns the parameters a provider expects. The default is a plain authorization code flow with a nonce; the new WithFormPostCallback() option adds the form_post pair and is applied to the Google client, so its behaviour is unchanged. - CallbackSameSite() reports the SameSite mode the callback needs, replacing the two provider == "google" comparisons in the auth service. The callback handler now clears the cookies with the same mode they were issued with instead of deciding again. The new provider configures itself from the issuer's discovery document, verifies the ID token signature and nonce, and resolves the email from the ID token, falling back to the UserInfo endpoint for providers that do not put an email there. Discovery runs once at startup with a ten second deadline: an unreachable identity provider is logged and skipped rather than blocking the server from starting. Configuration follows the existing providers — OAUTH_OIDC_ISSUER, OAUTH_OIDC_CLIENT_ID, OAUTH_OIDC_CLIENT_SECRET and optional OAUTH_OIDC_SCOPES, defaulting to openid,email,profile. The provider reaches the frontend through the existing /info providers list and renders on the login screen as "Continue with SSO". Accounts are matched by email and created with the regular User role, exactly as with the existing providers. Verified end to end against Keycloak 26 and Authentik 2025.8: both complete the authorization code flow with PKCE, create the user on first login and land an authenticated session.
dexion
force-pushed
the
feat/generic-oidc-sso
branch
from
August 25, 2026 16:59
5fdb3f0 to
db1003c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
PentAGI can delegate login to Google or GitHub. A self-hosted installation sitting behind a corporate identity provider — Keycloak, Authentik, Okta, Entra ID, Auth0 — has no way to use it, even though all of them speak plain OpenID Connect and
OAuthClientis already an abstraction over a provider.Writing the provider surfaced a smaller problem worth fixing along the way. The authorization request was built for Google and applied to everyone:
and the SameSite mode of the temporary state/nonce cookies was decided by comparing the provider name to the string
"google"in two separate places. GitHub tolerates the extra parameters, but a standards-compliant OIDC provider readsresponse_type=code id_tokenas a hybrid flow request and refuses it unless the client may use the implicit flow. Keycloak, with a normal confidential client, answers exactly this:What changed
Both decisions now belong to the provider:
AuthCodeOptions(nonce)returns the parameters a provider expects. The default is a plain authorization code flow with a nonce. The newWithFormPostCallback()option adds theform_postpair and is applied to the Google client, so its behaviour is unchanged.CallbackSameSite()reports the SameSite mode the callback needs —Nonefor a cross-site form POST,Laxotherwise. This replaces bothprovider == "google"comparisons, and the callback handler now clears the cookies with the same mode they were issued with instead of deciding again.The new provider (
oidc) configures itself from the issuer's discovery document, verifies the ID token signature and nonce, and resolves the email from the ID token, falling back to the UserInfo endpoint for providers that do not put an email there. Discovery runs once at startup with a ten second deadline: an unreachable identity provider is logged and skipped rather than blocking the server from starting.Configuration
PUBLIC_URL=https://pentagi.example.com OAUTH_OIDC_ISSUER=https://keycloak.example.com/realms/pentagi OAUTH_OIDC_CLIENT_ID=pentagi OAUTH_OIDC_CLIENT_SECRET=your_client_secret # optional, defaults to openid,email,profile OAUTH_OIDC_SCOPES=openid,email,profileThe client is registered in the identity provider with the standard authorization code flow and the redirect URI
${PUBLIC_URL}/api/v1/auth/login-callback— the same callback the existing providers use. The provider reaches the frontend through the existing/infoproviders list and renders on the login screen as Continue with SSO.Accounts are matched by the
emailclaim and created on first login with the regularUserrole, exactly as with Google and GitHub, so administrators are still promoted explicitly. PentAGI does not filter who may log in, which is noted in the docs together with the advice to bind the client to a group or policy on the identity provider side.Verification
Verified end to end on a local stand — PentAGI built from this branch, a real Keycloak and a real Authentik, browser driven through the full flow.
Keycloak 26.0, confidential client, implicit flow explicitly disabled:
/api/v1/infoadvertisesproviders: ["oidc"]response_type=code,code_challenge_method=S256,scope=openid email profile, with a noncesso-userlands an authenticated session and redirects to the return URISSObadge and the email taken from the ID tokenAuthentik 2025.8, confidential OAuth2 provider created through its API:
http://…/application/o/pentagi/succeeds at startupak-usercreates the account and lands an authenticated sessionResulting users, both provisioned by the provider under test:
The "before" behaviour is reproducible against the same Keycloak client: a request carrying
response_type=code id_token&response_mode=form_postis refused with the implicit-flow error quoted above, whileresponse_type=coderenders the login page.Compatibility
form_post+SameSite=None, GitHub keeps its GET callback. GitHub no longer receives the two parameters it used to ignore.github.com/coreos/go-oidc/v3is already used by the Google provider.Checks
New tests cover the authorization parameters and cookie mode of both provider flavours, OIDC discovery with default and custom scopes, the unreachable-issuer path, and the login screen rendering only the providers the server advertises.