dsl struct tags; wip
diff --git a/openstack/identity/v2/extensions/admin/roles/requests.go b/openstack/identity/v2/extensions/admin/roles/requests.go
index 891ab62..d80c53f 100644
--- a/openstack/identity/v2/extensions/admin/roles/requests.go
+++ b/openstack/identity/v2/extensions/admin/roles/requests.go
@@ -8,26 +8,25 @@
 // List is the operation responsible for listing all available global roles
 // that a user can adopt.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page {
 		return RolePage{pagination.SinglePageBase(r)}
-	}
-	return pagination.NewPager(client, rootURL(client), createPage)
+	})
 }
 
-// AddUserRole is the operation responsible for assigning a particular role to
+// AddUser is the operation responsible for assigning a particular role to
 // a user. This is confined to the scope of the user's tenant - so the tenant
 // ID is a required argument.
-func AddUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
-	var result UserRoleResult
-	_, result.Err = client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, nil)
-	return result
+func AddUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
+	var r UserRoleResult
+	_, r.Err = client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, nil)
+	return r
 }
 
-// DeleteUserRole is the operation responsible for deleting a particular role
+// DeleteUser is the operation responsible for deleting a particular role
 // from a user. This is confined to the scope of the user's tenant - so the
 // tenant ID is a required argument.
-func DeleteUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
-	var result UserRoleResult
-	_, result.Err = client.Delete(userRoleURL(client, tenantID, userID, roleID), nil)
-	return result
+func DeleteUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
+	var r UserRoleResult
+	_, r.Err = client.Delete(userRoleURL(client, tenantID, userID, roleID), nil)
+	return r
 }
diff --git a/openstack/identity/v2/extensions/admin/roles/requests_test.go b/openstack/identity/v2/extensions/admin/roles/requests_test.go
index af809a5..cf3402d 100644
--- a/openstack/identity/v2/extensions/admin/roles/requests_test.go
+++ b/openstack/identity/v2/extensions/admin/roles/requests_test.go
@@ -41,24 +41,24 @@
 	th.AssertEquals(t, 1, count)
 }
 
-func TestAddUserRole(t *testing.T) {
+func TestAddUser(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
 
 	MockAddUserRoleResponse(t)
 
-	err := AddUserRole(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
+	err := AddUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
 
 	th.AssertNoErr(t, err)
 }
 
-func TestDeleteUserRole(t *testing.T) {
+func TestDeleteUser(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
 
 	MockDeleteUserRoleResponse(t)
 
-	err := DeleteUserRole(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
+	err := DeleteUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr()
 
 	th.AssertNoErr(t, err)
 }
diff --git a/openstack/identity/v2/extensions/delegate.go b/openstack/identity/v2/extensions/delegate.go
index 4b2c6a7..cf6cc81 100644
--- a/openstack/identity/v2/extensions/delegate.go
+++ b/openstack/identity/v2/extensions/delegate.go
@@ -14,23 +14,19 @@
 // IsEmpty returns true if the current page contains at least one Extension.
 func (page ExtensionPage) IsEmpty() (bool, error) {
 	is, err := ExtractExtensions(page)
-	if err != nil {
-		return true, err
-	}
-	return len(is) == 0, nil
+	return len(is) == 0, err
 }
 
 // ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the
 // elements into a slice of Extension structs.
 func ExtractExtensions(page pagination.Page) ([]common.Extension, error) {
-	r := page.(ExtensionPage)
 	// Identity v2 adds an intermediate "values" object.
 	var s struct {
 		Extensions struct {
 			Values []common.Extension `json:"values"`
 		} `json:"extensions"`
 	}
-	err := r.ExtractInto(&s)
+	err := page.(ExtensionPage).ExtractInto(&s)
 	return s.Extensions.Values, err
 }
 
diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go
index d4ea632..b9d7de6 100644
--- a/openstack/identity/v2/tenants/requests.go
+++ b/openstack/identity/v2/tenants/requests.go
@@ -9,17 +9,12 @@
 type ListOpts struct {
 	// Marker is the ID of the last Tenant on the previous page.
 	Marker string `q:"marker"`
-
 	// Limit specifies the page size.
 	Limit int `q:"limit"`
 }
 
 // List enumerates the Tenants to which the current token has access.
 func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
-		return TenantPage{pagination.LinkedPageBase{PageResult: r}}
-	}
-
 	url := listURL(client)
 	if opts != nil {
 		q, err := gophercloud.BuildQueryString(opts)
@@ -28,6 +23,7 @@
 		}
 		url += q.String()
 	}
-
-	return pagination.NewPager(client, url, createPage)
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return TenantPage{pagination.LinkedPageBase{PageResult: r}}
+	})
 }
diff --git a/openstack/identity/v2/tokens/errors.go b/openstack/identity/v2/tokens/errors.go
deleted file mode 100644
index 12570f5..0000000
--- a/openstack/identity/v2/tokens/errors.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package tokens
-
-import "fmt"
-
-var (
-	// ErrUserIDProvided is returned if you attempt to authenticate with a UserID.
-	ErrUserIDProvided = unacceptedAttributeErr("UserID")
-
-	// ErrAPIKeyProvided is returned if you attempt to authenticate with an APIKey.
-	ErrAPIKeyProvided = unacceptedAttributeErr("APIKey")
-
-	// ErrDomainIDProvided is returned if you attempt to authenticate with a DomainID.
-	ErrDomainIDProvided = unacceptedAttributeErr("DomainID")
-
-	// ErrDomainNameProvided is returned if you attempt to authenticate with a DomainName.
-	ErrDomainNameProvided = unacceptedAttributeErr("DomainName")
-)
-
-func unacceptedAttributeErr(attribute string) error {
-	return fmt.Errorf("The base Identity V2 API does not accept authentication by %s", attribute)
-}
diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go
index 6064bc7..a2a0685 100644
--- a/openstack/identity/v2/tokens/requests.go
+++ b/openstack/identity/v2/tokens/requests.go
@@ -4,74 +4,9 @@
 
 // AuthOptionsBuilder describes any argument that may be passed to the Create call.
 type AuthOptionsBuilder interface {
-
 	// ToTokenCreateMap assembles the Create request body, returning an error if parameters are
 	// missing or inconsistent.
-	ToTokenCreateMap() (map[string]interface{}, error)
-}
-
-// AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
-// interface.
-type AuthOptions struct {
-	gophercloud.AuthOptions
-}
-
-// WrapOptions embeds a root AuthOptions struct in a package-specific one.
-func WrapOptions(original gophercloud.AuthOptions) AuthOptions {
-	return AuthOptions{AuthOptions: original}
-}
-
-// ToTokenCreateMap converts AuthOptions into nested maps that can be serialized into a JSON
-// request.
-func (auth AuthOptions) ToTokenCreateMap() (map[string]interface{}, error) {
-	// Error out if an unsupported auth option is present.
-	if auth.UserID != "" {
-		return nil, ErrUserIDProvided
-	}
-	if auth.APIKey != "" {
-		return nil, ErrAPIKeyProvided
-	}
-	if auth.DomainID != "" {
-		return nil, ErrDomainIDProvided
-	}
-	if auth.DomainName != "" {
-		return nil, ErrDomainNameProvided
-	}
-
-	// Populate the request map.
-	authMap := make(map[string]interface{})
-
-	if auth.Username != "" {
-		if auth.Password == "" {
-			err := gophercloud.ErrMissingInput{}
-			err.Function = "tokens.ToTokenCreateMap"
-			err.Argument = "tokens.AuthOptions.Password"
-			return nil, err
-		}
-		authMap["passwordCredentials"] = map[string]interface{}{
-			"username": auth.Username,
-			"password": auth.Password,
-		}
-	} else if auth.TokenID != "" {
-		authMap["token"] = map[string]interface{}{
-			"id": auth.TokenID,
-		}
-	} else {
-		err := gophercloud.ErrMissingInput{}
-		err.Function = "tokens.ToTokenCreateMap"
-		err.Argument = "tokens.AuthOptions.Username/tokens.AuthOptions.TokenID"
-		err.Info = "You must provide either username/password or tenantID/token values."
-		return nil, err
-	}
-
-	if auth.TenantID != "" {
-		authMap["tenantId"] = auth.TenantID
-	}
-	if auth.TenantName != "" {
-		authMap["tenantName"] = auth.TenantName
-	}
-
-	return map[string]interface{}{"auth": authMap}, nil
+	ToTokenV2CreateMap() (map[string]interface{}, error)
 }
 
 // Create authenticates to the identity service and attempts to acquire a Token.
@@ -79,23 +14,23 @@
 // Generally, rather than interact with this call directly, end users should call openstack.AuthenticatedClient(),
 // which abstracts all of the gory details about navigating service catalogs and such.
 func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) CreateResult {
-	request, err := auth.ToTokenCreateMap()
+	var r CreateResult
+	b, err := auth.ToTokenV2CreateMap()
 	if err != nil {
-		return CreateResult{gophercloud.Result{Err: err}}
+		r.Err = err
+		return r
 	}
-
-	var result CreateResult
-	_, result.Err = client.Post(CreateURL(client), request, &result.Body, &gophercloud.RequestOpts{
+	_, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 203},
 	})
-	return result
+	return r
 }
 
 // Get validates and retrieves information for user's token.
 func Get(client *gophercloud.ServiceClient, token string) GetResult {
-	var result GetResult
-	_, result.Err = client.Get(GetURL(client, token), &result.Body, &gophercloud.RequestOpts{
+	var r GetResult
+	_, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200, 203},
 	})
-	return result
+	return r
 }
diff --git a/openstack/identity/v2/tokens/requests_test.go b/openstack/identity/v2/tokens/requests_test.go
index 9b3273e..d25c2d7 100644
--- a/openstack/identity/v2/tokens/requests_test.go
+++ b/openstack/identity/v2/tokens/requests_test.go
@@ -1,6 +1,7 @@
 package tokens
 
 import (
+	"reflect"
 	"testing"
 
 	"github.com/gophercloud/gophercloud"
@@ -12,8 +13,7 @@
 	th.SetupHTTP()
 	defer th.TeardownHTTP()
 	HandleTokenPost(t, requestJSON)
-
-	return Create(client.ServiceClient(), AuthOptions{options})
+	return Create(client.ServiceClient(), options)
 }
 
 func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr error) {
@@ -21,15 +21,30 @@
 	defer th.TeardownHTTP()
 	HandleTokenPost(t, "")
 
-	actualErr := Create(client.ServiceClient(), AuthOptions{options}).Err
-	th.CheckDeepEquals(t, expectedErr, actualErr)
+	actualErr := Create(client.ServiceClient(), options).Err
+	th.CheckDeepEquals(t, reflect.TypeOf(expectedErr), reflect.TypeOf(actualErr))
+}
+
+func TestCreateWithToken(t *testing.T) {
+	options := gophercloud.AuthOptions{
+		TokenID: "cbc36478b0bd8e67e89469c7749d4127",
+	}
+
+	IsSuccessful(t, tokenPost(t, options, `
+    {
+      "auth": {
+        "token": {
+          "id": "cbc36478b0bd8e67e89469c7749d4127"
+        }
+      }
+    }
+  `))
 }
 
 func TestCreateWithPassword(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "swordfish",
-	}
+	options := gophercloud.AuthOptions{}
+	options.Username = "me"
+	options.Password = "swordfish"
 
 	IsSuccessful(t, tokenPost(t, options, `
     {
@@ -44,11 +59,10 @@
 }
 
 func TestCreateTokenWithTenantID(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "opensesame",
-		TenantID: "fc394f2ab2df4114bde39905f800dc57",
-	}
+	options := gophercloud.AuthOptions{}
+	options.Username = "me"
+	options.Password = "opensesame"
+	options.TenantID = "fc394f2ab2df4114bde39905f800dc57"
 
 	IsSuccessful(t, tokenPost(t, options, `
     {
@@ -64,11 +78,10 @@
 }
 
 func TestCreateTokenWithTenantName(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username:   "me",
-		Password:   "opensesame",
-		TenantName: "demo",
-	}
+	options := gophercloud.AuthOptions{}
+	options.Username = "me"
+	options.Password = "opensesame"
+	options.TenantName = "demo"
 
 	IsSuccessful(t, tokenPost(t, options, `
     {
@@ -83,63 +96,21 @@
   `))
 }
 
-func TestProhibitUserID(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		UserID:   "1234",
-		Password: "thing",
-	}
-
-	tokenPostErr(t, options, ErrUserIDProvided)
-}
-
-func TestProhibitAPIKey(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "thing",
-		APIKey:   "123412341234",
-	}
-
-	tokenPostErr(t, options, ErrAPIKeyProvided)
-}
-
-func TestProhibitDomainID(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-		Password: "thing",
-		DomainID: "1234",
-	}
-
-	tokenPostErr(t, options, ErrDomainIDProvided)
-}
-
-func TestProhibitDomainName(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username:   "me",
-		Password:   "thing",
-		DomainName: "wat",
-	}
-
-	tokenPostErr(t, options, ErrDomainNameProvided)
-}
-
 func TestRequireUsername(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Password: "thing",
-	}
+	options := gophercloud.AuthOptions{}
+	options.Password = "thing"
+
 	expected := gophercloud.ErrMissingInput{}
-	expected.Function = "tokens.ToTokenCreateMap"
 	expected.Argument = "tokens.AuthOptions.Username/tokens.AuthOptions.TokenID"
 	expected.Info = "You must provide either username/password or tenantID/token values."
 	tokenPostErr(t, options, expected)
 }
 
 func TestRequirePassword(t *testing.T) {
-	options := gophercloud.AuthOptions{
-		Username: "me",
-	}
+	options := gophercloud.AuthOptions{}
+	options.Username = "me"
+
 	expected := gophercloud.ErrMissingInput{}
-	expected.Function = "tokens.ToTokenCreateMap"
 	expected.Argument = "tokens.AuthOptions.Password"
 	tokenPostErr(t, options, expected)
 }
diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go
index 7fa5fc3..f62f979 100644
--- a/openstack/identity/v2/users/requests.go
+++ b/openstack/identity/v2/users/requests.go
@@ -7,41 +7,25 @@
 
 // List lists the existing users.
 func List(client *gophercloud.ServiceClient) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page {
 		return UserPage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, rootURL(client), createPage)
+	})
 }
 
-// EnabledState represents whether the user is enabled or not.
-type EnabledState *bool
-
-// Useful variables to use when creating or updating users.
-var (
-	iTrue  = true
-	iFalse = false
-
-	Enabled  EnabledState = &iTrue
-	Disabled EnabledState = &iFalse
-)
-
 // CommonOpts are the parameters that are shared between CreateOpts and
 // UpdateOpts
 type CommonOpts struct {
 	// Either a name or username is required. When provided, the value must be
 	// unique or a 409 conflict error will be returned. If you provide a name but
 	// omit a username, the latter will be set to the former; and vice versa.
-	Name, Username string
-
+	Name     string `json:"name,omitempty"`
+	Username string `json:"username,omitempty"`
 	// The ID of the tenant to which you want to assign this user.
-	TenantID string
-
+	TenantID string `json:"tenant_id,omitempty"`
 	// Indicates whether this user is enabled or not.
-	Enabled EnabledState
-
+	Enabled *bool `json:"enabled,omitempty"`
 	// The email address of this user.
-	Email string
+	Email string `json:"email,omitempty"`
 }
 
 // CreateOpts represents the options needed when creating new users.
@@ -54,33 +38,13 @@
 
 // ToUserCreateMap assembles a request body based on the contents of a CreateOpts.
 func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) {
-	m := make(map[string]interface{})
-
 	if opts.Name == "" && opts.Username == "" {
 		err := gophercloud.ErrMissingInput{}
-		err.Function = "users.ToUserCreateMap"
 		err.Argument = "users.CreateOpts.Name/users.CreateOpts.Username"
 		err.Info = "Either a Name or Username must be provided"
-		return m, err
+		return nil, err
 	}
-
-	if opts.Name != "" {
-		m["name"] = opts.Name
-	}
-	if opts.Username != "" {
-		m["username"] = opts.Username
-	}
-	if opts.Enabled != nil {
-		m["enabled"] = &opts.Enabled
-	}
-	if opts.Email != "" {
-		m["email"] = opts.Email
-	}
-	if opts.TenantID != "" {
-		m["tenant_id"] = opts.TenantID
-	}
-
-	return map[string]interface{}{"user": m}, nil
+	return gophercloud.BuildRequestBody(opts, "user")
 }
 
 // Create is the operation responsible for creating new users.
@@ -109,57 +73,41 @@
 
 // UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
 type UpdateOptsBuilder interface {
-	ToUserUpdateMap() map[string]interface{}
+	ToUserUpdateMap() (map[string]interface{}, error)
 }
 
 // UpdateOpts specifies the base attributes that may be updated on an existing server.
 type UpdateOpts CommonOpts
 
 // ToUserUpdateMap formats an UpdateOpts structure into a request body.
-func (opts UpdateOpts) ToUserUpdateMap() map[string]interface{} {
-	m := make(map[string]interface{})
-
-	if opts.Name != "" {
-		m["name"] = opts.Name
-	}
-	if opts.Username != "" {
-		m["username"] = opts.Username
-	}
-	if opts.Enabled != nil {
-		m["enabled"] = &opts.Enabled
-	}
-	if opts.Email != "" {
-		m["email"] = opts.Email
-	}
-	if opts.TenantID != "" {
-		m["tenant_id"] = opts.TenantID
-	}
-
-	return map[string]interface{}{"user": m}
+func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) {
+	return gophercloud.BuildRequestBody(opts, "user")
 }
 
 // Update is the operation responsible for updating exist users by their UUID.
 func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
-	var result UpdateResult
-	reqBody := opts.ToUserUpdateMap()
-	_, result.Err = client.Put(ResourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
+	var r UpdateResult
+	b, err := opts.ToUserUpdateMap()
+	if err != nil {
+		r.Err = err
+		return r
+	}
+	_, r.Err = client.Put(ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
 		OkCodes: []int{200},
 	})
-	return result
+	return r
 }
 
 // Delete is the operation responsible for permanently deleting an API user.
 func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
-	var result DeleteResult
-	_, result.Err = client.Delete(ResourceURL(client, id), nil)
-	return result
+	var r DeleteResult
+	_, r.Err = client.Delete(ResourceURL(client, id), nil)
+	return r
 }
 
 // ListRoles lists the existing roles that can be assigned to users.
 func ListRoles(client *gophercloud.ServiceClient, tenantID, userID string) pagination.Pager {
-	createPage := func(r pagination.PageResult) pagination.Page {
+	return pagination.NewPager(client, listRolesURL(client, tenantID, userID), func(r pagination.PageResult) pagination.Page {
 		return RolePage{pagination.SinglePageBase(r)}
-	}
-
-	return pagination.NewPager(client, listRolesURL(client, tenantID, userID), createPage)
+	})
 }
diff --git a/openstack/identity/v2/users/requests_test.go b/openstack/identity/v2/users/requests_test.go
index 8604ab1..0e03653 100644
--- a/openstack/identity/v2/users/requests_test.go
+++ b/openstack/identity/v2/users/requests_test.go
@@ -6,6 +6,7 @@
 	"github.com/gophercloud/gophercloud/pagination"
 	th "github.com/gophercloud/gophercloud/testhelper"
 	"github.com/gophercloud/gophercloud/testhelper/client"
+	"github.com/jrperritt/gophercloud"
 )
 
 func TestList(t *testing.T) {
@@ -19,10 +20,7 @@
 	err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
 		count++
 		actual, err := ExtractUsers(page)
-		if err != nil {
-			t.Errorf("Failed to extract users: %v", err)
-			return false, err
-		}
+		th.AssertNoErr(t, err)
 
 		expected := []User{
 			User{
@@ -42,12 +40,9 @@
 				TenantID: "12345",
 			},
 		}
-
 		th.CheckDeepEquals(t, expected, actual)
-
 		return true, nil
 	})
-
 	th.AssertNoErr(t, err)
 	th.AssertEquals(t, 1, count)
 }
@@ -61,7 +56,7 @@
 	opts := CreateOpts{
 		Name:     "new_user",
 		TenantID: "12345",
-		Enabled:  Disabled,
+		Enabled:  gophercloud.Disabled,
 		Email:    "new_user@foo.com",
 	}
 
@@ -109,7 +104,7 @@
 	id := "c39e3de9be2d4c779f1dfd6abacc176d"
 	opts := UpdateOpts{
 		Name:    "new_name",
-		Enabled: Enabled,
+		Enabled: gophercloud.Enabled,
 		Email:   "new_email@foo.com",
 	}