Files
CLIProxyAPI/docs/sdk-access.md
Luis Pater a4767fdd8e feat(auth, docs): add SDK guides and local password support for management
- Added extensive SDK usage guides for `cliproxy`, `sdk/access`, and watcher integration.
- Introduced `--password` flag for specifying local management access passwords.
- Enhanced management API with local password checks to secure localhost requests.
- Updated documentation to reflect the new password functionality.
2025-09-25 11:32:14 +08:00

6.7 KiB

@sdk/access SDK Reference

The github.com/router-for-me/CLIProxyAPI/v6/sdk/access package centralizes inbound request authentication for the proxy. It offers a lightweight manager that chains credential providers, so servers can reuse the same access control logic inside or outside the CLI runtime.

Importing

import (
    sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access"
    "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
)

Add the module with go get github.com/router-for-me/CLIProxyAPI/v6/sdk/access.

Manager Lifecycle

manager := sdkaccess.NewManager()
providers, err := sdkaccess.BuildProviders(cfg)
if err != nil {
    return err
}
manager.SetProviders(providers)
  • NewManager constructs an empty manager.
  • SetProviders replaces the provider slice using a defensive copy.
  • Providers retrieves a snapshot that can be iterated safely from other goroutines.
  • BuildProviders translates config.Config access declarations into runnable providers. When the config omits explicit providers but defines inline API keys, the helper auto-installs the built-in config-api-key provider.

Authenticating Requests

result, err := manager.Authenticate(ctx, req)
switch {
case err == nil:
    // Authentication succeeded; result describes the provider and principal.
case errors.Is(err, sdkaccess.ErrNoCredentials):
    // No recognizable credentials were supplied.
case errors.Is(err, sdkaccess.ErrInvalidCredential):
    // Supplied credentials were present but rejected.
default:
    // Transport-level failure was returned by a provider.
}

Manager.Authenticate walks the configured providers in order. It returns on the first success, skips providers that surface ErrNotHandled, and tracks whether any provider reported ErrNoCredentials or ErrInvalidCredential for downstream error reporting.

If the manager itself is nil or no providers are registered, the call returns nil, nil, allowing callers to treat access control as disabled without branching on errors.

Each Result includes the provider identifier, the resolved principal, and optional metadata (for example, which header carried the credential).

Configuration Layout

The manager expects access providers under the auth.providers key inside config.yaml:

auth:
  providers:
    - name: inline-api
      type: config-api-key
      api-keys:
        - sk-test-123
        - sk-prod-456

Fields map directly to config.AccessProvider: name labels the provider, type selects the registered factory, sdk can name an external module, api-keys seeds inline credentials, and config passes provider-specific options.

Loading providers from external SDK modules

To consume a provider shipped in another Go module, point the sdk field at the module path and import it for its registration side effect:

auth:
  providers:
    - name: partner-auth
      type: partner-token
      sdk: github.com/acme/xplatform/sdk/access/providers/partner
      config:
        region: us-west-2
        audience: cli-proxy
import (
    _ "github.com/acme/xplatform/sdk/access/providers/partner" // registers partner-token
    sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access"
)

The blank identifier import ensures init runs so sdkaccess.RegisterProvider executes before BuildProviders is called.

Built-in Providers

The SDK ships with one provider out of the box:

  • config-api-key: Validates API keys declared inline or under top-level api-keys. It accepts the key from Authorization: Bearer, X-Goog-Api-Key, X-Api-Key, or the ?key= query string and reports ErrInvalidCredential when no match is found.

Additional providers can be delivered by third-party packages. When a provider package is imported, it registers itself with sdkaccess.RegisterProvider.

Metadata and auditing

Result.Metadata carries provider-specific context. The built-in config-api-key provider, for example, stores the credential source (authorization, x-goog-api-key, x-api-key, or query-key). Populate this map in custom providers to enrich logs and downstream auditing.

Writing Custom Providers

type customProvider struct{}

func (p *customProvider) Identifier() string { return "my-provider" }

func (p *customProvider) Authenticate(ctx context.Context, r *http.Request) (*sdkaccess.Result, error) {
    token := r.Header.Get("X-Custom")
    if token == "" {
        return nil, sdkaccess.ErrNoCredentials
    }
    if token != "expected" {
        return nil, sdkaccess.ErrInvalidCredential
    }
    return &sdkaccess.Result{
        Provider:  p.Identifier(),
        Principal: "service-user",
        Metadata:  map[string]string{"source": "x-custom"},
    }, nil
}

func init() {
    sdkaccess.RegisterProvider("custom", func(cfg *config.AccessProvider, root *config.Config) (sdkaccess.Provider, error) {
        return &customProvider{}, nil
    })
}

A provider must implement Identifier() and Authenticate(). To expose it to configuration, call RegisterProvider inside init. Provider factories receive the specific AccessProvider block plus the full root configuration for contextual needs.

Error Semantics

  • ErrNoCredentials: no credentials were present or recognized by any provider.
  • ErrInvalidCredential: at least one provider processed the credentials but rejected them.
  • ErrNotHandled: instructs the manager to fall through to the next provider without affecting aggregate error reporting.

Return custom errors to surface transport failures; they propagate immediately to the caller instead of being masked.

Integration with cliproxy Service

sdk/cliproxy wires @sdk/access automatically when you build a CLI service via cliproxy.NewBuilder. Supplying a preconfigured manager allows you to extend or override the default providers:

coreCfg, _ := config.LoadConfig("config.yaml")
providers, _ := sdkaccess.BuildProviders(coreCfg)
manager := sdkaccess.NewManager()
manager.SetProviders(providers)

svc, _ := cliproxy.NewBuilder().
  WithConfig(coreCfg).
  WithAccessManager(manager).
  Build()

The service reuses the manager for every inbound request, ensuring consistent authentication across embedded deployments and the canonical CLI binary.

Hot reloading providers

When configuration changes, rebuild providers and swap them into the manager:

providers, err := sdkaccess.BuildProviders(newCfg)
if err != nil {
    log.Errorf("reload auth providers failed: %v", err)
    return
}
accessManager.SetProviders(providers)

This mirrors the behaviour in cliproxy.Service.refreshAccessProviders and api.Server.applyAccessConfig, enabling runtime updates without restarting the process.