package wristband import ( "net/http" "strings" ) // htmlEscapePHP ports PHP's htmlspecialchars($s, ENT_QUOTES, 'UTF-8') byte // for byte: Go's stdlib html.EscapeString differs on the quote entities // ('/" vs PHP's '/"), which would diverge from the // recorded redirect body whenever a redirect_uri or state value contains a // quote character. func htmlEscapePHP(s string) string { var b strings.Builder b.Grow(len(s)) for _, r := range s { switch r { case '&': b.WriteString("&") case '"': b.WriteString(""") case '\'': b.WriteString("'") case '<': b.WriteString("<") case '>': b.WriteString(">") default: b.WriteRune(r) } } return b.String() } // writeRedirectHTML ports Laravel/Symfony's RedirectResponse default HTML // body byte-for-byte (T-08-OPEN-REDIRECT/D-04). Go's net/http never emits a // body for a 3xx Location redirect; every wristband redirect needs this // exact body plus Content-Type because real recorded PHP traffic includes // it, and an unchanged browser-based client (the Nuxt /connect handoff) // observes it. Cache-Control is the caller's responsibility -- callers set // it before invoking this helper because its value differs between the // authorize success path and error redirects versus other endpoints. func writeRedirectHTML(w http.ResponseWriter, status int, target string) { escaped := htmlEscapePHP(target) var b strings.Builder b.WriteString("\n\n
\n \n \n\n