feat: add generic OpenID Connect (OIDC) auth provider#805
Open
we4sz wants to merge 11 commits into
Open
Conversation
Adds `generic-oidc-auth-provider`, a generic OIDC auth provider that works with any OIDC-compliant IdP (Keycloak, Authentik, Dex, Auth0, etc.) by discovering endpoints from the issuer's /.well-known/openid-configuration. Today the OSS edition only ships Google and GitHub auth providers, so self-hosters who use their own IdP (commonly Keycloak) cannot use it for hub login. This provider closes that gap. It mirrors the existing google-auth-provider, wrapping oauth2-proxy's built-in OIDC provider, and is registered in index.yaml under authProviders. Config: Issuer URL, Client ID/Secret, Email Domains (required); Scopes (default "openid email profile"), Email/Groups claims, and Allow Unverified Email (default true, since many self-hosted IdPs don't set email_verified). Includes unit tests for userinfo discovery/fetch and email_verified parsing.
Implements the group-listing shim endpoint to return the authenticated user's groups (from the configured groups claim / userinfo) instead of 404. This lets Obot enumerate groups for group-scoped MCP registries and group role assignments. Generic OIDC has no "list all groups" endpoint, so this reports the caller's own groups — sufficient for Obot to populate the groups it has seen across logins.
…userinfo Avoids spurious userinfo 401s when the access token is near/just expired by reading the groups claim directly from the JWT (signature already verified upstream by oauth2-proxy). Falls back to the userinfo endpoint for opaque tokens.
list-user-auth-groups now enumerates realm groups via Keycloak's Admin API using the OIDC client's own service-account (client_credentials), so Obot can populate the group picker for group-scoped registries and group role assignments. Requires the client to have a service account with query-groups/view-users; falls back to the caller's own token groups (and to userinfo) for non-Keycloak issuers or missing permissions.
Keycloak (v23+) only returns top-level groups in the groups listing; nested groups (e.g. offices/DEVBORAS) carry subGroupCount but no inline subGroups. Fetch each group's children via the /children endpoint and recurse so the full group tree is enumerable for group-scoped registries/role assignments.
Render the Keycloak group path as "parent / child" in the group picker (e.g. "offices / DEVBORAS") so nested groups read as subgroups in Obot's flat list. ID stays the leaf name to match the token's groups claim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Obot calls TWO group endpoints; the provider only implemented one, under
the wrong name:
- /obot-list-auth-groups (GET): enumerate ALL realm groups for the admin
group picker. The provider never served this path, so the picker fell
back to Obot's DB cache (leaf names only). Now implemented via the
Keycloak Admin API with hierarchical "parent / child" display names.
- /obot-list-user-auth-groups (POST, body=provider user ID): the groups a
SPECIFIC user belongs to, used to sync per-user memberships. The handler
previously returned ALL realm groups here, which would make every user a
member of every group. Now looks up only that user's groups via
/admin/realms/{realm}/users/{id}/groups.
Refactors the shared client_credentials token + path-name rendering into
keycloakAdmin/groupDisplayName/fetchKeycloakGroups helpers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the implicit "/realms/ in issuer" sniff with an explicit OBOT_OIDC_AUTH_PROVIDER_GROUP_ADMIN env var (only supported value: keycloak). This keeps the provider generic OIDC by default — groups come from the token claim for any IdP, exactly like the OSS Google/GitHub providers — and makes the vendor-specific group-admin features (enumerate all groups for the picker + resolve a user's real memberships via the Keycloak Admin API) an unambiguous opt-in. Exposed as an optional config field in tool.gpt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Accept the canonical "keycloak" plus common truthy values (true/1/yes/ on/enabled) for OBOT_OIDC_AUTH_PROVIDER_GROUP_ADMIN, and treat anything unrecognized (blank, typo, unsupported backend) as off — which only disables group enumeration, never blocks login. Obot's provider config form is text-only (no native dropdown), so the field label/description now make the expected value explicit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review hardening of /obot-list-user-auth-groups: - On a Keycloak admin-API lookup failure, return an error (502) instead of an empty 200. Obot syncs a user's group memberships from this endpoint and treats an empty list as "member of no groups", deleting their memberships — so a transient Keycloak failure during a periodic re-check would wipe them. An error makes Obot skip the sync and keep them. - When the Keycloak group admin is not opted in, return 404 (as the OSS Google/GitHub providers do) rather than an empty list. Generic-OIDC groups already reach Obot via the token claim in /obot-get-state. - Remove the now-unreachable JWT bearer-token group fallback: Obot calls this endpoint with only the provider user ID and no Authorization header. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow Keycloak's first/max pagination in fetchKeycloakGroups (page size 100) until a short page is returned, instead of capping every query at max=1000. Realms, group children, and user memberships larger than one page are now fully enumerated rather than silently truncated. Applies to all three admin queries (realm groups, /children, user groups). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
Adds a generic OpenID Connect auth provider (
generic-oidc-auth-provider) so Obot can authenticate users against any OIDC-compliant identity provider — Keycloak, Authentik, Dex, Auth0, Zitadel, etc. — by discovering endpoints from the issuer's/.well-known/openid-configuration.Why
The OSS edition currently ships only Google and GitHub auth providers. Self-hosters who run their own IdP (very commonly Keycloak) have no way to use it for hub login without the enterprise edition. This provider closes that gap with a standards-based OIDC implementation.
How
google-auth-providerexactly, wrapping oauth2-proxy's built-inoidcprovider (OIDCIssuerURL,OIDCEmailClaim,OIDCGroupsClaim).index.yamlunderauthProviders./obot-get-state,/obot-get-user-info,/obot-list-user-auth-groups); user info is fetched from the discovereduserinfo_endpoint.Configuration
*openid email profileemail/groupstrueTesting
go vet ./...clean; unit tests added (pkg/profile) for userinfo discovery/fetch and lenientemail_verifiedparsing (bool/string).email/preferred_username/nameclaims consumed by oauth2-proxy.🤖 Generated with Claude Code