On my local machine, everything works:
2024-10-20T01:05:13.135+01:00 INFO 3050 --- [BFFApplication] [ redisson-4-6] c.f.b.a.sessions.SessionListenerConfig : Session created: BFF-3f3400dd34c22190cebfc18ff8eda96e7d248ea3ef10e52e8a95c4df41a37e14-871786289805479-77135-ffb95668-1a41-46bc-aefe-ad2588b8f759
2024-10-20T01:05:14.068+01:00 INFO 3050 --- [BFFApplication] [ctor-http-nio-2] .b.a.r.t.CustomServerCsrfTokenRepository : Loading CSRF token
2024-10-20T01:05:14.068+01:00 INFO 3050 --- [BFFApplication] [ctor-http-nio-2] .b.a.r.t.CustomServerCsrfTokenRepository : Generating CSRF token
2024-10-20T01:05:14.068+01:00 INFO 3050 --- [BFFApplication] [ctor-http-nio-2] c.f.b.a.h.c.SPACsrfTokenRequestHandler : Handling CSRF token: MonoSwitchIfEmpty
2024-10-20T01:05:14.069+01:00 INFO 3050 --- [BFFApplication] [ctor-http-nio-2] f.b.a.r.s.RedisSecurityContextRepository : REMOVING AUTHORIZATION REQUEST
2024-10-20T01:05:14.081+01:00 INFO 3050 --- [BFFApplication] [sson-netty-3-19] f.b.a.r.s.RedisSecurityContextRepository : session id: BFF-3f3400dd34c22190cebfc18ff8eda96e7d248ea3ef10e52e8a95c4df41a37e14-871786289805479-77135-ffb95668-1a41-46bc-aefe-ad2588b8f759
2024-10-20T01:05:14.081+01:00 INFO 3050 --- [BFFApplication] [sson-netty-3-19] f.b.a.r.s.RedisSecurityContextRepository : session attributes: [AUTHORIZATION_REQUEST]
2024-10-20T01:05:14.094+01:00 INFO 3050 --- [BFFApplication] [sson-netty-3-19] f.b.a.r.s.RedisSecurityContextRepository : Successfully deserialized Authorization Request: org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest@59dfcd58
2024-10-20T01:05:14.094+01:00 INFO 3050 --- [BFFApplication] [sson-netty-3-19] f.b.a.r.s.RedisSecurityContextRepository : state: p3mFNsKg906_bYGYPa8UFjbMnq863CTH4RiAvlqMdoU= authorization request state: p3mFNsKg906_bYGYPa8UFjbMnq863CTH4RiAvlqMdoU=
In AWS Elasticache, the session ID logged, is different to the one created, so I get a state mismatch.
2024-10-20T00:06:30.399Z INFO 1 --- [BFFApplication] [ redisson-4-8] c.f.b.a.sessions.SessionListenerConfig : Session created: BFF-e4ef60281b13352e0d97531e04a928638da3cbdab75d4f51521868c33cce94a4-1062080912734-88788-8677d299-55a9-4b22-a3c1-4d3dad442670
2024-10-20T00:06:31.284Z INFO 1 --- [BFFApplication] [or-http-epoll-2] .b.a.r.t.CustomServerCsrfTokenRepository : Loading CSRF token
2024-10-20T00:06:31.286Z INFO 1 --- [BFFApplication] [or-http-epoll-2] c.f.b.a.h.c.SPACsrfTokenRequestHandler : Handling CSRF token: MonoSwitchIfEmpty
2024-10-20T00:06:31.285Z INFO 1 --- [BFFApplication] [or-http-epoll-2] .b.a.r.t.CustomServerCsrfTokenRepository : Generating CSRF token
2024-10-20T00:06:31.287Z INFO 1 --- [BFFApplication] [or-http-epoll-2] f.b.a.r.s.RedisSecurityContextRepository : REMOVING AUTHORIZATION REQUEST
2024-10-20T00:06:31.291Z INFO 1 --- [BFFApplication] [ parallel-1] f.b.a.r.s.RedisSecurityContextRepository : State in request does not match Authorization Request state in Session: org.springframework.security.config.web.server.ServerHttpSecurity$OAuth2LoginSpec$OidcSessionRegistryWebFilter$OidcSessionRegistryServerWebExchange$OidcSessionRegistryWebSession@12f7d381
2024-10-20T00:06:31.291Z INFO 1 --- [BFFApplication] [ parallel-1] f.b.a.r.s.RedisSecurityContextRepository : state: BVnmHIYnFoeWIem7FirrWiCnOUH3VZzsleS3kZEBfwE= authorization request state: null
2024-10-20T00:06:31.291Z WARN 1 --- [BFFApplication] [ parallel-1] f.b.a.r.s.RedisSecurityContextRepository : No Authorization Request found in WebSession
2024-10-20T00:06:31.291Z INFO 1 --- [BFFApplication] [ parallel-1] f.b.a.r.s.RedisSecurityContextRepository : session attributes: []
2024-10-20T00:06:31.291Z INFO 1 --- [BFFApplication] [ parallel-1] f.b.a.r.s.RedisSecurityContextRepository : session id: BFF-a457e79d6b6e6cde87dc25ce5711c683124bc4d179256e3ad60f52114e8b9902-1293912647308-21782-b256296b-fbd0-4270-bba3-7fc96cc4ddd2
2024-10-20T00:06:31.293Z INFO 1 --- [BFFApplication] [ parallel-1] OAuth2ServerAuthenticationFailureHandler : ON FAILURE REDIRECT URI: /
Here is the exact same code for the authrozation request function I have:
@Repository
internal class RedisAuthorizationRequestRepository(
private val redisSerialiserConfig: RedisSerialiserConfig
) : ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> {
private val logger = LoggerFactory.getLogger(RedisSecurityContextRepository::class.java)
private val springAuthorizationRequestAttrName: String = "AUTHORIZATION_REQUEST"
override fun saveAuthorizationRequest(
authorizationRequest: OAuth2AuthorizationRequest?,
exchange: ServerWebExchange
): Mono<Void> {
logger.info("SAVING AUTHORIZATION REQUEST")
return exchange.session
.doOnNext { session ->
if (authorizationRequest?.state == null) {
logger.info("Authorization Request State cannot be empty")
} else {
session.attributes[springAuthorizationRequestAttrName] = authorizationRequest
logger.info("Authorization Request state: ${authorizationRequest.state}")
logger.info("Saved Authorization Request $authorizationRequest in WebSession: $session")
}
}.then()
}
override fun loadAuthorizationRequest(exchange: ServerWebExchange): Mono<OAuth2AuthorizationRequest?> {
logger.info("LOADING AUTHORIZATION REQUEST")
val state = getURIStateParameter(exchange) ?: return Mono.empty()
return exchange.session
.flatMap { session ->
val authorizationRequest = getAuthorizationRequest(session)
if (state == authorizationRequest?.state) {
logger.info("Loading authorization request")
Mono.just(authorizationRequest)
} else {
logger.warn("State in request does not match Authorization Request state in Session: $session")
Mono.empty()
}
}
}
override fun removeAuthorizationRequest(exchange: ServerWebExchange): Mono<OAuth2AuthorizationRequest?> {
logger.info("REMOVING AUTHORIZATION REQUEST")
val state = getURIStateParameter(exchange) ?: return Mono.empty()
return exchange.session
.flatMap { session ->
val authorizationRequest = getAuthorizationRequest(session)
logger.info("state: $state authorization request state: ${authorizationRequest?.state}")
if (state == authorizationRequest?.state) {
session.attributes.remove(this.springAuthorizationRequestAttrName)
logger.info("Removed authorization request")
Mono.just(authorizationRequest)
} else {
logger.info("State in request does not match Authorization Request state in Session: $session")
Mono.empty()
}
}
}
// Helper methods
private fun getURIStateParameter(exchange: ServerWebExchange): String? {
requireNotNull(exchange) { "exchange cannot be null" }
return exchange.request.queryParams[OAuth2ParameterNames.STATE]?.firstOrNull()
}
private fun getAuthorizationRequest(session: WebSession): OAuth2AuthorizationRequest? {
Assert.notNull(session, "session cannot be null")
logger.info("session id: ${session.id}")
logger.info("session attributes: ${session.attributes.keys}")
val authRequestAttr = session.getAttribute<Map<String, Any>>(springAuthorizationRequestAttrName)
if (authRequestAttr != null) {
// Deserialize from Map to OAuth2AuthorizationRequest
val map = authRequestAttr as? Map<String, Any>
try {
val authorizationRequest = redisSerialiserConfig.redisObjectMapper()
.convertValue(map, OAuth2AuthorizationRequest::class.java)
logger.info("Successfully deserialized Authorization Request: $authorizationRequest")
return authorizationRequest
} catch (e: Exception) {
logger.error("Error deserializing Authorization Request: ${e.message}", e)
return null
}
} else {
logger.warn("No Authorization Request found in WebSession")
return null
}
}
}
I am also using Redisson rather than Lettuce on AWS Elasticache (as Lettuce kept giving a crosslot error: https://github.com/redisson/redisson/issues/3992)
Also neither work Redisson or Lettuce, with Spring work on the new AWS Serverless Elasticache, as they both give 'psubscribe' errors: https://github.com/spring-projects/spring-data-redis/issues/2815
Why is AWS Elasticache returning a different Session ID, and hence failing the authentication flow?
Comment From: dreamstar-enterprises
UPDATE:
I think the problem is that for some reason when I get redirected from Auth0 back to my App, the request does not send the original session cookie:
AWS
LocalHost
I have set Cookie SameSite to Lax in both cases. Does anyone know why the browser is not adding the Session Cookie when hosted on AWS?
Comment From: dreamstar-enterprises
I had Cookie secure set to true, when it should have been false, as my AWS even through aims to be production, is still on the http protocol (debugging!)