Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OAuth2 Client with Ditributed Session #6215

Closed
wangzw opened this issue Nov 17, 2018 · 4 comments
Closed

Fix OAuth2 Client with Ditributed Session #6215

wangzw opened this issue Nov 17, 2018 · 4 comments
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: bug A general bug
Milestone

Comments

@wangzw
Copy link
Contributor

wangzw commented Nov 17, 2018

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:

  1. With reactor server
  2. Configure redis session. (or hazelcast session)
  3. 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.

@rwinch
Copy link
Member

rwinch commented Nov 19, 2018

@wangzw I'm having trouble understanding what the exact issue is. Can you provide a complete sample?

@wangzw
Copy link
Contributor Author

wangzw commented Dec 3, 2018

Distributed session implementation such as redis and hazelcast, only update the modified part of session data to session storage.

And the session modification is captured by calling WebSession::setAttribute. Only modify session data's value without calling setAttribute, session data will not be updated anyway.

For example:

The following code will NOT add ("key", "value") to session keyA_DICT_TYPE_SESSION_KEY's value, if use redis or hazelcast based distributed session.

WebSession session = ...
Map<String, String> data = (Map<String, String>)session.getAttribute("A_DICT_TYPE_SESSION_KEY");
data.put("key", "value");

You have to add setAttribute to the end like this.

WebSession session = ...
Map<String, String> data = (Map<String, String>)session.getAttribute("A_DICT_TYPE_SESSION_KEY");
data.put("key", "value");

// The following line looks redundant but is important for redis or hazelcast based distributed session.
// It is used to tell redis or hazelcast that session data with key "A_DICT_TYPE_SESSION_KEY" has been updated.
session.setAttribute("A_DICT_TYPE_SESSION_KEY", data);

setAttribute is overwrite by redis or hazelcast based distributed session to capture session data's modification.

For the above reason, we finally got "authorization_request_not_found" in some case since the code failed to update session's data.

@rwinch rwinch transferred this issue from spring-projects/spring-security Dec 3, 2018
@rwinch rwinch transferred this issue from spring-projects/spring-session Dec 3, 2018
@rwinch
Copy link
Member

rwinch commented Dec 3, 2018

Thanks I re-read the issue and understand the problem. Would you be interested in putting a PR together?

@rwinch rwinch added status: ideal-for-contribution An issue that we actively are looking for someone to help us with in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: backport An issue that is a backport of another issue to a maintenance branch type: bug A general bug and removed type: backport An issue that is a backport of another issue to a maintenance branch labels Dec 3, 2018
@wangzw
Copy link
Contributor Author

wangzw commented Dec 4, 2018

#6109 is filed for this issue.

@rwinch rwinch closed this as completed in a60fd43 Feb 15, 2019
@rwinch rwinch removed the status: ideal-for-contribution An issue that we actively are looking for someone to help us with label Feb 15, 2019
@rwinch rwinch self-assigned this Feb 15, 2019
@rwinch rwinch added this to the 5.2.0.M2 milestone Feb 15, 2019
@rwinch rwinch changed the title OAuth2 client cannot work well with reactor redis session. Fix OAuth2 Client with Ditributed Session Feb 15, 2019
rwinch pushed a commit that referenced this issue Feb 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants