Skip to content

Add instance-managed OAuth providers - #339

Open
basnijholt wants to merge 1 commit into
Infisical:mainfrom
basnijholt:feat/managed-oauth-providers
Open

Add instance-managed OAuth providers#339
basnijholt wants to merge 1 commit into
Infisical:mainfrom
basnijholt:feat/managed-oauth-providers

Conversation

@basnijholt

Copy link
Copy Markdown

Agent Vault currently requires every vault user to create and enter a separate OAuth application, even when one deployment wants to offer the same provider to everyone.

This adds operator-managed OAuth providers, starting with Google. The server owns the application credentials and provider endpoints, while each vault receives its own separately encrypted access and refresh tokens. The UI exposes configured providers without revealing the shared client secret, and custom per-vault providers remain available.

Validation:

  • go test ./...
  • go vet ./...
  • frontend production build

@infisical-cla-app

Copy link
Copy Markdown

📝 Contributor License Agreement required

Before this PR can merge, every contributor must sign the Infisical CLA.
Signing is quick: sign in with GitHub, review the CLA, and accept.

👉 Sign the CLA

Still needs to sign:

Once everyone has signed, the check updates automatically — no need to close and reopen the PR.

@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds operator-managed OAuth providers to Agent Vault, starting with Google. The instance operator sets a shared client ID and secret via environment variables; each vault user then authorizes their own Google account and receives separately encrypted tokens, without ever seeing or supplying the shared credentials.

  • internal/oauth/managed.go loads provider config from env at startup, clears the secret from the process environment after reading it, and fails closed for partial config.
  • internal/server/managed_oauth.go and handle_oauth.go override the full provider config (URLs, client ID, secret, auth method) server-side before any further validation, so a user-supplied authorization_url or token_url in the request body is harmless when a managed provider is selected.
  • handle_spa.go exposes the list of configured provider IDs (no secrets) on the public /v1/status endpoint, which the frontend uses to render "Google (managed)" in the provider picker.

Confidence Score: 4/5

The core security invariant — that managed provider URLs and credentials are always overridden server-side before any token exchange — is correctly implemented and tested. The two findings are hardening gaps rather than exploitable flaws in the happy path.

The managed provider override in applyManagedOAuthProvider is correct and the test suite covers the key security properties (URL override, unknown provider rejection). Two gaps exist: the DisablePKCE flag is not reset for managed providers, and only the client ID is whitespace-trimmed at load time. Neither causes data loss or token exfiltration, but both are worth addressing before shipping to operators.

Files Needing Attention: internal/server/managed_oauth.go (DisablePKCE not enforced) and internal/oauth/managed.go (asymmetric trimming of client secret)

Important Files Changed

Filename Overview
internal/server/managed_oauth.go New file wiring managed provider lookup into connect requests. applyManagedOAuthProvider does not reset req.DisablePKCE, letting callers weaken the PKCE requirement for managed flows.
internal/oauth/managed.go New file loading managed OAuth provider config from env. Minor asymmetry: client ID is whitespace-trimmed but client secret is not.
internal/server/handle_oauth.go Managed provider override applied before all existing validation; URL validation, secret handling, and token exchange logic unchanged and correct.
internal/server/handle_spa.go Adds managed_oauth_providers list to the public /v1/status response; returns sorted IDs only, no secrets exposed.
internal/server/handle_credentials.go enrichOAuthEntry now identifies managed providers by matching stored URLs and client ID; correctly avoids exposing client secret.
cmd/server.go configureManagedOAuthProviders called in both server startup paths (direct and detached child) before serving requests.
web/src/pages/vault/CredentialsTab.tsx UI correctly hides client ID/secret fields and shows an info banner for managed providers; client-side isManagedProvider check is cosmetic only — backend enforces the real provider values.
web/src/pages/vault/shared.tsx useVaultParams now threads status.managed_oauth_providers from route context down to vault pages.
web/src/lib/oauthProviders.ts Adds read-only and modify-level Google scopes for Calendar, Drive, Gmail, Docs, and Slides — straightforward expansion of the scope preset list.
internal/server/server.go Adds managedOAuthProviders map field to Server struct; written once before startup, then only read concurrently — safe without a mutex.

Reviews (1): Last reviewed commit: "feat: add managed OAuth providers" | Re-trigger Greptile

Comment on lines +41 to +46
req.AuthorizationURL = provider.AuthorizationURL
req.TokenURL = provider.TokenURL
req.ClientID = provider.ClientID
req.ClientSecret = provider.ClientSecret
req.TokenAuthMethod = provider.TokenAuthMethod
return nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The applyManagedOAuthProvider override leaves req.DisablePKCE untouched, meaning any API caller can send "disable_pkce": true and weaken the PKCE requirement for the managed Google flow. Since PKCE is a security control against authorization-code interception, its enablement should be dictated by the operator-defined provider config, not the caller.

Suggested change
req.AuthorizationURL = provider.AuthorizationURL
req.TokenURL = provider.TokenURL
req.ClientID = provider.ClientID
req.ClientSecret = provider.ClientSecret
req.TokenAuthMethod = provider.TokenAuthMethod
return nil
req.AuthorizationURL = provider.AuthorizationURL
req.TokenURL = provider.TokenURL
req.ClientID = provider.ClientID
req.ClientSecret = provider.ClientSecret
req.TokenAuthMethod = provider.TokenAuthMethod
req.DisablePKCE = false // managed providers always use PKCE
return nil

Comment thread internal/oauth/managed.go
Comment on lines +32 to +33
googleClientID := strings.TrimSpace(os.Getenv(GoogleOAuthClientIDEnv))
googleClientSecret := os.Getenv(GoogleOAuthClientSecretEnv)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 googleClientID is whitespace-trimmed (to tolerate copy-paste errors) but googleClientSecret is not. An operator who accidentally copies the secret with a trailing newline or space will get a secret stored verbatim — causing token-exchange failures that are hard to diagnose. Trim both for consistency.

Suggested change
googleClientID := strings.TrimSpace(os.Getenv(GoogleOAuthClientIDEnv))
googleClientSecret := os.Getenv(GoogleOAuthClientSecretEnv)
googleClientID := strings.TrimSpace(os.Getenv(GoogleOAuthClientIDEnv))
googleClientSecret := strings.TrimSpace(os.Getenv(GoogleOAuthClientSecretEnv))

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant