Describe the bug
When using a custom Identity Provider (IDP) with SAP BTP Cloud Foundry, the UAA token endpoint requires a login_hint parameter to route the password grant to the correct IDP origin. The underlying go-cfclient/v3 library already supports this via config.Origin(origin), which appends ?login_hint={"origin":"<value>"} to the token URL. However, the provider's CfCredentials struct and GetCredentialConfig function do not expose or pass this parameter.
This means all CF resources fail with invalid_grant: User authentication failed when the credentials belong to a user from a custom IDP (e.g., SAP IAS with a custom tenant).
Tested with Provider Version
v0.3.3
To Reproduce
- Configure a ProviderConfig with credentials for a user that authenticates via a custom IDP (not
sap.ids):
{
"email": "user@example.com",
"password": "secret"
}
- Create any CF managed resource (e.g.,
Organization with Observe management policy)
- The resource fails to sync with:
cannot create new client: cannot config cloudfoundry client:
oauth2: "invalid_client" "{\"error\":\"invalid_grant\",\"error_description\":\"User authentication failed.\"}"
Expected behavior
The provider should support an origin field in the credentials secret (or in the ProviderConfig spec) and pass it to the go-cfclient library via config.Origin(). This would allow authentication with custom IDPs.
Root Cause Analysis
The go-cfclient/v3 library already fully supports this. In config/options.go:
func Origin(origin string) Option {
return func(c *Config) error {
c.origin = origin
return nil
}
}
And in config/config.go, when origin is set, it appends login_hint to the token URL:
if c.origin != "" {
authConfig.Endpoint.TokenURL = addLoginHintToURL(authConfig.Endpoint.TokenURL, c.origin)
}
However, the provider does not use this. In internal/clients/providerconfig.go:
// CfCredentials has no origin field
type CfCredentials struct {
Email string `json:"email"`
Username string `json:"username"`
Password string `json:"password"`
Passcode string `json:"passcode"`
}
// GetCredentialConfig never passes config.Origin()
func GetCredentialConfig(...) (*config.Config, error) {
// ...
return config.New(*url, config.UserPassword(cred.Email, cred.Password), config.SkipTLSValidation())
}
Suggested Fix
Two changes in internal/clients/providerconfig.go:
-
Add Origin to the credentials struct:
type CfCredentials struct {
Email string `json:"email"`
Username string `json:"username"`
Password string `json:"password"`
Passcode string `json:"passcode"`
Origin string `json:"origin"`
}
-
Pass it when creating the client:
opts := []config.Option{
config.UserPassword(cred.Email, cred.Password),
config.SkipTLSValidation(),
}
if cred.Origin != "" {
opts = append(opts, config.Origin(cred.Origin))
}
return config.New(*url, opts...)
This would cause the UAA token request to include ?login_hint={"origin":"<value>"}, correctly routing authentication to the custom IDP.
Additional context
- SAP BTP global accounts using SAP IAS as a custom IDP are affected — this is the recommended setup for production environments
- The
cf CLI handles this via cf login --origin <origin>, which uses the same login_hint mechanism
- The BTP Crossplane provider (
sap/crossplane-provider-btp) supports custom IDPs via an idp field in the credentials secret (added in v1.6.0, PR #466)
- This is a backwards-compatible change: the
origin field in the credentials JSON is optional, and existing users without custom IDP are not affected
Describe the bug
When using a custom Identity Provider (IDP) with SAP BTP Cloud Foundry, the UAA token endpoint requires a
login_hintparameter to route the password grant to the correct IDP origin. The underlyinggo-cfclient/v3library already supports this viaconfig.Origin(origin), which appends?login_hint={"origin":"<value>"}to the token URL. However, the provider'sCfCredentialsstruct andGetCredentialConfigfunction do not expose or pass this parameter.This means all CF resources fail with
invalid_grant: User authentication failedwhen the credentials belong to a user from a custom IDP (e.g., SAP IAS with a custom tenant).Tested with Provider Version
v0.3.3
To Reproduce
sap.ids):{ "email": "user@example.com", "password": "secret" }OrganizationwithObservemanagement policy)Expected behavior
The provider should support an
originfield in the credentials secret (or in the ProviderConfig spec) and pass it to thego-cfclientlibrary viaconfig.Origin(). This would allow authentication with custom IDPs.Root Cause Analysis
The
go-cfclient/v3library already fully supports this. Inconfig/options.go:And in
config/config.go, whenoriginis set, it appendslogin_hintto the token URL:However, the provider does not use this. In
internal/clients/providerconfig.go:Suggested Fix
Two changes in
internal/clients/providerconfig.go:Add
Originto the credentials struct:Pass it when creating the client:
This would cause the UAA token request to include
?login_hint={"origin":"<value>"}, correctly routing authentication to the custom IDP.Additional context
cfCLI handles this viacf login --origin <origin>, which uses the samelogin_hintmechanismsap/crossplane-provider-btp) supports custom IDPs via anidpfield in the credentials secret (added in v1.6.0, PR #466)originfield in the credentials JSON is optional, and existing users without custom IDP are not affected