feat: document all OAuthFlowType entries in authentication.mdx (#4565)

Previously, authentication.mdx only documented a subset of the existing
OAuthFlowType entries. We should document all of them.
This commit is contained in:
Colin Patrick McCabe
2026-10-21 14:26:47 -07:00
committed by GitHub
parent 1480a9ff57
commit 0fbc0981dd
+282 -30
View File
@@ -3,7 +3,7 @@ title: "Authentication"
sidebarTitle: "Authentication"
description: "Authentication modes for LanceDB Enterprise: API keys and OAuth credentials."
icon: key
keywords: ["authentication", "oauth", "api key", "azure managed identity", "client credentials"]
keywords: ["authentication", "oauth", "api key", "authorization code", "device code", "azure managed identity", "client credentials"]
availability:
oss: unavailable
enterprise: available
@@ -13,9 +13,9 @@ availability:
[Architecture](/architecture).
---
A deployment authenticates every request before
[authorization](/governance/authorization) decides what it may do, against a
`db://` remote table, in one of two ways:
A deployment authenticates each request over a `db://` connection before
[authorization](/governance/authorization) decides what it may do.
Choose one of these methods:
- **API keys**: a long-lived shared secret passed as `api_key` on connect. Works in every SDK.
- **OAuth 2.0**: short-lived bearer tokens obtained from your identity provider, refreshed automatically by the client.
@@ -28,55 +28,91 @@ identities so no secret material lives on the client.
Pass the API key from your Enterprise tenant on `connect`. This works with
both the synchronous and asynchronous Python clients, as well as TypeScript
and Rust.
and Rust. Load the key from an environment variable or secret manager.
```python
<CodeGroup>
```python Python icon="python"
import os
import lancedb
db = lancedb.connect(
uri="db://your-database-uri",
api_key="your-api-key",
api_key=os.environ["LANCEDB_API_KEY"],
region="us-east-1",
host_override="https://your-enterprise-endpoint.com",
)
```
An API key belongs in the environment rather than in source. See
[Database](/management/database#deployment) for the connection options.
```typescript TypeScript icon="square-js"
import * as lancedb from "@lancedb/lancedb";
const apiKey = process.env.LANCEDB_API_KEY;
if (!apiKey) throw new Error("LANCEDB_API_KEY is required");
const db = await lancedb.connect("db://your-database-uri", {
apiKey,
region: "us-east-1",
hostOverride: "https://your-enterprise-endpoint.com",
});
```
```rust Rust icon="rust"
let api_key = std::env::var("LANCEDB_API_KEY")?;
let db = lancedb::connect("db://your-database-uri")
.api_key(&api_key)
.region("us-east-1")
.host_override("https://your-enterprise-endpoint.com")
.execute()
.await?;
```
</CodeGroup>
For Rust, enable the `remote` feature on the `lancedb` dependency to use
API key or OAuth authentication.
See [Database](/management/database#deployment) for the connection options.
## OAuth {#oauth}
The async Python client and the TypeScript client can obtain bearer tokens
The async Python, TypeScript, and Rust clients can obtain bearer tokens
from an OIDC issuer and attach them to every request. Token acquisition,
caching, and refresh are handled inside the client. Your application code
only provides the configuration.
A deployment accepts these tokens only when its administrator has configured
it with your identity provider. Ask them for the issuer URL, client ID and
scopes to use.
it with your identity provider. Ask them for the issuer URL, client ID, scopes,
and any resource or audience required by the provider. For Authorization Code,
register the loopback redirect URI with the provider. For Device Code, enable
the provider's device authorization grant for your application.
<Info>
In Python, OAuth is supported through `lancedb.connect_async`. The synchronous
`connect` entry point continues to use API key authentication for `db://`
URIs. In TypeScript, `lancedb.connect` accepts `oauthConfig` directly.
In Rust, use `.oauth_config(config)` on the connection builder.
</Info>
### Supported flows {#supported-flows}
`OAuthFlowType` selects how the client acquires tokens:
`OAuthFlowType` (Python and TypeScript) or `OAuthFlow` (Rust) selects how the
client acquires tokens:
| Flow | Python value | TypeScript value | When to use |
| --- | --- | --- | --- |
| Client Credentials | `OAuthFlowType.CLIENT_CREDENTIALS` | `OAuthFlowType.ClientCredentials` | Service-to-service / machine-to-machine. Requires a client ID and client secret registered with your identity provider. |
| Azure Managed Identity | `OAuthFlowType.AZURE_MANAGED_IDENTITY` | `OAuthFlowType.AzureManagedIdentity` | Workloads running on Azure compute (VMs, AKS, App Service, Container Apps). Tokens are fetched from the Azure IMDS endpoint, so no client secret is stored on the client. |
| Flow | Python value | TypeScript value | Rust value | When to use |
| --- | --- | --- | --- | --- |
| Client Credentials | `OAuthFlowType.CLIENT_CREDENTIALS` | `OAuthFlowType.ClientCredentials` | `OAuthFlow::ClientCredentials` | Service-to-service / machine-to-machine. Requires a client ID and client secret registered with your identity provider. |
| Authorization Code | `OAuthFlowType.AUTHORIZATION_CODE` | `OAuthFlowType.AuthorizationCode` | `OAuthFlow::AuthorizationCode(AuthorizationCodeOptions::new())` | Interactive user sign-in through a browser and local callback server. Uses S256 PKCE by default. |
| Device Code | `OAuthFlowType.DEVICE_CODE` | `OAuthFlowType.DeviceCode` | `OAuthFlow::DeviceCode` | CLI or headless sign-in. The client displays a verification URL and user code for the user to enter on another device. |
| Azure Managed Identity | `OAuthFlowType.AZURE_MANAGED_IDENTITY` | `OAuthFlowType.AzureManagedIdentity` | `OAuthFlow::AzureManagedIdentity { client_id: None }` | Azure workloads with access to the IMDS endpoint. Tokens are fetched from IMDS, so no client secret is stored on the client. |
### Configure OAuth {#configure-oauth}
Build an OAuth config and pass it on connect. Use `oauth_config` in Python and
`oauthConfig` in TypeScript.
Rust, and `oauthConfig` in TypeScript.
<CodeGroup>
```python Python icon="python"
import os
import lancedb
from lancedb.remote import OAuthConfig, OAuthFlowType
@@ -84,7 +120,7 @@ from lancedb.remote import OAuthConfig, OAuthFlowType
oauth_config = OAuthConfig(
issuer_url="https://login.microsoftonline.com/{tenant}/v2.0",
client_id="your-application-client-id",
client_secret="your-application-client-secret",
client_secret=os.environ["LANCEDB_OAUTH_CLIENT_SECRET"],
scopes=["api://lancedb-api/.default"],
flow=OAuthFlowType.CLIENT_CREDENTIALS,
)
@@ -101,11 +137,14 @@ db = await lancedb.connect_async(
import * as lancedb from "@lancedb/lancedb";
import { OAuthConfig, OAuthFlowType } from "@lancedb/lancedb";
const clientSecret = process.env.LANCEDB_OAUTH_CLIENT_SECRET;
if (!clientSecret) throw new Error("LANCEDB_OAUTH_CLIENT_SECRET is required");
// Client Credentials (service-to-service)
const oauthConfig: OAuthConfig = {
issuerUrl: "https://login.microsoftonline.com/{tenant}/v2.0",
clientId: "your-application-client-id",
clientSecret: "your-application-client-secret",
clientSecret,
scopes: ["api://lancedb-api/.default"],
flow: OAuthFlowType.ClientCredentials,
};
@@ -116,9 +155,34 @@ const db = await lancedb.connect("db://your-database-uri", {
oauthConfig,
});
```
```rust Rust icon="rust"
use lancedb::remote::{OAuthConfig, OAuthFlow};
// Client Credentials (service-to-service)
let oauth_config = OAuthConfig {
issuer_url: "https://login.microsoftonline.com/{tenant}/v2.0".into(),
client_id: "your-application-client-id".into(),
client_secret: Some(std::env::var("LANCEDB_OAUTH_CLIENT_SECRET")?),
scopes: vec!["api://lancedb-api/.default".into()],
flow: OAuthFlow::ClientCredentials,
client_auth_method: None,
resource: None,
audience: None,
refresh_buffer_secs: None,
token_cache: None,
};
let db = lancedb::connect("db://your-database-uri")
.region("us-east-1")
.host_override("https://your-enterprise-endpoint.com")
.oauth_config(oauth_config)
.execute()
.await?;
```
</CodeGroup>
For workloads running on Azure compute, use a managed identity instead of a
For Azure workloads with access to IMDS, use a managed identity instead of a
client secret:
<CodeGroup>
@@ -130,8 +194,8 @@ oauth_config = OAuthConfig(
client_id="your-application-client-id",
scopes=["api://lancedb-api/.default"],
flow=OAuthFlowType.AZURE_MANAGED_IDENTITY,
# Optional: required only for user-assigned managed identities.
managed_identity_client_id="your-user-assigned-identity-client-id",
# Set only for a user-assigned managed identity:
# managed_identity_client_id="your-user-assigned-identity-client-id",
)
```
@@ -143,31 +207,219 @@ const oauthConfig: OAuthConfig = {
clientId: "your-application-client-id",
scopes: ["api://lancedb-api/.default"],
flow: OAuthFlowType.AzureManagedIdentity,
// Optional: required only for user-assigned managed identities.
managedIdentityClientId: "your-user-assigned-identity-client-id",
// Set only for a user-assigned managed identity:
// managedIdentityClientId: "your-user-assigned-identity-client-id",
};
```
```rust Rust icon="rust"
use lancedb::remote::{OAuthConfig, OAuthFlow};
let oauth_config = OAuthConfig {
issuer_url: "https://login.microsoftonline.com/{tenant}/v2.0".into(),
client_id: "your-application-client-id".into(),
client_secret: None,
scopes: vec!["api://lancedb-api/.default".into()],
flow: OAuthFlow::AzureManagedIdentity {
// Set to Some("your-user-assigned-identity-client-id".into())
// only for a user-assigned managed identity.
client_id: None,
},
client_auth_method: None,
resource: None,
audience: None,
refresh_buffer_secs: None,
token_cache: None,
};
```
</CodeGroup>
`managed_identity_client_id` / `managedIdentityClientId` selects a user-assigned
identity. Omit it for a system-assigned identity. The required `client_id` /
`clientId` field does not select the Azure managed identity.
In Rust, set the `client_id` inside `OAuthFlow::AzureManagedIdentity` to
`Some(...)` for a user-assigned identity, or `None` for a system-assigned
identity. The top-level `OAuthConfig::client_id` is the application client ID.
For interactive sign-in, configure Authorization Code or Device Code. The
authorization URL or device verification instructions are written to standard
error. Authorization Code also tries to open a browser and listens for the
callback at `http://127.0.0.1:8400/callback` by default. Register that redirect
URI with your identity provider. Public clients can omit `client_secret`;
request a refresh token from your provider (often by including
`offline_access`) if you want to reuse the session.
<CodeGroup>
```python Python icon="python"
import lancedb
from lancedb.remote import OAuthConfig, OAuthFlowType, TokenCacheOptions
# Use DEVICE_CODE for a CLI without a local browser or callback server.
oauth_config = OAuthConfig(
issuer_url="https://your-issuer.example.com",
client_id="your-public-client-id",
scopes=["openid", "offline_access", "lancedb.read"],
flow=OAuthFlowType.AUTHORIZATION_CODE,
token_cache=TokenCacheOptions(),
)
db = await lancedb.connect_async(
uri="db://your-database-uri",
region="us-east-1",
host_override="https://your-enterprise-endpoint.com",
oauth_config=oauth_config,
)
```
```typescript TypeScript icon="square-js"
import * as lancedb from "@lancedb/lancedb";
import { OAuthFlowType, type OAuthConfig } from "@lancedb/lancedb";
// Use OAuthFlowType.DeviceCode for a CLI without a local browser or callback server.
const oauthConfig: OAuthConfig = {
issuerUrl: "https://your-issuer.example.com",
clientId: "your-public-client-id",
scopes: ["openid", "offline_access", "lancedb.read"],
flow: OAuthFlowType.AuthorizationCode,
tokenCache: {},
};
const db = await lancedb.connect("db://your-database-uri", {
region: "us-east-1",
hostOverride: "https://your-enterprise-endpoint.com",
oauthConfig,
});
```
```rust Rust icon="rust"
use lancedb::remote::{AuthorizationCodeOptions, OAuthConfig, OAuthFlow, TokenCacheOptions};
// Use OAuthFlow::DeviceCode for a CLI without a local browser or callback server.
let oauth_config = OAuthConfig {
issuer_url: "https://your-issuer.example.com".into(),
client_id: "your-public-client-id".into(),
client_secret: None,
scopes: vec![
"openid".into(),
"offline_access".into(),
"lancedb.read".into(),
],
flow: OAuthFlow::AuthorizationCode(AuthorizationCodeOptions::new()),
client_auth_method: None,
resource: None,
audience: None,
refresh_buffer_secs: None,
token_cache: Some(TokenCacheOptions::new()),
};
let db = lancedb::connect("db://your-database-uri")
.region("us-east-1")
.host_override("https://your-enterprise-endpoint.com")
.oauth_config(oauth_config.clone())
.execute()
.await?;
```
</CodeGroup>
With `DEVICE_CODE` / `DeviceCode` / `OAuthFlow::DeviceCode`, the same
configuration and connection calls apply; only the `flow` value changes.
### Reuse an interactive session {#reuse-an-interactive-session}
By default, tokens stay in process memory. Set `token_cache` / `tokenCache`
for Authorization Code or Device Code to keep refresh tokens across process
restarts. Only refresh tokens are persisted; access tokens and client secrets
are not written to the cache. Set `cache_dir` / `cacheDir` to choose the
directory, or leave it unset to use the platform cache directory. Your
identity provider must issue a refresh token for a later process to sign in
without prompting again.
Use `OAuthSession` with the same config to log in before connecting, inspect
whether a refresh token is cached, or remove the local cached credential:
<CodeGroup>
```python Python icon="python"
from lancedb.remote import OAuthSession
session = OAuthSession(oauth_config)
status = await session.login()
print(status.refreshable)
status = await session.status() # Does not contact the provider.
print(status.refreshable)
logout = await session.logout()
print(logout.removed)
```
```typescript TypeScript icon="square-js"
import { OAuthSession } from "@lancedb/lancedb";
const session = new OAuthSession(oauthConfig);
const status = await session.login();
console.log(status.refreshable);
const current = await session.status(); // Does not contact the provider.
console.log(current.refreshable);
const logout = await session.logout();
console.log(logout.removed);
```
```rust Rust icon="rust"
use lancedb::remote::OAuthSession;
let session = OAuthSession::new(oauth_config)?;
let status = session.login().await?;
println!("{}", status.refreshable);
let status = session.status().await?; // Does not contact the provider.
println!("{}", status.refreshable);
let logout = session.logout().await?;
println!("{}", logout.removed);
```
</CodeGroup>
`login()` runs the interactive flow even when a session is cached. `logout()`
removes the local cache entry; it does not revoke the provider's token or end
a browser sign-in. `OAuthSession` requires a persistent cache and supports
only Authorization Code and Device Code. A cache is unused for Client
Credentials and rejected for Azure Managed Identity.
### Configuration reference {#configuration-reference}
The same configuration is available in both SDKs. Python uses `snake_case`
field names; TypeScript uses `camelCase`.
The same configuration is available in all three SDKs. Python and Rust use
`snake_case` field names; TypeScript uses `camelCase`. In Rust, set optional
`OAuthConfig` fields to `Some(value)` or `None`. Configure `redirect_uri`,
`callback_port`, and `use_pkce` through the builder methods on
`AuthorizationCodeOptions`, and select a managed identity through
`OAuthFlow::AzureManagedIdentity { client_id: ... }`.
| Python field / TypeScript field | Required | Description |
| --- | --- | --- |
| `issuer_url` / `issuerUrl` | Yes | OIDC issuer URL or OAuth authority URL. For Azure, use `https://login.microsoftonline.com/{tenant_id}/v2.0`. |
| `client_id` / `clientId` | Yes | Application / client ID registered with your identity provider. |
| `scopes` | Yes | List of OAuth scopes to request. For Azure managed identity, provide exactly one scope or resource, for example `["api://your-app-id/.default"]`. |
| `flow` | No | Selects the OAuth flow. Defaults to Client Credentials. |
| `client_secret` / `clientSecret` | Conditional | Required for Client Credentials. Redacted from the config's `repr` (Python) and `Debug` output (TypeScript native binding) so it does not leak into logs. |
| `flow` | Yes in Rust; otherwise no | Selects the OAuth flow. Defaults to Client Credentials in Python and TypeScript; Rust requires an explicit `OAuthFlow`. |
| `client_secret` / `clientSecret` | Conditional | Required for Client Credentials; only required for Authorization Code and Device Code when the provider registers a confidential client. Redacted from the config's `repr` (Python) and `Debug` output (Rust and TypeScript native binding). |
| `client_auth_method` / `clientAuthMethod` | No | Token endpoint authentication: `ClientAuthMethod.NONE` / `ClientAuthMethod.None` for a public client, `ClientAuthMethod.CLIENT_SECRET_BASIC` / `ClientAuthMethod.ClientSecretBasic`, or `ClientAuthMethod.CLIENT_SECRET_POST` / `ClientAuthMethod.ClientSecretPost`. Defaults to Basic when a client secret is set and no client authentication otherwise. The secret methods require `client_secret` / `clientSecret`. Omit for Azure Managed Identity. |
| `redirect_uri` / `redirectUri` | No | Authorization Code loopback callback URI. Defaults to `http://127.0.0.1:{callback_port}/callback`; register it with the provider. |
| `callback_port` / `callbackPort` | No | Authorization Code callback server port (default 8400). If `redirect_uri` / `redirectUri` has an explicit port, the two ports must match. |
| `use_pkce` / `usePkce` | No | Enable S256 PKCE for Authorization Code (default `true`). |
| `managed_identity_client_id` / `managedIdentityClientId` | No | Client ID of a user-assigned managed identity. Only used with Azure Managed Identity; omit for system-assigned identities. |
| `resource` | No | RFC 8707 resource indicator, an absolute URI without a fragment. Sent to authorization and token endpoints, including refresh requests. Not supported for Azure Managed Identity. |
| `audience` | No | Provider-specific audience sent to authorization and token endpoints, including refresh requests. Not supported for Azure Managed Identity. |
| `refresh_buffer_secs` / `refreshBufferSecs` | No | How many seconds before token expiry to proactively refresh. Defaults to 300. Keep this well below the token TTL: setting it greater than or equal to the TTL forces a refresh on every request. |
| `token_cache` / `tokenCache` | No | Opt in to a persistent refresh-token cache for Authorization Code or Device Code. Use `TokenCacheOptions()` in Python, `{}` in TypeScript, or `Some(TokenCacheOptions::new())` in Rust. Defaults to an in-memory session. |
`TokenCacheOptions` also accepts `cache_dir` / `cacheDir` to select a cache
directory and `lock_timeout_secs` / `lockTimeoutSecs` to set the wait for a
cross-process refresh lock (default 30 seconds).
In Rust, use the `TokenCacheOptions::cache_dir(...)` and
`TokenCacheOptions::lock_timeout_secs(...)` builder methods. Token endpoint
authentication uses `Some(ClientAuthMethod::None)`,
`Some(ClientAuthMethod::ClientSecretBasic)`, or
`Some(ClientAuthMethod::ClientSecretPost)`; `None` selects the default.
<Warning>
Treat the client secret like any other production credential. Load it from a
secret manager or environment variable rather than committing it to source
control. Both SDKs deliberately omit the secret from their debug/repr output
control. All three SDKs deliberately omit the secret from their debug/repr output
so accidental log lines do not expose the value.
</Warning>