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

OAuth2AuthorizationRequest not removed from session #7327

Closed
AndreasKl opened this issue Aug 29, 2019 · 4 comments
Closed

OAuth2AuthorizationRequest not removed from session #7327

AndreasKl opened this issue Aug 29, 2019 · 4 comments
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@AndreasKl
Copy link
Contributor

AndreasKl commented Aug 29, 2019

When #6215 was fixed only the adding of new OAuth2AuthorizationRequests was fixed, not the removal of those. With a distributed session store we observed an increase in session size for users having long running sessions.

A dump of the keys of the session attributes revealed a huge HashMap of OAuth2AuthorizationRequest. This is due to org.springframework.security.oauth2.client.web.server.WebSessionOAuth2ServerAuthorizationRequestRepository#removeAuthorizationRequest only removing the OAuth2AuthorizationRequest from the HashMap and not updating the session attributes leaving no clue to the session repository that the session was amended.

The expected behaviour would be that the stateToAuthzRequest HashMap should not grow without limit and OAuth2AuthorizationRequest should be removed after it was used to create a new session.

Used version: spring-security-oauth2-client-5.1.6.RELEASE.jar
however the issue exists on master: https://github.com/spring-projects/spring-security/blob/master/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/server/WebSessionOAuth2ServerAuthorizationRequestRepository.java#L85

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Aug 29, 2019
@jgrandja jgrandja changed the title Followup for #6215: Leaking authorization requests on distibuted sessions OAuth2AuthorizationRequest not removed from session Aug 30, 2019
@jgrandja jgrandja added in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Aug 30, 2019
@jgrandja jgrandja added this to the 5.2.0 milestone Aug 30, 2019
@jgrandja jgrandja added the status: ideal-for-contribution An issue that we actively are looking for someone to help us with label Aug 30, 2019
@jgrandja jgrandja self-assigned this Aug 30, 2019
@jgrandja
Copy link
Contributor

Thanks for the report @AndreasKl. You are right, WebSessionOAuth2ServerAuthorizationRequestRepository.removeAuthorizationRequest() does not update the sessions attributes with the updated Map of the removal of OAuth2AuthorizationRequest.

Would you be interested in submitting a PR for this fix?

@AndreasKl
Copy link
Contributor Author

AndreasKl commented Aug 30, 2019

@jgrandja Would be happy to provide the PR.

Do you think it is of any value, if I also provide a PR with an optional facility that allows to limit the amount of OAuth2AuthorizationRequests for a single session. This could be an additional session attribute backed by a size limited LinkedHashMap containing the state values as keys and an empty object as value (this would have the benefit that existing sessions can be de-serialized).

Something like:

  void limitSizeOfAuthorizationRequest(
      OAuth2AuthorizationRequest oAuth2AuthorizationRequest, WebSession webSession) {
    requireNonNull(oAuth2AuthorizationRequest, "oAuth2AuthorizationRequest must not be null");
    requireNonNull(webSession, "webSession must not be null");

    removeOldAuthorizationRequest(
        storeMostRecentAuthorizationRequests(oAuth2AuthorizationRequest, webSession), webSession);
  }

  private Map<String, String> storeMostRecentAuthorizationRequests(
      OAuth2AuthorizationRequest oAuth2AuthorizationRequest, WebSession webSession) {

    SizeLimitedHashMap<String, String> workingSet =
        webSession.getAttribute(DEFAULT_AUTHORIZATION_REQUEST_LIMIT_ATTR_NAME);
    if (workingSet == null) {
      workingSet = new SizeLimitedHashMap<>(limit);
    }

    workingSet.put(oAuth2AuthorizationRequest.getState(), SOME_VALUE);

    if (LOG.isDebugEnabled()) {
      LOG.debug("The current active auth request states are: {}", workingSet.keySet());
    }

    webSession.getAttributes().put(DEFAULT_AUTHORIZATION_REQUEST_LIMIT_ATTR_NAME, workingSet);
    return workingSet;
  }

  private void removeOldAuthorizationRequest(
      Map<String, String> workingSet, WebSession webSession) {
    Map<String, OAuth2AuthorizationRequest> oAuth2AuthorizationRequests =
        webSession.getAttribute(DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME);

    if (oAuth2AuthorizationRequests == null) {
      return;
    }

    if (LOG.isDebugEnabled()) {
      LOG.debug(
          "Auth request state keys before cleanup: {}", oAuth2AuthorizationRequests.keySet());
    }

    oAuth2AuthorizationRequests.keySet().retainAll(workingSet.keySet());

    if (LOG.isDebugEnabled()) {
      LOG.debug("Auth request state keys after cleanup: {}", oAuth2AuthorizationRequests.keySet());
    }

    webSession
        .getAttributes()
        .put(DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME, oAuth2AuthorizationRequests);
  }
public class SizeLimitedHashMap<TKey extends Serializable, TValue extends Serializable>
    extends LinkedHashMap<TKey, TValue> implements Serializable {

  private static final long serialVersionUID = -4358903105100110082L;

  private final int limit;

  SizeLimitedHashMap(int limit) {
    this.limit = limit;
  }

  @Override
  protected boolean removeEldestEntry(Entry<TKey, TValue> eldest) {
    return this.size() > limit;
  }
}

@jgrandja jgrandja removed the status: ideal-for-contribution An issue that we actively are looking for someone to help us with label Sep 3, 2019
@jgrandja
Copy link
Contributor

jgrandja commented Sep 3, 2019

@AndreasKl

Do you think it is of any value, if I also provide a PR with an optional facility that allows to limit the amount of OAuth2AuthorizationRequests for a single session

The existing issue #5145 tracks this enhancement. We would need this enhancement implemented on both the Servlet and Reactive side. It would be great if you can provide a PR?

@AndreasKl
Copy link
Contributor Author

@jgrandja Thanks for the hint, wasn't aware there is already an open issue.

@jgrandja jgrandja modified the milestones: 5.2.0, 5.2.0.RC1 Sep 5, 2019
@spring-projects-issues spring-projects-issues added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-5.1.x labels Sep 5, 2019
jgrandja pushed a commit that referenced this issue Sep 5, 2019
Dirties the WebSession by putting the amended AUTHORIZATION_REQUEST map into
the WebSession even it was already in the map. This causes common SessionRepository
implementations like Redis to persist the updated attribute.

Fixes gh-7327

Author: Andreas Kluth <[email protected]>
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) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants