Skip to content

feat(auth): JWT login endpoint + Spring Security chain for /admin/api/v1#11

Merged
jlc488 merged 1 commit into
mainfrom
feat/auth-login-jwt-swagger
May 27, 2026
Merged

feat(auth): JWT login endpoint + Spring Security chain for /admin/api/v1#11
jlc488 merged 1 commit into
mainfrom
feat/auth-login-jwt-swagger

Conversation

@jlc488

@jlc488 jlc488 commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase A1 of the admin console plan — wires the actual POST /admin/api/v1/auth/login REST endpoint, a JWT issuer, and a JWT-aware Spring Security filter chain that protects /admin/api/v1/**. Unblocks the admin-ui Login page.

Phase A4 (springdoc Swagger UI) was attempted in the same branch but springdoc-openapi 2.7.0 calls into Spring Data Commons 3 (org.springframework.data.util.TypeInformation), which Spring Data 4 (bundled with Spring Boot 4.0) moved/removed → processTestAot blows up. Deferred until a Spring Boot 4 compatible springdoc release ships, or until we vendor swagger-ui-dist + emit OpenAPI 3 manually.

What lands

identity-api

  • AuthToken record — value + issuedAt + expiresAt
  • AuthTokenService interface — issue(CurrentUser) / parse(token)

identity-core

  • JjwtAuthTokenService — jjwt 0.12 HS256
    • issue: packs subject / tenant / publicId / loginId / status / roles into a signed JWT
    • parse: verifies signature + issuer + expiry, hydrates CurrentUser
  • new deps: io.jsonwebtoken:jjwt-api / -impl / -jackson 0.12.6

autoconfigure

  • DevslabKitProperties.Identity nested Jwt:
    Property Default
    devslab.kit.identity.jwt.secret placeholder (must override for prod; min 32B / 256b HS256)
    devslab.kit.identity.jwt.ttl PT8H
    devslab.kit.identity.jwt.issuer devslab-kit
  • IdentityAutoConfiguration: @ConditionalOnMissingBean AuthTokenService bean

admin-api

  • AuthControllerPOST /admin/api/v1/auth/loginLocalLoginService.loginAuthTokenService.issue → returns { token, expiresAt, user{...} }
  • JwtAuthenticationFilter (OncePerRequestFilter) — Bearer header → AuthTokenService.parseUsernamePasswordAuthenticationToken with roles mapped to ROLE_* GrantedAuthority
  • AdminSecurityConfig.SecurityFilterChain (@ConditionalOnMissingBean):
    • securityMatcher /admin/api/v1/**
    • CSRF off (stateless API), SessionCreationPolicy.STATELESS
    • POST /admin/api/v1/auth/loginpermitAll, rest → authenticated
    • JwtAuthenticationFilter before UsernamePasswordAuthenticationFilter
  • new dep: spring-boot-starter-security

Verified

./gradlew :devslab-kit-sample-app:test  → BUILD SUCCESSFUL in 57s
  • ApplicationContext boots with the new SecurityFilterChain
  • All 8 starter beans + lockout + Group + ABAC + admin-api controllers + V1-V6 migrations still wire
  • HTTP round-trip of the new login flow will be covered by the upcoming admin-ui Playwright PR

Operator note

In production set devslab.kit.identity.jwt.secret to a long random string (min 32 UTF-8 bytes). Default value is a placeholder — JjwtAuthTokenService throws on construction if the key is shorter than 256 bits.

Follow-ups (separate PRs)

  • A4 (Swagger / OpenAPI) — pending a Spring Boot 4 compatible springdoc release, or swap to vendored swagger-ui-dist + manual OpenAPI generation
  • A2 (TenantService + Tenant CRUD)
  • A3 (Policy listing + diagnostics testers + Settings read + custom metrics)

Phase A1 of the admin console plan. Adds:

identity-api:
- AuthToken (record: value + issuedAt + expiresAt)
- AuthTokenService (interface: issue/parse)

identity-core:
- JjwtAuthTokenService — jjwt 0.12 HS256
  * issue(CurrentUser): packs subject/tenant/publicId/loginId/status/roles
    into a signed JWT with configurable issuer + TTL
  * parse(token): verifies signature + issuer + expiry, hydrates CurrentUser
- jjwt-api/-impl/-jackson 0.12.6 deps (BOM-free, version-pinned)

DevslabKitProperties.Identity gains nested `Jwt`:
  devslab.kit.identity.jwt.secret    (required, default placeholder; min 32B HS256)
  devslab.kit.identity.jwt.ttl       (default PT8H)
  devslab.kit.identity.jwt.issuer    (default "devslab-kit")

IdentityAutoConfiguration: @ConditionalOnMissingBean AuthTokenService bean.

admin-api:
- AuthController POST /admin/api/v1/auth/login → calls LocalLoginService
  → AuthTokenService.issue → returns { token, expiresAt, user{...} }
- JwtAuthenticationFilter (OncePerRequestFilter): Bearer header →
  AuthTokenService.parse → UsernamePasswordAuthenticationToken with
  roles mapped to ROLE_* GrantedAuthorities
- AdminSecurityConfig.SecurityFilterChain (@ConditionalOnMissingBean):
  * securityMatcher /admin/api/v1/**
  * CSRF off (stateless API), SessionCreationPolicy.STATELESS
  * /admin/api/v1/auth/login permitAll, rest authenticated
  * JwtAuthenticationFilter before UsernamePasswordAuthenticationFilter
- New dep: spring-boot-starter-security

Springdoc (Phase A4) was attempted but springdoc-openapi 2.7.0 calls
org.springframework.data.util.TypeInformation, which Spring Data 4
(bundled with Spring Boot 4.0) relocated/removed → processTestAot fails
in the AOT pipeline. Deferred until a Spring Boot 4 compatible
springdoc release (or switch to swagger-ui-dist + manual OpenAPI gen).

Verified: ./gradlew :devslab-kit-sample-app:test  -> BUILD SUCCESSFUL in 57s
- ApplicationContext boots with the new SecurityFilterChain
- All existing beans + V1-V6 migrations still green
- (Login round-trip via HTTP exercised by upcoming admin-ui Playwright PR)
@jlc488
jlc488 merged commit 9ebed32 into main May 27, 2026
1 check passed
@jlc488
jlc488 deleted the feat/auth-login-jwt-swagger branch May 30, 2026 06:37
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