Ensure authenticate never re-auths.

Other API functions will support re-auth as a matter of course.
If an auth token expires, we need to re-authenticate to acquire a new
token.  If re-authentication were itself to attempt re-auth, we
would end up in an endless loop.

If after authenticating gophercloud receives a 401 Unauthorized
response, then we must assume that the provided credentials are
incorrect.
diff --git a/authenticate_test.go b/authenticate_test.go
index a19fec4..e08a0a6 100644
--- a/authenticate_test.go
+++ b/authenticate_test.go
@@ -212,3 +212,28 @@
 		return
 	}
 }
+
+func TestAuthenticationNeverReauths(t *testing.T) {
+	tt := newTransport().WithError(401)
+	c := TestContext().
+		UseCustomClient(&http.Client{Transport: tt}).
+		WithProvider("provider", Provider{AuthEndpoint: "http://localhost"})
+
+	_, err := c.Authenticate("provider", AuthOptions{Username: "u", Password: "p"})
+	if err == nil {
+		t.Error("Expected an error from a 401 Unauthorized response")
+		return
+	}
+
+	rc, _ := ActualResponseCode(err)
+	if rc != 401 {
+		t.Error("Expected a 401 error code")
+		return
+	}
+
+	err = tt.VerifyCalls(t, 1)
+	if err != nil {
+		// Test object already flagged.
+		return
+	}
+}
diff --git a/errors.go b/errors.go
index f113446..5ea3991 100644
--- a/errors.go
+++ b/errors.go
@@ -30,3 +30,8 @@
 // exists in the service catalog.  This can also happen if your tenant lacks
 // adequate permissions to access a given endpoint.
 var ErrEndpoint = fmt.Errorf("Missing endpoint, or insufficient privileges to access endpoint")
+
+// ErrError errors happen when you attempt to discover the response code
+// responsible for a previous request bombing with an error, but pass in an
+// error interface which doesn't belong to the web client.
+var ErrError = fmt.Errorf("Attempt to solicit actual HTTP response code from error entity which doesn't know")
diff --git a/global_context.go b/global_context.go
index c89ac17..9977aa8 100644
--- a/global_context.go
+++ b/global_context.go
@@ -1,5 +1,9 @@
 package gophercloud
 
+import (
+	"github.com/racker/perigee"
+)
+
 // globalContext is the, well, "global context."
 // Most of this SDK is written in a manner to facilitate easier testing,
 // which doesn't require all the configuration a real-world application would require.
@@ -46,3 +50,13 @@
 func ServersApi(acc AccessProvider, criteria ApiCriteria) (CloudServersProvider, error) {
 	return globalContext.ServersApi(acc, criteria)
 }
+
+// ActualResponseCode inspects a returned error, and discovers the actual response actual
+// response code that caused the error to be raised.
+func ActualResponseCode(e error) (int, error) {
+	err, ok := e.(*perigee.UnexpectedResponseCodeError)
+	if !ok {
+		return 0, ErrError
+	}
+	return err.Actual, nil
+}
\ No newline at end of file
diff --git a/transport_double.go b/transport_double.go
index b65d5db..e764c9a 100644
--- a/transport_double.go
+++ b/transport_double.go
@@ -5,6 +5,8 @@
 	"io/ioutil"
 	"net/http"
 	"strings"
+	"fmt"
+	"testing"
 )
 
 type transport struct {
@@ -12,6 +14,7 @@
 	response       string
 	expectTenantId bool
 	tenantIdFound  bool
+	status int
 }
 
 func (t *transport) RoundTrip(req *http.Request) (rsp *http.Response, err error) {
@@ -24,9 +27,17 @@
 
 	body := ioutil.NopCloser(strings.NewReader(t.response))
 
+	if t.status == 0 {
+		t.status = 200
+	}
+	statusMsg := "OK"
+	if (t.status < 200) || (299 < t.status) {
+		statusMsg = "Error"
+	}
+
 	rsp = &http.Response{
-		Status:           "200 OK",
-		StatusCode:       200,
+		Status:           fmt.Sprintf("%d %s", t.status, statusMsg),
+		StatusCode:       t.status,
 		Proto:            "HTTP/1.1",
 		ProtoMajor:       1,
 		ProtoMinor:       1,
@@ -72,5 +83,21 @@
 
 func (t *transport) WithResponse(r string) *transport {
 	t.response = r
+	t.status = 200
 	return t
 }
+
+func (t *transport) WithError(code int) *transport {
+	t.response = fmt.Sprintf("Error %d", code)
+	t.status = code
+	return t
+}
+
+func (t *transport) VerifyCalls(test *testing.T, n int) error {
+	if t.called != n {
+		err := fmt.Errorf("Expected Transport to be called %d times; found %d instead", n, t.called)
+		test.Error(err)
+		return err
+	}
+	return nil
+}
\ No newline at end of file