Use version negotiation in openstack.NewClient().
diff --git a/openstack/client.go b/openstack/client.go
index f1e8258..6e0de06 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -1,18 +1,60 @@
 package openstack
 
 import (
+	"errors"
+
 	"github.com/rackspace/gophercloud"
 	identity3 "github.com/rackspace/gophercloud/openstack/identity/v3"
+	"github.com/rackspace/gophercloud/openstack/utils"
 )
 
 // Client provides access to service clients for this OpenStack cloud.
-type Client gophercloud.ProviderClient
+type Client struct {
+	gophercloud.ProviderClient
+}
+
+const (
+	v20 = "v2.0"
+	v30 = "v3.0"
+)
 
 // NewClient authenticates to an OpenStack cloud with the provided credentials.
 // It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
 // the most recent identity service available to proceed.
 func NewClient(authOptions gophercloud.AuthOptions) (*Client, error) {
-	return nil, nil
+	versions := []*utils.Version{
+		&utils.Version{ID: v20, Priority: 20},
+		&utils.Version{ID: v30, Priority: 30},
+	}
+
+	chosen, endpoint, err := utils.ChooseVersion(authOptions.IdentityEndpoint, versions)
+	if err != nil {
+		return nil, err
+	}
+
+	client := Client{
+		ProviderClient: gophercloud.ProviderClient{
+			IdentityEndpoint: endpoint,
+			Options:          authOptions,
+		},
+	}
+
+	switch chosen.ID {
+	case v20:
+	case v30:
+		identityClient := identity3.NewClient(&client.ProviderClient)
+		token, err := identityClient.Authenticate(authOptions)
+		if err != nil {
+			return nil, err
+		}
+
+		client.ProviderClient.TokenID = token.ID
+	default:
+		// The switch must be out of sync with "versions".
+		return nil, errors.New("Wat")
+	}
+
+	return &client, nil
 }
 
 // IdentityV3 explicitly accesses the v3 identity service.
diff --git a/openstack/client_test.go b/openstack/client_test.go
index 8068554..5476271 100644
--- a/openstack/client_test.go
+++ b/openstack/client_test.go
@@ -1,12 +1,64 @@
 package openstack
 
 import (
+	"fmt"
+	"net/http"
 	"testing"
 
+	"github.com/rackspace/gophercloud"
 	"github.com/rackspace/gophercloud/testhelper"
 )
 
-func TestAuthenticate(t *testing.T) {
+func TestNewClientV3(t *testing.T) {
 	testhelper.SetupHTTP()
 	defer testhelper.TeardownHTTP()
+
+	const ID = "0123456789"
+
+	testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, `
+			{
+				"versions": {
+					"values": [
+						{
+							"status": "stable",
+							"id": "v3.0",
+							"links": [
+								{ "href": "%s", "rel": "self" }
+							]
+						},
+						{
+							"status": "stable",
+							"id": "v2.0",
+							"links": [
+								{ "href": "%s", "rel": "self" }
+							]
+						}
+					]
+				}
+			}
+		`, testhelper.Endpoint()+"v3/", testhelper.Endpoint()+"v2.0/")
+	})
+
+	testhelper.Mux.HandleFunc("/v3/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("X-Subject-Token", ID)
+
+		w.WriteHeader(http.StatusCreated)
+		fmt.Fprintf(w, `{ "token": { "expires_at": "2013-02-02T18:30:59.000000Z" } }`)
+	})
+
+	options := gophercloud.AuthOptions{
+		UserID:           "me",
+		Password:         "secret",
+		IdentityEndpoint: testhelper.Endpoint(),
+	}
+	client, err := NewClient(options)
+
+	if err != nil {
+		t.Fatalf("Unexpected error from NewClient: %s", err)
+	}
+
+	if client.TokenID != ID {
+		t.Errorf("Expected token ID to be [%s], but was [%s]", ID, client.TokenID)
+	}
 }
diff --git a/openstack/identity/v3/client.go b/openstack/identity/v3/client.go
index 5f25ee6..984d03e 100644
--- a/openstack/identity/v3/client.go
+++ b/openstack/identity/v3/client.go
@@ -24,7 +24,7 @@
 	return &Client{
 		ServiceClient: gophercloud.ServiceClient{
 			ProviderClient: *provider,
-			Endpoint:       provider.IdentityEndpoint + "v3/",
+			Endpoint:       provider.IdentityEndpoint,
 		},
 	}
 }
diff --git a/openstack/identity/v3/client_test.go b/openstack/identity/v3/client_test.go
index 0744e7d..c9cf3c2 100644
--- a/openstack/identity/v3/client_test.go
+++ b/openstack/identity/v3/client_test.go
@@ -16,7 +16,7 @@
 	defer testhelper.TeardownHTTP()
 
 	provider := &gophercloud.ProviderClient{
-		IdentityEndpoint: testhelper.Endpoint(),
+		IdentityEndpoint: testhelper.Endpoint() + "v3/",
 	}
 	client := NewClient(provider)
 
@@ -39,7 +39,7 @@
 	})
 
 	provider := &gophercloud.ProviderClient{
-		IdentityEndpoint: testhelper.Endpoint(),
+		IdentityEndpoint: testhelper.Endpoint() + "v3/",
 	}
 	client := NewClient(provider)