feat(auth): bootstrap admin on first boot + forced password change + admin reset
CI / Build Native (push) Has been cancelled

Backend changes (no frontend yet):

Schema (User entity)
- + mustChange_password (boolean NOT NULL, default false)
- + is_admin (boolean NOT NULL, default false)
Hibernate update mode adds both columns automatically.

BootstrapAdmin (new, ApplicationScoped, @Observes StartupEvent)
- runs only when User.count() == 0 and app.bootstrap.admin.enabled=true
- generates a 20-char random password (alphabet without 0/o/O/1/l/I)
- persists the user with isAdmin=true, mustChangePassword=true
- prints a banner to stdout AND to the JBoss logger so docker logs
  picks it up:
    BOOTSTRAP-ADMIN-USERNAME admin
    BOOTSTRAP-ADMIN-PASSWORD <random>
    BOOTSTRAP-ADMIN-CHANGE   This password MUST be changed on first login ...
- idempotent: skips if any user already exists

MustChangePasswordFilter (new, @Provider ContainerRequestFilter)
- runs after JWT auth (Priorities.AUTHENTICATION + 100)
- for authenticated requests with mustChangePassword=true, returns
  403 with {error, mustChangePassword:true} unless the path is
  /api/auth/change-password or /api/auth/logout

Change-password endpoint (POST /api/auth/change-password)
- @Authenticated, body {currentPassword, newPassword}
- verifies currentPassword via bcrypt, validates newPassword>=8 chars,
  updates hash and sets mustChangePassword=false
- returns updated AuthMeResponse and re-issues the auth cookie

Admin reset endpoint (POST /api/auth/admin/reset-password)
- @RolesAllowed("admin")
- body {username, newPassword}
- sets target's passwordHash and mustChangePassword=true (forces change
  on next login)
- security: only users in the JWT 'admin' group can hit it; isAdmin
  is stored on the user record so a stale token can't promote itself

JWT groups now include 'admin' for isAdmin users; previously everyone
was just 'user'.

Config (application.properties)
- app.bootstrap.admin.enabled=true
- app.bootstrap.admin.username=admin
This commit is contained in:
2026-08-14 20:04:44 -04:00
parent fb021b165b
commit 1d6fc08a25
9 changed files with 287 additions and 6 deletions
@@ -23,4 +23,8 @@ app.auth.cookie-max-age-seconds=86400
# Security
quarkus.http.auth.proactive=false
# Bootstrap admin (default user created on first boot of an empty DB)
app.bootstrap.admin.enabled=true
app.bootstrap.admin.username=admin
%native.quarkus.native.resources.includes=META-INF/resources/.*,publicKey.pem,privateKey.pem