From 47fc9df7162011c72b985ea03b7829cf14f75818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Mon, 6 Apr 2026 21:05:17 +0200 Subject: [PATCH] Handle OAuth2 error responses in connector callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a provider returns an error (e.g. user denies consent), the callback now logs the error with provider name and redirects to the base URL with error and error_description query parameters instead of falling through to the code exchange. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- pkg/server/api/console/v1/resolver.go | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pkg/server/api/console/v1/resolver.go b/pkg/server/api/console/v1/resolver.go index 308813b5e..b2df5f032 100644 --- a/pkg/server/api/console/v1/resolver.go +++ b/pkg/server/api/console/v1/resolver.go @@ -171,6 +171,11 @@ func handleConnectorComplete( return func(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() + if oauthErr := query.Get("error"); oauthErr != "" { + handleConnectorOAuth2Error(w, r, logger, baseURL, safeRedirect, query) + return + } + stateToken := query.Get("state") if stateToken == "" { httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("missing state parameter")) @@ -251,6 +256,41 @@ func handleConnectorComplete( } } +func handleConnectorOAuth2Error( + w http.ResponseWriter, + r *http.Request, + logger *log.Logger, + baseURL *baseurl.BaseURL, + safeRedirect *saferedirect.SafeRedirect, + query url.Values, +) { + oauthErr := query.Get("error") + oauthErrDesc := query.Get("error_description") + + provider := "unknown" + if stateToken := query.Get("state"); stateToken != "" { + if p, err := connector.ExtractProviderFromState(stateToken); err == nil { + provider = p + } + } + + logger.WarnCtx(r.Context(), "OAuth2 callback returned error", + log.String("provider", provider), + log.String("error", oauthErr), + log.String("error_description", oauthErrDesc), + ) + + parsedURL, _ := url.Parse(baseURL.String()) + q := parsedURL.Query() + q.Set("error", oauthErr) + if oauthErrDesc != "" { + q.Set("error_description", oauthErrDesc) + } + parsedURL.RawQuery = q.Encode() + + safeRedirect.Redirect(w, r, parsedURL.String(), "/", http.StatusSeeOther) +} + func (r *Resolver) ProboService(ctx context.Context, tenantID gid.TenantID) *probo.TenantService { return r.probo.WithTenant(tenantID) }