From 0320b569116a2923e11ca7610355a92b6721e17a Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 16 Jun 2026 08:54:20 +0000 Subject: [PATCH] oidc: consolidate cookie helpers and logging --- hscontrol/oidc.go | 48 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/hscontrol/oidc.go b/hscontrol/oidc.go index 706b5820..9fb9096d 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -296,8 +296,6 @@ func (a *AuthProviderOIDC) OIDCCallbackHandler( if userinfo2.Groups != nil { claims.Groups = userinfo2.Groups } - } else { - util.LogErr(err, "could not get userinfo; only using claims from id token") } // The user claims are now updated from the userinfo endpoint so we can verify the user @@ -473,7 +471,7 @@ func (a *AuthProviderOIDC) getOauth2Token( return nil, NewHTTPError(http.StatusForbidden, "invalid code", fmt.Errorf("exchanging code for token: %w", err)) } - return oauth2Token, err + return oauth2Token, nil } // extractIDToken extracts the ID token from the oauth2 token. @@ -651,6 +649,28 @@ func (a *AuthProviderOIDC) createOrUpdateUserFromClaim( // browser do not collide. const registerConfirmCSRFCookie = "headscale_register_confirm" +// setRegisterConfirmCookie writes the per-session register-confirm CSRF +// cookie. Pass the CSRF token and authCacheExpiration seconds to set it; +// pass ("", -1) to clear it after the registration is finalised. +func setRegisterConfirmCookie( + writer http.ResponseWriter, + req *http.Request, + authID types.AuthID, + value string, + maxAge int, +) { + //nolint:gosec // G124: Secure set conditionally via req.TLS; HttpOnly + SameSite already set + http.SetCookie(writer, &http.Cookie{ + Name: registerConfirmCSRFCookie, + Value: value, + Path: "/register/confirm/" + authID.String(), + MaxAge: maxAge, + Secure: req.TLS != nil, + HttpOnly: true, + SameSite: http.SameSiteStrictMode, + }) +} + // renderRegistrationConfirmInterstitial captures the resolved OIDC // identity and node expiry into the cached [types.AuthRequest], sets the CSRF // cookie, and renders the confirmation page that the user must @@ -692,16 +712,7 @@ func (a *AuthProviderOIDC) renderRegistrationConfirmInterstitial( CSRF: csrf, }) - //nolint:gosec // G124: Secure set conditionally via req.TLS; HttpOnly + SameSite already set - http.SetCookie(writer, &http.Cookie{ - Name: registerConfirmCSRFCookie, - Value: csrf, - Path: "/register/confirm/" + authID.String(), - MaxAge: int(authCacheExpiration.Seconds()), - Secure: req.TLS != nil, - HttpOnly: true, - SameSite: http.SameSiteStrictMode, - }) + setRegisterConfirmCookie(writer, req, authID, csrf, int(authCacheExpiration.Seconds())) regData := authReq.RegistrationData() @@ -818,16 +829,7 @@ func (a *AuthProviderOIDC) RegisterConfirmHandler( } // Clear the CSRF cookie now that the registration is final. - //nolint:gosec // G124: Secure set conditionally via req.TLS; HttpOnly + SameSite already set - http.SetCookie(writer, &http.Cookie{ - Name: registerConfirmCSRFCookie, - Value: "", - Path: "/register/confirm/" + authID.String(), - MaxAge: -1, - Secure: req.TLS != nil, - HttpOnly: true, - SameSite: http.SameSiteStrictMode, - }) + setRegisterConfirmCookie(writer, req, authID, "", -1) content := renderRegistrationSuccessTemplate(user, newNode)