no naked returns in go; fix auth v3 unit tests
diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go
index 30ceacf..fc44365 100644
--- a/openstack/identity/v3/endpoints/requests.go
+++ b/openstack/identity/v3/endpoints/requests.go
@@ -31,6 +31,7 @@
 		return
 	}
 	_, r.Err = client.Post(listURL(client), &b, &r.Body, nil)
+	return
 }
 
 type ListOptsBuilder interface {
@@ -91,9 +92,11 @@
 		return
 	}
 	_, r.Err = client.Patch(endpointURL(client, endpointID), &b, &r.Body, nil)
+	return
 }
 
 // Delete removes an endpoint from the service catalog.
 func Delete(client *gophercloud.ServiceClient, endpointID string) (r DeleteResult) {
 	_, r.Err = client.Delete(endpointURL(client, endpointID), nil)
+	return
 }
diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go
index 81c8960..bb7bb04 100644
--- a/openstack/identity/v3/services/requests.go
+++ b/openstack/identity/v3/services/requests.go
@@ -9,6 +9,7 @@
 func Create(client *gophercloud.ServiceClient, serviceType string) (r CreateResult) {
 	b := map[string]string{"type": serviceType}
 	_, r.Err = client.Post(listURL(client), b, &r.Body, nil)
+	return
 }
 
 type ListOptsBuilder interface {
@@ -45,16 +46,19 @@
 // Get returns additional information about a service, given its ID.
 func Get(client *gophercloud.ServiceClient, serviceID string) (r GetResult) {
 	_, r.Err = client.Get(serviceURL(client, serviceID), &r.Body, nil)
+	return
 }
 
 // Update changes the service type of an existing service.
 func Update(client *gophercloud.ServiceClient, serviceID string, serviceType string) (r UpdateResult) {
 	b := map[string]string{"type": serviceType}
 	_, r.Err = client.Patch(serviceURL(client, serviceID), &b, &r.Body, nil)
+	return
 }
 
 // Delete removes an existing service.
 // It either deletes all associated endpoints, or fails until all endpoints are deleted.
 func Delete(client *gophercloud.ServiceClient, serviceID string) (r DeleteResult) {
 	_, r.Err = client.Delete(serviceURL(client, serviceID), nil)
+	return
 }
diff --git a/openstack/identity/v3/tokens/errors.go b/openstack/identity/v3/tokens/errors.go
index 4476109..9cc1d59 100644
--- a/openstack/identity/v3/tokens/errors.go
+++ b/openstack/identity/v3/tokens/errors.go
@@ -1,72 +1,139 @@
 package tokens
 
 import (
-	"errors"
 	"fmt"
+
+	"github.com/gophercloud/gophercloud"
 )
 
-func unacceptedAttributeErr(attribute string) error {
-	return fmt.Errorf("The base Identity V3 API does not accept authentication by %s", attribute)
+func unacceptedAttributeErr(attribute string) string {
+	return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute)
 }
 
-func redundantWithTokenErr(attribute string) error {
-	return fmt.Errorf("%s may not be provided when authenticating with a TokenID", attribute)
+func redundantWithTokenErr(attribute string) string {
+	return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute)
 }
 
-func redundantWithUserID(attribute string) error {
-	return fmt.Errorf("%s may not be provided when authenticating with a UserID", attribute)
+func redundantWithUserID(attribute string) string {
+	return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute)
 }
 
-var (
-	// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used.
-	ErrAPIKeyProvided = unacceptedAttributeErr("APIKey")
+// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used.
+type ErrAPIKeyProvided struct{ gophercloud.BaseError }
 
-	// ErrTenantIDProvided indicates that a TenantID was provided but can't be used.
-	ErrTenantIDProvided = unacceptedAttributeErr("TenantID")
+func (e ErrAPIKeyProvided) Error() string {
+	return unacceptedAttributeErr("APIKey")
+}
 
-	// ErrTenantNameProvided indicates that a TenantName was provided but can't be used.
-	ErrTenantNameProvided = unacceptedAttributeErr("TenantName")
+// ErrTenantIDProvided indicates that a TenantID was provided but can't be used.
+type ErrTenantIDProvided struct{ gophercloud.BaseError }
 
-	// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead.
-	ErrUsernameWithToken = redundantWithTokenErr("Username")
+func (e ErrTenantIDProvided) Error() string {
+	return unacceptedAttributeErr("TenantID")
+}
 
-	// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead.
-	ErrUserIDWithToken = redundantWithTokenErr("UserID")
+// ErrTenantNameProvided indicates that a TenantName was provided but can't be used.
+type ErrTenantNameProvided struct{ gophercloud.BaseError }
 
-	// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead.
-	ErrDomainIDWithToken = redundantWithTokenErr("DomainID")
+func (e ErrTenantNameProvided) Error() string {
+	return unacceptedAttributeErr("TenantName")
+}
 
-	// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s
-	ErrDomainNameWithToken = redundantWithTokenErr("DomainName")
+// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead.
+type ErrUsernameWithToken struct{ gophercloud.BaseError }
 
-	// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once.
-	ErrUsernameOrUserID = errors.New("Exactly one of Username and UserID must be provided for password authentication")
+func (e ErrUsernameWithToken) Error() string {
+	return redundantWithTokenErr("Username")
+}
 
-	// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used.
-	ErrDomainIDWithUserID = redundantWithUserID("DomainID")
+// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead.
+type ErrUserIDWithToken struct{ gophercloud.BaseError }
 
-	// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used.
-	ErrDomainNameWithUserID = redundantWithUserID("DomainName")
+func (e ErrUserIDWithToken) Error() string {
+	return redundantWithTokenErr("UserID")
+}
 
-	// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it.
-	// It may also indicate that both a DomainID and a DomainName were provided at once.
-	ErrDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName to authenticate by Username")
+// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead.
+type ErrDomainIDWithToken struct{ gophercloud.BaseError }
 
-	// ErrMissingPassword indicates that no password was provided and no token is available.
-	ErrMissingPassword = errors.New("You must provide a password to authenticate")
+func (e ErrDomainIDWithToken) Error() string {
+	return redundantWithTokenErr("DomainID")
+}
 
-	// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present.
-	ErrScopeDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName in a Scope with ProjectName")
+// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s
+type ErrDomainNameWithToken struct{ gophercloud.BaseError }
 
-	// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope.
-	ErrScopeProjectIDOrProjectName = errors.New("You must provide at most one of ProjectID or ProjectName in a Scope")
+func (e ErrDomainNameWithToken) Error() string {
+	return redundantWithTokenErr("DomainName")
+}
 
-	// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope.
-	ErrScopeProjectIDAlone = errors.New("ProjectID must be supplied alone in a Scope")
+// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once.
+type ErrUsernameOrUserID struct{ gophercloud.BaseError }
 
-	// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope.
-	ErrScopeDomainName = errors.New("DomainName must be supplied with a ProjectName or ProjectID in a Scope.")
+func (e ErrUsernameOrUserID) Error() string {
+	return "Exactly one of Username and UserID must be provided for password authentication"
+}
 
-	// ErrScopeEmpty indicates that no credentials were provided in a Scope.
-	ErrScopeEmpty = errors.New("You must provide either a Project or Domain in a Scope")
-)
+// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used.
+type ErrDomainIDWithUserID struct{ gophercloud.BaseError }
+
+func (e ErrDomainIDWithUserID) Error() string {
+	return redundantWithUserID("DomainID")
+}
+
+// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used.
+type ErrDomainNameWithUserID struct{ gophercloud.BaseError }
+
+func (e ErrDomainNameWithUserID) Error() string {
+	return redundantWithUserID("DomainName")
+}
+
+// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it.
+// It may also indicate that both a DomainID and a DomainName were provided at once.
+type ErrDomainIDOrDomainName struct{ gophercloud.BaseError }
+
+func (e ErrDomainIDOrDomainName) Error() string {
+	return "You must provide exactly one of DomainID or DomainName to authenticate by Username"
+}
+
+// ErrMissingPassword indicates that no password was provided and no token is available.
+type ErrMissingPassword struct{ gophercloud.BaseError }
+
+func (e ErrMissingPassword) Error() string {
+	return "You must provide a password to authenticate"
+}
+
+// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present.
+type ErrScopeDomainIDOrDomainName struct{ gophercloud.BaseError }
+
+func (e ErrScopeDomainIDOrDomainName) Error() string {
+	return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName"
+}
+
+// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope.
+type ErrScopeProjectIDOrProjectName struct{ gophercloud.BaseError }
+
+func (e ErrScopeProjectIDOrProjectName) Error() string {
+	return "You must provide at most one of ProjectID or ProjectName in a Scope"
+}
+
+// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope.
+type ErrScopeProjectIDAlone struct{ gophercloud.BaseError }
+
+func (e ErrScopeProjectIDAlone) Error() string {
+	return "ProjectID must be supplied alone in a Scope"
+}
+
+// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope.
+type ErrScopeDomainName struct{ gophercloud.BaseError }
+
+func (e ErrScopeDomainName) Error() string {
+	return "DomainName must be supplied with a ProjectName or ProjectID in a Scope"
+}
+
+// ErrScopeEmpty indicates that no credentials were provided in a Scope.
+type ErrScopeEmpty struct{ gophercloud.BaseError }
+
+func (e ErrScopeEmpty) Error() string {
+	return "You must provide either a Project or Domain in a Scope"
+}
diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go
index 6978186..12930f9 100644
--- a/openstack/identity/v3/tokens/requests.go
+++ b/openstack/identity/v3/tokens/requests.go
@@ -1,16 +1,275 @@
 package tokens
 
-import (
-	"net/http"
+import "github.com/gophercloud/gophercloud"
 
-	"github.com/gophercloud/gophercloud"
-)
+// Scope allows a created token to be limited to a specific domain or project.
+type Scope struct {
+	ProjectID   string `json:"scope.project.id,omitempty" not:"ProjectName,DomainID,DomainName"`
+	ProjectName string `json:"scope.project.name,omitempty"`
+	DomainID    string `json:"scope.project.id,omitempty" not:"ProjectName,ProjectID,DomainName"`
+	DomainName  string `json:"scope.project.id,omitempty"`
+}
 
 // AuthOptionsBuilder describes any argument that may be passed to the Create call.
 type AuthOptionsBuilder interface {
 	// ToTokenV3CreateMap assembles the Create request body, returning an error if parameters are
 	// missing or inconsistent.
-	ToTokenV3CreateMap(*gophercloud.ScopeOptsV3) (map[string]interface{}, error)
+	ToTokenV3CreateMap(*Scope) (map[string]interface{}, error)
+}
+
+type AuthOptions struct {
+	// IdentityEndpoint specifies the HTTP endpoint that is required to work with
+	// the Identity API of the appropriate version. While it's ultimately needed by
+	// all of the identity services, it will often be populated by a provider-level
+	// function.
+	IdentityEndpoint string `json:"-"`
+
+	// Username is required if using Identity V2 API. Consult with your provider's
+	// control panel to discover your account's username. In Identity V3, either
+	// UserID or a combination of Username and DomainID or DomainName are needed.
+	Username string `json:"username,omitempty"`
+	UserID   string `json:"id,omitempty"`
+
+	Password string `json:"password,omitempty"`
+
+	// At most one of DomainID and DomainName must be provided if using Username
+	// with Identity V3. Otherwise, either are optional.
+	DomainID   string `json:"id,omitempty"`
+	DomainName string `json:"name,omitempty"`
+
+	// The TenantID and TenantName fields are optional for the Identity V2 API.
+	// Some providers allow you to specify a TenantName instead of the TenantId.
+	// Some require both. Your provider's authentication policies will determine
+	// how these fields influence authentication.
+	TenantID   string `json:"tenantId,omitempty"`
+	TenantName string `json:"tenantName,omitempty"`
+
+	// AllowReauth should be set to true if you grant permission for Gophercloud to
+	// cache your credentials in memory, and to allow Gophercloud to attempt to
+	// re-authenticate automatically if/when your token expires.  If you set it to
+	// false, it will not cache these settings, but re-authentication will not be
+	// possible.  This setting defaults to false.
+	AllowReauth bool `json:"-"`
+
+	// TokenID allows users to authenticate (possibly as another user) with an
+	// authentication token ID.
+	TokenID string
+}
+
+func (opts AuthOptions) ToTokenV3CreateMap(scope *Scope) (map[string]interface{}, error) {
+	type domainReq struct {
+		ID   *string `json:"id,omitempty"`
+		Name *string `json:"name,omitempty"`
+	}
+
+	type projectReq struct {
+		Domain *domainReq `json:"domain,omitempty"`
+		Name   *string    `json:"name,omitempty"`
+		ID     *string    `json:"id,omitempty"`
+	}
+
+	type userReq struct {
+		ID       *string    `json:"id,omitempty"`
+		Name     *string    `json:"name,omitempty"`
+		Password string     `json:"password"`
+		Domain   *domainReq `json:"domain,omitempty"`
+	}
+
+	type passwordReq struct {
+		User userReq `json:"user"`
+	}
+
+	type tokenReq struct {
+		ID string `json:"id"`
+	}
+
+	type identityReq struct {
+		Methods  []string     `json:"methods"`
+		Password *passwordReq `json:"password,omitempty"`
+		Token    *tokenReq    `json:"token,omitempty"`
+	}
+
+	type scopeReq struct {
+		Domain  *domainReq  `json:"domain,omitempty"`
+		Project *projectReq `json:"project,omitempty"`
+	}
+
+	type authReq struct {
+		Identity identityReq `json:"identity"`
+		Scope    *scopeReq   `json:"scope,omitempty"`
+	}
+
+	type request struct {
+		Auth authReq `json:"auth"`
+	}
+
+	// Populate the request structure based on the provided arguments. Create and return an error
+	// if insufficient or incompatible information is present.
+	var req request
+
+	// Test first for unrecognized arguments.
+	if opts.TenantID != "" {
+		return nil, ErrTenantIDProvided{}
+	}
+	if opts.TenantName != "" {
+		return nil, ErrTenantNameProvided{}
+	}
+
+	if opts.Password == "" {
+		if opts.TokenID != "" {
+			// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
+			// parameters.
+			if opts.Username != "" {
+				return nil, ErrUsernameWithToken{}
+			}
+			if opts.UserID != "" {
+				return nil, ErrUserIDWithToken{}
+			}
+			if opts.DomainID != "" {
+				return nil, ErrDomainIDWithToken{}
+			}
+			if opts.DomainName != "" {
+				return nil, ErrDomainNameWithToken{}
+			}
+
+			// Configure the request for Token authentication.
+			req.Auth.Identity.Methods = []string{"token"}
+			req.Auth.Identity.Token = &tokenReq{
+				ID: opts.TokenID,
+			}
+		} else {
+			// If no password or token ID are available, authentication can't continue.
+			return nil, ErrMissingPassword{}
+		}
+	} else {
+		// Password authentication.
+		req.Auth.Identity.Methods = []string{"password"}
+
+		// At least one of Username and UserID must be specified.
+		if opts.Username == "" && opts.UserID == "" {
+			return nil, ErrUsernameOrUserID{}
+		}
+
+		if opts.Username != "" {
+			// If Username is provided, UserID may not be provided.
+			if opts.UserID != "" {
+				return nil, ErrUsernameOrUserID{}
+			}
+
+			// Either DomainID or DomainName must also be specified.
+			if opts.DomainID == "" && opts.DomainName == "" {
+				return nil, ErrDomainIDOrDomainName{}
+			}
+
+			if opts.DomainID != "" {
+				if opts.DomainName != "" {
+					return nil, ErrDomainIDOrDomainName{}
+				}
+
+				// Configure the request for Username and Password authentication with a DomainID.
+				req.Auth.Identity.Password = &passwordReq{
+					User: userReq{
+						Name:     &opts.Username,
+						Password: opts.Password,
+						Domain:   &domainReq{ID: &opts.DomainID},
+					},
+				}
+			}
+
+			if opts.DomainName != "" {
+				// Configure the request for Username and Password authentication with a DomainName.
+				req.Auth.Identity.Password = &passwordReq{
+					User: userReq{
+						Name:     &opts.Username,
+						Password: opts.Password,
+						Domain:   &domainReq{Name: &opts.DomainName},
+					},
+				}
+			}
+		}
+
+		if opts.UserID != "" {
+			// If UserID is specified, neither DomainID nor DomainName may be.
+			if opts.DomainID != "" {
+				return nil, ErrDomainIDWithUserID{}
+			}
+			if opts.DomainName != "" {
+				return nil, ErrDomainNameWithUserID{}
+			}
+
+			// Configure the request for UserID and Password authentication.
+			req.Auth.Identity.Password = &passwordReq{
+				User: userReq{ID: &opts.UserID, Password: opts.Password},
+			}
+		}
+	}
+
+	// Add a "scope" element if a Scope has been provided.
+	if scope != nil {
+		if scope.ProjectName != "" {
+			// ProjectName provided: either DomainID or DomainName must also be supplied.
+			// ProjectID may not be supplied.
+			if scope.DomainID == "" && scope.DomainName == "" {
+				return nil, ErrScopeDomainIDOrDomainName{}
+			}
+			if scope.ProjectID != "" {
+				return nil, ErrScopeProjectIDOrProjectName{}
+			}
+
+			if scope.DomainID != "" {
+				// ProjectName + DomainID
+				req.Auth.Scope = &scopeReq{
+					Project: &projectReq{
+						Name:   &scope.ProjectName,
+						Domain: &domainReq{ID: &scope.DomainID},
+					},
+				}
+			}
+
+			if scope.DomainName != "" {
+				// ProjectName + DomainName
+				req.Auth.Scope = &scopeReq{
+					Project: &projectReq{
+						Name:   &scope.ProjectName,
+						Domain: &domainReq{Name: &scope.DomainName},
+					},
+				}
+			}
+		} else if scope.ProjectID != "" {
+			// ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
+			if scope.DomainID != "" {
+				return nil, ErrScopeProjectIDAlone{}
+			}
+			if scope.DomainName != "" {
+				return nil, ErrScopeProjectIDAlone{}
+			}
+
+			// ProjectID
+			req.Auth.Scope = &scopeReq{
+				Project: &projectReq{ID: &scope.ProjectID},
+			}
+		} else if scope.DomainID != "" {
+			// DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
+			if scope.DomainName != "" {
+				return nil, ErrScopeDomainIDOrDomainName{}
+			}
+
+			// DomainID
+			req.Auth.Scope = &scopeReq{
+				Domain: &domainReq{ID: &scope.DomainID},
+			}
+		} else if scope.DomainName != "" {
+			return nil, ErrScopeDomainName{}
+		} else {
+			return nil, ErrScopeEmpty{}
+		}
+	}
+
+	b, err2 := gophercloud.BuildRequestBody(req, "")
+	if err2 != nil {
+		return nil, err2
+	}
+	return b, nil
 }
 
 func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
@@ -20,34 +279,36 @@
 }
 
 // Create authenticates and either generates a new token, or changes the Scope of an existing token.
-func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder, scopeOpts *gophercloud.ScopeOptsV3) (r CreateResult) {
+func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder, scopeOpts *Scope) (r CreateResult) {
 	b, err := opts.ToTokenV3CreateMap(scopeOpts)
 	if err != nil {
 		r.Err = err
 		return
 	}
-	var resp *http.Response
-	resp, r.Err = c.Post(tokenURL(c), b, &r.Body, nil)
+	resp, err := c.Post(tokenURL(c), b, &r.Body, nil)
 	if resp != nil {
+		r.Err = err
 		r.Header = resp.Header
 	}
+	return
 }
 
 // Get validates and retrieves information about another token.
 func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
-	var resp *http.Response
-	resp, r.Err = c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{
+	resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{
 		MoreHeaders: subjectTokenHeaders(c, token),
 		OkCodes:     []int{200, 203},
 	})
 	if resp != nil {
+		r.Err = err
 		r.Header = resp.Header
 	}
+	return
 }
 
 // Validate determines if a specified token is valid or not.
 func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
-	response, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{
+	resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{
 		MoreHeaders: subjectTokenHeaders(c, token),
 		OkCodes:     []int{204, 404},
 	})
@@ -55,7 +316,7 @@
 		return false, err
 	}
 
-	return response.StatusCode == 204, nil
+	return resp.StatusCode == 204, nil
 }
 
 // Revoke immediately makes specified token invalid.
@@ -63,4 +324,5 @@
 	_, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{
 		MoreHeaders: subjectTokenHeaders(c, token),
 	})
+	return
 }
diff --git a/openstack/identity/v3/tokens/requests_test.go b/openstack/identity/v3/tokens/requests_test.go
index a39a6f4..faa79e0 100644
--- a/openstack/identity/v3/tokens/requests_test.go
+++ b/openstack/identity/v3/tokens/requests_test.go
@@ -11,15 +11,13 @@
 )
 
 // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure.
-func authTokenPost(t *testing.T, options AuthOptionsBuilder, scope *gophercloud.ScopeOptsV3, requestJSON string) {
+func authTokenPost(t *testing.T, options AuthOptions, scope *Scope, requestJSON string) {
 	testhelper.SetupHTTP()
 	defer testhelper.TeardownHTTP()
 
 	client := gophercloud.ServiceClient{
-		ProviderClient: &gophercloud.ProviderClient{
-			TokenID: "12345abcdef",
-		},
-		Endpoint: testhelper.Endpoint(),
+		ProviderClient: &gophercloud.ProviderClient{},
+		Endpoint:       testhelper.Endpoint(),
 	}
 
 	testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
@@ -42,7 +40,7 @@
 	}
 }
 
-func authTokenPostErr(t *testing.T, options AuthOptionsBuilder, scope *gophercloud.ScopeOptsV3, includeToken bool, expectedErr error) {
+func authTokenPostErr(t *testing.T, options AuthOptions, scope *Scope, includeToken bool, expectedErr error) {
 	testhelper.SetupHTTP()
 	defer testhelper.TeardownHTTP()
 
@@ -64,10 +62,7 @@
 }
 
 func TestCreateUserIDAndPassword(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "me"
-	ao.Password = "squirrel!"
-	authTokenPost(t, ao, nil, `
+	authTokenPost(t, AuthOptions{UserID: "me", Password: "squirrel!"}, nil, `
 		{
 			"auth": {
 				"identity": {
@@ -82,10 +77,7 @@
 }
 
 func TestCreateUsernameDomainIDPassword(t *testing.T) {
-	ao := gophercloud.AuthOptions{DomainID: "abc123"}
-	ao.Username = "fakey"
-	ao.Password = "notpassword"
-	authTokenPost(t, ao, nil, `
+	authTokenPost(t, AuthOptions{Username: "fakey", Password: "notpassword", DomainID: "abc123"}, nil, `
 		{
 			"auth": {
 				"identity": {
@@ -106,10 +98,7 @@
 }
 
 func TestCreateUsernameDomainNamePassword(t *testing.T) {
-	ao := gophercloud.AuthOptions{DomainName: "spork.net"}
-	ao.Username = "frank"
-	ao.Password = "swordfish"
-	authTokenPost(t, ao, nil, `
+	authTokenPost(t, AuthOptions{Username: "frank", Password: "swordfish", DomainName: "spork.net"}, nil, `
 		{
 			"auth": {
 				"identity": {
@@ -130,7 +119,7 @@
 }
 
 func TestCreateTokenID(t *testing.T) {
-	authTokenPost(t, gophercloud.AuthOptions{TokenID: "12345abcdef"}, nil, `
+	authTokenPost(t, AuthOptions{TokenID: "12345abcdef"}, nil, `
 		{
 			"auth": {
 				"identity": {
@@ -145,11 +134,9 @@
 }
 
 func TestCreateProjectIDScope(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "fenris"
-	ao.Password = "g0t0h311"
-	scope := &gophercloud.ScopeOptsV3{ProjectID: "123456"}
-	authTokenPost(t, ao, scope, `
+	options := AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &Scope{ProjectID: "123456"}
+	authTokenPost(t, options, scope, `
 		{
 			"auth": {
 				"identity": {
@@ -172,11 +159,9 @@
 }
 
 func TestCreateDomainIDScope(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "fenris"
-	ao.Password = "g0t0h311"
-	scope := &gophercloud.ScopeOptsV3{DomainID: "1000"}
-	authTokenPost(t, ao, scope, `
+	options := AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &Scope{DomainID: "1000"}
+	authTokenPost(t, options, scope, `
 		{
 			"auth": {
 				"identity": {
@@ -199,11 +184,9 @@
 }
 
 func TestCreateProjectNameAndDomainIDScope(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "fenris"
-	ao.Password = "g0t0h311"
-	scope := &gophercloud.ScopeOptsV3{ProjectName: "world-domination", DomainID: "1000"}
-	authTokenPost(t, ao, scope, `
+	options := AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &Scope{ProjectName: "world-domination", DomainID: "1000"}
+	authTokenPost(t, options, scope, `
 		{
 			"auth": {
 				"identity": {
@@ -229,11 +212,9 @@
 }
 
 func TestCreateProjectNameAndDomainNameScope(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "fenris"
-	ao.Password = "g0t0h311"
-	scope := &gophercloud.ScopeOptsV3{ProjectName: "world-domination", DomainName: "evil-plans"}
-	authTokenPost(t, ao, scope, `
+	options := AuthOptions{UserID: "fenris", Password: "g0t0h311"}
+	scope := &Scope{ProjectName: "world-domination", DomainName: "evil-plans"}
+	authTokenPost(t, options, scope, `
 		{
 			"auth": {
 				"identity": {
@@ -278,10 +259,8 @@
 		}`)
 	})
 
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "me"
-	ao.Password = "shhh"
-	token, err := Create(&client, ao, nil).Extract()
+	options := AuthOptions{UserID: "me", Password: "shhh"}
+	token, err := Create(&client, options, nil).Extract()
 	if err != nil {
 		t.Fatalf("Create returned an error: %v", err)
 	}
@@ -292,127 +271,123 @@
 }
 
 func TestCreateFailureEmptyAuth(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{}, nil, false, ErrMissingPassword)
+	authTokenPostErr(t, AuthOptions{}, nil, false, ErrMissingPassword{})
+}
+
+func TestCreateFailureTenantID(t *testing.T) {
+	authTokenPostErr(t, AuthOptions{TenantID: "something"}, nil, false, ErrTenantIDProvided{})
+}
+
+func TestCreateFailureTenantName(t *testing.T) {
+	authTokenPostErr(t, AuthOptions{TenantName: "something"}, nil, false, ErrTenantNameProvided{})
 }
 
 func TestCreateFailureTokenIDUsername(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.Username = "somthing"
-	authTokenPostErr(t, ao, nil, true, ErrUsernameWithToken)
+	authTokenPostErr(t, AuthOptions{Username: "something", TokenID: "12345"}, nil, true, ErrUsernameWithToken{})
 }
 
 func TestCreateFailureTokenIDUserID(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{UserID: "something"}, nil, true, ErrUserIDWithToken)
+	authTokenPostErr(t, AuthOptions{UserID: "something", TokenID: "12345"}, nil, true, ErrUserIDWithToken{})
 }
 
 func TestCreateFailureTokenIDDomainID(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{DomainID: "something"}, nil, true, ErrDomainIDWithToken)
+	authTokenPostErr(t, AuthOptions{DomainID: "something", TokenID: "12345"}, nil, true, ErrDomainIDWithToken{})
 }
 
 func TestCreateFailureTokenIDDomainName(t *testing.T) {
-	authTokenPostErr(t, gophercloud.AuthOptions{DomainName: "something"}, nil, true, ErrDomainNameWithToken)
+	authTokenPostErr(t, AuthOptions{DomainName: "something", TokenID: "12345"}, nil, true, ErrDomainNameWithToken{})
 }
 
 func TestCreateFailureMissingUser(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.Password = "supersecure"
-	authTokenPostErr(t, ao, nil, false, ErrUsernameOrUserID)
+	options := AuthOptions{Password: "supersecure"}
+	authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID{})
 }
 
 func TestCreateFailureBothUser(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "redundancy"
-	ao.Username = "oops"
-	ao.Password = "supersecure"
-	authTokenPostErr(t, ao, nil, false, ErrUsernameOrUserID)
+	options := AuthOptions{
+		Password: "supersecure",
+		Username: "oops",
+		UserID:   "redundancy",
+	}
+	authTokenPostErr(t, options, nil, false, ErrUsernameOrUserID{})
 }
 
 func TestCreateFailureMissingDomain(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.Username = "notuniqueenough"
-	ao.Password = "supersecure"
-	authTokenPostErr(t, ao, nil, false, ErrDomainIDOrDomainName)
+	options := AuthOptions{
+		Password: "supersecure",
+		Username: "notuniqueenough",
+	}
+	authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName{})
 }
 
 func TestCreateFailureBothDomain(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.Username = "someone"
-	ao.Password = "supersecure"
-	ao.DomainID = "hurf"
-	ao.DomainName = "durf"
-	authTokenPostErr(t, ao, nil, false, ErrDomainIDOrDomainName)
+	options := AuthOptions{
+		Password:   "supersecure",
+		Username:   "someone",
+		DomainID:   "hurf",
+		DomainName: "durf",
+	}
+	authTokenPostErr(t, options, nil, false, ErrDomainIDOrDomainName{})
 }
 
 func TestCreateFailureUserIDDomainID(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "100"
-	ao.Password = "stuff"
-	ao.DomainID = "oops"
-	authTokenPostErr(t, ao, nil, false, ErrDomainIDWithUserID)
+	options := AuthOptions{
+		UserID:   "100",
+		Password: "stuff",
+		DomainID: "oops",
+	}
+	authTokenPostErr(t, options, nil, false, ErrDomainIDWithUserID{})
 }
 
 func TestCreateFailureUserIDDomainName(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "100"
-	ao.Password = "sssh"
-	ao.DomainName = "oops"
-	authTokenPostErr(t, ao, nil, false, ErrDomainNameWithUserID)
+	options := AuthOptions{
+		UserID:     "100",
+		Password:   "sssh",
+		DomainName: "oops",
+	}
+	authTokenPostErr(t, options, nil, false, ErrDomainNameWithUserID{})
 }
 
 func TestCreateFailureScopeProjectNameAlone(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "myself"
-	ao.Password = "swordfish"
-	scope := &gophercloud.ScopeOptsV3{ProjectName: "notenough"}
-	authTokenPostErr(t, ao, scope, false, ErrScopeDomainIDOrDomainName)
+	options := AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &Scope{ProjectName: "notenough"}
+	authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName{})
 }
 
 func TestCreateFailureScopeProjectNameAndID(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "myself"
-	ao.Password = "swordfish"
-	scope := &gophercloud.ScopeOptsV3{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"}
-	authTokenPostErr(t, ao, scope, false, ErrScopeProjectIDOrProjectName)
+	options := AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &Scope{ProjectName: "whoops", ProjectID: "toomuch", DomainID: "1234"}
+	authTokenPostErr(t, options, scope, false, ErrScopeProjectIDOrProjectName{})
 }
 
 func TestCreateFailureScopeProjectIDAndDomainID(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "myself"
-	ao.Password = "swordfish"
-	scope := &gophercloud.ScopeOptsV3{ProjectID: "toomuch", DomainID: "notneeded"}
-	authTokenPostErr(t, ao, scope, false, ErrScopeProjectIDAlone)
+	options := AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &Scope{ProjectID: "toomuch", DomainID: "notneeded"}
+	authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone{})
 }
 
 func TestCreateFailureScopeProjectIDAndDomainNAme(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "myself"
-	ao.Password = "swordfish"
-	scope := &gophercloud.ScopeOptsV3{ProjectID: "toomuch", DomainName: "notneeded"}
-	authTokenPostErr(t, ao, scope, false, ErrScopeProjectIDAlone)
+	options := AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &Scope{ProjectID: "toomuch", DomainName: "notneeded"}
+	authTokenPostErr(t, options, scope, false, ErrScopeProjectIDAlone{})
 }
 
 func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "myself"
-	ao.Password = "swordfish"
-	scope := &gophercloud.ScopeOptsV3{DomainID: "toomuch", DomainName: "notneeded"}
-	authTokenPostErr(t, ao, scope, false, ErrScopeDomainIDOrDomainName)
+	options := AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &Scope{DomainID: "toomuch", DomainName: "notneeded"}
+	authTokenPostErr(t, options, scope, false, ErrScopeDomainIDOrDomainName{})
 }
 
 func TestCreateFailureScopeDomainNameAlone(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "myself"
-	ao.Password = "swordfish"
-	scope := &gophercloud.ScopeOptsV3{DomainName: "notenough"}
-	authTokenPostErr(t, ao, scope, false, ErrScopeDomainName)
+	options := AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &Scope{DomainName: "notenough"}
+	authTokenPostErr(t, options, scope, false, ErrScopeDomainName{})
 }
 
 func TestCreateFailureEmptyScope(t *testing.T) {
-	ao := gophercloud.AuthOptions{}
-	ao.UserID = "myself"
-	ao.Password = "swordfish"
-	scope := &gophercloud.ScopeOptsV3{}
-	authTokenPostErr(t, ao, scope, false, ErrScopeEmpty)
+	options := AuthOptions{UserID: "myself", Password: "swordfish"}
+	scope := &Scope{}
+	authTokenPostErr(t, options, scope, false, ErrScopeEmpty{})
 }
 
 func TestGetRequest(t *testing.T) {