MCPcopy Create free account
hub / github.com/authorizerdev/authorizer / LogoutHandler

Method LogoutHandler

internal/http_handlers/logout.go:30–174  ·  view source on GitHub ↗

Handler to logout user

()

Source from the content-addressed store, hash-verified

28
29// Handler to logout user
30func (h *httpProvider) LogoutHandler() gin.HandlerFunc {
31 log := h.Log.With().Str("func", "LogoutHandler").Logger()
32 return func(gc *gin.Context) {
33 // OIDC RP-initiated logout uses GET on the end_session_endpoint.
34 // Without protection, an attacker can place <img src="/logout">
35 // on a page they control and the victim's browser will silently
36 // terminate their session via the cookie. Defence-in-depth:
37 //
38 // - GET with id_token_hint → proceed (the ID token proves the
39 // request originates from a real authenticated session — an
40 // <img> tag cannot forge an ID token)
41 // - GET without id_token_hint → render an HTML confirmation
42 // page; only the POST that follows actually deletes the
43 // session
44 // - POST → proceed unconditionally (existing behaviour; CSRF
45 // middleware enforces Origin/Referer for all POSTs)
46 if gc.Request.Method == http.MethodGet {
47 idTokenHint := strings.TrimSpace(gc.Query("id_token_hint"))
48 if idTokenHint == "" || !h.isValidIDTokenHint(idTokenHint) {
49 log.Debug().Bool("had_hint", idTokenHint != "").Msg("serving logout confirmation page")
50 gc.Header("Cache-Control", "no-store")
51 gc.HTML(http.StatusOK, logoutConfirmTemplate, gin.H{
52 "redirect_uri": gc.Query("redirect_uri"),
53 "post_logout_redirect_uri": gc.Query("post_logout_redirect_uri"),
54 "state": gc.Query("state"),
55 })
56 return
57 }
58 // Valid id_token_hint — fall through to the normal logout flow.
59 }
60
61 // OIDC RP-Initiated Logout 1.0 §3 uses post_logout_redirect_uri.
62 // Fall back to the legacy redirect_uri for backward compatibility.
63 redirectURL := strings.TrimSpace(gc.Query("post_logout_redirect_uri"))
64 if redirectURL == "" {
65 redirectURL = strings.TrimSpace(gc.PostForm("post_logout_redirect_uri"))
66 }
67 if redirectURL == "" {
68 redirectURL = strings.TrimSpace(gc.Query("redirect_uri"))
69 }
70 if redirectURL == "" {
71 redirectURL = strings.TrimSpace(gc.PostForm("redirect_uri"))
72 }
73
74 // state, when present, MUST be echoed on the final redirect per
75 // OIDC RP-Initiated Logout §3.
76 state := strings.TrimSpace(gc.Query("state"))
77 if state == "" {
78 state = strings.TrimSpace(gc.PostForm("state"))
79 }
80 // get fingerprint hash
81 fingerprintHash, err := cookie.GetSession(gc)
82 if err != nil {
83 log.Debug().Err(err).Msg("failed GetSession")
84 gc.JSON(http.StatusUnauthorized, gin.H{
85 "error": "unauthorized",
86 })
87 return

Callers

nothing calls this directly

Calls 14

isValidIDTokenHintMethod · 0.95
GetSessionFunction · 0.92
DecryptAESFunction · 0.92
DeleteSessionFunction · 0.92
ParseSameSiteFunction · 0.92
RecordAuthEventFunction · 0.92
GetIPFunction · 0.92
GetUserAgentFunction · 0.92
GetHostFunction · 0.92
IsValidRedirectURIFunction · 0.92
QueryMethod · 0.65
DeleteUserSessionMethod · 0.65

Tested by

no test coverage detected