fix(08-09): match wristband OAuth byte contract to live-recorded PHP
Recording the full mcp-lifecycle fixture against real isolated PHP (08-09-PLAN.md Task 2) uncovered three byte-level gaps between wristband's assumed contract and actual production PHP behavior: - Every explicit "Cache-Control: no-store" PHP sets is actually delivered as "no-store, private" (Laravel's session-cookie default merges "private" onto any explicit value); wristband's own default for unheadered JSON error responses is "no-cache, private" (matching the house convention already used elsewhere), not empty. - PHP's redirect responses (authorize success and every error redirect) render Symfony's default HTML redirect body with Content-Type "text/html; charset=utf-8"; Go's bare 302 with no body never matched. wristband/redirect_html.go ports that exact byte template, including PHP's htmlspecialchars(ENT_QUOTES) escaping (Go's html.EscapeString uses different quote entities). tide/normalize.go: isIDKey now also masks "_ids" plural array fields (e.g. collection_ids), a latent parity-corpus gap no prior fixture had exercised with a literal, non-empty, non-placeholder array value.
This commit is contained in:
@@ -164,16 +164,19 @@ func (s *Server) Authorize(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
spa := s.opts.Issuer + "/connect?request=" + rfc3986Escape(requestID)
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Location", spa)
|
||||
w.WriteHeader(http.StatusFound)
|
||||
// Laravel appends ", private" to every explicit Cache-Control this
|
||||
// endpoint sets (session-cookie default merge); recorded PHP traffic is
|
||||
// "no-store, private", never a bare "no-store" (D-04, live-recorded byte
|
||||
// contract, 08-09-PLAN.md Task 2).
|
||||
w.Header().Set("Cache-Control", "no-store, private")
|
||||
writeRedirectHTML(w, http.StatusFound, spa)
|
||||
}
|
||||
|
||||
// writeAuthorizeLocalError writes the PHP localError() response: a bare
|
||||
// text/plain 400 with no Location and no house envelope
|
||||
// (T-08-OPEN-REDIRECT).
|
||||
func writeAuthorizeLocalError(w http.ResponseWriter, message string) {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Cache-Control", "no-store, private")
|
||||
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(message))
|
||||
@@ -191,9 +194,8 @@ func (s *Server) authorizeErrorRedirect(w http.ResponseWriter, redirectURI, errC
|
||||
if state != nil {
|
||||
pairs = append(pairs, [2]string{"state", *state})
|
||||
}
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Location", appendOrderedQuery(redirectURI, pairs))
|
||||
w.WriteHeader(http.StatusFound)
|
||||
w.Header().Set("Cache-Control", "no-store, private")
|
||||
writeRedirectHTML(w, http.StatusFound, appendOrderedQuery(redirectURI, pairs))
|
||||
}
|
||||
|
||||
// parseAuthorizeScopes ports OAuthAuthorizeController::parseScopes. An
|
||||
|
||||
Reference in New Issue
Block a user