git@cosmocr.at/gitweb.git @ HEAD · summary refs log
HEAD / internal / server / auth.go /
internal/server/auth.go · 2.3 KiB · raw
view: SourceMarkdown ☑ force

package server

import ( "net/http" "strings"

"gitweb/internal/auth"
"gitweb/internal/store"

)

// authenticate identifies the caller from HTTP Basic auth. It returns the // verified upspin user name, or "" for an anonymous request (no credentials). // // The second result is false when credentials were supplied but invalid; in // that case a 401 challenge has already been written and the caller must stop. // // The Basic-auth password is either a vault-issued SSO bearer token (from the // passkey login flow) or a personal access token. Vault tokens are self- // describing, so the username field is ignored for them; for PATs the username // is the upspin user whose token file is consulted. func (s *Server) authenticate(w http.ResponseWriter, r *http.Request) (string, bool) { // Browser session: a gitweb_session cookie carries an issuer-signed SSO // token from the passkey login flow. It is verified statelessly (same as a // Basic-auth token), so a browser never needs to send Basic credentials. An // expired or tampered cookie is treated as anonymous — never a hard failure — // and cleared, so the next request re-prompts login rather than 401-looping. if s.vault != nil { if c, err := r.Cookie(sessionCookie); err == nil && c.Value != "" { if sub, err := s.vault.Verify(c.Value); err == nil { return sub, true } clearCookie(w, sessionCookie) } }

user, pass, ok := r.BasicAuth()
if !ok || pass == "" {
	return "", true // anonymous
}

if s.vault != nil && auth.LooksLikeVaultToken(pass) {
	sub, err := s.vault.Verify(pass)
	if err != nil {
		s.challenge(w)
		return "", false
	}
	return sub, true
}

// Personal access token: the username names the token file's owner.
if !store.ValidUser(user) {
	s.challenge(w)
	return "", false
}
valid, err := s.pat.Verify(user, pass)
if err != nil {
	s.serverErr(w, err)
	return "", false
}
if !valid {
	s.challenge(w)
	return "", false
}
return user, true

}

// parseUpspinPath splits a URL path "/user@domain/rest..." into the upspin user // and a cleaned remainder beginning with "/". ok is false if the user element // is missing or malformed. func parseUpspinPath(urlPath string) (user, clean string, ok bool) { raw := strings.TrimPrefix(urlPath, "/") user, rest, _ := strings.Cut(raw, "/") if !store.ValidUser(user) { return "", "", false } return user, cleanRest(rest), true }