Docs Operations

neoserver 0.1.2

Authentication and access

Configure password login, browser sessions, bootstrap tokens, API keys, OIDC, roles, and layer access.

neoserver separates identity validation from authorization. Credentials identify a principal; global/workspace roles and layer rules determine what it may access.

Browser sign-out completes only after server-side session revocation succeeds. A session-store write failure returns HTTP 503 and leaves the browser cookie available for retry; the console reports Sign-out failed and stays on the current page. Retry End session when storage is available. Refresh also returns 503 for persistence failures; HTTP 409 is reserved for an actual concurrent rotation, which the browser recovers from by loading its current identity.

Credential types

Requests may use:

  • Stored API keys in X-API-Key
  • Self-signed JWTs in Authorization: Bearer
  • OIDC bearer tokens
  • A server-configured static API key
  • Server-configured HTTP Basic credentials
  • An encrypted browser session created by the administration console

Health and generated API documentation are public. Management handlers and non-public workspace services enforce their own access requirements.

An invalid, expired, or unverifiable credential returns 401 even when the requested service is public. Omit credentials entirely for anonymous access.

Bootstrap and recovery tokens

The init command generates an ECDSA signing key inside the encrypted store and prints a super_admin JWT valid for 24 hours.

Create a replacement:

export NEOSRV_STORE_KEY="the-original-store-key"
neoserver create-token --store-path ./data/neoserver.db \
  --role super_admin --subject recovery --expires 24h

Rotate the signing key only when all existing self-signed tokens should be invalidated:

neoserver rotate-signing-key --store-path ./data/neoserver.db

Stored API keys and OIDC tokens are not signed by this internal key.

Browser sessions

The administration console at /admin/ exchanges a password, API key, self-signed JWT, or OIDC ID token through POST /api/v1/auth/login. The original credential is not stored in the browser. The server persists only session and CSRF hashes in the encrypted catalog and returns an HttpOnly, SameSite=Lax session cookie plus a readable CSRF cookie. Unsafe HTTP methods and protocol mutations must echo the latter as X-CSRF-Token. This includes GET requests for WFS transactions, locks, and stored-query changes; a cookie alone never authorizes those mutations. Explicit header credentials do not require CSRF tokens.

Auth.Session.TTLSec bounds absolute lifetime, IdleTimeoutSec bounds inactivity, and CleanupIntervalSec controls expired-session cleanup. The current session is rotated with /auth/refresh and revoked with /auth/logout. Super administrators can list and revoke sessions under /auth/sessions.

Ordinary authenticated requests do not extend idle expiry. Only a CSRF-protected POST /api/v1/auth/refresh does, capped at the original absolute expiry. The console refreshes near the idle deadline only after recent pointer/keyboard activity in a visible tab. Dashboard polling, mounting or reloading a tab does not count as activity. An expired session cannot be refreshed; sign in again.

Refresh rotation is conditional on the authenticated token generation. The preceding session/CSRF hashes remain usable for at most 30 seconds to cover requests already in flight; accepting them never extends that window. Reusing the preceding token to refresh returns 409 Conflict, not a successful response with an invalid cookie. Refreshing the current token inside that window is coalesced without another rotation. Revocation and absolute/idle expiry apply to both generations. The console shares in-flight refreshes within a tab, serializes them across tabs using Web Locks where available, and reloads the current identity after a refresh conflict.

Explicit authorization credentials that no configured validator accepts return 401, including unsupported schemes and unknown JWT issuers. They never fall back to an existing browser session or anonymous public access. Requests without credentials may still access public services.

Header credentials take precedence over cookies. An invalid explicit credential therefore fails closed instead of silently falling back to an existing browser session.

Stored API keys

Create workspace-scoped keys through the management API. The full secret is returned once and only its hash is stored.

curl -X POST \
  http://localhost:9000/api/v1/workspaces/acme/apikeys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "map-client",
    "owner_name": "Web map",
    "role_id": "viewer"
  }'

Revoke a key with DELETE on its management URL. A revoked key can no longer authenticate but stays listed, so its prefix can still be traced to a name and owner. Once that record is no longer needed, remove it permanently with DELETE on /api/v1/workspaces/{workspace}/apikeys/{keyId}/permanent; this also deletes browser sessions created from the key. Active keys are refused with 409 and must be revoked first. Prefer separate keys per application and use expires_at for temporary access.

OIDC

[Auth]
Enabled = true
Method = "oidc"
RequireHTTPS = true

[Auth.OIDC]
IssuerURL = "https://identity.example.com/realms/geo"
ClientID = "neoserver"
RequiredScopes = ["openid"]
SkipIssuerCheck = false
BrowserLoginEnabled = true
BrowserScopes = ["openid", "profile", "email"]
GroupClaims = ["groups", "cognito:groups", "realm_access.roles", "roles"]

The server validates signature, issuer, audience/client, and expiry. API bearer tokens must also contain every configured RequiredScopes value in a recognized string or string-array scope claim; missing, empty, malformed, or insufficient scopes are rejected when requirements are configured. Keep issuer checking enabled in production.

BrowserScopes are requested during browser Authorization Code + PKCE login. That login verifies the ID token separately and does not require access-token scope claims on the ID token. RequiredScopes governs resource/API bearer authentication, not ID-token login. Local role mappings still govern what a logged-in principal can access.

For console login, register the callback URL reported by /api/v1/console/config and configure a public Authorization Code client with PKCE S256. The provider’s discovery and token endpoints must permit browser access. The console never uses or exposes a client secret. See Administration console for provider-specific claim conventions.

Map claims to roles:

neoserver add-claim-mapping \
  --store-path ./data/neoserver.db \
  --workspace acme \
  --claim groups \
  --value acme-editors \
  --role editor

Use workspace * for a global mapping. Higher priority mappings are checked first.

OIDC sessions retain the safe claims presented at login and re-evaluate local mappings from those values. Changes at the identity provider are visible only after a new login or session expiry; revoke the browser session for immediate termination.

Authorizing with identity-provider groups

Group values match exactly. When several mappings match the same workspace, neoserver chooses priority descending, then built-in role strength (super_admin, admin, editor, viewer), then role ID lexicographically. A workspace-specific and a global * mapping may both apply. The console Identity page can build a mapping from claims captured in the current session; use the init bootstrap JWT when the first OIDC administrator has not been mapped yet.

Every provider uses the callback shown by /api/v1/console/config, normally https://host[/base-path]/admin/auth/callback, on a public PKCE S256 client:

Provider Group claim Provider setup Example mapping
AWS Cognito cognito:groups Use a public user-pool app client, enable authorization code + PKCE, and allow the callback and browser token request cognito:groups = gis-admins → workspace admin
Clerk groups or a namespaced template claim Add organization/group values to the JWT template used by the OIDC application groups = org:acme:gis → workspace admin
Keycloak realm_access.roles Add realm roles to the ID token through the client scope; group mappers may instead emit groups realm_access.roles = neoserver-admin → workspace admin
Auth0 A namespaced claim such as https://geo.example/roles Add the claim to the ID token in an Action and include that exact name in GroupClaims https://geo.example/roles = publisher → workspace admin
Okta groups Add a groups claim to the ID token and filter it to the groups neoserver needs groups = GIS Operators → workspace admin
Microsoft Entra ID groups or roles Configure group object IDs or app roles in the ID token groups = <group-object-id> → workspace admin

Entra’s group-overage form (_claim_names/_claim_sources) is detected and rejected because neoserver does not call Microsoft Graph to expand it. Prefer app roles or a filtered group claim for users with more than the token limit. Literal claim names are checked before dotted traversal, so both namespaced claims and nested realm_access.roles work. Identity-provider membership changes propagate on the next login; local mapping changes take effect during the existing session.

Static API key and Basic auth

[Auth]
Enabled = true
Method = "apikey"
ApiKey = "shared-secret"
DefaultRole = "viewer"
RequireHTTPS = true
AllowAPIKeyInQuery = false

For Basic auth, set Auth.Method to "basic" and define Auth.Users or AUTH_USERS. Static and Basic principals receive Auth.DefaultRole globally. Only set it to "super_admin" when every configured credential is intended to have full server administration access.

Browser sessions for these credentials re-evaluate Auth.DefaultRole on every authenticated request, just like direct Basic/static-key authentication. Restart with the updated configuration to apply a policy change; existing cookies cannot retain the old elevated role. Removing or changing the credential still invalidates its sessions.

Query-string API keys are disabled by default because URLs leak through logs, caches, browser history, and referrers.

Set up a super admin with a password

In neoserver 0.1.2, Basic usernames and passwords are configured on the server. There is no separate user-creation step in the console. Once configured, you can sign in with a username and password without pasting a bootstrap token.

Start with an initialized server, as described in the console tutorial. Keep its existing catalog and store encryption key; enabling password login does not require running init again.

  1. Update the [Auth] and [Auth.Users] sections in your server’s TOML configuration. Merge these settings into the existing file rather than adding duplicate sections. Replace the password placeholder with a strong, unique password:

    [Auth]
    Enabled = true
    Method = "basic"
    DefaultRole = "super_admin"
    RequireHTTPS = true
    
    [Auth.Users]
    admin = "REPLACE_WITH_A_STRONG_UNIQUE_PASSWORD"

    The admin key is the username; you can choose another name. These are literal passwords, so restrict access to the configuration file and keep it out of source control. AUTH_USERS is an alternative configuration source accepting username:password pairs; see configuration sources.

  2. Restart the service or container with the updated configuration, preserving its existing store path and encryption key. For a directly managed process, stop the existing server and start its replacement with the same configuration file, for example:

    neoserver serve --config /etc/neoserver/production.toml
  3. Open your server’s /admin/ console over HTTPS, choose Password, enter admin and your configured password, then choose Start session. Use the configured base path if your deployment has one.

All Basic users share the same role. With DefaultRole = "super_admin", every username in Auth.Users or AUTH_USERS has global access to all workspaces and server administration. A user named viewer would also become a super admin. Basic authentication does not assign roles individually; use OIDC role mappings when people need different permissions.

For the loopback-only HTTP tutorial at http://localhost:9000, set Auth.RequireHTTPS = false in that local configuration. Keep HTTPS enforcement enabled for production. When TLS terminates at a reverse proxy, configure the trusted proxy and forwarded scheme.

If Password is missing, check that Auth.Enabled=true, Auth.Method="basic", and at least one user are present in the effective configuration, then restart and reload the console. Environment overrides such as NEOSRV_AUTH_METHOD take precedence over TOML, and a valid AUTH_USERS value replaces the configured user map. Changing or removing a password and restarting invalidates sessions created with the previous credential.

Roles

Role Scope Typical access Admin console
super_admin Global All workspaces and global administration Yes, including server administration
admin Workspace Manage services, layers, credentials, settings, and stored queries Yes, for the assigned workspace
editor Workspace Modify layers/styles via the management API and perform WFS write/lock operations No
viewer Workspace Read visible OGC resources and the management API No

The admin console requires admin for a workspace or super_admin. Editor and viewer credentials still work for OGC clients and the management API; signing in to the console with them shows a page that explains this and lets the user copy an access request for an administrator.

The management API can create additional role records, but access policy is based on the permissions configured by the server’s RBAC model.

Use Roles & policies or POST /api/v1/roles/{roleId}/policies to add an operation grant. workspace accepts an existing UUID or name and is stored as a UUID; * explicitly grants across all workspaces. Blank or * operations grant all operations of that service with the chosen action. Grants are additive, not deny rules, and do not bypass service access or layer visibility.

WFS operations Required action
Capabilities, feature/schema/property reads, stored-query listings/descriptions read
Transaction, LockFeature, GetFeatureWithLock write
CreateStoredQuery, DropStoredQuery manage

Other supported protocols currently expose read operations. Unsupported service/operation/action combinations and unknown workspaces return 422 instead of creating ineffective policies. XML POST authorization uses the XML root operation, just like dispatch; conflicting duplicate routing parameters and malformed XML are rejected. Equivalent repeated parameters remain compatible with existing clients. Legacy unmatched workspace policies are shown with a warning and remain removable using their exact stored scope; replace them with an existing workspace selection.

Public services and layer rules

Each workspace service has a public flag:

  • false requires a workspace role
  • true permits anonymous access

Layer access is then evaluated:

Layer setting Result
public: true Visible to every caller that can reach the service
allowed_roles non-empty Visible only to those roles and super_admin
allowed_roles empty Visible to any caller with service access

Listings, capabilities, feature reads, maps, feature info, and tiles all apply the same filtering. A disallowed direct layer request returns not found.

Transport security

With Auth.RequireHTTPS enabled, authenticated requests must arrive over TLS or through a trusted proxy that reports X-Forwarded-Proto: https. Configure Server.TrustedProxyCIDRs so untrusted clients cannot spoof forwarding headers.

Security checklist:

  • Keep the store encryption key and credentials out of TOML committed to source control
  • Prefer OIDC or scoped stored API keys
  • Use viewer by default and grant editor/admin narrowly
  • Rotate and revoke application credentials independently
  • Restrict CORS and datasource network/path access
  • Protect bootstrap and recovery token output

Related: Deployment · Management API

Adapted from neoserver’s source documentation . Maintained independently for this website.

Search documentation

Type to search guides and reference pages.