Stacked on #4173 (`jack/restore-oidc-flows`, base branch mirrored to this repo so the diff shows only this change); context from review: https://github.com/lancedb/lancedb/pull/4173#issuecomment-5674048100. Rebase to `main` once #4173 and #4179 merge. ## What moved to the `oauth2` crate (5.0, no default features) - Authorization URL generation and CSRF state (`authorize_url`, `CsrfToken`) - PKCE S256 challenge/verifier generation and code exchange - Client-credentials, authorization-code, refresh-token, and device-code grant request construction - Device authorization request and the device token polling loop (`authorization_pending`, `slow_down` +5s, expiry deadline, denial, network backoff capped at 10s) - Standard success/error response parsing (`RequestTokenError`) - Token-endpoint client authentication and standards-compliant parameter encoding (RFC 6749 2.3.1 Basic encoding) LanceDB keeps ownership of OIDC discovery (compared `openidconnect`: no measurable win for our 3-field metadata + strict validation, at real dependency cost), HTTPS-or-loopback endpoint enforcement, the loopback callback server, browser/stderr prompts, token caching and refresh orchestration, and the dedicated hardened Azure IMDS source, which is unchanged. ## Client authentication methods New `ClientAuthMethod` enum (`none` | `client_secret_basic` | `client_secret_post`), exposed in Rust, Python, and Node. Unset resolves to `client_secret_basic` when a secret is present (RFC 6749 2.3.1 recommendation and the normal Okta confidential-app default, so a default Okta app works without weakening its configuration) and to `none` for public clients (PKCE/device). Explicit `none` with a secret, or basic/post without one, is rejected. The method applies to client credentials, code exchange, refresh, and device requests. Deliberate behavior change: confidential clients previously always sent the secret in the POST body; they now default to Basic (Keycloak accepts both). No `audience`/`resource` parameters were added: the supported target is an Okta custom authorization server with the API audience configured server-side, so client-provided audience parameters are unnecessary; `add_extra_param` support exists if a concrete provider contract ever needs them. ## Device polling behavior changes (deliberate, tested) - The first token poll now happens immediately rather than after one interval (RFC 8628 allows both). - Transient failures (HTTP 429, 5xx, `temporarily_unavailable`, network errors) now retry with exponential backoff capped at 10s instead of retrying at the fixed interval; polling never spins faster than once per second even if a server reports a zero interval. ## Security and compatibility - Issuer and discovered endpoints (and device verification URIs) still require HTTPS except explicit loopback HTTP, enforced before any crate URL type is built - Token HTTP client keeps the hardened redirect policy that refuses insecure redirect targets; regression test added - Errors never embed raw response bodies (avoids leaking tokens through parse failures); all credential types stay redacted in Debug - Transient conditions (429/5xx/`temporarily_unavailable`) remain retryable in device polling and hard errors elsewhere; refresh keeps rotation and reauthentication semantics - Existing public APIs stay source-compatible except the added `OAuthConfig.client_auth_method` field ## Tests Rust: client-auth methods across code exchange/refresh/client-credentials/device (none/basic/post), auth-method resolution and validation, transient device retries, denial/expiry, redirect rejection, malformed-response leak check, PKCE URL assertions, redaction. Python and Node: enum values, conversion, unknown-method errors, config defaults. Manual Okta validation recipe (no automated Okta credentials): create a custom authorization server with an API audience, one confidential web app (Basic) for authorization-code, one native app (PKCE, no secret), one native device app; point `issuer_url` at the custom server, set `client_auth_method` only for the POST-required case; verify token acquisition, refresh after expiry, and `x-lancedb-credential-type: oidc` against a LanceDB deployment. Never commit tenant URLs or secrets. Co-authored-by: Xuanwo <github@xuanwo.io>
2.5 KiB
@lancedb/lancedb • Docs
@lancedb/lancedb / NativeOAuthConfig
Interface: NativeOAuthConfig
OAuth configuration for LanceDB authentication.
This is the generated napi-rs binding shape. TypeScript users should prefer
the public OAuthConfig type exported from @lancedb/lancedb.
All token acquisition and refresh is handled in the Rust layer.
Properties
audience?
optional audience: string;
Optional provider-specific audience for authorization and token requests.
callbackPort?
optional callbackPort: number;
Port for the authorization_code loopback callback server.
clientAuthMethod?
optional clientAuthMethod: string;
How the client authenticates to the token endpoint: "none", "client_secret_basic", or "client_secret_post". Defaults to "client_secret_basic" when a client secret is set, and "none" for public clients.
clientId
clientId: string;
Application / Client ID.
clientSecret?
optional clientSecret: string;
Client secret (required for client_credentials).
flow?
optional flow: string;
Authentication flow: "client_credentials", "authorization_code", "device_code", or "azure_managed_identity"
issuerUrl
issuerUrl: string;
OIDC issuer URL or OAuth authority URL.
For Azure: https://login.microsoftonline.com/{tenant_id}/v2.0
managedIdentityClientId?
optional managedIdentityClientId: string;
Client ID for user-assigned managed identity (azure_managed_identity).
redirectUri?
optional redirectUri: string;
Loopback redirect URI for authorization_code.
refreshBufferSecs?
optional refreshBufferSecs: number;
Seconds before expiry to trigger proactive refresh (default: 300). Keep this well below the token TTL; if it is greater than or equal to the TTL, each request refreshes the token.
resource?
optional resource: string;
Optional resource indicator for authorization and token requests.
scopes
scopes: string[];
OAuth scopes to request. For Azure managed identity, exactly one scope
or resource is required. For example: ["api://{app_id}/.default"]
tokenCache?
optional tokenCache: TokenCacheOptions;
Opt in to the persistent token cache so short-lived processes reuse one session. Only refresh tokens are persisted.
usePkce?
optional usePkce: boolean;
Whether authorization_code uses S256 PKCE (default: true).