Description
I got the following error message and after the whole day debug, I found that oauth2 client failed to save AuthorizationRequest
to session in some case.
Resolved [OAuth2AuthorizationException: [authorization_request_not_found] ]
To trigger the issue, you have to:
- With reactor server
- Configure redis session. (or hazelcast session)
- Some errors happened before and one or more legacy
AuthorizationRequest
leaved in the session
Result:
Oauth2 client will continue failing with error authorization_request_not_found
Root cause:
in ReactiveRedisOperationsSessionRepository.java
@Override
public Mono<Void> save(RedisSession session) {
Mono<Void> result = session.saveChangeSessionId().and(session.saveDelta())
.and((s) -> {
session.isNew = false;
s.onComplete();
});
if (session.isNew) {
return result;
}
else {
String sessionKey = getSessionKey(
session.hasChangedSessionId() ? session.originalSessionId
: session.getId());
return this.sessionRedisOperations.hasKey(sessionKey)
.flatMap((exists) -> exists ? result
: Mono.error(new IllegalStateException(
"Session was invalidated")));
}
}
Only the delta data session.saveDelta()
will be update in redis session.
And the delta is captured by WebSession::getAttributes::setAttribute
@Override
public void setAttribute(String attributeName, Object attributeValue) {
this.cached.setAttribute(attributeName, attributeValue);
putAndFlush(getAttributeKey(attributeName), attributeValue);
}
private void putAndFlush(String a, Object v) {
this.delta.put(a, v);
flushImmediateIfNecessary();
}
But In WebSessionOAuth2ServerAuthorizationRequestRepository.java
It get state to AuthorizationRequest map from session's attribute and update its value, And does not put the map back to session's attribute again by calling setAttribute
. So redis session will not capture such change and fail to update the modification.
@Override
public Mono<Void> saveAuthorizationRequest(
OAuth2AuthorizationRequest authorizationRequest, ServerWebExchange exchange) {
Assert.notNull(authorizationRequest, "authorizationRequest cannot be null");
return getStateToAuthorizationRequest(exchange, true)
.doOnNext(stateToAuthorizationRequest -> stateToAuthorizationRequest.put(authorizationRequest.getState(), authorizationRequest))
.then();
}
private Mono<Map<String, OAuth2AuthorizationRequest>> getStateToAuthorizationRequest(ServerWebExchange exchange, boolean create) {
Assert.notNull(exchange, "exchange cannot be null");
return getSessionAttributes(exchange)
.doOnNext(sessionAttrs -> {
if (create) {
sessionAttrs.putIfAbsent(this.sessionAttributeName, new HashMap<String, OAuth2AuthorizationRequest>());
}
})
.flatMap(sessionAttrs -> Mono.justOrEmpty(this.sessionAttrsMapStateToAuthorizationRequest(sessionAttrs)));
}
private Map<String, OAuth2AuthorizationRequest> sessionAttrsMapStateToAuthorizationRequest(Map<String, Object> sessionAttrs) {
return (Map<String, OAuth2AuthorizationRequest>) sessionAttrs.get(this.sessionAttributeName);
}
And then authorization_request_not_found
will be raised since AuthorizationRequest
is not in session.