yudai/gotty · warning

authorization failed

Error message

authorization failed

What it means

GoTTY's wrapBasicAuth middleware in server/middleware.go:44 returns HTTP 401 'authorization failed' when the Base64-decoded Authorization payload does not exactly equal the credential string the server was started with (-credential user:pass). The comparison `credential != string(payload)` is a plain string match, so any mismatch in username, password, or the 'user:pass' combined format rejects the request and sets the WWW-Authenticate: Basic realm="GoTTY" challenge.

Source

Thrown at server/middleware.go:44

func (server *Server) wrapBasicAuth(handler http.Handler, credential string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := strings.SplitN(r.Header.Get("Authorization"), " ", 2)

		if len(token) != 2 || strings.ToLower(token[0]) != "basic" {
			w.Header().Set("WWW-Authenticate", `Basic realm="GoTTY"`)
			http.Error(w, "Bad Request", http.StatusUnauthorized)
			return
		}

		payload, err := base64.StdEncoding.DecodeString(token[1])
		if err != nil {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}

		if credential != string(payload) {
			w.Header().Set("WWW-Authenticate", `Basic realm="GoTTY"`)
			http.Error(w, "authorization failed", http.StatusUnauthorized)
			return
		}

		log.Printf("Basic Authentication Succeeded: %s", r.RemoteAddr)
		handler.ServeHTTP(w, r)
	})
}

View on GitHub (pinned to a080c85cbc)

Solutions

  1. Re-encode the exact expected value: printf '%s' 'user:pass' | base64 and send that in 'Authorization: Basic <token>' (or use curl -u user:pass)
  2. Verify the credential the server was started with (the -credential flag / config) matches what the client is sending, including case and any special characters
  3. Confirm the Authorization header survives intermediaries; test directly against the GoTTY port bypassing proxies
  4. Inspect for hidden whitespace/newlines in the encoded token (base64 with line wrapping) and strip them

Example fix

// before
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("wrongpass")))
// 401 authorization failed

// after
cred := "user:pass" // must match server's -credential exactly
req.SetBasicAuth("user", "pass") // sends Basic base64("user:pass")
Defensive patterns

Strategy: validation

Validate before calling

expected := "user:pass" // must match server -credential exactly
if base64.StdEncoding.EncodeToString([]byte(expected)) != token {
	log.Printf("credential mismatch: re-encode from the server's configured credential")
}

Try / catch

resp, err := client.Do(req)
if err == nil && resp.StatusCode == http.StatusUnauthorized {
	// 401 'authorization failed': check credential vs server's -credential,
	// refresh credentials, then retry once with corrected values
}

Prevention

When it happens

Trigger: A request sends a validly Base64-encoded Basic header whose decoded value is not exactly the configured credential: wrong password, wrong username, missing the 'username:password' colon format (sending just 'password'), or the server was started with a different -credential value than the client uses.

Common situations: Stale credentials in client config or environment variables after a server restart with a new -credential flag, URL special characters in a password that shell-encode differently than expected, a reverse proxy stripping or rewriting Authorization headers, clients encoding 'user:pass ' with a trailing space/newline, or team members sharing an encoded token generated from different credentials.

Related errors


AI-assisted analysis of yudai/gotty@a080c85cbc (2026-09-02). Data as JSON: /api/errors/c2f511d82308a05a. Report an issue: GitHub.