This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Spring User Framework is a reusable Spring Boot library (not an application) that provides user authentication and management features built on Spring Security. It supports Spring Boot 4.0 (Java 21+) and Spring Boot 3.5 (Java 17+).
This is a library, not an app. All Spring Boot starters are compileOnly dependencies. Consuming applications provide their own database, mail server, and security configuration. Never add Spring starters as implementation dependencies.
# Build
./gradlew build
# Run tests
./gradlew test
# Run single test
./gradlew test --tests "com.digitalsanctuary.spring.user.service.UserServiceTest"
# Test with specific JDK
./gradlew testJdk17
./gradlew testJdk21
# Test all JDKs
./gradlew testAll
# Lint/check
./gradlew check
# Publish locally (for testing in consuming apps)
./gradlew publishLocalThe SpringUserFrameworkDemoApp is a Spring Boot app that consumes this library for testing and demonstration. It is typically checked out alongside this repo at ../SpringUserFrameworkDemoApp.
-
Publish the library locally:
./gradlew publishLocal
This publishes the current SNAPSHOT version (from
gradle.properties) to your local Maven repository. -
Update the demo app dependency (if needed): In
../SpringUserFrameworkDemoApp/build.gradle, ensure the dependency version matches the SNAPSHOT:implementation 'com.digitalsanctuary:ds-spring-user-framework:X.Y.Z-SNAPSHOT'Check
gradle.propertiesfor the current version. -
Start the demo app:
cd ../SpringUserFrameworkDemoApp ./gradlew bootRun --args='--spring.profiles.active=local,playwright-test'
The app runs on
http://localhost:8080by default. Theplaywright-testprofile activatesTestDataControllerandTestApiSecurityConfig, which the Playwright tests require for test data setup/teardown. Omitplaywright-testif only doing manual browser testing. -
Run Playwright tests:
cd ../SpringUserFrameworkDemoApp/playwright npx playwright test --project=chromium
-
Manual browser testing can be done with Playwright MCP tools or directly in Chrome at
http://localhost:8080.
com.digitalsanctuary.spring.user
├── api/ # REST endpoints (UserAPI)
├── audit/ # Audit logging system
├── controller/ # MVC controllers for HTML pages
├── dev/ # Dev login auto-configuration (local profile only)
├── dto/ # Data transfer objects
├── event/ # Spring application events
├── exceptions/ # Custom exceptions
├── listener/ # Event listeners (auth, registration)
├── mail/ # Email service components
├── persistence/ # JPA entities and repositories
│ ├── model/ # User, Role, Privilege, tokens
│ └── repository/ # Spring Data repositories
├── profile/ # User profile extension framework
├── roles/ # Role/privilege configuration
├── security/ # Spring Security configuration
├── service/ # Business logic services
├── validation/ # Custom validators
└── web/ # Web interceptors and config
Entry Points:
UserConfiguration- Main auto-configuration class, enables async/retry/scheduling/method securityWebSecurityConfig- Spring Security filter chain configurationUserAPI- REST endpoints at/user/*(registration, password reset, profile update)
Core Services:
UserService- User CRUD, password management, token handlingUserEmailService- Verification and password reset emailsPasswordPolicyService- Password strength validation using PassayDSUserDetailsService- Spring Security UserDetailsService implementationSessionInvalidationService- Session management for security events
Security:
DSUserDetails- Custom UserDetails implementation wrapping User entityDSOAuth2UserService/DSOidcUserService- OAuth2/OIDC user servicesLoginAttemptService- Brute force protection with account lockoutHtmxAwareAuthenticationEntryPoint- Returns 401 JSON for HTMX requests instead of 302 redirect on session expiry
Extension Points:
BaseUserProfile- Extend for custom user data (see PROFILE.md)UserProfileService<T>- Interface for profile managementBaseSessionProfile<T>- Session-scoped profile accessUserPreDeleteEvent- Listen for user deletion to clean up related dataAuthenticationEntryPoint- Override via@ConditionalOnMissingBeanto customize session expiry behavior
- Entry point:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports→UserConfiguration UserAutoConfigurationRegistrardynamically registers the library package for entity/repository scanning- No conditional annotations — all features load, controlled by
user.*properties
RolePrivilegeSetupServicelistens forContextRefreshedEventand creates/updates Role and Privilege entities fromuser.roles-and-privilegesconfig. Must complete before any auth requests.PasswordHashTimeTesterruns async onApplicationStartedEventto benchmark bcrypt performance (controlled byuser.security.testHashTime).
UserService.createUser()uses@Transactional(isolation = Isolation.SERIALIZABLE)to prevent race conditions during concurrent registration- Event-driven architecture:
AuthenticationEventListener,RegistrationListener,AuditEventListener,BaseAuthenticationListener— don't bypass events - All Spring starters are
compileOnly— this is intentional for library consumers
All configuration uses user.* prefix in application.yml. Key property groups:
user.security.*- URIs, default action (allow/deny), bcrypt strength, lockout settings, testHashTimeuser.registration.*- Email verification toggle, OAuth provider togglesuser.mail.*- Email sender settings (fromAddress)user.audit.*- Audit logging (logFilePath, flushOnWrite, logEvents, maxQueryResults)user.roles-and-privileges- Role-to-privilege mapping (applied on startup)user.role-hierarchy- Role inheritance (e.g., ROLE_ADMIN > ROLE_MANAGER)user.gdpr.*- GDPR features (enabled, exportBeforeDeletion, consentTracking)user.dev.*- Dev login (autoLoginEnabled, loginRedirectUrl) — requireslocalprofileuser.purgetokens.cron.*- Token cleanup scheduleuser.session.invalidation.warn-threshold- Performance warning thresholduser.actuallyDeleteAccount- Hard delete vs disable
- Imports: Alphabetical, no wildcards
- Indentation: 4 spaces
- Logging: SLF4J via Lombok
@Slf4j - DI:
@RequiredArgsConstructorwithfinalfields - Documentation: JavaDoc on public classes/methods
- Assertions: AssertJ fluent style (not JUnit assertEquals)
- Test naming:
should[ExpectedBehavior]When[Condition]pattern
Tests use H2 in-memory database with JUnit 5 parallel execution. Key dependencies: Testcontainers, WireMock, GreenMail, AssertJ, REST Assured.
| Annotation | Use When | Spring Context | Key Imports |
|---|---|---|---|
@ServiceTest |
Unit testing services with mocks | None (Mockito only) | BaseTestConfiguration |
@DatabaseTest |
Testing JPA repositories | @DataJpaTest slice |
DatabaseTestConfiguration |
@IntegrationTest |
Full workflow testing | Full context | All 5 test configs |
@SecurityTest |
Testing auth/authorization | Full context + security | SecurityTestConfiguration |
@OAuth2Test |
Testing OAuth2/OIDC flows | Full context + OAuth2 | OAuth2TestConfiguration |
All annotations are in com.digitalsanctuary.spring.user.test.annotations.
BaseTestConfiguration- BCrypt strength 4 (fast), fixed Clock (2024-01-15 10:00 UTC), mock EventPublisher, SessionRegistrySecurityTestConfiguration- Pre-built test users:user@test.com,admin@test.com,moderator@test.com(password: "password").TestSecurityContextFactoryfor custom contexts.OAuth2TestConfiguration- Mock OAuth2/OIDC providers (Google, GitHub, OIDC), test token factoryMockMailConfiguration- Captures sent emails viaTestMailCaptureinstead of sendingDatabaseTestConfiguration- Database-specific test setup
Integration tests use TestApplication (in test.app package) as their Spring Boot context class.
TESTING.md- Comprehensive testing guide with patterns and troubleshootingPROFILE.md- User profile extension frameworkREGISTRATION-GUARD.md- Registration Guard SPI for pre-registration hooksCONFIG.md- Configuration referenceMIGRATION.md- Version migration guideCONTRIBUTING.md- Contributor guidelines (fork/branch/PR workflow)
Version is in gradle.properties. Do not manually update version numbers. The release process (./gradlew release) handles versioning, changelog generation, tagging, and Maven Central publishing automatically.