From 4151fddf3a3d6fea3c904a55dedecdc06043506e Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Thu, 7 Nov 2024 12:55:39 +0000 Subject: [PATCH] fixup! Downgrade gorilla sessions preventing the publishing by traefik hub temporarily. --- helpers.go | 12 ++++++------ main.go | 25 +++++++++++++------------ main_test.go | 35 ++++++++++++++++++----------------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/helpers.go b/helpers.go index 699a75a..216a593 100644 --- a/helpers.go +++ b/helpers.go @@ -90,7 +90,7 @@ func (t *TraefikOidc) getNewTokenWithRefreshToken(refreshToken string) (*TokenRe } // handleExpiredToken handles the case when a token has expired -func (t *TraefikOidc) handleExpiredToken(rw http.ResponseWriter, req *http.Request, session *sessions.Session) { +func (t *TraefikOidc) handleExpiredToken(rw http.ResponseWriter, req *http.Request, session *sessions.Session, redirectURL string) { // Clear the existing session session.Options.MaxAge = -1 for k := range session.Values { @@ -111,11 +111,11 @@ func (t *TraefikOidc) handleExpiredToken(rw http.ResponseWriter, req *http.Reque } // Initiate a new authentication flow - t.initiateAuthenticationFunc(rw, req, session, t.redirectURL) + t.initiateAuthenticationFunc(rw, req, session, redirectURL) } // handleCallback handles the callback from the OIDC provider -func (t *TraefikOidc) handleCallback(rw http.ResponseWriter, req *http.Request) { +func (t *TraefikOidc) handleCallback(rw http.ResponseWriter, req *http.Request, redirectURL string) { session, err := t.store.Get(req, cookieName) if err != nil { t.logger.Errorf("Session error: %v", err) @@ -160,7 +160,7 @@ func (t *TraefikOidc) handleCallback(rw http.ResponseWriter, req *http.Request) return } - tokenResponse, err := t.exchangeCodeForTokenFunc(code) + tokenResponse, err := t.exchangeCodeForTokenFunc(code, redirectURL) if err != nil { t.logger.Errorf("Failed to exchange code for token: %v", err) http.Error(rw, "Authentication failed", http.StatusInternalServerError) @@ -346,9 +346,9 @@ func (tc *TokenCache) Cleanup() { } // exchangeCodeForToken exchanges the authorization code for tokens -func (t *TraefikOidc) exchangeCodeForToken(code string) (*TokenResponse, error) { +func (t *TraefikOidc) exchangeCodeForToken(code string, redirectURL string) (*TokenResponse, error) { ctx := context.Background() - tokenResponse, err := t.exchangeTokens(ctx, "authorization_code", code, t.redirectURL) + tokenResponse, err := t.exchangeTokens(ctx, "authorization_code", code, redirectURL) if err != nil { return nil, fmt.Errorf("failed to exchange code for token: %w", err) } diff --git a/main.go b/main.go index a1ad919..7be689a 100644 --- a/main.go +++ b/main.go @@ -53,14 +53,13 @@ type TraefikOidc struct { tokenCache *TokenCache httpClient *http.Client logger *Logger - redirectURL string tokenVerifier TokenVerifier jwtVerifier JWTVerifier excludedURLs map[string]struct{} allowedUserDomains map[string]struct{} allowedRolesAndGroups map[string]struct{} initiateAuthenticationFunc func(rw http.ResponseWriter, req *http.Request, session *sessions.Session, redirectURL string) - exchangeCodeForTokenFunc func(code string) (*TokenResponse, error) + exchangeCodeForTokenFunc func(code string, redirectURL string) (*TokenResponse, error) extractClaimsFunc func(tokenString string) (map[string]interface{}, error) initComplete chan struct{} endSessionURL string @@ -382,10 +381,12 @@ func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) { defaultSessionOptions.Secure = t.scheme == "https" host := t.determineHost(req) + redirectURL := buildFullURL(t.scheme, host, t.redirURLPath) + // Build the redirect URL if not already set - if t.redirectURL == "" { - t.redirectURL = buildFullURL(t.scheme, host, t.redirURLPath) - t.logger.Debugf("Redirect URL updated to: %s", t.redirectURL) + if redirectURL == "" { + redirectURL = buildFullURL(t.scheme, host, t.redirURLPath) + t.logger.Debugf("Redirect URL updated to: %s", redirectURL) } // Get the session @@ -406,7 +407,7 @@ func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Handle callback URL if req.URL.Path == t.redirURLPath { - t.handleCallback(rw, req) + t.handleCallback(rw, req, redirectURL) return } @@ -414,19 +415,19 @@ func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) { authenticated, needsRefresh, expired := t.isUserAuthenticated(session) if expired { - t.handleExpiredToken(rw, req, session) + t.handleExpiredToken(rw, req, session, redirectURL) return } if !authenticated { - t.defaultInitiateAuthentication(rw, req, session, t.redirectURL) + t.defaultInitiateAuthentication(rw, req, session, redirectURL) return } if needsRefresh { refreshed := t.refreshToken(rw, req, session) if !refreshed { - t.handleExpiredToken(rw, req, session) + t.handleExpiredToken(rw, req, session, redirectURL) return } } @@ -435,21 +436,21 @@ func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) { idToken, ok := session.Values["id_token"].(string) if !ok || idToken == "" { t.logger.Errorf("No id_token found in session") - t.defaultInitiateAuthentication(rw, req, session, t.redirectURL) + t.defaultInitiateAuthentication(rw, req, session, redirectURL) return } claims, err := extractClaims(idToken) if err != nil { t.logger.Errorf("Failed to extract claims: %v", err) - t.defaultInitiateAuthentication(rw, req, session, t.redirectURL) + t.defaultInitiateAuthentication(rw, req, session, redirectURL) return } email, _ := claims["email"].(string) if email == "" { t.logger.Debugf("No email found in token claims") - t.defaultInitiateAuthentication(rw, req, session, t.redirectURL) + t.defaultInitiateAuthentication(rw, req, session, redirectURL) return } diff --git a/main_test.go b/main_test.go index e91728a..530d28f 100644 --- a/main_test.go +++ b/main_test.go @@ -104,7 +104,7 @@ func (ts *TestSuite) Setup() { } // Helper functions used by TraefikOidc -func (ts *TestSuite) exchangeCodeForTokenFunc(code string) (*TokenResponse, error) { +func (ts *TestSuite) exchangeCodeForTokenFunc(code string, redirectURL string) (*TokenResponse, error) { return &TokenResponse{ IDToken: ts.token, RefreshToken: "test-refresh-token", @@ -452,10 +452,12 @@ func TestHandleCallback(t *testing.T) { ts := &TestSuite{t: t} ts.Setup() + redirectURL := "http://example.com/" + tests := []struct { name string queryParams string - exchangeCodeForToken func(code string) (*TokenResponse, error) + exchangeCodeForToken func(code string, redirectURL string) (*TokenResponse, error) extractClaimsFunc func(tokenString string) (map[string]interface{}, error) sessionSetupFunc func(session *sessions.Session) expectedStatus int @@ -463,7 +465,7 @@ func TestHandleCallback(t *testing.T) { { name: "Success", queryParams: "?code=test-code&state=test-csrf-token", - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { return &TokenResponse{ IDToken: ts.token, RefreshToken: "test-refresh-token", @@ -493,7 +495,7 @@ func TestHandleCallback(t *testing.T) { { name: "Exchange Code Error", queryParams: "?code=test-code&state=test-csrf-token", - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { return nil, fmt.Errorf("exchange code error") }, sessionSetupFunc: func(session *sessions.Session) { @@ -505,7 +507,7 @@ func TestHandleCallback(t *testing.T) { { name: "Missing ID Token", queryParams: "?code=test-code&state=test-csrf-token", - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { return &TokenResponse{}, nil }, sessionSetupFunc: func(session *sessions.Session) { @@ -517,7 +519,7 @@ func TestHandleCallback(t *testing.T) { { name: "Disallowed Email", queryParams: "?code=test-code&state=test-csrf-token", - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { return &TokenResponse{ IDToken: ts.token, RefreshToken: "test-refresh-token", @@ -538,7 +540,7 @@ func TestHandleCallback(t *testing.T) { { name: "Invalid State Parameter", queryParams: "?code=test-code&state=invalid-csrf-token", - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { return &TokenResponse{ IDToken: ts.token, RefreshToken: "test-refresh-token", @@ -559,7 +561,7 @@ func TestHandleCallback(t *testing.T) { { name: "Nonce Mismatch", queryParams: "?code=test-code&state=test-csrf-token", - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { return &TokenResponse{ IDToken: ts.token, RefreshToken: "test-refresh-token", @@ -580,7 +582,7 @@ func TestHandleCallback(t *testing.T) { { name: "Missing Nonce in Claims", queryParams: "?code=test-code&state=test-csrf-token", - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { return &TokenResponse{ IDToken: ts.token, RefreshToken: "test-refresh-token", @@ -633,7 +635,7 @@ func TestHandleCallback(t *testing.T) { rr = httptest.NewRecorder() // Call handleCallback - tOidc.handleCallback(rr, req) + tOidc.handleCallback(rr, req, redirectURL) // Check response if rr.Code != tc.expectedStatus { @@ -688,7 +690,7 @@ func TestOIDCHandler(t *testing.T) { tests := []struct { name string queryParams string - exchangeCodeForToken func(code string) (*TokenResponse, error) + exchangeCodeForToken func(code string, redirectURL string) (*TokenResponse, error) extractClaimsFunc func(tokenString string) (map[string]interface{}, error) sessionSetupFunc func(session *sessions.Session) expectedStatus int @@ -704,7 +706,7 @@ func TestOIDCHandler(t *testing.T) { session.Values["csrf"] = "test-csrf-token" session.Values["nonce"] = "test-nonce" }, - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { // Simulate token exchange return &TokenResponse{ IDToken: ts.token, @@ -728,7 +730,7 @@ func TestOIDCHandler(t *testing.T) { session.Values["csrf"] = "test-csrf-token" session.Values["nonce"] = "test-nonce" }, - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { // Simulate token exchange return &TokenResponse{ IDToken: ts.token, @@ -751,7 +753,7 @@ func TestOIDCHandler(t *testing.T) { session.Values["csrf"] = "test-csrf-token" session.Values["nonce"] = "test-nonce" }, - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { // Simulate token exchange return &TokenResponse{ IDToken: ts.token, @@ -775,7 +777,7 @@ func TestOIDCHandler(t *testing.T) { session.Values["csrf"] = "test-csrf-token" session.Values["nonce"] = "test-nonce" }, - exchangeCodeForToken: func(code string) (*TokenResponse, error) { + exchangeCodeForToken: func(code string, redirectURL string) (*TokenResponse, error) { // Simulate token exchange return &TokenResponse{ IDToken: ts.token, @@ -1183,7 +1185,6 @@ func TestHandleExpiredToken(t *testing.T) { tOidc := &TraefikOidc{ store: sessions.NewCookieStore([]byte("test-secret-key")), logger: NewLogger("info"), - redirectURL: "http://example.com/callback", tokenVerifier: ts.tOidc.tokenVerifier, jwtVerifier: ts.tOidc.jwtVerifier, initComplete: make(chan struct{}), @@ -1204,7 +1205,7 @@ func TestHandleExpiredToken(t *testing.T) { tc.setupSession(session) // Handle expired token - tOidc.handleExpiredToken(rr, req, session) + tOidc.handleExpiredToken(rr, req, session, tc.expectedPath) // Verify session is cleaned if len(session.Values) != 3 { // Should only have csrf, incoming_path, and nonce