Skip to content

feat(remote): make the OAuth2 client authentication method configurable per service #154

Description

@Aravinda-HWK

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_postclient_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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions