Back to Openviking

Authentication

docs/en/guides/04-authentication.md

0.4.1323.3 KB
Original Source

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

Modeserver.auth_modeIdentity SourceTypical UseRecommendation
API Key Modeapi_keyAPI key. Data ownership is resolved from the user/admin key.Standard multi-tenant deployment⭐⭐⭐⭐⭐
OIDC ModeoidcJWT tokens from external identity providersEnterprise SSO integration with Okta/Auth0/Keycloak⭐⭐⭐⭐
LDAP ModeldapEnterprise LDAP/AD directory servicesIntegrate with existing enterprise user directory⭐⭐⭐⭐
Trusted ModetrustedX-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 ModedevNo authentication, always ROOTLocal 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.

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 TypeCreated ByRolePurpose
Root KeyServer config (root_api_key)ROOTAccount management + selected system/monitoring operations
User KeyAdmin APIADMIN or USERPer-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: <user-key>"

**Authorization: Bearer ***

bash
curl http://localhost:1933/api/v1/fs/ls?uri=viking:// \
  -H "Authorization: Bearer <user-key>"

Python SDK (HTTP)

python
import openviking as ov

client = ov.SyncHTTPClient(
    url="http://localhost:1933",
    api_key="<user-key>",
)

CLI (via ovcli.conf)

json
{
  "url": "http://localhost:1933",
  "api_key": "<user-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": "<user-key>",
  "root_api_key": "<root-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

OptionDescription
issuerOIDC provider issuer URL (required)
client_idOIDC client ID (optional)
client_secretOIDC client secret (optional)
audienceAudience to validate (optional)
jwks_uriJWKS URL, auto-discovered from issuer by default
token_locationToken location: header (default) or query
token_header_nameHeader name, default Authorization
token_header_prefixHeader 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"
  }
}
  1. 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

OptionDescription
hostLDAP server address (required)
portLDAP server port, default 389
use_sslWhether to use LDAPS (SSL), default false
use_starttlsWhether to use StartTLS, default false
bind_dnBind DN for user searches (recommended)
bind_passwordBind password, supports environment variables
base_dnBase DN for searches (required)
user_search_filterUser search filter, default (uid=%s)
user_search_baseUser search base DN, defaults to base_dn
username_attributeUsername attribute, default uid
email_attributeEmail attribute, default mail
name_attributeName attribute, default cn
user_dn_patternUser 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

RoleScopeCapabilities
ROOTGlobalAll operations + Admin API (create/delete accounts, manage users)
ADMINOwn accountRegular operations + manage users in own account
USEROwn accountRegular 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

MethodEndpointRoleDescription
POST/api/v1/admin/accountsROOTCreate account with first admin
GET/api/v1/admin/accountsROOTList all accounts
DELETE/api/v1/admin/accounts/{id}ROOTDelete account
POST/api/v1/admin/accounts/{id}/usersROOT, ADMINRegister user
GET/api/v1/admin/accounts/{id}/usersROOT, ADMINList users
DELETE/api/v1/admin/accounts/{id}/users/{uid}ROOT, ADMINRemove user
PUT/api/v1/admin/accounts/{id}/users/{uid}/roleROOT, ADMINPromote a user to ADMIN; ADMIN is limited to its own account
POST/api/v1/admin/accounts/{id}/users/{uid}/keyROOT, ADMINRegenerate 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)

MethodPurpose
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:

FieldRequiredDescription
urlYesOpenViking server URL
auth_modeYesAuthentication mode, set to "ldap" to enable LDAP
ldap_usernameYesLDAP username (UID)
ldap_passwordNoLDAP password (omit to skip password in CLI)
accountNoOpenViking 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