Files
git-pages/auth.go
2025-09-15 04:13:07 +00:00

65 lines
1.7 KiB
Go

package main
import (
"crypto/sha256"
"fmt"
"net"
"net/http"
"slices"
"strings"
)
func getHost(r *http.Request) string {
// FIXME: handle IDNA
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
// dirty but the go stdlib doesn't have a "split port if present" function
host = r.Host
}
return host
}
func authorize(w http.ResponseWriter, r *http.Request) error {
host := getHost(r)
authorization := r.Header.Get("Authorization")
if authorization == "" {
http.Error(w, "missing Authorization header", http.StatusUnauthorized)
return fmt.Errorf("missing Authorization header")
}
scheme, param, success := strings.Cut(authorization, " ")
if !success {
http.Error(w, "malformed Authorization header", http.StatusBadRequest)
return fmt.Errorf("malformed Authorization header")
}
if scheme != "Pages" {
http.Error(w, "unknown Authorization scheme", http.StatusBadRequest)
return fmt.Errorf("unknown Authorization scheme")
}
challengeHostname := fmt.Sprintf("_git-pages-challenge.%s", host)
actualChallenges, err := net.LookupTXT(challengeHostname)
if err != nil {
http.Error(w, "failed to look up DNS challenge", http.StatusUnauthorized)
return fmt.Errorf("failed to look up %s: %s", challengeHostname, err)
}
expectedChallenge := fmt.Sprintf("%x", sha256.Sum256(fmt.Appendf(nil, "%s %s", host, param)))
if !slices.Contains(actualChallenges, expectedChallenge) {
http.Error(w,
fmt.Sprintf("defeated by DNS challenge (%s not in %s)", expectedChallenge, challengeHostname),
http.StatusUnauthorized,
)
return fmt.Errorf(
"challenge mismatch for %s: %s does not contain %s",
challengeHostname,
actualChallenges,
expectedChallenge,
)
}
return nil
}