diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/IETFExchangeTokenGranter.java b/orcid-core/src/main/java/org/orcid/core/oauth/IETFExchangeTokenGranter.java index 2e4f2381051..acf8139b2d5 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/IETFExchangeTokenGranter.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/IETFExchangeTokenGranter.java @@ -50,7 +50,6 @@ */ public class IETFExchangeTokenGranter implements TokenGranter { - public static final String IETF_EXCHANGE = "urn:ietf:params:oauth:grant-type:token-exchange"; private AuthorizationServerTokenServices tokenServices; @Resource(name = "orcidOauth2AuthoriziationCodeDetailDao") @@ -75,7 +74,7 @@ public class IETFExchangeTokenGranter implements TokenGranter { @Resource OpenIDConnectTokenEnhancer openIDConnectTokenEnhancer; - private List doNotAllowDeleteOnTheseRevokeReasons = List.of(RevokeReason.CLIENT_REVOKED.name(), RevokeReason.STAFF_REVOKED.name()); + private final List doNotAllowDeleteOnTheseRevokeReasons = List.of(RevokeReason.CLIENT_REVOKED, RevokeReason.STAFF_REVOKED, RevokeReason.RECORD_DEACTIVATED, RevokeReason.AUTH_CODE_REUSED); public IETFExchangeTokenGranter(AuthorizationServerTokenServices tokenServices) { this.tokenServices = tokenServices; @@ -239,10 +238,12 @@ private OAuth2AccessToken generateAccessToken(TokenRequest tokenRequest, String Set inactiveScopesOBO = Sets.newHashSet(); boolean issueRevokedToken = false; RevokeReason revokeReason = null; + // Lets consider token expiration time anything that goes beyond this date + Date now = new Date(); for (OrcidOauth2TokenDetail d : details) { Set scopesInToken = ScopePathType.getScopesFromSpaceSeparatedString(d.getScope()); // If token is expired, we should ignore it - if (d.getTokenExpiration().after(new Date())) { + if (d.getTokenExpiration().after(now)) { // If token is disabled, we should know if it have the /activities/update scope on it if(d.getTokenDisabled() == null || !d.getTokenDisabled()) { activeScopesOBO.addAll(scopesInToken); @@ -257,8 +258,12 @@ private OAuth2AccessToken generateAccessToken(TokenRequest tokenRequest, String // Keep only the /activities/update scope if the token was not revoked by a client or staff member if(revokeReason == null || !doNotAllowDeleteOnTheseRevokeReasons.contains(revokeReason)) { inactiveScopesOBO.add(ScopePathType.ACTIVITIES_UPDATE); + } else { + throw new OrcidInvalidScopeException("The id_token is disabled and does not contain any valid scope"); } - } + } else { + throw new OrcidInvalidScopeException("The id_token is disabled and does not contain any valid scope"); + } } } } diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java index 58308246b57..a277afcaa6f 100644 --- a/orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java +++ b/orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java @@ -289,14 +289,14 @@ public void grantDisabledTokenDoesntWorkTest() throws NoSuchAlgorithmException, tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/read-limited"))); fail(); } catch (OrcidInvalidScopeException oise) { - assertEquals("The id_token is not associated with a valid scope", oise.getMessage()); + assertEquals("The id_token is disabled and does not contain any valid scope", oise.getMessage()); } catch (Exception e) { fail(); } } @Test - public void grantDisabledTokenWithActivitiesReadLimitedGenerateDeactivatedTokenTest() + public void grantUserDisabledTokenWithActivitiesReadLimitedGenerateDeactivatedTokenTest() throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); token1.setRevokeReason(RevokeReason.USER_REVOKED.name()); @@ -309,6 +309,38 @@ public void grantDisabledTokenWithActivitiesReadLimitedGenerateDeactivatedTokenT verify(tokenServicesMock, never()).createAccessToken(any()); } + @Test + public void grantClientDisabledTokenWithActivitiesReadLimitedThrowExceptionTest() + throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { + OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); + token1.setRevokeReason(RevokeReason.CLIENT_REVOKED.name()); + + when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1)); + try { + tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); + } catch(OrcidInvalidScopeException e) { + assertEquals("The id_token is disabled and does not contain any valid scope", e.getMessage()); + } catch(Exception e) { + fail("Unhandled exception:" + e.getMessage()); + } + } + + @Test + public void grantStaffDisabledTokenWithActivitiesReadLimitedThrowExceptionTest() + throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { + OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); + token1.setRevokeReason(RevokeReason.STAFF_REVOKED.name()); + + when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1)); + try { + tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); + } catch(OrcidInvalidScopeException e) { + assertEquals("The id_token is disabled and does not contain any valid scope", e.getMessage()); + } catch(Exception e) { + fail("Unhandled exception:" + e.getMessage()); + } + } + @Test public void grantDisabledTokenWithActivitiesUpdateAndOtherActiveTokenWithOtherScopesGenerateDeactivatedTokenTest() throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException {