What happened?
When authenticating via clouds.yaml with a user and project in different domains, gophercloud v2
ignores the distinction between user_domain_name and project_domain_name and collapses both into
a single DomainName field:
// openstack/config/clouds/clouds.go
DomainName: coalesce(options.domainName, cloud.AuthInfo.UserDomainName, cloud.AuthInfo.ProjectDomainName, cloud.AuthInfo.DomainName),
This single DomainName value is then used for both the user identity lookup and the project
scope in the resulting gophercloud.AuthOptions. The clouds.yaml spec explicitly supports separate
user_domain_name and project_domain_name for exactly this scenario, but gophercloud silently
discards the distinction.
For example, with the following clouds.yaml:
clouds:
openstack:
auth:
auth_url: https://identity.example.com/v3
username: myuser
password: mypassword
user_domain_name: Default # user lives in Default domain
project_name: myproject
project_domain_name: some_domain # project lives in some_domain
auth_type: password
The coalesce picks user_domain_name: Default (first non-empty value) and uses it for both the user
lookup and the project scope. Keystone then looks for myproject in Default, finds nothing, and
returns a 401 Unauthorized — with no indication that the domain scoping is the cause.
All three combinations fail:
user_domain_name: Default only → project scoped to Default → project not found → 401
project_domain_name: some_domain only → user looked up in some_domain → user not found → 401
- both set →
coalesce picks the first, same result as above
The only workaround is to use IDs instead of names (user_domain_id + project_id), since
DomainID and TenantID are separate fields that are not coalesced together.
Expectation:
user_domain_name and project_domain_name should be treated as distinct fields, mapping to
separate parts of the Keystone v3 auth request — user_domain_name scoping the user identity
lookup, and project_domain_name scoping the project. This is how the OpenStack clouds.yaml
spec defines them and how direct use of tokens.AuthOptions already handles it via the Scope
struct:
// tokens.AuthOptions correctly separates user domain from project domain
opts := &tokens.AuthOptions{
Username: "myuser",
Password: "mypassword",
DomainName: "Default", // user domain
Scope: tokens.Scope{
ProjectName: "myproject",
DomainName: "some_domain", // project domain — separate field
},
}
The clouds.yaml parsing path should produce equivalent behaviour to constructing tokens.AuthOptions
directly.
Version
v2 (current)
What happened?
When authenticating via
clouds.yamlwith a user and project in different domains, gophercloud v2ignores the distinction between
user_domain_nameandproject_domain_nameand collapses both intoa single
DomainNamefield:This single
DomainNamevalue is then used for both the user identity lookup and the projectscope in the resulting
gophercloud.AuthOptions. Theclouds.yamlspec explicitly supports separateuser_domain_nameandproject_domain_namefor exactly this scenario, but gophercloud silentlydiscards the distinction.
For example, with the following
clouds.yaml:The coalesce picks
user_domain_name: Default(first non-empty value) and uses it for both the userlookup and the project scope. Keystone then looks for
myprojectinDefault, finds nothing, andreturns a 401 Unauthorized — with no indication that the domain scoping is the cause.
All three combinations fail:
user_domain_name: Defaultonly → project scoped toDefault→ project not found → 401project_domain_name: some_domainonly → user looked up insome_domain→ user not found → 401coalescepicks the first, same result as aboveThe only workaround is to use IDs instead of names (
user_domain_id+project_id), sinceDomainIDandTenantIDare separate fields that are not coalesced together.Expectation:
user_domain_nameandproject_domain_nameshould be treated as distinct fields, mapping toseparate parts of the Keystone v3 auth request —
user_domain_namescoping the user identitylookup, and
project_domain_namescoping the project. This is how the OpenStackclouds.yamlspec defines them and how direct use of
tokens.AuthOptionsalready handles it via theScopestruct:
The
clouds.yamlparsing path should produce equivalent behaviour to constructingtokens.AuthOptionsdirectly.
Version
v2 (current)