# Authentication OpenViking Server supports multiple built-in authentication modes with role-based access control. The mode is auto-detected if not explicitly configured. In addition, custom authentication plugins can be registered to support arbitrary identity sources. > **Recommended:** API Key mode is the most common and secure default choice, suitable for most deployment scenarios. ## Authentication Modes Overview | Mode | `server.auth_mode` | Identity Source | Typical Use | Recommendation | |------|--------------------|-----------------|-------------|----------------| | **API Key Mode** ⭐ | `api_key` | API key. Data ownership is resolved from the user/admin key. | Standard multi-tenant deployment | ⭐⭐⭐⭐⭐ | | **OIDC Mode** | `oidc` | JWT tokens from external identity providers | Enterprise SSO integration with Okta/Auth0/Keycloak | ⭐⭐⭐⭐ | | **LDAP Mode** | `ldap` | Enterprise LDAP/AD directory services | Integrate with existing enterprise user directory | ⭐⭐⭐⭐ | | **Trusted Mode** | `trusted` | `X-OpenViking-Account` / `X-OpenViking-User`, plus `root_api_key` on non-localhost deployments. Role is looked up from APIKeyManager if the user exists. | Behind a trusted gateway or internal network boundary | ⭐⭐⭐ | | **Dev Mode** | `dev` | No authentication, always ROOT | Local development only | ⭐⭐ | If `auth_mode` is not explicitly configured: - If `root_api_key` is set (non-empty): auto-selects `api_key` mode - If `root_api_key` is not set: auto-selects `dev` mode > **Note:** Setting `root_api_key` to an empty string `""` is invalid. Either set a non-empty value or remove the setting entirely. ## Quick Start: API Key Mode (Recommended) API Key mode is the most secure and easiest default choice. ### Configuration Configure the authentication mode in the `server` section of `ov.conf`: ```json { "server": { "auth_mode": "api_key", "root_api_key": "your-secret-root-key" } } ``` ### Start the Server ```bash openviking-server ``` ### Two-Layer API Key System | Key Type | Created By | Role | Purpose | |----------|-----------|------|---------| | Root Key | Server config (`root_api_key`) | ROOT | Account management + selected system/monitoring operations | | User Key | Admin API | ADMIN or USER | Per-account data access; ADMIN can also manage users in its account | User keys are generated by the Admin API. By default they use random secret material; Admin API callers may also provide a `seed` to derive a predictable key secret from `sha256(user_id + "\0" + seed)`. The server still verifies the resulting key against the stored key or key hash. ### Managing Accounts and Users Use the root key to create accounts (workspaces) and users via the Admin API: ```bash # Create account with first admin curl -X POST http://localhost:1933/api/v1/admin/accounts \ -H "X-API-Key: your-secret-root-key" \ -H "Content-Type: application/json" \ -d '{"account_id": "acme", "admin_user_id": "alice"}' # Returns: {"result": {"account_id": "acme", "admin_user_id": "alice", "user_key": "..."}} # Register a regular user (as ROOT or ADMIN) curl -X POST http://localhost:1933/api/v1/admin/accounts/acme/users \ -H "X-API-Key: your-secret-root-key" \ -H "Content-Type: application/json" \ -d '{"user_id": "bob", "role": "user"}' # Returns: {"result": {"account_id": "acme", "user_id": "bob", "user_key": "..."}} ``` ### Client Usage OpenViking accepts API keys via two headers: **X-API-Key header** ```bash curl http://localhost:1933/api/v1/fs/ls?uri=viking:// \ -H "X-API-Key: " ``` **Authorization: Bearer *** ```bash curl http://localhost:1933/api/v1/fs/ls?uri=viking:// \ -H "Authorization: Bearer " ``` **Python SDK (HTTP)** ```python import openviking as ov client = ov.SyncHTTPClient( url="http://localhost:1933", api_key="", ) ``` **CLI (via ovcli.conf)** ```json { "url": "http://localhost:1933", "api_key": "" } ``` When you use a user key or admin key, the server derives `account` and `user` from the key. Do not send `X-OpenViking-Account` / `X-OpenViking-User` in `api_key` mode; those identity headers are accepted only in `trusted` mode. **CLI override flags** ```bash openviking ls viking:// ``` ### Using --sudo with Root API Key The CLI supports configuring both `api_key` (for regular user operations) and `root_api_key` (for admin operations) in `ovcli.conf`: ```json { "url": "http://localhost:1933", "api_key": "", "root_api_key": "" } ``` When you need to perform admin commands (`admin`, `system`, `reindex`), use the `--sudo` flag to elevate privileges: ```bash # List all accounts (requires root privileges) ov --sudo admin list-accounts # Reindex content ov --sudo reindex viking:// # System commands ov --sudo system status ``` The `--sudo` flag: - Only works with management/system commands: `admin`, `system`, `reindex` - Will error if used with non-admin commands - Will error if `root_api_key` is not configured in `ovcli.conf` - Uses `root_api_key` instead of `api_key` for the request ### Tenant Data Access Tenant-scoped data APIs (for example `ls`, `find`, resources, and sessions) must use a key that is bound to an account/user in `api_key` mode. That can be a `USER` key or an `ADMIN` key; an `ADMIN` key accesses data as its own user and cannot switch identity with `X-OpenViking-Account` / `X-OpenViking-User`. A `ROOT` key is not bound to a tenant user, so it cannot access tenant-scoped data APIs in `api_key` mode. If a deployment needs an upstream gateway to assert `account` / `user`, use `trusted` mode instead of passing identity headers with a root key. ## OIDC Authentication OIDC (OpenID Connect) authentication allows using JWT tokens from external identity providers (Okta, Auth0, Keycloak, Azure AD, etc.) for authentication. ### Install Dependencies OIDC and LDAP authentication require optional dependencies. You can install them as: ```bash # Install auth features only uv pip install openviking[auth] # Or install with all features (including bot) uv pip install openviking[bot] ``` ### Configure OIDC Configure OIDC in `ov.conf`: ```json { "server": { "auth_mode": "oidc", "oidc": { "issuer": "https://your-oidc-provider.com/", "client_id": "your-client-id", "audience": "openviking", "jwks_uri": "https://your-oidc-provider.com/.well-known/jwks.json", "identity": { "account_id": { "mode": "organization", "source": "claim", "claim": "tenant_id", "prefix": "org-", "fallback": "default-org" }, "user_id": { "source": "claim", "claim": "sub", "prefix": "user-", "normalize": "lowercase" }, "role": { "source": "claim", "claim": "openviking_role", "mapping": { "admin": "admin", "developer": "user" }, "default": "user" } } } } } ``` ### OIDC Configuration Options | Option | Description | |--------|-------------| | `issuer` | OIDC provider issuer URL (required) | | `client_id` | OIDC client ID (optional) | | `client_secret` | OIDC client secret (optional) | | `audience` | Audience to validate (optional) | | `jwks_uri` | JWKS URL, auto-discovered from issuer by default | | `token_location` | Token location: `header` (default) or `query` | | `token_header_name` | Header name, default `Authorization` | | `token_header_prefix` | Header prefix, default `Bearer ` | ### Identity Mapping Configuration #### Account ID Mapping Two modes are supported: 1. **Organization mode** (`mode: "organization"`) - entire organization shares one account ```json { "account_id": { "mode": "organization", "source": "claim", "claim": "tenant_id" } } ``` 2. **Team mode** (`mode: "team"`) - isolation by team ```json { "account_id": { "mode": "team", "source": "claim", "claim": "department", "prefix": "team-" } } ``` #### User ID Mapping ```json { "user_id": { "source": "claim", "claim": "sub", "prefix": "user-", "normalize": "lowercase" } } ``` #### Role Mapping ```json { "role": { "source": "claim", "claim": "role", "mapping": { "administrator": "admin", "developer": "user", "viewer": "user" }, "default": "user" } } ``` ### Advanced Mapping Features #### Composite Mapping ```json { "account_id": { "source": "composite", "parts": [ {"source": "claim", "claim": "tenant_id"}, {"literal": "-"}, {"source": "claim", "claim": "department"} ] } } ``` #### Regex Extraction ```json { "user_id": { "source": "claim", "claim": "email", "regex": "^([^@]+)@", "regex_group": 1 } } ``` #### Multiple Field Fallback ```json { "account_id": { "source": "claim", "claim": ["department", "team", "organization"], "fallback": "default" } } ``` ### Using OIDC Authentication Clients carry the JWT token in requests: ```bash curl https://openviking.example.com/api/v1/fs/ls?uri=viking:// \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." ``` ## LDAP Authentication LDAP authentication allows using enterprise LDAP servers (Active Directory, OpenLDAP, etc.) for authentication. ### Configure LDAP #### Using Search Bind (Recommended) ```json { "server": { "auth_mode": "ldap", "ldap": { "host": "ldap.example.com", "port": 636, "use_ssl": true, "use_starttls": false, "bind_dn": "cn=openviking,dc=example,dc=com", "bind_password": "${LDAP_BIND_PASSWORD}", "base_dn": "dc=example,dc=com", "user_search_filter": "(uid=%s)", "username_attribute": "uid", "email_attribute": "mail", "name_attribute": "cn", "identity": { "account_id": { "mode": "team", "source": "dn_attribute", "attribute": "ou", "prefix": "team-", "fallback": "default" }, "user_id": { "source": "attribute", "attribute": "uid", "normalize": "lowercase" }, "role": { "source": "group_membership", "group_mapping": { "cn=openviking-admins,ou=groups,dc=example,dc=com": "admin", "cn=openviking-users,ou=groups,dc=example,dc=com": "user" }, "default": "user" } } } } } ``` #### Using Direct Bind ```json { "server": { "auth_mode": "ldap", "ldap": { "host": "ldap.example.com", "port": 389, "use_ssl": false, "use_starttls": true, "base_dn": "dc=example,dc=com", "user_dn_pattern": "uid=%s,ou=users,dc=example,dc=com", "identity": { "account_id": { "mode": "organization", "source": "fixed", "value": "my-company" }, "user_id": { "source": "attribute", "attribute": "uid" }, "role": { "source": "fixed", "value": "user" } } } } } ``` ### LDAP Configuration Options | Option | Description | |--------|-------------| | `host` | LDAP server address (required) | | `port` | LDAP server port, default 389 | | `use_ssl` | Whether to use LDAPS (SSL), default false | | `use_starttls` | Whether to use StartTLS, default false | | `bind_dn` | Bind DN for user searches (recommended) | | `bind_password` | Bind password, supports environment variables | | `base_dn` | Base DN for searches (required) | | `user_search_filter` | User search filter, default `(uid=%s)` | | `user_search_base` | User search base DN, defaults to `base_dn` | | `username_attribute` | Username attribute, default `uid` | | `email_attribute` | Email attribute, default `mail` | | `name_attribute` | Name attribute, default `cn` | | `user_dn_pattern` | User DN pattern for direct bind | ### LDAP Identity Mapping #### Extract Organizational Unit from DN ```json { "account_id": { "mode": "team", "source": "dn_attribute", "attribute": "ou", "prefix": "team-" } } ``` #### Map from Attribute ```json { "account_id": { "mode": "team", "source": "attribute", "attribute": "department", "mapping": { "Engineering": "eng-team", "Sales": "sales-team" } } } ``` #### Role Mapping based on Group Membership ```json { "role": { "source": "group_membership", "group_mapping": { "cn=admins,ou=groups,dc=example,dc=com": "admin", "cn=users,ou=groups,dc=example,dc=com": "user" }, "default": "user" } } ``` ### Using LDAP Authentication Clients submit credentials using Basic Auth: ```bash curl https://openviking.example.com/api/v1/fs/ls?uri=viking:// \ -u alice:password123 ``` ## Trusted Mode Trusted mode skips user-key lookup and instead trusts explicit identity headers on each request: ```json { "server": { "auth_mode": "trusted", "host": "127.0.0.1" } } ``` ### Rules in Trusted Mode - Normal data access does not require user registration or user-key provisioning first. - `X-OpenViking-Account` and `X-OpenViking-User` are required on tenant-scoped requests. - A trusted upstream may also send `X-OpenViking-Role: user` or `X-OpenViking-Role: admin` to assert the request role. This header is accepted only when `root_api_key` is configured and the request presents the matching API key. `root` is not an allowed asserted value; ROOT is reserved for validated Admin API fallback. - `/api/v1/admin/*` is special: when a configured `root_api_key` is presented, trusted mode treats the request as ROOT. Explicit account/user headers are allowed only when they are complete and match the target URL. - For ordinary trusted data APIs, role is determined by `X-OpenViking-Role` when present and authorized; otherwise by looking up the account/user in APIKeyManager. If the user exists, their configured role is used; otherwise it defaults to `USER`. - Trusted identity comes from the headers, not from a user key. If `root_api_key` is configured, it acts as proof that the caller is an approved trusted upstream. - If `root_api_key` is also configured, every request must still provide a matching API key. - Only expose this mode behind a trusted network boundary or an identity-injecting gateway. ### Trusted Deployment Admin API Trusted deployments can also call Admin API through a trusted gateway. There are two supported patterns: 1. Present the trusted deployment's `root_api_key`. For `/api/v1/admin/*`, the server treats the request as ROOT after validating that key. 2. Optionally also present `X-OpenViking-Account` + `X-OpenViking-User` when the admin route targets a specific account/user. Those headers must match the target URL and are kept as the request identity, but authorization still comes from the trusted `root_api_key`. Example using a trusted upstream identity: ```bash # First, register the gateway admin (do this once in api_key mode) curl -X POST http://localhost:1933/api/v1/admin/accounts \ -H "X-API-Key: your-secret-root-key" \ -H "Content-Type: application/json" \ -d '{"account_id": "platform", "admin_user_id": "gateway-admin"}' # Then use that identity in trusted mode; admin authorization comes from root_api_key curl -X POST http://localhost:1933/api/v1/admin/accounts \ -H "X-API-Key: your-secret-root-key" \ -H "X-OpenViking-Account: platform" \ -H "X-OpenViking-User: gateway-admin" \ -H "Content-Type: application/json" \ -d '{ "account_id": "acme", "admin_user_id": "alice" }' ``` ### Trusted Mode Client Configuration **ovcli.conf** ```json { "url": "http://localhost:1933", "auth_mode": "trusted", "api_key": "your-trusted-server-key", "account": "acme", "user": "alice" } ``` **curl Example** ```bash curl http://localhost:1933/api/v1/fs/ls?uri=viking:// \ -H "X-OpenViking-Account: acme" \ -H "X-OpenViking-User: alice" ``` **Python SDK** ```python import openviking as ov client = ov.SyncHTTPClient( url="http://localhost:1933", account="acme", user="alice", ) ``` ## Dev Mode When `auth_mode = "dev"` (or auto-detected when no `root_api_key` is configured), authentication is disabled. All requests are accepted as ROOT with the default account. **This is only allowed when the server binds to localhost** (`127.0.0.1`, `localhost`, or `::1`). If `host` is set to a non-loopback address (e.g. `0.0.0.0`) in `dev` mode, the server will refuse to start. ```json { "server": { "host": "127.0.0.1", "port": 1933 } } ``` Or explicitly: ```json { "server": { "auth_mode": "dev", "host": "127.0.0.1", "port": 1933 } } ``` > **Security note:** The default `host` is `127.0.0.1`. If you need to expose the server on the network, you **must** configure `root_api_key`. ## Roles and Permissions | Role | Scope | Capabilities | |------|-------|-------------| | ROOT | Global | All operations + Admin API (create/delete accounts, manage users) | | ADMIN | Own account | Regular operations + manage users in own account | | USER | Own account | Regular operations (ls, read, find, sessions, etc.) | In `trusted` mode, ordinary tenant requests default to `USER` unless the account/user is registered with a higher role or the gateway asserts `X-OpenViking-Role: admin` with the configured root API key. `X-OpenViking-Role: root` is rejected. Admin routes also allow a trusted ROOT fallback when no explicit identity is provided. ## Unauthenticated Endpoints The `/health` endpoint never requires authentication. This allows load balancers and monitoring tools to check server health. ```bash curl http://localhost:1933/health ``` ## Admin API Reference | Method | Endpoint | Role | Description | |--------|----------|------|-------------| | POST | `/api/v1/admin/accounts` | ROOT | Create account with first admin | | GET | `/api/v1/admin/accounts` | ROOT | List all accounts | | DELETE | `/api/v1/admin/accounts/{id}` | ROOT | Delete account | | POST | `/api/v1/admin/accounts/{id}/users` | ROOT, ADMIN | Register user | | GET | `/api/v1/admin/accounts/{id}/users` | ROOT, ADMIN | List users | | DELETE | `/api/v1/admin/accounts/{id}/users/{uid}` | ROOT, ADMIN | Remove user | | PUT | `/api/v1/admin/accounts/{id}/users/{uid}/role` | ROOT, ADMIN | Promote a user to ADMIN; ADMIN is limited to its own account | | POST | `/api/v1/admin/accounts/{id}/users/{uid}/key` | ROOT, ADMIN | Regenerate user key | ## Custom Authentication Plugins The server uses a plugin-based auth architecture. Each `auth_mode` maps to an `AuthPlugin` implementation. Built-in plugins (`dev`, `api_key`, `trusted`, `oidc`, `ldap`) are auto-registered; third-party plugins can be added by subclassing `AuthPlugin` and registering it before startup. ### Plugin Interface (`openviking.server.auth.plugin.AuthPlugin`) | Method | Purpose | |--------|---------| | `resolve_identity(request, api_key, x_openviking_account, x_openviking_user)` | Resolve credentials to a `ResolvedIdentity`. | | `validate_config(config)` | Validate `ServerConfig` at startup; should `sys.exit(1)` on fatal misconfiguration. | | `initialize(app, service, config)` | Initialize runtime state (e.g., `APIKeyManager`) on `app.state`. | | `get_request_context_checks(path, identity)` | Optional post-auth path/identity checks. | | `requires_api_key_manager()` | Whether Admin API routes need an `APIKeyManager`. | | `can_skip_api_key_for_bot_proxy()` | Whether the bot proxy may skip API key validation (e.g., `dev` mode). | ### Register a Custom Plugin ```python from openviking.server.auth.plugin import AuthPlugin from openviking.server.auth.registry import register_auth_plugin from openviking.server.identity import ResolvedIdentity, Role @register_auth_plugin class CustomAuthPlugin(AuthPlugin): auth_mode = "custom" async def resolve_identity(self, request, *, api_key=None, x_openviking_account=None, x_openviking_user=None): # ... Custom identity resolution logic ... return ResolvedIdentity(role=Role.USER, account_id="...", user_id="...") def validate_config(self, config): pass async def initialize(self, app, service, config): pass ``` Then set `server.auth_mode = "custom"` in `ov.conf`. ### Custom Roles The built-in `Role` class supports dynamic registration of custom roles with privilege ranks: ```python from openviking.server.identity import Role Role.register("operator", rank=1) # Between USER (0) and ADMIN (1) ``` Custom roles work with `require_role()` and `require_auth_role()` decorators out of the box. --- ## CLI LDAP Authentication Configuration The OpenViking CLI (`ov`) supports LDAP authentication. Once configured, all CLI commands automatically use LDAP credentials. ### Configuration Methods #### 1. Config File (Recommended) Edit `~/.openviking/ovcli.conf` to add LDAP authentication settings: ```json { "url": "http://localhost:1933", "auth_mode": "ldap", "ldap_username": "alice", "ldap_password": "password123", "account": "default" } ``` **Configuration Fields:** | Field | Required | Description | |-------|----------|-------------| | `url` | Yes | OpenViking server URL | | `auth_mode` | Yes | Authentication mode, set to `"ldap"` to enable LDAP | | `ldap_username` | Yes | LDAP username (UID) | | `ldap_password` | No | LDAP password (omit to skip password in CLI) | | `account` | No | OpenViking account ID (defaults to `"default"`) | #### 2. Mixed Configuration You can configure some settings in the file and override others with environment variables (e.g. `OPENVIKING_URL`, `OPENVIKING_ACCOUNT`). ### Using the CLI Once configured, all CLI commands automatically use LDAP authentication: ```bash # List resources ov ls viking:// # Read a resource ov read viking://resources/example.md # Write a resource ov write viking://resources/test.md --content "Hello LDAP!" ``` ### Building the Rust CLI (if updated) If you modify the Rust CLI source code, rebuild it: ```bash make build-cli ``` The built binary is located at `openviking/bin/ov`. ### Switching Authentication Modes You can switch between LDAP and API Key authentication by editing `~/.openviking/ovcli.conf` and changing `auth_mode` to `"api_key"` or removing the field. ### Security Recommendations 1. **Avoid storing plaintext passwords**: Prefer environment variables or secret managers over hardcoding in config files 2. **Use HTTPS**: Always use HTTPS in production environments 3. **Least privilege**: Use regular user accounts for daily operations, admin accounts only for management tasks 4. **Rotate passwords regularly**: Follow your organization's password security policy ### Troubleshooting **"Missing LDAP credentials" error:** - Check that `auth_mode` is set to `"ldap"` - Verify `username` and `password` are configured correctly **"LDAP authentication failed" error:** - Verify LDAP username and password are correct - Check that the LDAP server is reachable - Review server logs for detailed error messages **"Permission denied" error:** - Confirm the user's LDAP groups map to the correct OpenViking role - Check if the operation requires admin privileges - Contact your system administrator to verify permissions **Debug mode:** ```bash # Enable verbose logging RUST_LOG=debug ov ls viking:// # Check configuration ov doctor ``` --- ## Related Documentation - [Multi-Tenant](../concepts/11-multi-tenant.md) - Multi-tenant capabilities, sharing boundaries, and integration patterns - [Configuration](01-configuration.md) - Configuration file reference - [Deployment](03-deployment.md) - Server deployment - [API Overview](../api/01-overview.md) - API reference