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