Skip to content

feat(auth): JWT authentication, user management, and hybrid CSRF - #45

Merged
lekhrocks merged 3 commits into
mainfrom
feat/jwt-auth
Aug 5, 2026
Merged

feat(auth): JWT authentication, user management, and hybrid CSRF#45
lekhrocks merged 3 commits into
mainfrom
feat/jwt-auth

Conversation

@lekhrocks

Copy link
Copy Markdown
Owner

Summary

Implements real authentication for the control plane: JWT login against a users table (BCrypt hashes), full user management, JWT validation via Spring Security oauth2 resource-server, and a hybrid CSRF policy. This resolves the previously-hollow security config (CSRF disabled, no auth mechanism) and the "Disabled Spring CSRF protection" scan finding.

Changes

Auth core

  • Dependencies: spring-security-oauth2-jose + spring-security-oauth2-resource-server
  • JWT: JwtProperties (syncflow.jwt.*) + JwtSecurityConfig — HS256 encoder/decoder via Nimbus; secret validated (base64, ≥ 32 bytes) with clear startup errors
  • Beans: BCrypt PasswordEncoder, AuthenticationManager (non-deprecated DaoAuthenticationProvider constructor), JwtAuthenticationConverter
  • Security chain: oauth2ResourceServer JWT bearer for /api/**, hybrid CSRF (cookie repo, /api/** bearer paths ignored), stateless, /api/auth/** public

Users

  • V9 migration: app_users table + seeded admin (BCrypt). Named app_users (not users) to avoid colliding with sample/integration tables.
  • Domain: UserEntity, UserRepository, UserService (create/update/roles/delete, role allow-list, password encoding), RoleConstants, DbUserDetailsService (CSV roles → ROLE_* authorities)
  • Endpoints: POST /api/auth/login, GET /api/auth/me, /api/users CRUD + role assignment (guarded by existing RBAC, @Valid Bean Validation)
  • AuthService: issues JWT with scope claim carrying roles; carries authorities from the authenticated principal (no duplicate user-store query)

RBAC fixes

  • AdminController.createOrg: AI_USEORG_WRITE (copy-paste bug)
  • Added guards on revokeApiKey (APIKEY_REVOKE) and getQuota (ORG_READ)

Tests

  • ApiAuthContractTest: login success / bad-password 401 / token-authed 200 / unauthenticated 401
  • AuthServiceTest, DbUserDetailsServiceTest, UserServiceTest, UserEntityMappingTest (guards the app_users table name)

Docs

  • docs/api/auth.md — login/me endpoints, token usage, syncflow.jwt.* config
  • docs/security/authentication.md — auth model, CSRF policy, threat mapping
  • API stability matrix updated

Design notes

  • Stateless bearer auth (no session); HS256 symmetric (RS256 documented upgrade path).
  • Auth plugs into the existing RBAC (TenantFilterAuthorizationService/PolicyResolver) — no parallel authz model.
  • Login returns uniform 401 for bad/disabled/locked accounts (no account enumeration).

Validation

  • Full ./gradlew test green; all security + user + PgMongo suites pass.

…d CSRF

- oauth2-jose + oauth2-resource-server dependencies
- JwtProperties (syncflow.jwt.*) + JwtSecurityConfig: HS256 encoder/decoder
  via Nimbus; secret validated (base64, >= 32 bytes) with clear startup errors
- AuthSecurityBeans: BCrypt PasswordEncoder, AuthenticationManager (non-
  deprecated DaoAuthenticationProvider constructor), JwtAuthenticationConverter
- WebSecurityConfig: oauth2ResourceServer JWT bearer for /api/**, hybrid CSRF
  (cookie repo, /api/** ignored), stateless, /api/auth/** public
- V9 migration: app_users table + seeded admin (BCrypt)
- SecurityConfig: /api/auth/** added to public paths
…omain

- DbUserDetailsService: loads app_users, maps CSV roles to ROLE_ authorities
- AuthService: authenticate via AuthenticationManager, issue JWT with scope
  claim carrying roles (no duplicate user-store query; carries authorities
  from the principal)
- AuthController: /api/auth/login (401 for bad/disabled/locked), /api/auth/me
- UserService + RoleConstants: user CRUD, role allow-list, password encoding,
  duplicate detection (thin controller, domain rules centralized)
- UserManagementController: /api/users CRUD + roles, guarded by RBAC,
  @Valid Bean Validation
- UserEntity (app_users) + UserRepository
- AdminController: fix createOrg AI_USE->ORG_WRITE, guard revokeApiKey/getQuota
- ApiAuthContractTest: login success/bad-password/token-authed/unauthenticated
  assertions against the real security chain
- AuthServiceTest, DbUserDetailsServiceTest, UserServiceTest, UserEntityMappingTest
  (guards the app_users table name against sample-table collision)
- docs/api/auth.md: login/me endpoints, token usage, syncflow.jwt.* config
- docs/security/authentication.md: auth model, accounts, CSRF policy,
  security considerations, threat mapping
- api-stability-matrix: link auth endpoints
@lekhrocks lekhrocks self-assigned this Aug 5, 2026
@lekhrocks
lekhrocks merged commit e9c71ad into main Aug 5, 2026
18 checks passed
@lekhrocks
lekhrocks deleted the feat/jwt-auth branch August 5, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant