Accounts and sign-in

self-hostingauthentication

Sign-in on the DocuCommit server answers one question: who wrote this comment, and who should be notified about it. It is not an access-control system, and it never stands between a reader and a page.

What an account is for

Exactly four things need one:

  • Posting and resolving comments — the account supplies the name on the comment.
  • @mentions — the mention autocomplete draws its handles from the account list.
  • Following a document and changing your notification settings.
  • Triggering a licence refresh with POST /license/refresh.

Everything else is public: every page, the search index, revision history, exports, attachments, robots.txt, and /health. The security configuration ends with anyRequest().permitAll(), and the handful of comment, mention, and notification endpoints above are the only ones marked authenticated().

There are no roles and no permissions. Every signed-in account is granted the single authority ROLE_USER. There is no admin role, no per-library or per-project permission, no read restriction, and no way to grant one account more than another. If two people are signed in, they can do exactly the same things.

Local accounts

The default and the fallback. A visitor supplies an email address, a display name, and a password.

  • The email is the canonical identity, trimmed and lower-cased. One account per address.
  • The password is hashed with BCrypt and never stored or logged in any other form. It must be 12 to 128 characters and may not contain control characters.
  • The display name is limited to 100 characters and may only use letters, digits, spaces and . _ -, because it doubles as the @mention handle.

Self-registration is on by default. To freeze the user list — after everyone who needs an account has one, or on a public instance where you do not want strangers registering — set:

DOCUCOMMIT_REGISTRATION_ENABLED=false

Existing accounts keep working; new ones are refused. This is the only user-management control the server has.

Google and GitHub sign-in

Both ship disabled. The OAuth block in application.yml is commented out, and with nothing configured the sign-in page offers email and password only — the provider buttons are not rendered at all. Supply GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET or GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET to switch them on. See the configuration reference for where those go.

Both providers are available on every licence tier.

A verified email is mandatory. Google supplies it in the ID token. GitHub does not put it in the standard profile, so the server calls https://api.github.com/user/emails with the user’s access token and takes the primary address, falling back to the first verified one. If the provider returns no email, or returns one it has not verified, the sign-in is rejected with email_unverified rather than creating an account.

Accounts link by verified email. Signing in with Google using an address that already has a local password account attaches google to that same account — same identity, same comment history, same mentions. There is no separate “connect account” step and no way to link two different addresses to one person.

An account created by a provider has no password, so it cannot use the email-and-password form until someone sets one. There is no password-reset flow in the server.

OIDC single sign-on

OIDC SSO is a Team or Enterprise entitlement. It uses the same Spring Security OAuth2 client configuration as Google and GitHub: a registration block naming your identity provider, plus a provider block pointing at its issuer. Any RFC-compliant OIDC provider works — the server does nothing provider-specific. It reads the standard email, email_verified, and name claims, and your provider must return email_verified: true or the sign-in is refused, exactly as for Google.

Licence tier decides which registrations survive at startup:

TierOAuth registrations kept
Progoogle and github only
TeamAll configured registrations
EnterpriseAll configured registrations

On a Pro licence, any additional registration is dropped when the server boots. A warning naming what was stripped is logged and returned by /api/session, so the site can show it. Nothing breaks — you just get the Google and GitHub buttons and not your own.

Where accounts live

One file:

<workdir>/.auth/users.json

<workdir> is DOCUCOMMIT_WORKDIR, which the standard compose file maps to the persistent /var/repos volume. DOCUCOMMIT_USER_STORE can point the file somewhere else. It holds one entry per account: the lower-cased email, display name, BCrypt hash (null for provider-only accounts), the set of linked providers, and created/updated timestamps. It is written atomically — to a temporary file, then moved into place.

This file is not in Git. Losing the volume loses every account, every comment, and every notification subscription. It is part of the full server backup — see Backup and restore.

What the server does not do

State this plainly to anyone evaluating it, because none of it is planned around:

  • No SAML. OIDC only.
  • No SCIM, no LDAP, no directory sync. Accounts are created when someone signs in, never provisioned in bulk.
  • No admin UI for user management. There is no user list, no “delete account” button, and no API for either.
  • No password reset, no email verification for local accounts, and no MFA on local accounts. Delegate to Google, GitHub, or your OIDC provider if you need any of those.
  • No group or role mapping from the identity provider. Claims are read for email and name and nothing else.

To remove an account, stop the server, edit .auth/users.json on the volume, and start it again. The store is read once at boot and held in memory, so editing the file underneath a running server does not take effect and will be overwritten the next time an account is created or linked. The distroless image has no shell, so reach the file with a helper container:

docker compose stop docucommit-server
docker run --rm -it -v docucommit-repos:/var/repos alpine vi /var/repos/.auth/users.json
docker compose start docucommit-server

Comments already attributed to the deleted address keep the name that was recorded with them.