Add user:pass Basic auth mode for API-key connectors
The API-key connection transport could present a key as a Bearer token, an x-api-key header, a custom scheme (SSWS/Token), or HTTP Basic with an empty password (Cursor). None of these can carry a real password, which providers such as ClickHouse Cloud (keyId:keySecret) and Langfuse (publicKey:secretKey) require. Add a fourth mode, APIKeyBasicAuthUserPass, that base64-encodes the stored "username:password" credential verbatim into Authorization: Basic. SetBasicAuth cannot express this -- it re-appends a ":" and corrupts the credential. The mode is wired generically through the registry and the create-connector resolver and is mutually exclusive with the other API-key auth modes. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
@@ -16,6 +16,7 @@ package connector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
@@ -41,6 +42,15 @@ 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"`
|
||||
// BasicAuthUserPass, when true, presents the API key as a complete HTTP
|
||||
// Basic credential (`Authorization: Basic base64(<key>)`), with the
|
||||
// stored key already holding the `username:password` pair — required
|
||||
// by providers such as ClickHouse Cloud (keyId:keySecret) and
|
||||
// Langfuse (publicKey:secretKey) whose Basic credential carries a real
|
||||
// password, which BasicAuth (empty password) cannot express. It is
|
||||
// mutually exclusive with the other modes and is populated from the
|
||||
// provider Registration at connector creation time.
|
||||
BasicAuthUserPass bool `json:"basic_auth_user_pass,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
|
||||
@@ -72,6 +82,15 @@ func (c *APIKeyConnection) Client(ctx context.Context) (*http.Client, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
if c.BasicAuthUserPass {
|
||||
return &http.Client{
|
||||
Transport: &basicAuthUserPassTransport{
|
||||
credential: c.APIKey,
|
||||
underlying: underlying,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
if c.Header != "" {
|
||||
return &http.Client{
|
||||
Transport: &apiKeyHeaderTransport{
|
||||
@@ -153,6 +172,25 @@ func (t *basicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error
|
||||
return t.underlying.RoundTrip(req2)
|
||||
}
|
||||
|
||||
// basicAuthUserPassTransport presents a complete HTTP Basic credential whose
|
||||
// `username:password` pair is already encoded in the stored key
|
||||
// (`Authorization: Basic base64(<credential>)`). Providers such as
|
||||
// ClickHouse Cloud (keyId:keySecret) and Langfuse (publicKey:secretKey)
|
||||
// authenticate with a real password, which basicAuthTransport's empty
|
||||
// password cannot carry; SetBasicAuth would also re-append a ":" and
|
||||
// corrupt the credential, so the value is base64-encoded verbatim.
|
||||
type basicAuthUserPassTransport struct {
|
||||
credential string
|
||||
underlying http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *basicAuthUserPassTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req2 := req.Clone(req.Context())
|
||||
req2.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(t.credential)))
|
||||
|
||||
return t.underlying.RoundTrip(req2)
|
||||
}
|
||||
|
||||
func (c APIKeyConnection) MarshalJSON() ([]byte, error) {
|
||||
type Alias APIKeyConnection
|
||||
|
||||
|
||||
Reference in New Issue
Block a user