74 lines
1.9 KiB
Go
74 lines
1.9 KiB
Go
|
|
package oidc
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"net/url"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"keycape/internal/domain"
|
||
|
|
profileerrors "keycape/internal/errors"
|
||
|
|
)
|
||
|
|
|
||
|
|
// LogoutHandler implements GET /logout (OIDC RP-initiated logout subset).
|
||
|
|
// It clears the KeyCape login session and, when requested, redirects only to
|
||
|
|
// a statically registered client redirect URI.
|
||
|
|
type LogoutHandler struct {
|
||
|
|
ClientConfig map[string]*domain.Client
|
||
|
|
Logins *LoginSessionStore
|
||
|
|
SecureCookie bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// ServeHTTP handles GET /logout.
|
||
|
|
func (h *LogoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if r.Method != http.MethodGet {
|
||
|
|
w.Header().Set("Allow", "GET")
|
||
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if sess := h.Logins.fromRequest(r); sess != nil {
|
||
|
|
h.Logins.Delete(sess.ID)
|
||
|
|
}
|
||
|
|
clearLoginCookie(w, h.SecureCookie)
|
||
|
|
|
||
|
|
clientID := r.URL.Query().Get("client_id")
|
||
|
|
postLogout := r.URL.Query().Get("post_logout_redirect_uri")
|
||
|
|
if postLogout == "" {
|
||
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
_, _ = w.Write([]byte("logged out"))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
client, ok := h.ClientConfig[clientID]
|
||
|
|
if !ok {
|
||
|
|
profileerrors.InvalidProfileUsage("unknown client_id", "client_id").
|
||
|
|
Write(w, http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !uriRegistered(client.RedirectURIs, postLogout) {
|
||
|
|
profileerrors.RejectedForSafety(
|
||
|
|
"post_logout_redirect_uri is not a registered redirect URI",
|
||
|
|
"post_logout_redirect_uri",
|
||
|
|
).Write(w, http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
loc, err := url.Parse(postLogout)
|
||
|
|
if err != nil {
|
||
|
|
profileerrors.InvalidProfileUsage("invalid post_logout_redirect_uri", "post_logout_redirect_uri").
|
||
|
|
Write(w, http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if state := r.URL.Query().Get("state"); state != "" {
|
||
|
|
q := loc.Query()
|
||
|
|
q.Set("state", state)
|
||
|
|
loc.RawQuery = q.Encode()
|
||
|
|
}
|
||
|
|
http.Redirect(w, r, loc.String(), http.StatusFound)
|
||
|
|
}
|
||
|
|
|
||
|
|
func issuerIsHTTPS(issuer string) bool {
|
||
|
|
return strings.HasPrefix(strings.ToLower(issuer), "https://")
|
||
|
|
}
|