fixup! Downgrade gorilla sessions preventing the publishing by traefik hub temporarily.

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