github.com/argoproj/argo-cd@v1.8.7/server/logout/logout.go (about)

     1  package logout
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/dgrijalva/jwt-go/v4"
    10  
    11  	"github.com/argoproj/argo-cd/common"
    12  	"github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
    13  	"github.com/argoproj/argo-cd/util/session"
    14  	"github.com/argoproj/argo-cd/util/settings"
    15  
    16  	jwtutil "github.com/argoproj/argo-cd/util/jwt"
    17  )
    18  
    19  //NewHandler creates handler serving to do api/logout endpoint
    20  func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, sessionMgr *session.SessionManager, rootPath, namespace string) *Handler {
    21  	return &Handler{
    22  		appClientset: appClientset,
    23  		namespace:    namespace,
    24  		settingsMgr:  settingsMrg,
    25  		rootPath:     rootPath,
    26  		verifyToken:  sessionMgr.VerifyToken,
    27  	}
    28  }
    29  
    30  type Handler struct {
    31  	namespace    string
    32  	appClientset versioned.Interface
    33  	settingsMgr  *settings.SettingsManager
    34  	rootPath     string
    35  	verifyToken  func(tokenString string) (jwt.Claims, error)
    36  }
    37  
    38  var (
    39  	tokenPattern             = regexp.MustCompile(`{{token}}`)
    40  	logoutRedirectURLPattern = regexp.MustCompile(`{{logoutRedirectURL}}`)
    41  )
    42  
    43  func constructLogoutURL(logoutURL, token, logoutRedirectURL string) string {
    44  	constructedLogoutURL := tokenPattern.ReplaceAllString(logoutURL, token)
    45  	return logoutRedirectURLPattern.ReplaceAllString(constructedLogoutURL, logoutRedirectURL)
    46  }
    47  
    48  // ServeHTTP is the logout handler for ArgoCD and constructs OIDC logout URL and redirects to it for OIDC issued sessions,
    49  // and redirects user to '/login' for argocd issued sessions
    50  func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    51  	var tokenString string
    52  	var oidcConfig *settings.OIDCConfig
    53  
    54  	argoCDSettings, err := h.settingsMgr.GetSettings()
    55  	if err != nil {
    56  		w.WriteHeader(http.StatusInternalServerError)
    57  		http.Error(w, "Failed to retrieve argoCD settings: "+fmt.Sprintf("%s", err), http.StatusInternalServerError)
    58  		return
    59  	}
    60  
    61  	logoutRedirectURL := strings.TrimRight(strings.TrimLeft(argoCDSettings.URL, "/"), "/") + strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/")
    62  
    63  	argocdCookie, err := r.Cookie(common.AuthCookieName)
    64  	if err != nil {
    65  		w.WriteHeader(http.StatusBadRequest)
    66  		http.Error(w, "Failed to retrieve ArgoCD auth token: "+fmt.Sprintf("%s", err), http.StatusBadRequest)
    67  		return
    68  	}
    69  
    70  	tokenString = argocdCookie.Value
    71  
    72  	argocdCookie.Value = ""
    73  	argocdCookie.Path = fmt.Sprintf("/%s", strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/"))
    74  	w.Header().Set("Set-Cookie", argocdCookie.String())
    75  
    76  	claims, err := h.verifyToken(tokenString)
    77  	if err != nil {
    78  		http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
    79  		return
    80  	}
    81  
    82  	mapClaims, err := jwtutil.MapClaims(claims)
    83  	if err != nil {
    84  		http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
    85  		return
    86  	}
    87  
    88  	issuer := jwtutil.StringField(mapClaims, "iss")
    89  
    90  	if argoCDSettings.OIDCConfig() == nil || argoCDSettings.OIDCConfig().LogoutURL == "" || issuer == session.SessionManagerClaimsIssuer {
    91  		http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
    92  	} else {
    93  		oidcConfig = argoCDSettings.OIDCConfig()
    94  		logoutURL := constructLogoutURL(oidcConfig.LogoutURL, tokenString, logoutRedirectURL)
    95  		http.Redirect(w, r, logoutURL, http.StatusSeeOther)
    96  	}
    97  }