Skip to content

Feature Guide User Management

fuomag9 edited this page Sep 27, 2026 · 7 revisions

Feature Guide: User Management & Roles

Manage user accounts, roles, and access from the admin dashboard.

Table of Contents

  1. User Roles
  2. Users Page
  3. Managing Users
  4. Passwords
  5. Sign-in Usernames
  6. Groups
  7. Default Accounts
  8. OAuth Users
  9. API Access

User Roles

CPM has three roles with increasing privileges:

Capability Viewer User Admin
Log in to the dashboard Yes Yes Yes
View own profile Yes Yes Yes
Access forward-auth-protected apps (when granted) Yes Yes Yes
Manage proxy hosts, certificates, access lists No No Yes
Manage users, groups, and settings No No Yes
View analytics, audit log, and API docs No No Yes
Create and manage own API tokens Yes Yes Yes
Access role-appropriate REST API endpoints (/api/v1/) Yes Yes Yes

Role descriptions

  • Viewer — Can log in and access forward-auth-protected apps they've been granted. Cannot see or change any configuration.
  • User — Same as Viewer. Intended for users who only need forward auth access.
  • Admin — Full access to all features, settings, and the REST API.

New users (including OAuth sign-ups) default to the user role.

API tokens can only be created from an authenticated dashboard session; an existing bearer token cannot mint replacement credentials. Viewer and user tokens are limited to the same user-scoped API capabilities as their owner. See Feature Guide REST API.


Users Page

The Users page is available to admins at Users in the sidebar.

Features:

  • Search — Filter users by name, email, or role
  • User count — Shows total matching users
  • Inline editing — Edit role, status, display name, email, and sign-in username (since v1.13.1) without leaving the page
  • Status management — Disable or re-enable accounts
  • Deletion — Permanently remove user accounts
  • Sign-in usernames — A user's sign-in username is shown next to their email when the two differ (since v1.13.1), and no sign-in username marks an account that has no username the login page can use (since v1.13.1; see Sign-in Usernames)

Errors such as a rejected password or a duplicate email ("A user with this email already exists") are shown on the page, and the form keeps what was entered (since v1.13.1).


Managing Users

Create a user

Admins can create new users directly from the Users page:

  1. Click Create User (top right of the Users page).
  2. Fill in the form: email, display name, role, and password. The password must meet the password policy.
  3. Click Create.

The new user can immediately log in at /login with the provided password and their email address as username, when that address is a valid username that no other account uses. Otherwise the user row shows no sign-in username: set one with edit before the user can sign in (see Sign-in Usernames).

Edit a user

  1. Click the edit icon on a user row.
  2. Change the role, display name, email, or Username (the sign-in username, since v1.13.1).
  3. Click Save.

Name, email and username are saved together: when one of them is refused, none of them changes and the reason is shown. Changing the email does not change the username.

Disable a user

  1. Click the disable icon on a user row.
  2. The user's status changes to "disabled".
  3. Disabled users cannot log in or access forward-auth-protected hosts.
  4. To re-enable, click the enable icon.

Delete a user

  1. Click the delete icon on a user row.
  2. Confirm the deletion.
  3. The user is permanently removed.

(since v1.13.1) Deleting a user (Users page or DELETE /api/v1/users/:id) also deletes their sessions, the API tokens they created, their sign-in methods (password and OAuth accounts) and pending OAuth links, their forward-auth sessions and access grants, and their group memberships. Proxy hosts, L4 hosts, certificates, CAs, client certificates, access lists, mTLS roles and rules, and groups they owned or created are kept without an owner, and their audit log entries are kept without a user.

Older releases deleted only the user row. On startup, CPM removes the rows those deletions left behind, logged as Cleared rows left by deleted user id(s) <ids>, so a new account that gets a deleted user's id (the primary admin is always id 1) does not inherit their API tokens, sessions or OAuth links. Nothing needs to be done.

Safety guards

  • You cannot change your own role or status (prevents accidental lockout).
  • You cannot delete your own account.

Passwords

(since v1.13.1)

Password policy

Every way of setting a password uses the same rules as ADMIN_PASSWORD (at least 12 characters, upper- and lowercase letters, a digit, and a special character), and additionally allows at most 256 characters. It applies to:

  • Create User on the Users page, and POST /api/v1/users
  • Change Password and Set Password on the Profile page
  • Better Auth self-registration (AUTH_ALLOW_SELF_REGISTRATION=true); Better Auth's /api/auth/reset-password is checked too, although CPM has no way to issue reset tokens

A rejected password is answered with the requirements it misses, e.g. Password must be at least 12 characters long, must include at least one number (the Profile page's server check says New password must …).

Changing a password

Changing or setting a password on the Profile page signs out the user's other dashboard sessions and all of their forward-auth sessions. The current session stays signed in.

API tokens are separate credentials and keep working. The confirmation says so: "Password updated. Your other sessions have been signed out. API tokens are not affected; revoke them under API Tokens if needed." Revoke them under Profile → API Tokens if they may be compromised.

First password for an OAuth-only account

An account without a password has no current password to prove, so Set Password only works within 10 minutes of signing in. After that it fails with "Please sign in again before setting a password." Sign out, sign in again with OAuth, and set the password right away.

Setting it gives an account without a usable username its own email address as username, when that qualifies (since v1.13.1). The user can then sign in at /login with the Sign-in username shown on their Profile page. Otherwise the Profile page says that an administrator has to set a sign-in username (see below).

The Profile page only receives whether the account has a password, never the password hash.


Sign-in Usernames

(since v1.13.1)

The login page signs in by username only, ignoring case. CPM never makes a username up from an email address. An account's username is either:

  • its own email address, lowercased and otherwise unchanged, when that address is 3–255 characters from A-Z a-z 0-9 _ . @ - and no other account signs in with it or has it as its email address, or
  • one an administrator sets (see Setting a username).

An account that has neither has no sign-in username: it cannot sign in at /login with a password until an administrator sets one. OAuth sign-in still works. An address containing + (such as alice+cpm@example.com) never becomes a username by itself.

When an account gets its own email address

  • Accounts created on the Users page, with POST /api/v1/users without username, or by self-registration get it when they are created. Self-registration (AUTH_ALLOW_SELF_REGISTRATION=true) ignores a requested username or displayUsername.
  • Accounts created by an OAuth sign-in, and any account without a usable username, get it when their password is set (Profile → Set Password or Change Password).

Nothing else changes a stored username. Startup leaves every username as it is (except that applying a changed ADMIN_USERNAME/ADMIN_PASSWORD resets the primary admin's, see Default Accounts), and so do profile edits: changing an account's email address keeps its username.

Setting a username

Administrators set a user's username:

  • On the Users page: click the edit icon on the user row, enter it in Username and click Save. Name, email and username are saved together; when one of them is refused, nothing is changed and the reason is shown.
  • With the REST API: "username" in PUT /api/v1/users/:id, or in POST /api/v1/users when creating the user (see API Access).

A username must be 3–255 characters of lowercase letters (a-z), digits and _ . @ -; surrounding whitespace is removed. Otherwise the change is refused with "Username must be 3-255 characters long and use only lowercase letters (a-z), digits and the characters _ . @ -". A username can be replaced but not removed.

Each name reaches one account only, compared ignoring case:

  • A username must not be another account's username, email address or forward-auth portal name (the <name> of an email <name>@localhost, see Feature Guide Forward Auth#login-methods): "Another account already signs in with this name or has it as its email address".
  • An email address must not be another account's email address ("A user with this email already exists") or username ("Another account signs in with this email address as its username"). For a @localhost address, the part before @localhost must not be another account's username either ("Another account signs in with the name before @localhost as its username").

Setting the username a user already has is no change. A changed username is recorded in the audit log as Changed user <id> sign-in username to <username>, with the previous username in the entry's data.

Where the username is shown

  • Profile page: a working username as Sign-in username.
  • Users page: next to the email when the two differ, or no sign-in username when the account has no username the login page can use.
  • REST API: username in /api/v1/users responses, null when the account has none.

Tell users whose username differs from their email.

The forward-auth portal's credential form does not use sign-in usernames; see Feature Guide Forward Auth#login-methods.

Accounts without a usable username

Some accounts have no username, and older releases could store one the login page cannot use (an email containing +, or a mixed-case username). The user's Profile page says what is needed:

  • "Your account has no sign-in username the login page can use, so you cannot sign in there with a password. An administrator has to set a sign-in username for your account. This page then shows it." (on an account without a password: "…, then you can set your password here."): set a username on the Users page.
  • "Your password cannot be used on the sign-in page yet. Change it once here to enable password sign-in.": the account's own email address qualifies, or the password was set on an older release. Changing the password once fixes it; the user has to be signed in another way to do so, such as with OAuth. An administrator can also set a username.

Startup check

On every start, CPM logs a warning, without changing anything, for each stored username an administrator should review:

Sign-in username "<username>" of user <id> is also another account's username, email address or forward-auth portal name; give one of them a different username on the Users page
Sign-in username "<username>" of user <id> is an email address other than the account's own and can be somebody else's; check it on the Users page

See Troubleshooting#sign-in-username-warnings-on-startup.

Upgrading from v1.13.0

The withdrawn v1.13.0 release generated sign-in usernames from email addresses, rewriting characters it could not use (alice+cpm@example.com → alice-cpm@example.com) and adding -2, -3, … before the @ on a collision. It did this when an account was created or its password or profile changed, and on every start for each account with a password whose stored username the login page could not use. Such a username can be somebody else's email address. v1.13.1 removes this and keeps stored usernames as they are, including those v1.13.0 generated.

  1. Before upgrading, save the v1.13.0 web container log lines Gave user <id> the sign-in username <username> (docker compose logs web | grep "Gave user"); recreating the container usually discards its log.
  2. Review those accounts' usernames on the Users page (edit → Username).
  3. Usernames generated outside startup were not logged. After upgrading, check the startup warnings: they list every username that is an email address other than the account's own, or that also reaches another account.
  4. Give each such account a username the user should sign in with, for example one that is not an email address (alice) or the account's own address when it qualifies, and tell the user.

Groups

Groups organise users for forward auth access control.

Create a group

  1. Open Groups in the sidebar.
  2. Click New Group.
  3. Name the group and add members.
  4. Click Create.

Use groups for access control

When enabling forward auth on a proxy host, select groups in the access list. All members of those groups gain access.

See Feature Guide Forward Auth for details.


Default Accounts

The initial admin account (the primary admin, user id 1) is created from environment variables:

ADMIN_USERNAME=admin
ADMIN_PASSWORD=<choose-your-own: 12+ chars, upper, lower, digit, symbol>

ADMIN_USERNAME should be 3–255 characters from A-Z a-z 0-9 _ . @ -; the login page refuses other usernames and ignores case. In production, the web container refuses to start when ADMIN_PASSWORD is admin, does not meet the password policy, or is an example password from the documentation or an earlier .env.example ("ADMIN_PASSWORD is an example value from the documentation; choose your own password", since v1.13.1).

Changing and recovering the admin password

(since v1.13.1)

The environment credentials are applied when the admin account is created and whenever ADMIN_USERNAME or ADMIN_PASSWORD change, no longer on every start. A password changed in the UI is therefore kept across restarts.

To recover a lost admin password, change ADMIN_PASSWORD (or ADMIN_USERNAME) and recreate the web container with docker compose up -d (docker compose restart keeps the old values). This:

  • resets the primary admin's password,
  • resets its username to ADMIN_USERNAME and its email to <ADMIN_USERNAME>@localhost,
  • restores its admin role,
  • re-activates it if it was disabled, and
  • when the password changed, signs out all of its dashboard and forward-auth sessions.

A username set for the primary admin on the Users page is kept until ADMIN_USERNAME or ADMIN_PASSWORD changes.

On the first start after upgrading from v1.12.0 or earlier, a stored admin password that differs from ADMIN_PASSWORD (and is not admin or a documented example) is kept, since it was probably changed in the UI. A warning starting with ADMIN_PASSWORD differs from the stored admin password; keeping the stored password… is logged. Change ADMIN_PASSWORD again and recreate the web container to force it. A changed ADMIN_USERNAME is still applied on that start.

(since v1.13.1) A new ADMIN_USERNAME that another account already signs in with, or has as its email address (also as <ADMIN_USERNAME>@localhost), is not applied. Nothing changes on that start, and the log shows ADMIN_USERNAME "…" is not applied: another account already signs in with it or has it as its email address. Give that account a different username or email address on the Users page, or choose another ADMIN_USERNAME. Every start tries again until you do.

This also applies when you change only ADMIN_PASSWORD after the primary admin's username or email was changed on the Users page: applying the environment credentials resets them to ADMIN_USERNAME, so if another account holds that name (or <ADMIN_USERNAME>@localhost) the password is not reset either and the same error is logged.


OAuth Users

When OAuth is enabled:

  • New OAuth identities create users only when AUTH_ALLOW_OAUTH_REGISTRATION=true; it is disabled by default. Since v1.13.1, this also holds when the sign-in request asks for sign-up.
  • OAuth-created users receive the user role, unless AUTH_ALLOW_OAUTH_ROLE_FROM_CLAIMS=true lets the IdP's claims set it.
  • Admins can promote OAuth users to any role from the Users page.
  • OAuth-created users have no sign-in username until they set a password (since v1.13.1).
  • OAuth users can also set a password from their Profile page for credential-based login. This requires a recent sign-in and gives them their email address as sign-in username when it qualifies; otherwise an administrator sets one (see First password for an OAuth-only account).
  • Unlinking OAuth from the Profile page requires a working username/password sign-in (since v1.13.1). Users who set a password on an older release must change it once before the Unlink OAuth Account button appears.
  • Disabling an OAuth user prevents both OAuth and credential login.

See OAuth Authentication Setup for account creation, linking and unlinking.


API Access

User management is available via the REST API (admin only, except that any user can read their own record with GET /api/v1/users/:id):

  • GET /api/v1/users — List all users
  • GET /api/v1/users/:id — Get a specific user
  • POST /api/v1/users — Create a new user (the password must meet the password policy; username is optional)
  • PUT /api/v1/users/:id — Update role, status, name, email, sign-in username
  • DELETE /api/v1/users/:id — Delete a user and the rows listed under Delete a user

Responses never include the password hash. Since v1.13.1 they include the sign-in username (null when the account has none).

(since v1.13.1) "username" in PUT and POST sets the sign-in username under the rules in Setting a username:

  • A refused username or email address is answered with 400 and the reason, and nothing is changed: PUT does not apply role or status either, and POST creates no user.
  • "username": null, or no username, keeps the current username, so a GET response can be sent back as it is. Sending the username the user already has is no change.
  • Without username, POST gives the new user their email address as username when it qualifies, and none otherwise.

See Feature Guide REST API#users for an example.

See the interactive API docs at /api-docs for full request/response schemas.

Better Auth self-service endpoints

(since v1.13.1)

Profile, password and account changes go through CPM's own routes (the Profile page and /api/v1/), which apply the password policy and write audit log entries. The Better Auth endpoints that would bypass them are disabled:

  • /api/auth/update-user
  • /api/auth/change-password
  • /api/auth/change-email
  • /api/auth/delete-user
  • /api/auth/unlink-account
  • /api/auth/update-session
  • /api/auth/verify-password
  • /api/auth/is-username-available

Among other things, users can no longer rename themselves this way. (Up to v1.13.1 their name fed the forward-auth X-CPM-User header; since v1.13.2 that header carries the sign-in username, or the email address for an account without one.)


Related Documentation


Need help? Open an issue.

Clone this wiki locally