feat: give redis-session mode a way to authenticate — the BFF login path
auth-mode=redis-session was unreachable: getStudioSession answered 503 on
every call because the CSRF token is null when CsrfFilter never runs, and
CsrfFilter only runs in the session branch, which could not be selected
because AuthenticationModeCompositionConfig requires a
`redisVersionedSessionRepository` / `springSessionRepositoryFilter` pair
and only the second existed. Even with the pair present nothing could
create a session — there was no login endpoint and no client registration.
This is the surface the contract already describes: securitySchemes
declares a session cookie plus X-CSRF-TOKEN on mutations, not a bearer
token, and SecurityConfig's session branch (cookie CSRF repository,
session-fixation migration) plus RedisSessionWebConfig (servlet filter,
host-only cookie) were already written for it. The SPA never holds a
token; the backend owns the session.
- StudioSessionInfrastructureConfig supplies the missing repository
under the name the composition validator looks for. @EnableRedisHttp
Session is not used because it pins the bean name to sessionRepository.
- StudioOidcLoginSuccessHandler converts the OidcUser into an
AuthenticatedPrincipal. PrimitiveSessionSecurityContextRepository
rejects anything else on save — deliberately, so credentials and
framework object graphs never cross the session boundary — and it
restores the same type on load. Roles are unioned from realm_access
and resource_access exactly as the JWT converter does, so both modes
resolve the same set and the studio:read / studio:write mapping
behaves identically.
- SecurityConfig wires oauth2Login (only when a success handler bean is
present, so JWT mode is untouched) and a /logout that invalidates the
session. The envelope 401 stays the entry point: an unauthenticated
API call must not answer 302, which an XHR cannot follow. The SPA
navigates the browser to /oauth2/authorization/{id} instead.
Verified in a browser against a real Keycloak realm:
/oauth2/authorization/keycloak → Keycloak → callback
TECHLOG_SESSION cookie set, httpOnly
GET /api/v1/studio/session 200 {authenticated, displayName, roles,
csrfToken, csrfHeaderName}
POST /api/v1/studio/documents 403 without the CSRF header
201 with it
GET /api/v1/studio/documents 200
Also removes the same broken placeholder-in-map-key role mapping from the
dev profile that the previous commit fixed in local and prod.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
37d5614129
commit
a828b5d9fe
@@ -76,6 +76,17 @@ dependencies {
|
||||
// Security types for ManagementSecurityConfig (not reachable via adapter-web's implementation dep). See README.
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
|
||||
// Redis-backed HTTP session for auth-mode=redis-session (the BFF surface the Studio contract
|
||||
// declares: sessionCookie TECHLOG_SESSION + X-CSRF-TOKEN). AuthenticationModeCompositionConfig
|
||||
// requires the `redisVersionedSessionRepository` / `springSessionRepositoryFilter` pair once
|
||||
// that mode is active; StudioSessionInfrastructureConfig supplies the first, Spring Session's
|
||||
// SpringHttpSessionConfiguration the second.
|
||||
// OIDC Authorization Code 로그인 자동설정(ClientRegistrationRepository 등). 기존의
|
||||
// spring-security-oauth2-client 는 라이브러리만 주고 Boot 자동설정은 스타터가 준다.
|
||||
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
|
||||
implementation 'org.springframework.session:spring-session-data-redis'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||
|
||||
// test-only: ArchUnit needs actuator types to verify the health-shape guardrail. See README.
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
// test-only: @WithMockUser for the actuator security authorization tests. See README.
|
||||
|
||||
Reference in New Issue
Block a user