Files
lancedb/docs/web/enterprise/authentication.mdx
T
Colin Patrick McCabe 0fbc0981dd 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.
2026-10-21 14:26:47 -07:00

428 lines
18 KiB
Plaintext

---
title: "Authentication"
sidebarTitle: "Authentication"
description: "Authentication modes for LanceDB Enterprise: API keys and OAuth credentials."
icon: key
keywords: ["authentication", "oauth", "api key", "authorization code", "device code", "azure managed identity", "client credentials"]
availability:
oss: unavailable
enterprise: available
summary: >-
Requests to a deployment carry an API key or an OAuth token, whichever it is
configured to accept. Embedded LanceDB has no server to sign in to; see
[Architecture](/architecture).
---
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.
OAuth is the recommended option when you want to rotate credentials centrally,
plug into an existing identity provider, or run on Azure with managed
identities so no secret material lives on the client.
## API key {#api-key}
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. Load the key from an environment variable or secret manager.
<CodeGroup>
```python Python icon="python"
import os
import lancedb
db = lancedb.connect(
uri="db://your-database-uri",
api_key=os.environ["LANCEDB_API_KEY"],
region="us-east-1",
host_override="https://your-enterprise-endpoint.com",
)
```
```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, 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, 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` (Python and TypeScript) or `OAuthFlow` (Rust) selects how the
client acquires tokens:
| 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
Rust, and `oauthConfig` in TypeScript.
<CodeGroup>
```python Python icon="python"
import os
import lancedb
from lancedb.remote import OAuthConfig, OAuthFlowType
# Client Credentials (service-to-service)
oauth_config = OAuthConfig(
issuer_url="https://login.microsoftonline.com/{tenant}/v2.0",
client_id="your-application-client-id",
client_secret=os.environ["LANCEDB_OAUTH_CLIENT_SECRET"],
scopes=["api://lancedb-api/.default"],
flow=OAuthFlowType.CLIENT_CREDENTIALS,
)
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 { 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,
scopes: ["api://lancedb-api/.default"],
flow: OAuthFlowType.ClientCredentials,
};
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::{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 Azure workloads with access to IMDS, use a managed identity instead of a
client secret:
<CodeGroup>
```python Python icon="python"
from lancedb.remote import OAuthConfig, OAuthFlowType
oauth_config = OAuthConfig(
issuer_url="https://login.microsoftonline.com/{tenant}/v2.0",
client_id="your-application-client-id",
scopes=["api://lancedb-api/.default"],
flow=OAuthFlowType.AZURE_MANAGED_IDENTITY,
# Set only for a user-assigned managed identity:
# managed_identity_client_id="your-user-assigned-identity-client-id",
)
```
```typescript TypeScript icon="square-js"
import { OAuthConfig, OAuthFlowType } from "@lancedb/lancedb";
const oauthConfig: OAuthConfig = {
issuerUrl: "https://login.microsoftonline.com/{tenant}/v2.0",
clientId: "your-application-client-id",
scopes: ["api://lancedb-api/.default"],
flow: OAuthFlowType.AzureManagedIdentity,
// 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 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` | 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. All three SDKs deliberately omit the secret from their debug/repr output
so accidental log lines do not expose the value.
</Warning>
Embedded LanceDB has nothing to authenticate to: your process opens the files
directly, and the object store's own credentials are the only gate.