Skip to content
getgeolens.com

User Management & RBAC

GeoLens uses role-based access control with three roles: viewer, editor, and admin. The role set is fixed; what each role can do is configurable. Most day-to-day administration happens through the admin web UI at /admin; the same actions are available via the REST API for scripting and automation.

Replace https://geolens.example.com with your GeoLens instance’s URL in every example below.

The default role hierarchy is viewer -> editor -> admin. The default permission matrix is defined in code at backend/app/core/permissions.py (validated by validate_permission_matrix in backend/app/modules/auth/permissions.py) and can be customized by an admin via Admin -> Settings -> Permissions.

Capabilityviewereditoradmin
upload (upload data files)NoYesYes
create_layers (create empty layers)NoYesYes
export (export datasets)YesYesYes
edit_metadata (edit dataset metadata)NoYesYes
manage_collections (create/delete collections)NoYesYes
use_ai_chat (use AI styling/chat)NoYesYes
manage_users (create/edit users)NoNoYes
manage_settings (edit system settings)NoNoYes

Endpoints that require a capability declare it through the require_permission() dependency, which resolves the caller’s roles from the database and evaluates them against the effective matrix on each request. A user receives a 403 Forbidden if their role lacks the capability for the requested endpoint, regardless of dataset visibility.

GeoLens ships with self-registration disabled by default. With REGISTRATION_ENABLED=true in .env, the login screen exposes a Sign up link; new accounts created through it land in a pending state and require an admin to approve them before they can log in.

To enable:

.env
REGISTRATION_ENABLED=true

REGISTRATION_ENABLED is a runtime setting: the environment variable supplies the initial value, and an admin can toggle it afterwards under Admin -> Settings -> Auth without restarting the stack (unless the instance runs with ENV_ONLY_CONFIG=true, which pins every setting to its env value).

Pending users appear in Admin -> Users under the Pending status filter. Use the row’s actions menu: Approve (pick the role to assign — a self-registered account has no role until then) or Reject, which deletes the registration. A pending account’s role and status cannot be edited directly; a PATCH /api/admin/users/{user_id} that carries role, status, or the legacy is_active for a pending user is refused with 422 (an email-only PATCH still succeeds). Use POST /api/admin/users/{user_id}/approve/ (body {"role": "viewer"}) or POST /api/admin/users/{user_id}/reject/ instead. Until approved, login attempts return 403 Forbidden because the account is pending approval.

For SSO-managed organizations, leave REGISTRATION_ENABLED=false and provision users through OAuth/OIDC. See OAuth/OIDC setup for the provider configuration walkthrough.

Navigate to Admin -> Users to see every account on the instance. The list shows username, email, roles, status (active, pending, suspended, deactivated), file storage used, last login, and creation date. The header + Add User button opens the create dialog.

The create dialog requires a username (3 characters or more), an email address, a password, and a role. Email is mandatory in the UI; the REST API accepts a user without one. The username is the unique identifier used for login and audit logs; the password must meet the configured policy (by default at least 12 characters drawn from 3 of the 4 classes — lowercase, uppercase, digit, symbol; see Configuration Reference). Role is required and defaults to viewer.

Use the Deactivate row action to block login without deleting the user’s data. Deactivated users keep their datasets, maps, and collections. The suspended status also blocks login. Reactivate either status by setting it to active.

Use the Reset password menu action to set a new password for a user who has locked themselves out, or who has lost access to their own account recovery. The dialog asks for the new password only — no current password — since holding manage_users is the authorization and the action is written to the audit log. Resetting revokes the account’s existing sessions, refresh tokens, and API keys, so a leaked old password stops working immediately; tell the user the new password out of band. You can reset your own local password; doing so signs you out too.

Reset is supported for local-password accounts, including local accounts that have linked an OAuth/OIDC identity. It is refused for accounts managed only by an identity provider: reset those credentials with the provider. A GeoLens reset does not change the provider’s password or override the instance’s password-login policy.

Unreleased UI changes: the next release disables Reset password for provider-only accounts and adds an All roles filter to the user list. Role filtering can be combined with status and search.

Deleting a user is permanent. Their API keys, saved searches, OAuth links, and role assignments are deleted with them; their datasets, maps, collections, and jobs survive but lose their owner (the owner field is set to null), and their audit-log entries are retained with a null actor. An ownerless dataset is also exempt from per-user quota accounting — its bytes and its row count against nobody — so after a deletion the instance total no longer equals the sum of the per-user meters. You cannot delete your own account, and you cannot delete the last remaining active admin — both are refused with 400.

All user-administration endpoints require the manage_users capability (admin role). Obtain a JWT token first:

Terminal window
# Both admin credentials are written to .env at install time. The username
# defaults to `admin` but the installer lets you change it, so read both:
# export GEOLENS_ADMIN_USERNAME="$(grep '^GEOLENS_ADMIN_USERNAME=' .env | cut -d= -f2-)"
# export GEOLENS_ADMIN_PASSWORD="$(grep '^GEOLENS_ADMIN_PASSWORD=' .env | cut -d= -f2-)"
TOKEN=$(curl -s -X POST https://geolens.example.com/api/auth/login \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "username=$GEOLENS_ADMIN_USERNAME" \
--data-urlencode "password=$GEOLENS_ADMIN_PASSWORD" | jq -r '.access_token')
Terminal window
curl -X POST https://geolens.example.com/api/admin/users \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "analyst1",
"password": "Analyst-Temp-2026!",
"role": "editor"
}'

Passwords must meet the configured policy — by default at least 12 characters drawn from 3 of the 4 classes (lowercase, uppercase, digit, symbol). A password that fails the policy is refused with 422.

Terminal window
curl https://geolens.example.com/api/admin/users \
-H "Authorization: Bearer $TOKEN"

Supports pagination: ?skip=0&limit=50.

Unreleased: the development branch also accepts role=admin, role=editor, or role=viewer. The filter matches an assigned role, including users with more than one role; omit it to include all roles. User responses in that release also expose can_reset_password so clients can gate the reset action without inferring eligibility from linked providers.

Terminal window
curl https://geolens.example.com/api/admin/users/{user_id} \
-H "Authorization: Bearer $TOKEN"
Terminal window
curl -X PATCH https://geolens.example.com/api/admin/users/{user_id} \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "admin"}'

A role change takes effect immediately, including for tokens already issued: role is never read from the JWT — every capability check resolves the caller’s roles from the database on the request that needs them. Changing a role does invalidate that user’s existing API keys; see API keys below.

Terminal window
curl -X POST https://geolens.example.com/api/admin/users/{user_id}/approve/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "viewer"}'

Rejecting deletes the registration:

Terminal window
curl -X POST https://geolens.example.com/api/admin/users/{user_id}/reject/ \
-H "Authorization: Bearer $TOKEN"

Deactivating a user prevents them from logging in without deleting their data:

Terminal window
curl -X POST https://geolens.example.com/api/admin/users/{user_id}/deactivate \
-H "Authorization: Bearer $TOKEN"
Terminal window
curl -X POST https://geolens.example.com/api/admin/users/{user_id}/reset-password/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"password": "Temp-Reset-2026!"}'

The new password must meet the configured policy. The response is 422 for a provider-only account, and 404 if no such user exists. Local accounts with linked OAuth/OIDC identities remain eligible. Resetting revokes the account’s existing sessions, refresh tokens, and API keys — including the caller’s own, if resetting their own account.

API keys are long-lived credentials suitable for scripts, scheduled jobs, and machine clients. Each key is scoped to a single user account. A full key (the default) inherits that user’s permissions; a read_only key authenticates safe methods (GET, HEAD, OPTIONS) and refuses other methods with 403, with a small documented carve-out for endpoints that are reads in spirit — the SQL sandbox POST /api/query/ and STAC item search. The carve-out runs both ways: GET /api/datasets/{dataset_id}/validate/?refresh=true recomputes and persists a quality score, so it is the one safe-method request a read_only key is refused (the same route without refresh=true is served normally). There is no separate “service account” role. Keys can only be minted for active users; any other owner status is refused with 409.

  1. Navigate to Admin -> Users, open the target user’s actions menu, and choose Edit.
  2. Scroll to the API Keys section of the dialog.
  3. Click Create Key, enter a key name (e.g., “ETL pipeline 2026-01”), pick a scope (Full access or Read only), and copy the key value when it is revealed.

The key value is shown once. There is no way to retrieve it again. Keys are stored as SHA-256 hashes; the raw key is never persisted and cannot be recovered. If lost, revoke the key and issue a new one.

Terminal window
curl -X POST https://geolens.example.com/api/admin/api-keys/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"user_id": "{user_id}",
"name": "ETL pipeline 2026-01",
"scope": "full"
}'

scope is full (the default) or read_only. An optional expires_at — an RFC 3339 timestamp that must be in the future — mints an expiring key; omit it for a non-expiring one. Expired keys stop authenticating.

The response includes the full key value. Capture it immediately. Subsequent reads (GET /api/admin/api-keys/) return only the key name and metadata, never the key value.

Terminal window
curl -X DELETE https://geolens.example.com/api/admin/api-keys/{key_id} \
-H "Authorization: Bearer $TOKEN"

Revocation takes effect on the next request. There is no “rotate” operation: issue a new key, update consumers, then revoke the old one.

For the request-side details (header form vs query-string form, resolution order against JWT and OAuth), see API authentication.

The admin web UI (/admin) exposes several operator tools beyond user management. The most useful are:

  • Audit Log (/admin/audit): Browse and filter the full audit log with action, user, resource, and date filters. Use this page to investigate “who changed what” without writing API queries.

  • Jobs (/admin/jobs): Lists all ingestion jobs across all users with status, source filename, and timing. Failed jobs link to the error message and the user who started them. Useful for triaging stuck or repeatedly failing imports.

  • Published Maps (/admin/shared-maps): system-wide view of every share link on the instance — map name, link status, embed-token count, expiry, creation date, and creator. Expand a row to see that map’s embed tokens with their use count, last-used time, expiry, and allowed origins; share links and embed tokens (individually or in bulk) can be revoked from here without finding the parent map first. Use this to audit external sharing, especially before public events or when rotating leaked tokens.

  • Config Ops (/admin/config-ops): Export the entire instance configuration (settings + OAuth providers, secrets redacted) as a JSON file, or import a previously exported configuration in either merge or overwrite mode. The dry-run button shows exactly what would change before applying anything. Use this to copy configuration between dev/staging/prod instances or to back up settings before major upgrades.

The companion Validate Connectivity button on Config Ops probes storage, cache, and every enabled OIDC provider and reports per-provider latency and error details. Useful for diagnosing post-deployment issues without SSH access. See Infrastructure & Monitoring for the connectivity-probe details.