IDP - Internal Database Implementation¶
Implement identity-provider authentication and user/group persistence with an internal reactive database.
Configure the database and signing/password settings, apply the included schema, and invoke IdpAdapter through the common identity services. Review credential storage, token issuance, and user lifecycle tests before deploying an internal authority.
| Maven artifact | Packaging | Source project |
|---|---|---|
org.fireflyframework:fireflyframework-security-idp-internal-db |
jar |
modules/security-idp-internal-db |
Part of Security and identity.
In the onboarding service¶
Use the internal database adapter when this service owns its identity store. Configure its database, schema, and token settings; keep those credentials and users separate from application submission records.
Add the component¶
Installation and version availability explains source builds and published releases.
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-security-idp-internal-db</artifactId>
<version>26.10.20-SNAPSHOT</version>
</dependency>
Quick start¶
The Java examples in this section are API fragments for application code. Use the stated imports and supply the application-owned collaborators described alongside them.
- Select this adapter and configure R2DBC. The adapter activates only when
firefly.security.idp.provider=internal-db:
firefly:
security:
idp:
provider: internal-db
internal-db:
jwt:
secret: "${JWT_SECRET}" # min 32 chars / 256 bits — generate with: openssl rand -base64 32
issuer: my-platform
spring:
r2dbc:
url: r2dbc:postgresql://localhost:5432/firefly
username: ${DB_USERNAME:firefly}
password: ${DB_PASSWORD}
# JDBC datasource + Flyway are used to run the bundled schema migrations
datasource:
url: jdbc:postgresql://localhost:5432/firefly
username: ${DB_USERNAME:firefly}
password: ${DB_PASSWORD}
flyway:
enabled: true
locations: classpath:db/migration
- Inject the
IdpAdapter. No internal-db classes need to be referenced. Code against the core SPI so the same controller works regardless of provider:
import org.fireflyframework.security.idp.adapter.IdpAdapter;
import org.fireflyframework.security.idp.dtos.LoginRequest;
import org.fireflyframework.security.idp.dtos.TokenResponse;
import org.springframework.http.ResponseEntity;
import reactor.core.publisher.Mono;
@RestController
@RequiredArgsConstructor
public class AuthController {
private final IdpAdapter idp; // InternalDbIdpAdapter, wired by auto-configuration
@PostMapping("/auth/login")
public Mono<ResponseEntity<TokenResponse>> login(@RequestBody LoginRequest request) {
return idp.login(request);
}
}
That is all that is required: the IdpAdapter bean, all supporting services
(AuthenticationService, JwtTokenService, UserManagementService, RoleService, PasswordResetService,
PasswordPolicyService, MfaService), the PasswordEncoder and the R2DBC repositories are registered automatically by
InternalDbIdpAutoConfiguration. Each bean is @ConditionalOnMissingBean, so any of them can be overridden.
Configuration and wiring¶
The firefly.security.idp.internal-db namespace is bound by InternalDbProperties.java. Nested configuration is declared in the linked class. Defaults are the field initializers.
| Setting | Java type | Source default |
|---|---|---|
firefly.security.idp.internal-db.jwt |
JwtConfig |
new JwtConfig() |
firefly.security.idp.internal-db.password-reset |
PasswordResetConfig |
new PasswordResetConfig() |
firefly.security.idp.internal-db.lockout |
LockoutConfig |
new LockoutConfig() |
firefly.security.idp.internal-db.password-policy |
PasswordPolicyConfig |
new PasswordPolicyConfig() |
firefly.security.idp.internal-db.mfa |
MfaConfig |
new MfaConfig() |
Property-based wiring conditions found in this module:
name = "firefly.security.idp.provider", havingValue = "internal-db". InternalDbIdpAutoConfiguration.java.
Public API and extension points¶
| Type | Contract and responsibility |
|---|---|
InternalDbIdpAdapter |
Internal Database implementation of the Firefly IDP Adapter. |
InternalDbProperties |
Configuration properties for Internal Database IDP implementation. |
PasswordResetTokenRepository |
Reactive repository for password reset token operations. |
RefreshTokenRepository |
Reactive repository for RefreshToken entity operations. |
RoleRepository |
Reactive repository for Role entity operations. |
Related components¶
| Related component | Why it belongs nearby |
|---|---|
| Identity Provider | Define identity-provider contracts and common authentication/user/group management services. |
| R2DBC | Build reactive repository filtering and pagination on Spring Data R2DBC. |
Build and test¶
Use Java 25. From the repository root, build this component with its required dependencies:
git clone https://github.com/fireflyframework/fireflyframework-jafly.git
cd fireflyframework-jafly
./mvnw -pl :fireflyframework-security-idp-internal-db -am verify
./mvnw -pl :fireflyframework-security-idp-internal-db -am install
verify runs the selected projects' tests. install makes the current build available in your local Maven repository.
Representative test sources:
Mock, local-container, and application-context tests do not establish acceptance against a live provider account. Validate the intended provider deployment and credentials separately.
Test fixture¶
The shouldCreateMultipleRoles test demonstrates one concrete behavior. Excerpt from RoleServiceTest.java. The test class supplies its fixtures and imports.
@Test
void shouldCreateMultipleRoles() {
// Given
when(roleRepository.existsByName(anyString())).thenReturn(Mono.just(false));
when(roleRepository.save(any(Role.class))).thenAnswer(invocation -> {
Role role = invocation.getArgument(0);
role.setId(UUID.randomUUID());
return Mono.just(role);
});
// When & Then
StepVerifier.create(roleService.createRoles(Arrays.asList("USER", "ADMIN", "GUEST")))
.expectNextCount(3)
.verifyComplete();
verify(roleRepository, times(3)).existsByName(anyString());
verify(roleRepository, times(3)).save(any(Role.class));
}
Detailed reference¶
Configuration¶
All adapter settings live under the firefly.security.idp.internal-db prefix and are bound by InternalDbProperties. The
provider selector (firefly.security.idp.provider) belongs to the IDP core.
firefly:
security:
idp:
provider: internal-db # selects THIS adapter (required, from the IDP core)
internal-db:
jwt:
secret: "${JWT_SECRET}" # REQUIRED — HMAC signing key, min 256 bits (32 chars)
issuer: firefly-idp-internal-db # JWT "iss" claim
access-token-expiration: 900000 # access token TTL in ms (default 15 min)
refresh-token-expiration: 604800000 # refresh token TTL in ms (default 7 days)
password-reset:
token-expiry: 1h # reset-token validity (java.time.Duration, default 1h)
lockout:
max-failed-attempts: 5 # failed logins before lockout (default 5)
lockout-duration: 30m # lockout window (Duration, default 30m)
password-policy:
min-length: 8 # default 8
max-length: 128 # default 128 (0 = unlimited)
require-uppercase: true # default true
require-lowercase: true # default true
require-digit: true # default true
require-special-char: false # default false
mfa:
available: true # allow users to enrol TOTP MFA (default true)
| Property | Default | Description |
|---|---|---|
firefly.security.idp.provider |
(none, required) | Must be internal-db to activate this adapter. |
firefly.security.idp.internal-db.jwt.secret |
(none, required) | HMAC secret used to sign and verify JWTs; must be at least 256 bits. |
firefly.security.idp.internal-db.jwt.issuer |
firefly-idp-internal-db |
Value of the JWT iss claim. |
firefly.security.idp.internal-db.jwt.access-token-expiration |
900000 (15 min) |
Access token lifetime, in milliseconds. |
firefly.security.idp.internal-db.jwt.refresh-token-expiration |
604800000 (7 days) |
Refresh token lifetime, in milliseconds. |
firefly.security.idp.internal-db.password-reset.token-expiry |
1h |
Validity window for password-reset tokens. |
firefly.security.idp.internal-db.lockout.max-failed-attempts |
5 |
Consecutive failed logins before the account is locked. |
firefly.security.idp.internal-db.lockout.lockout-duration |
30m |
How long an account stays locked. |
firefly.security.idp.internal-db.password-policy.min-length |
8 |
Minimum password length. |
firefly.security.idp.internal-db.password-policy.max-length |
128 |
Maximum password length (0 = unlimited). |
firefly.security.idp.internal-db.password-policy.require-uppercase |
true |
Require at least one uppercase letter. |
firefly.security.idp.internal-db.password-policy.require-lowercase |
true |
Require at least one lowercase letter. |
firefly.security.idp.internal-db.password-policy.require-digit |
true |
Require at least one digit. |
firefly.security.idp.internal-db.password-policy.require-special-char |
false |
Require at least one special character. |
firefly.security.idp.internal-db.mfa.available |
true |
Whether users may enrol TOTP-based MFA. |
The adapter also requires a standard spring.r2dbc.* connection (runtime data access) and a spring.datasource.* JDBC
connection with spring.flyway.* enabled so the bundled migrations can build the schema.
Database Schema¶
Flyway migrations under classpath:db/migration create and evolve the schema:
V1__Create_IDP_Tables.sql.idp_users,idp_roles,idp_user_roles,idp_sessions,idp_refresh_tokens(with the necessary indexes, unique constraints and foreign keys).V2__Add_Password_Reset_Tokens.sql.idp_password_reset_tokens.V3__Add_Lockout_And_Policy.sql. failed-attempt counters and lockout columns supporting the account-lockout and password-policy features.
User records store BCrypt password hashes, account status flags, and TOTP MFA enrolment (mfa_enabled, mfa_secret).
How It Works¶
InternalDbIdpAutoConfiguration is registered via
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports and is gated by
@ConditionalOnProperty(name = "firefly.security.idp.provider", havingValue = "internal-db") and the presence of an R2DBC
ConnectionFactory on the classpath. When active it:
- Enables
InternalDbPropertiesand the R2DBC repositories inorg.fireflyframework.security.idp.internaldb.repository. - Registers a strength-12
BCryptPasswordEncoder(unless one is already defined). - Wires the service layer. JWT, authentication, user management, roles, password reset, password policy and MFA.
- Exposes the
InternalDbIdpAdapteras theIdpAdapterbean consumed by the IDP core web layer.
Because every bean is @ConditionalOnMissingBean, you can replace any individual collaborator (for example, supply a
custom PasswordEncoder or JwtTokenService) without forking the module.
Continue the application¶
Use the Identity Provider guide for the next part of this integration. The runnable submission/query service is in Getting started.
Source and further reading¶
- Maven project and dependency declarations.
- Production source.
- org.springframework.boot.autoconfigure.AutoConfiguration.imports.
- application-internal-db-example.yml.
- V1__Create_IDP_Tables.sql.
- V2__Add_Password_Reset_Tokens.sql.
- V3__Add_Lockout_And_Policy.sql.
- Module catalog and getting started.
- Maven platform and architecture.