User Management
Manage system users, passwords, roles, two-factor authentication, and OIDC single sign-on.
User List
Go to System → Users to see all users with their username, role, and tenant assignment.
Creating a User
- Click Create User
- Fill in:
- Username
- Password
- Role —
viewer,operator,tenant-admin, orsuper-admin(only super-admins can grant the last two) - Tenant — which tenant this user belongs to
- Click Create
The user can immediately log in with the provided credentials.
Users created in the UI are local WatchGrid users. OIDC users are created automatically on first successful SSO login when auto-provisioning is enabled.
Roles
| Role | Capabilities |
|---|---|
super-admin |
Full access across all tenants — only super-admins can create other super-admins or tenant-admins |
tenant-admin |
Full access to all features within their tenant |
operator |
Day-to-day operations (deploy apps, approve devices, run terminal sessions) |
viewer |
Read-only access to apps and devices |
Super-admins in the default tenant can manage all tenants and system-wide settings.
Changing Your Password
- Go to System → Users
- Click Change My Password
- Enter your current password, new password, and confirmation
- Click Change Password
For OIDC-backed users, this sets or rotates the local fallback password stored in WatchGrid. SSO login itself remains controlled by your identity provider, and the current password field can be left empty when setting that fallback for the first time from an active SSO session.
Deleting a User
Click the Delete button next to any user in the list. This requires confirmation.
Session Settings
The Session card at the top of System → Users (admin-only) controls how all interactive sessions in this tenant behave.
| Setting | Default | What it does |
|---|---|---|
| Session timeout (minutes) | 60 |
New sign-ins receive a JWT that expires after this many minutes. Range: 5–1440. Existing sessions keep their original expiry until they're renewed by a fresh login. |
| Bind session to IP | On |
When enabled, the client IP at login is embedded in the JWT. Any subsequent request from a different IP is rejected with 401, forcing the user to sign in again. Useful for catching token-theft scenarios; can be disruptive for mobile users handing off between cellular and Wi-Fi. |
These values are stored in the system_settings table and read on every auth check (1-minute in-memory cache). They apply to all users in the tenant.
The IP is detected from X-Forwarded-For, X-Real-IP, or RemoteAddr in that order — so behind a reverse proxy you'll see the real client IP, not the proxy's address.
Migration note: tokens minted before this feature have no IP claim. They are grandfathered (skip the IP check) until they expire so that turning IP binding on doesn't immediately sign every active user out.
Tokens issued for the Docker registry (docker login) are deliberately not IP-bound — the docker daemon connects from a different code path than the browser, and binding would break image pushes.
Two-Factor Authentication (2FA)
Watchgrid supports TOTP-based two-factor authentication, compatible with Google Authenticator, Authy, 1Password, and other TOTP apps.
Enabling 2FA
- Go to System → Users
- Click 2FA Settings
- The setup wizard shows a QR code
- Scan the QR code with your authenticator app
- Enter the 6-digit verification code to confirm
- 2FA is now active for your account
Logging In with 2FA
- Enter your username and password as usual
- A second screen asks for your 6-digit verification code
- Enter the code from your authenticator app
- You're logged in
OIDC / SSO Login
The login page can show a Login with SSO button backed by an OpenID Connect provider. WatchGrid exchanges the authorization code server-side, reads the user profile from the provider's userinfo endpoint, and then creates the normal WatchGrid session cookie.
Super-admins configure this on its own page at System → SSO. Settings are saved on the server and survive restarts — no environment variables required.
Environment variable fallbacks
All OIDC settings can alternatively be provided via environment variables. These are read as fallbacks when no value is saved in the UI. Useful for automated deployments or infrastructure-as-code setups.
| Variable | Description |
|---|---|
OIDC_ISSUER_URL |
Provider discovery URL (recommended) |
OIDC_CLIENT_ID |
Application client ID |
OIDC_CLIENT_SECRET |
Application client secret |
OIDC_AUTH_URL |
Authorization endpoint (if no discovery) |
OIDC_TOKEN_URL |
Token endpoint (if no discovery) |
OIDC_USERINFO_URL |
Userinfo endpoint (if no discovery) |
OIDC_PROVIDER_NAME |
Display name, default SSO |
OIDC_BUTTON_TEXT |
Login button label, default Login with SSO |
OIDC_SCOPES |
Default openid profile email |
OIDC_USERNAME_CLAIM |
Default preferred_username |
OIDC_DEFAULT_TENANT_ID |
Tenant for auto-provisioned users, default tenant-default |
OIDC_DEFAULT_ROLE |
Role for auto-provisioned users, default tenant-admin |
OIDC_AUTO_PROVISION |
Default true |
OIDC_AUTO_LINK_USERS |
Default true |
Provider Setup: Microsoft Entra ID (Office 365)
- Go to portal.azure.com → Microsoft Entra ID → App registrations → New registration
- Fill in:
- Name:
Watchgrid(or any name you like) - Supported account types: Accounts in this organizational directory only
- Redirect URI: select
Weband enterhttps://your-watchgrid-domain/api/auth/oidc/callback - Click Register
- Note the Application (client) ID and Directory (tenant) ID from the Overview page
- Go to Certificates & secrets → New client secret → choose an expiry → click Add
- Copy the secret Value immediately (it is only shown once)
Then configure Watchgrid in System → Users → Single Sign-On:
| Field | Value |
|---|---|
| Enabled | On |
| Issuer URL | https://login.microsoftonline.com/{Directory tenant ID}/v2.0 |
| Client ID | Application (client) ID from Azure |
| Client Secret | Secret value from step 6 |
| Scopes | openid profile email |
| Username claim | preferred_username (maps to the user's UPN / email) |
| Button text | Login with Microsoft (or anything you like) |
The login page will show the button immediately after saving.
Behavior
- If the SSO user matches an existing WatchGrid username and auto-linking is enabled, WatchGrid links that user to the OIDC subject and reuses the existing role and tenant access.
- If the SSO user is new and auto-provisioning is enabled, WatchGrid creates the user automatically with the configured default tenant and role.
- In System → Users, the user list shows each account's auth source as
localoroidc.
Reverse Proxy / HTTPS
If Watchgrid runs behind a reverse proxy that does not forward X-Forwarded-Proto, the OIDC redirect_uri may be built with http:// instead of https://, causing most providers to reject the callback. Set the WATCHGRID_EXTERNAL_URL environment variable to fix this:
This overrides auto-detection and ensures the correct scheme and host are used in the redirect URI.
First-Time Onboarding
When a user logs in for the first time (or after being created by an admin), they may see an onboarding wizard that guides them through:
- Setting a new password
- Configuring initial preferences
- Optional 2FA setup
This ensures every user has a secure, personalized account from the start.
Security Notes
OIDC Issuer URL Validation
The OIDC issuer URL is validated before Watchgrid fetches the discovery document. The URL must use HTTPS, and the hostname must not resolve to a loopback, private, or link-local address. This prevents server-side request forgery (SSRF) via a misconfigured or malicious issuer URL.
JWT Secret Strength
JWT_SECRET must be at least 32 characters (256 bits). The server will refuse to start with a shorter secret. Generate a secure secret with:
GDPR — Data-Subject-Access Requests
Two super-admin-only endpoints implement the GDPR Article 15 (right of access) and Article 17 (right to erasure) obligations for user accounts:
Export (Article 15)
curl -fsS -b session.cookie \
-H "X-CSRF-Token: $(grep watchgrid_csrf session.cookie | awk '{print $NF}')" \
"https://watchgrid.example.com/api/users/alice/export" \
> gdpr-export-alice-$(date +%Y%m%d).json
The response bundles every row across users, admin_audit_log, device_security_log, ssh_certificates, license_audit_log, and device_profile_runs that references the given username. The password hash and 2FA secret in the user section are redacted.
Purge (Article 17)
curl -fsS -X DELETE -b session.cookie \
-H "X-CSRF-Token: $(grep watchgrid_csrf session.cookie | awk '{print $NF}')" \
"https://watchgrid.example.com/api/users/alice?purge=true"
The purge runs inside a transaction and:
- Writes a
user_gdpr_purgeentry to the audit log before deleting, so the action survives its own cascade. - Deletes rows in
ssh_certificates,admin_audit_log,license_audit_log, anddevice_profile_runsthat reference the user. - Redacts
device_security_log.details— the log row is kept because it pertains to the device, but any JSON value equal to the purged username is replaced with"<purged>". - Deletes the
usersrow itself.
A regular DELETE /api/users/alice (without ?purge=true) keeps its existing behaviour of removing the login while leaving historic references intact — prefer this for "off-boarding" flows and reserve the purge=true variant for genuine data-subject-erasure requests.
Retention notes for regulated customers
The Audit Log Retention sweeper will eventually delete the residual device_security_log rows that survived the purge; run the sweeper (or a manual archive-to-object-storage job) within your regulatory deadline. Document the GDPR purge evidence in your DPIA — the server-side user_gdpr_purge audit entry is the authoritative record.