github.com/argoproj/argo-cd/v2@v2.10.9/server/logout/logout.go (about) 1 package logout 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "regexp" 8 "strings" 9 "time" 10 11 "github.com/golang-jwt/jwt/v4" 12 log "github.com/sirupsen/logrus" 13 14 "github.com/argoproj/argo-cd/v2/common" 15 "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" 16 httputil "github.com/argoproj/argo-cd/v2/util/http" 17 jwtutil "github.com/argoproj/argo-cd/v2/util/jwt" 18 "github.com/argoproj/argo-cd/v2/util/session" 19 "github.com/argoproj/argo-cd/v2/util/settings" 20 ) 21 22 // NewHandler creates handler serving to do api/logout endpoint 23 func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, sessionMgr *session.SessionManager, rootPath, baseHRef, namespace string) *Handler { 24 return &Handler{ 25 appClientset: appClientset, 26 namespace: namespace, 27 settingsMgr: settingsMrg, 28 rootPath: rootPath, 29 baseHRef: baseHRef, 30 verifyToken: sessionMgr.VerifyToken, 31 revokeToken: sessionMgr.RevokeToken, 32 } 33 } 34 35 type Handler struct { 36 namespace string 37 appClientset versioned.Interface 38 settingsMgr *settings.SettingsManager 39 rootPath string 40 verifyToken func(tokenString string) (jwt.Claims, string, error) 41 revokeToken func(ctx context.Context, id string, expiringAt time.Duration) error 42 baseHRef string 43 } 44 45 var ( 46 tokenPattern = regexp.MustCompile(`{{token}}`) 47 logoutRedirectURLPattern = regexp.MustCompile(`{{logoutRedirectURL}}`) 48 ) 49 50 func constructLogoutURL(logoutURL, token, logoutRedirectURL string) string { 51 constructedLogoutURL := tokenPattern.ReplaceAllString(logoutURL, token) 52 return logoutRedirectURLPattern.ReplaceAllString(constructedLogoutURL, logoutRedirectURL) 53 } 54 55 // ServeHTTP is the logout handler for ArgoCD and constructs OIDC logout URL and redirects to it for OIDC issued sessions, 56 // and redirects user to '/login' for argocd issued sessions 57 func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 58 var tokenString string 59 var oidcConfig *settings.OIDCConfig 60 61 argoCDSettings, err := h.settingsMgr.GetSettings() 62 if err != nil { 63 w.WriteHeader(http.StatusInternalServerError) 64 http.Error(w, "Failed to retrieve argoCD settings: "+fmt.Sprintf("%s", err), http.StatusInternalServerError) 65 return 66 } 67 68 argoURL := argoCDSettings.URL 69 if argoURL == "" { 70 // golang does not provide any easy way to determine scheme of current request 71 // so redirecting ot http which will auto-redirect too https if necessary 72 host := strings.TrimRight(r.Host, "/") 73 argoURL = fmt.Sprintf("http://%s", host) + "/" + strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/") 74 } 75 76 logoutRedirectURL := strings.TrimRight(strings.TrimLeft(argoURL, "/"), "/") 77 78 cookies := r.Cookies() 79 tokenString, err = httputil.JoinCookies(common.AuthCookieName, cookies) 80 if tokenString == "" || err != nil { 81 w.WriteHeader(http.StatusBadRequest) 82 http.Error(w, "Failed to retrieve ArgoCD auth token: "+fmt.Sprintf("%s", err), http.StatusBadRequest) 83 return 84 } 85 86 for _, cookie := range cookies { 87 if !strings.HasPrefix(cookie.Name, common.AuthCookieName) { 88 continue 89 } 90 91 argocdCookie := http.Cookie{ 92 Name: cookie.Name, 93 Value: "", 94 } 95 96 argocdCookie.Path = fmt.Sprintf("/%s", strings.TrimRight(strings.TrimLeft(h.baseHRef, "/"), "/")) 97 w.Header().Add("Set-Cookie", argocdCookie.String()) 98 } 99 100 claims, _, err := h.verifyToken(tokenString) 101 if err != nil { 102 http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther) 103 return 104 } 105 106 mapClaims, err := jwtutil.MapClaims(claims) 107 if err != nil { 108 http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther) 109 return 110 } 111 112 issuer := jwtutil.StringField(mapClaims, "iss") 113 id := jwtutil.StringField(mapClaims, "jti") 114 if exp, err := jwtutil.ExpirationTime(mapClaims); err == nil && id != "" { 115 if err := h.revokeToken(context.Background(), id, time.Until(exp)); err != nil { 116 log.Warnf("failed to invalidate token '%s': %v", id, err) 117 } 118 } 119 120 if argoCDSettings.OIDCConfig() == nil || argoCDSettings.OIDCConfig().LogoutURL == "" || issuer == session.SessionManagerClaimsIssuer { 121 http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther) 122 } else { 123 oidcConfig = argoCDSettings.OIDCConfig() 124 logoutURL := constructLogoutURL(oidcConfig.LogoutURL, tokenString, logoutRedirectURL) 125 http.Redirect(w, r, logoutURL, http.StatusSeeOther) 126 } 127 }