The OAuth2 authenticator always authenticates with client_secret_basic — credentials form-url encoded into an Authorization header (auth/oauth2.go):
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// Authenticate with client_secret_basic (RFC 6749 §2.3.1) ...
req.SetBasicAuth(url.QueryEscape(a.clientID), url.QueryEscape(a.clientSecret))
That is the right default — RFC 6749 §2.3.1 recommends it, and every service in the registry today works with it. But it is the only method available, so a provider that requires client_secret_post (credentials as parameters in the request body, also permitted by §2.3.1) cannot be reached through the registry at all. The caller has to hand-roll a token exchange outside core/remote, losing the caching, refresh margin and secret resolution the authenticator already provides.
Proposal
One optional field on OAuth2Config, defaulting to the current behaviour:
"auth": {
"type": "oauth2",
"options": {
"token_url": "https://provider.example/token",
"client_id": "…",
"client_secret": "env:PROVIDER_SECRET",
"client_auth_method": "client_secret_post"
}
}
client_secret_basic (default when the field is absent) — unchanged behaviour for every existing service.
client_secret_post — client_id and client_secret added to the form body, no Authorization header.
Naming follows RFC 6749 §2.3.1 and OpenID Connect Discovery's token_endpoint_auth_methods_supported, so the config value is the same string a provider's discovery document advertises.
Worth validating the value at config load rather than at first token request, as endpoint_params already does — a typo should fail when services are loaded, not on the first outbound call in production.
Context
This surfaced while integrating the SLPA Cargo Management System from nsw-srilanka. Its published example posts credentials in a JSON body, which suggested the standard form shape would not work. Testing showed all three shapes are accepted there — client_secret_basic, client_secret_post, and JSON — so that integration needs nothing from this issue and ships on the current default.
I prototyped the change locally (~150 lines including tests: the config field, a single request builder differing only in credential placement, and validation) and reverted it, since nothing in flight requires it. Happy to open the PR if the design above looks right.
Not proposed
private_key_jwt and client_secret_jwt — no caller needs them, and they bring key management with them. Rejecting unknown values at load leaves room to add them later.
The OAuth2 authenticator always authenticates with client_secret_basic — credentials form-url encoded into an
Authorizationheader (auth/oauth2.go):That is the right default — RFC 6749 §2.3.1 recommends it, and every service in the registry today works with it. But it is the only method available, so a provider that requires client_secret_post (credentials as parameters in the request body, also permitted by §2.3.1) cannot be reached through the registry at all. The caller has to hand-roll a token exchange outside
core/remote, losing the caching, refresh margin and secret resolution the authenticator already provides.Proposal
One optional field on
OAuth2Config, defaulting to the current behaviour:client_secret_basic(default when the field is absent) — unchanged behaviour for every existing service.client_secret_post—client_idandclient_secretadded to the form body, noAuthorizationheader.Naming follows RFC 6749 §2.3.1 and OpenID Connect Discovery's
token_endpoint_auth_methods_supported, so the config value is the same string a provider's discovery document advertises.Worth validating the value at config load rather than at first token request, as
endpoint_paramsalready does — a typo should fail when services are loaded, not on the first outbound call in production.Context
This surfaced while integrating the SLPA Cargo Management System from
nsw-srilanka. Its published example posts credentials in a JSON body, which suggested the standard form shape would not work. Testing showed all three shapes are accepted there —client_secret_basic,client_secret_post, and JSON — so that integration needs nothing from this issue and ships on the current default.I prototyped the change locally (~150 lines including tests: the config field, a single request builder differing only in credential placement, and validation) and reverted it, since nothing in flight requires it. Happy to open the PR if the design above looks right.
Not proposed
private_key_jwtandclient_secret_jwt— no caller needs them, and they bring key management with them. Rejecting unknown values at load leaves room to add them later.