Documentation
¶
Overview ¶
Package oauth2 ships Harbor's V1 default OAuth provider driver (closes issue #116 and the deferred construction gap).
The driver implements the §4.4 seam pattern for OAuth flow strategies: it self-registers under the canonical driver name `"oauth2"` via init() so operators declaring `tools.oauth_providers[].driver: oauth2` in `harbor.yaml` get a working provider at boot with zero Go wiring code.
The OAuth2 + PKCE Authorization Code flow ¶
The driver delegates to `internal/tools/auth.Provider` (the concrete `OAuthProvider`), which already implements the full Authorization Code + PKCE + RFC 7591 dynamic registration + metadata discovery flow. This package's responsibility is the operator-config → `Provider` boundary: read env-var-indirected credentials, validate fail-loud, build the underlying `auth.OAuthConfig`, and call `auth.NewProvider`.
Source ID binding (V1 simplification) ¶
The `*Provider.Token(ctx, source)` API keys by `tools.ToolSourceID` — each provider holds one `OAuthConfig` per source. For V1, the `oauth2` driver constructs ONE `*Provider` per `tools.oauth_providers[]` entry with a single `OAuthConfig` whose `Source = tools.ToolSourceID(cfg.Name)`. The catalog wrapper (`internal/tools/catalog.WrapWithOAuth`) passes the underlying tool's source ID, which may not match the provider name; the driver transparently substitutes by wrapping the `*Provider` in a small adapter that retargets every `Token` call onto the operator- configured source.
Future per-vendor drivers (Google Workspace, GitHub, Slack, ...) may implement more sophisticated multi-source mappings; the V1 default keeps the operator's mental model simple: one provider declaration → one OAuth attachment.
Fail-loud at construction ¶
CLAUDE.md §13 amendment — operator-facing seams demand explicit configuration. The driver fails closed on:
- A nil `cfg.CredentialSource` (BuildProviders always threads one).
- A resolved credential with an empty `client_id` (the env var named by `ClientIDEnv` was unset or empty).
- A resolved credential with an empty `client_secret`.
- Missing `cfg.TokenURL` AND `cfg.AuthURL` (no server endpoints configured; OAuth2/PKCE requires both).
- Missing `cfg.RedirectURL`.
- Missing deps (Store / Bus / Redactor / Coordinator).
The client credential resolves through the §4.4 credential-source seam. The generic `oauth2` driver supports the `env` source only — the interactive flow bakes the credential at construction, so it resolves EAGERLY here; the config validator restricts `remote` to the non-interactive `tokenexchange` driver.
Every failure mode surfaces a wrapped error naming the offending config field; the dev stack propagates the error up so the boot banner names the field the operator needs to set.
Index ¶
Constants ¶
const DriverName = "oauth2"
DriverName is the canonical name the driver registers under. The `internal/config` validator's `allowedOAuthDrivers` allowlist mirrors this constant.
Variables ¶
var ( // ErrMissingClientID — the credential resolved through the source // seam carried an empty client_id (the env var named by // client_id_env was unset or empty). Fail-loud per §13 amendment. ErrMissingClientID = errors.New("auth/oauth2: resolved client_id is empty (the env var named by client_id_env was unset or empty)") // ErrMissingClientSecret — the resolved credential carried an empty // client_secret. Same fail-loud rationale as `ErrMissingClientID`. ErrMissingClientSecret = errors.New("auth/oauth2: resolved client_secret is empty (the env var named by client_secret_env was unset or empty)") // ErrMissingEndpoints — both `cfg.AuthURL` and `cfg.TokenURL` are // empty. OAuth2/PKCE requires the authorization-server endpoints; // driver-specific drivers may infer them, but the generic // `oauth2` driver does NOT do discovery — the operator MUST // declare both. ErrMissingEndpoints = errors.New("auth/oauth2: both auth_url and token_url must be set (the oauth2 driver does not auto-discover endpoints — declare them in tools.oauth_providers[])") // ErrMissingRedirectURL — `cfg.RedirectURL` was empty. The // redirect_uri names the callback endpoint; `harbor dev` mounts // Harbor's production handler (`auth.CallbackHandler`) // at `GET /v1/tools/oauth/callback`, so the dev // value is `http://<bind>/v1/tools/oauth/callback`. Headless // embedders mount the same handler on their own mux (see // docs/recipes/steer-and-resume-a-run.md). ErrMissingRedirectURL = errors.New("auth/oauth2: redirect_url must not be empty (point it at the mounted auth.CallbackHandler — harbor dev serves GET /v1/tools/oauth/callback)") )
Sentinel errors specific to the oauth2 driver. Driver-internal / upstream-server failures continue to use the parent package's sentinels (`auth.ErrAuthRequired`, `auth.ErrExchangeFailed`, etc.).
var ErrMissingCredentialSource = errors.New("auth/oauth2: CredentialSource is nil (BuildProviders threads the source; direct callers pass credsource.Static)")
ErrMissingCredentialSource — cfg.CredentialSource was nil at construction. BuildProviders always threads one; a nil source is a direct-construction bug.
Functions ¶
func New ¶
func New(cfg auth.ProviderConfig, deps auth.FactoryDeps) (auth.OAuthProvider, error)
New constructs the `oauth2` driver's OAuthProvider for one operator-config entry.
The function is registered as the `oauth2` driver's `auth.Factory`. Per §4.4 the dev stack never calls this directly — `auth.Resolve` dispatches by driver name.
Types ¶
This section is empty.