Commit a08b1d13 authored by Pieter Droogendijk's avatar Pieter Droogendijk Committed by Brad Fitzpatrick

net/http: Various fixes to Basic authentication

There were some issues with the code sometimes using base64.StdEncoding,
and sometimes base64.URLEncoding.
Encoding basic authentication is now always done by the same code.

Fixes #5970.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12397043
parent 2b45124a
...@@ -161,18 +161,9 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) { ...@@ -161,18 +161,9 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
} }
if u := req.URL.User; u != nil { if u := req.URL.User; u != nil {
auth := u.String() username := u.Username()
// UserInfo.String() only returns the colon when the password, _ := u.Password()
// password is set, so we must add it here. req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
//
// See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt
// "To receive authorization, the client sends the userid and password,
// separated by a single colon (":") character, within a base64
// encoded string in the credentials."
if _, hasPassword := u.Password(); !hasPassword {
auth += ":"
}
req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(auth)))
} }
resp, err = t.RoundTrip(req) resp, err = t.RoundTrip(req)
if err != nil { if err != nil {
...@@ -184,6 +175,16 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) { ...@@ -184,6 +175,16 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
return resp, nil return resp, nil
} }
// See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt
// "To receive authorization, the client sends the userid and password,
// separated by a single colon (":") character, within a base64
// encoded string in the credentials."
// It is not meant to be urlencoded.
func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}
// True if the specified HTTP status code is one for which the Get utility should // True if the specified HTTP status code is one for which the Get utility should
// automatically redirect. // automatically redirect.
func shouldRedirectGet(statusCode int) bool { func shouldRedirectGet(statusCode int) bool {
......
...@@ -765,3 +765,37 @@ func TestEmptyPasswordAuth(t *testing.T) { ...@@ -765,3 +765,37 @@ func TestEmptyPasswordAuth(t *testing.T) {
} }
defer resp.Body.Close() defer resp.Body.Close()
} }
func TestBasicAuth(t *testing.T) {
defer afterTest(t)
tr := &recordingTransport{}
client := &Client{Transport: tr}
url := "http://My%20User:My%20Pass@dummy.faketld/"
expected := "My User:My Pass"
client.Get(url)
if tr.req.Method != "GET" {
t.Errorf("got method %q, want %q", tr.req.Method, "GET")
}
if tr.req.URL.String() != url {
t.Errorf("got URL %q, want %q", tr.req.URL.String(), url)
}
if tr.req.Header == nil {
t.Fatalf("expected non-nil request Header")
}
auth := tr.req.Header.Get("Authorization")
if strings.HasPrefix(auth, "Basic ") {
encoded := auth[6:]
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
t.Fatal(err)
}
s := string(decoded)
if expected != s {
t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected)
}
} else {
t.Errorf("Invalid auth %q", auth)
}
}
...@@ -10,7 +10,6 @@ import ( ...@@ -10,7 +10,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"io" "io"
...@@ -467,8 +466,7 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { ...@@ -467,8 +466,7 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
// With HTTP Basic Authentication the provided username and password // With HTTP Basic Authentication the provided username and password
// are not encrypted. // are not encrypted.
func (r *Request) SetBasicAuth(username, password string) { func (r *Request) SetBasicAuth(username, password string) {
s := username + ":" + password r.Header.Set("Authorization", "Basic "+basicAuth(username, password))
r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
} }
// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts. // parseRequestLine parses "GET /foo HTTP/1.1" into its three parts.
......
...@@ -13,7 +13,6 @@ import ( ...@@ -13,7 +13,6 @@ import (
"bufio" "bufio"
"compress/gzip" "compress/gzip"
"crypto/tls" "crypto/tls"
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"io" "io"
...@@ -273,7 +272,9 @@ func (cm *connectMethod) proxyAuth() string { ...@@ -273,7 +272,9 @@ func (cm *connectMethod) proxyAuth() string {
return "" return ""
} }
if u := cm.proxyURL.User; u != nil { if u := cm.proxyURL.User; u != nil {
return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String())) username := u.Username()
password, _ := u.Password()
return "Basic " + basicAuth(username, password)
} }
return "" return ""
} }
......
...@@ -451,8 +451,8 @@ func (u *URL) String() string { ...@@ -451,8 +451,8 @@ func (u *URL) String() string {
} else { } else {
if u.Scheme != "" || u.Host != "" || u.User != nil { if u.Scheme != "" || u.Host != "" || u.User != nil {
buf.WriteString("//") buf.WriteString("//")
if u := u.User; u != nil { if ui := u.User; ui != nil {
buf.WriteString(u.String()) buf.WriteString(ui.String())
buf.WriteByte('@') buf.WriteByte('@')
} }
if h := u.Host; h != "" { if h := u.Host; h != "" {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment