Files
lancedb/docs/src/js/interfaces/OAuthConfig.md
T
Jack YeandXuanwo f3ef21b8ca feat: use oauth2 crate for OAuth with configurable client auth (#4181)
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>
2026-09-16 20:11:08 +08:00

4.2 KiB

@lancedb/lancedb • Docs


@lancedb/lancedb / OAuthConfig

Interface: OAuthConfig

OAuth configuration for LanceDB authentication.

This is the public TypeScript OAuth configuration type. The generated NativeOAuthConfig type has the same runtime shape but is an implementation detail of the napi-rs binding.

All token acquisition and refresh is handled in the Rust layer. This config is passed through to Rust via napi-rs.

Examples

const config: OAuthConfig = {
  issuerUrl: "https://login.microsoftonline.com/{tenant}/v2.0",
  clientId: "app-id",
  clientSecret: "secret",
  scopes: ["api://lancedb-api/.default"],
};

Providers requiring an explicit target can set resource and/or audience:

const targeted: OAuthConfig = {
  issuerUrl: "https://issuer.example.com",
  clientId: "app-id",
  clientSecret: "secret",
  scopes: ["read"],
  resource: "https://api.example.com",
  audience: "lancedb-api",
};
const config: OAuthConfig = {
  issuerUrl: "https://login.microsoftonline.com/{tenant}/v2.0",
  clientId: "app-id",
  scopes: ["api://lancedb-api/.default"],
  flow: OAuthFlowType.AzureManagedIdentity,
};

The authorization URL is written to stderr before LanceDB tries to open a browser, so it can be copied in headless environments.

const config: OAuthConfig = {
  issuerUrl: "https://login.microsoftonline.com/{tenant}/v2.0",
  clientId: "app-id",
  scopes: ["openid", "api://lancedb-api/access"],
  flow: OAuthFlowType.AuthorizationCode,
};

Device Authorization writes the verification URL and user code to stderr before polling begins.

Properties

audience?

optional audience: string;

Provider-specific audience, forwarded to authorization and token endpoints, including refresh requests. Not supported for Azure managed identity.


callbackPort?

optional callbackPort: number;

Port for the AuthorizationCode loopback callback server (default: 8400).


clientAuthMethod?

optional clientAuthMethod: ClientAuthMethod;

How the client authenticates to the token endpoint (default: auto). With a clientSecret the default is ClientAuthMethod.ClientSecretBasic, which matches the RFC 6749 recommendation and the default configuration of Okta confidential applications; without a secret the client is public and no client authentication is sent.


clientId

clientId: string;

Application / Client ID.


clientSecret?

optional clientSecret: string;

Client secret (required for ClientCredentials).


flow?

optional flow: OAuthFlowType;

Authentication flow (default: ClientCredentials).


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 (AzureManagedIdentity).


redirectUri?

optional redirectUri: string;

Loopback redirect URI for AuthorizationCode.


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;

Resource indicator (RFC 8707), forwarded verbatim to authorization and token endpoints, including refresh requests. Must be an absolute URI without a fragment. Not supported for Azure managed identity.


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. Only supported by AuthorizationCode and DeviceCode; Azure managed identity is rejected. Default: unset (memory only).


usePkce?

optional usePkce: boolean;

Protect AuthorizationCode with S256 PKCE (default: true).