mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-17 04:37:48 +00:00
fixup! fixup! Downgrade gorilla sessions preventing the publishing by traefik hub temporarily.
This commit is contained in:
+60
-78
@@ -27,14 +27,6 @@ func generateNonce() (string, error) {
|
|||||||
return base64.URLEncoding.EncodeToString(nonceBytes), nil
|
return base64.URLEncoding.EncodeToString(nonceBytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildFullURL constructs a full URL from scheme, host, and path
|
|
||||||
func buildFullURL(scheme, host, path string) string {
|
|
||||||
if scheme == "" {
|
|
||||||
scheme = "http"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s://%s%s", scheme, host, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// exchangeTokens exchanges a code or refresh token for tokens
|
// exchangeTokens exchanges a code or refresh token for tokens
|
||||||
func (t *TraefikOidc) exchangeTokens(ctx context.Context, grantType, codeOrToken, redirectURL string) (*TokenResponse, error) {
|
func (t *TraefikOidc) exchangeTokens(ctx context.Context, grantType, codeOrToken, redirectURL string) (*TokenResponse, error) {
|
||||||
data := url.Values{
|
data := url.Values{
|
||||||
@@ -97,76 +89,6 @@ func (t *TraefikOidc) getNewTokenWithRefreshToken(refreshToken string) (*TokenRe
|
|||||||
return tokenResponse, nil
|
return tokenResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleLogout handles the logout process
|
|
||||||
func (t *TraefikOidc) handleLogout(w http.ResponseWriter, r *http.Request) {
|
|
||||||
session, err := t.store.Get(r, cookieName)
|
|
||||||
if err != nil {
|
|
||||||
handleError(w, fmt.Sprintf("Error getting session: %v", err), http.StatusInternalServerError, t.logger)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get tokens from session
|
|
||||||
idToken, _ := session.Values["id_token"].(string)
|
|
||||||
refreshToken, _ := session.Values["refresh_token"].(string)
|
|
||||||
accessToken, _ := session.Values["access_token"].(string)
|
|
||||||
|
|
||||||
// Revoke tokens if they exist
|
|
||||||
if refreshToken != "" {
|
|
||||||
t.RevokeTokenWithProvider(refreshToken, "refresh_token")
|
|
||||||
t.RevokeToken(refreshToken)
|
|
||||||
}
|
|
||||||
if accessToken != "" {
|
|
||||||
t.RevokeTokenWithProvider(accessToken, "access_token")
|
|
||||||
t.RevokeToken(accessToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear session
|
|
||||||
session.Options.MaxAge = -1
|
|
||||||
session.Values = make(map[interface{}]interface{})
|
|
||||||
if err := session.Save(r, w); err != nil {
|
|
||||||
handleError(w, fmt.Sprintf("Error saving session: %v", err), http.StatusInternalServerError, t.logger)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine redirect URL
|
|
||||||
host := r.Header.Get("X-Forwarded-Host")
|
|
||||||
if host == "" {
|
|
||||||
host = r.Host
|
|
||||||
}
|
|
||||||
scheme := "http"
|
|
||||||
if r.Header.Get("X-Forwarded-Proto") == "https" || t.forceHTTPS {
|
|
||||||
scheme = "https"
|
|
||||||
}
|
|
||||||
baseURL := fmt.Sprintf("%s://%s/", scheme, host)
|
|
||||||
|
|
||||||
if t.endSessionURL != "" && idToken != "" {
|
|
||||||
logoutURL, err := BuildLogoutURL(t.endSessionURL, idToken, baseURL)
|
|
||||||
if err != nil {
|
|
||||||
handleError(w, fmt.Sprintf("Invalid end session URL: %v", err), http.StatusInternalServerError, t.logger)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, logoutURL, http.StatusFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Redirect(w, r, baseURL, http.StatusFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildLogoutURL constructs the logout URL with proper encoding
|
|
||||||
func BuildLogoutURL(endSessionURL, idToken, postLogoutRedirectURI string) (string, error) {
|
|
||||||
u, err := url.Parse(endSessionURL)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("invalid end session URL: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
q := u.Query()
|
|
||||||
q.Set("id_token_hint", idToken)
|
|
||||||
q.Set("post_logout_redirect_uri", postLogoutRedirectURI)
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
return u.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
// Clear the existing session
|
// Clear the existing session
|
||||||
@@ -441,3 +363,63 @@ func createStringMap(keys []string) map[string]struct{} {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleLogout handles the logout request
|
||||||
|
func (t *TraefikOidc) handleLogout(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
session, err := t.store.Get(req, cookieName)
|
||||||
|
if err != nil {
|
||||||
|
t.logger.Errorf("Error getting session: %v", err)
|
||||||
|
http.Error(rw, "Session error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the id_token before clearing the session
|
||||||
|
idToken, _ := session.Values["id_token"].(string)
|
||||||
|
|
||||||
|
// Clear and expire the session
|
||||||
|
session.Values = make(map[interface{}]interface{})
|
||||||
|
session.Options.MaxAge = -1
|
||||||
|
if err := session.Save(req, rw); err != nil {
|
||||||
|
t.logger.Errorf("Error saving session: %v", err)
|
||||||
|
http.Error(rw, "Session error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build post logout redirect URI if not set
|
||||||
|
if t.postLogoutRedirectURI == "" {
|
||||||
|
t.postLogoutRedirectURI = buildFullURL(t.scheme, t.determineHost(req), "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have an end session endpoint and an ID token, use OIDC end session
|
||||||
|
if t.endSessionURL != "" && idToken != "" {
|
||||||
|
logoutURL, err := BuildLogoutURL(t.endSessionURL, idToken, t.logoutURLPath)
|
||||||
|
if err != nil {
|
||||||
|
handleError(rw, fmt.Sprintf("Failed to build logout URL: %v", err), http.StatusInternalServerError, t.logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.logger.Debugf("Redirecting to end session URL: %s", logoutURL)
|
||||||
|
http.Redirect(rw, req, logoutURL, http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no end session endpoint or no ID token, just redirect to the post logout URI
|
||||||
|
t.logger.Debugf("Redirecting to post logout URI: %s", t.postLogoutRedirectURI)
|
||||||
|
http.Redirect(rw, req, t.postLogoutRedirectURI, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildLogoutURL constructs the OIDC end session URL
|
||||||
|
func BuildLogoutURL(endSessionURL, idToken, postLogoutRedirectURI string) (string, error) {
|
||||||
|
u, err := url.Parse(endSessionURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse end session URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := u.Query()
|
||||||
|
q.Set("id_token_hint", idToken)
|
||||||
|
if postLogoutRedirectURI != "" {
|
||||||
|
q.Set("post_logout_redirect_uri", postLogoutRedirectURI)
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ type TraefikOidc struct {
|
|||||||
initComplete chan struct{}
|
initComplete chan struct{}
|
||||||
endSessionURL string
|
endSessionURL string
|
||||||
baseURL string
|
baseURL string
|
||||||
|
postLogoutRedirectURI string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProviderMetadata holds OIDC provider metadata
|
// ProviderMetadata holds OIDC provider metadata
|
||||||
@@ -241,6 +242,7 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
|
|||||||
allowedUserDomains: createStringMap(config.AllowedUserDomains),
|
allowedUserDomains: createStringMap(config.AllowedUserDomains),
|
||||||
allowedRolesAndGroups: createStringMap(config.AllowedRolesAndGroups),
|
allowedRolesAndGroups: createStringMap(config.AllowedRolesAndGroups),
|
||||||
initComplete: make(chan struct{}),
|
initComplete: make(chan struct{}),
|
||||||
|
postLogoutRedirectURI: config.PostLogoutRedirectURI,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.extractClaimsFunc = extractClaims
|
t.extractClaimsFunc = extractClaims
|
||||||
@@ -767,3 +769,11 @@ func (t *TraefikOidc) extractGroupsAndRoles(idToken string) ([]string, []string,
|
|||||||
|
|
||||||
return groups, roles, nil
|
return groups, roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildFullURL constructs a full URL from scheme, host and path
|
||||||
|
func buildFullURL(scheme, host, path string) string {
|
||||||
|
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s://%s%s", scheme, host, path)
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type Config struct {
|
|||||||
AllowedUserDomains []string `json:"allowedUserDomains"`
|
AllowedUserDomains []string `json:"allowedUserDomains"`
|
||||||
AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`
|
AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`
|
||||||
OIDCEndSessionURL string `json:"oidcEndSessionURL"`
|
OIDCEndSessionURL string `json:"oidcEndSessionURL"`
|
||||||
|
PostLogoutRedirectURI string `json:"postLogoutRedirectURI"`
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +63,10 @@ func CreateConfig() *Config {
|
|||||||
c.RateLimit = 100
|
c.RateLimit = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.PostLogoutRedirectURI == "" {
|
||||||
|
c.PostLogoutRedirectURI = c.CallbackURL
|
||||||
|
}
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user