Add SSWS API-key Authorization scheme

Okta API tokens authenticate as "Authorization: SSWS <token>", a
scheme none of the existing API-key modes (Bearer, custom header,
Basic) can express. Add Registration.APIKeyAuthScheme, plumb it onto
APIKeyConnection.Scheme, and send it via a new schemeAuthTransport.

The three API-key presentations (BasicAuth, Header, Scheme) are
mutually exclusive; Register rejects setting more than one so a
misconfiguration fails at process start rather than silently.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
Aurélien Sibiril
2026-06-04 14:44:51 +02:00
parent 5dd8769d19
commit 511472aca3
5 changed files with 122 additions and 5 deletions

View File

@@ -41,6 +41,13 @@ type APIKeyConnection struct {
// It is mutually exclusive with Header and is populated from the
// provider Registration at connector creation time.
BasicAuth bool `json:"basic_auth,omitempty"`
// Scheme selects a non-Bearer Authorization scheme: when non-empty
// the key is sent as `Authorization: <Scheme> <key>` instead of
// `Authorization: Bearer <key>` — required by providers such as Okta
// whose API tokens use the `SSWS` scheme. It is mutually exclusive
// with Header and BasicAuth and is populated from the provider
// Registration at connector creation time.
Scheme string `json:"scheme,omitempty"`
}
var _ Connection = (*APIKeyConnection)(nil)
@@ -75,6 +82,16 @@ func (c *APIKeyConnection) Client(ctx context.Context) (*http.Client, error) {
}, nil
}
if c.Scheme != "" {
return &http.Client{
Transport: &schemeAuthTransport{
scheme: c.Scheme,
token: c.APIKey,
underlying: underlying,
},
}, nil
}
return &http.Client{
Transport: &oauth2Transport{
token: c.APIKey,
@@ -84,6 +101,24 @@ func (c *APIKeyConnection) Client(ctx context.Context) (*http.Client, error) {
}, nil
}
// schemeAuthTransport presents the API key in the Authorization header
// under a non-Bearer scheme (`Authorization: <scheme> <token>`).
// Providers such as Okta document the `SSWS` scheme for their API tokens
// and reject Bearer, so neither oauth2Transport (which hardcodes Bearer)
// nor apiKeyHeaderTransport (which sets a non-Authorization header) fits.
type schemeAuthTransport struct {
scheme string
token string
underlying http.RoundTripper
}
func (t *schemeAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req2 := req.Clone(req.Context())
req2.Header.Set("Authorization", t.scheme+" "+t.token)
return t.underlying.RoundTrip(req2)
}
// apiKeyHeaderTransport injects the API key into a custom request header
// (for example "x-api-key") and, unlike oauth2Transport, never sets
// Authorization. Providers such as Anthropic require the key in their