mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-15 04:18:03 +00:00
fixup! Downgrade gorilla sessions preventing the publishing by traefik hub temporarily.
This commit is contained in:
+6
-6
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user