Finish KEY-WP-0008: registration handoff and client MFA isolation
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 34s
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 34s
Add signed registration/enrollment handoffs, per-request assurance policy with login-session isolation, and /logout. coulomb-social stays AAL1 unless acr_values or another client raises the bar.
This commit is contained in:
parent
fff9e39478
commit
b6af6c5268
22 changed files with 1636 additions and 42 deletions
73
src/internal/server/oidc/logout.go
Normal file
73
src/internal/server/oidc/logout.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
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://")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue