🪪 feat: Add Google OAuth Login to Admin Panel#36
Conversation
Renders a "Continue with Google" button on the admin login page alongside the existing OpenID flow, fully delegating to LibreChat's already-wired /api/admin/oauth/google routes (no upstream changes needed). Provider availability is now sourced from LibreChat's public /api/config startup payload — the same endpoint LibreChat's own client uses — instead of the openid-only /admin/oauth/openid/check endpoint. This makes the admin panel pick up github/discord/saml automatically once they're added to the registry, with no per-provider check endpoints required. Detection layer landed as a small src/constants/oauth.ts registry plus a generalized oauthLoginFn / oauthExchangeFn pair parameterized by provider. The exchange wire body to LibreChat is unchanged; provider tagging is admin-panel-internal and drives session.tokenProvider for telemetry. Refresh-token cookie forwarding stays openid-only because LibreChat's /api/auth/refresh only branches on token_provider=openid (and only when OPENID_REUSE_TOKENS is set). Google sessions correctly fall through the default JWT refresh path. ADMIN_SSO_ONLY semantics extended for the multi-provider world: hides the password form whenever any SSO provider is enabled, auto-redirects only when exactly one provider is configured.
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
danny-avila
left a comment
There was a problem hiding this comment.
Thanks for this, @dustinhealy — the feature itself is solid and the backend supports it (the admin Google routes exist in LibreChat: api/server/routes/admin/auth.js /oauth/google and /oauth/google/callback, backed by the googleAdmin passport strategy). Requesting changes purely on mergeability/freshness, not on the approach.
Blocking: branch is conflicting and stale
The PR currently reports mergeable: CONFLICTING / DIRTY against main, and it predates the OpenID/admin-auth refactors that have since landed:
- #46 — body-based OpenID refresh for cross-origin admin panels
- #50 — OpenID redirect origin handling + startup logs
- #59 — guard admin SSO PKCE verifier loss
Those reworked the same files this PR touches, so it needs a real rebase before it can be reviewed/merged. Expect to re-reconcile at least:
src/server/auth.ts— the OAuth init/exchange + PKCE/origin handling has changed substantially; the new Google flow should reuse the currentopenidLoginFn/oauthExchangeFnpatterns (PKCEcode_challenge, origin binding, sessioncodeVerifier) rather than the older shape this branch was built on.src/components/AuthCard.tsxandsrc/routes/login.tsx— SSO button/auto-redirect wiring has moved.src/routeTree.gen.ts— regenerate (don't hand-merge) after addingsrc/routes/auth/google/callback.tsx.src/types/auth.ts,src/types/server.ts,src/constants/oauth.ts, locales — re-check against current definitions.
Requested changes
- Rebase onto latest
mainand resolve conflicts; regeneraterouteTree.gen.tsrather than resolving it by hand. - After rebase, align the Google callback/exchange with the current admin OAuth contract (PKCE + origin binding via request headers; the backend strips
redirect_uri/code_challenge/redirectTo, seepackages/api/src/auth/exchange.ts). - Confirm
tsc --noEmitandvitest runare green post-rebase, and that the e2emock-backendupdates still reflect the current exchange shape.
Happy to re-review as soon as it's rebased and green. Thanks again!
Summary
Adds a "Continue with Google" button to the admin login page alongside the existing OpenID flow. LibreChat's
/api/admin/oauth/googleand/api/admin/oauth/google/callbackroutes were already wired, we just needed to consume them in the admin panel.The provider availability layer was rewritten while in the area: instead of the openid-only
GET /api/admin/oauth/openid/checkprobe, the loader now reads LibreChat's existing/api/configstartup payload (the same endpoint LibreChat's chat client uses) and derives a provider availability map from*LoginEnabledflags. This eliminates the need for per-provider check endpoints, and makes adding future providers (GitHub, Discord, SAML) a one-line registry append.Provider registry:
src/constants/oauth.ts— single registry entry per provider (startPath,callbackRoute,enabledKey, optionallabelKey/imageKeyfor OIDC/SAML branding overrides, optional click-uiLogoname).OAuthProviderunion widened from'openid'to'openid' | 'google'; same shape onSessionData.tokenProvider.Server functions (
src/server/auth.ts):checkOpenIdFn/openIdCheckOptionswithgetStartupConfigFn/startupConfigOptionswhich fetches/api/configonce and returns{ providers, ssoOnly }.openidLoginFnwith provider-parameterizedoauthLoginFn(PKCE generation centralized inbuildOAuthLoginUrl).oauthExchangeFnnow takes{ code, provider }; the upstream wire body to/api/admin/oauth/exchangeis unchanged (still{ code, code_verifier }) —provideris admin-panel-internal and only drives the session'stokenProviderfield.refreshAdminToken) stays openid-only. Verified upstream: LibreChat's/api/auth/refreshonly branches ontoken_provider=openid(and only whenOPENID_REUSE_TOKENS=true); atoken_provider=googlecookie would be ignored. Google sessions correctly fall through the default JWT refresh path.UI (
src/components/AuthCard.tsx,src/routes/login.tsx):Logoglyph; OIDC consumesopenidLabel/openidImageUrlfrom/api/configwhen set by the deployer.ADMIN_SSO_ONLYsemantics extended for the multi-provider world: hides the password form whenever any SSO provider is enabled; auto-redirects only when exactly one provider is configured. Per product requirement,ssoOnlywith multiple providers shows all buttons with no auto-redirect./api/configfetch on the SSR boundary — no client-visible discovery roundtrip.Routes:
src/routes/auth/google/callback.tsxmirroring the openid callback; passesprovider: 'google'tooauthExchangeFn.provider: 'openid'for symmetry.Change Type
Testing
Manual end-to-end (real Google OAuth client):
/oauth/google/callback(LibreChat) and/api/admin/oauth/google/callback(admin panel).GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET/ALLOW_SOCIAL_LOGIN=true.GET /api/configreturnsgoogleLoginEnabled: true.ALLOW_SOCIAL_REGISTRATIONonly applies to the chat path)./login→ Google consent → exchange succeeded → admin dashboard rendered,tokenProvider: 'google'set in the encrypted admin session.Automated:
e2e/login.spec.ts— added "shows Google button alongside OpenID when both are configured" assertion. Existing OpenID/SSO/2FA tests adapted to the new button labels (Continue with OpenID/Continue with Google).e2e/mock-backend.mjs— addedGET /api/confighandler returning bothopenidLoginEnabled: trueandgoogleLoginEnabled: trueso multi-provider rendering exercises in CI.e2e/auth.setup.ts— fixed pre-existingSign In/Sign incasing mismatch that was blocking the suite locally.Test Configuration
devbranch (last syncb632367c8)feat/admin-google-oauth(this branch)Checklist