Fixed revoking latest access token when refreshing OAuth2 access token.

Change-Id: I8b2a0a4eb4ca3b7374e2347dececb9801f1ba0df
diff --git a/full/Changes b/full/Changes
index ed71a4d..18e2c34 100644
--- a/full/Changes
+++ b/full/Changes
@@ -13,6 +13,7 @@
 	  users when deregistering/deleting a client (margaretha)
 	- Fixed update OAuth2 access token (margaretha)
 	- Implemented reset client secret (margaretha)
+	- Fixed revoking latest access token when refreshing OAuth2 access token (margaretha)
 	  
 
 # version 0.60.5
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
index c53fbb6..bb9baa5 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
@@ -136,6 +136,7 @@
         }
 
         if (!latestAccessToken.isRevoked()) {
+            latestAccessToken.setRevoked(true);
             tokenDao.updateAccessToken(latestAccessToken);
         }
 
@@ -324,7 +325,7 @@
         return OAuthASResponse.tokenResponse(Status.OK.getStatusCode())
                 .setAccessToken(accessToken)
                 .setTokenType(TokenType.BEARER.toString())
-                .setExpiresIn(String.valueOf(config.getTokenTTL()))
+                .setExpiresIn(String.valueOf(config.getAccessTokenExpiry()))
                 .setRefreshToken(refreshToken)
                 .setScope(String.join(" ", scopes)).buildJSONMessage();
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
index 45735ed..91e724a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
@@ -174,7 +174,7 @@
         authenticationTime = authenticateUser(username, password, scopeSet);
 
         AccessToken accessToken =
-                new BearerAccessToken(config.getTokenTTL(), scope);
+                new BearerAccessToken(config.getAccessTokenExpiry(), scope);
 
         RefreshToken refreshToken = new RefreshToken();
 
@@ -228,7 +228,7 @@
                 .toArray(String[]::new);
         Scope scope = new Scope(scopeArray);
         AccessToken accessToken =
-                new BearerAccessToken(config.getTokenTTL(), scope);
+                new BearerAccessToken(config.getAccessTokenExpiry(), scope);
         RefreshToken refreshToken = new RefreshToken();
 
         tokenDao.storeAccessToken(accessToken.getValue(),
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index 61f8237..082f976 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -198,14 +198,20 @@
     }
 
     /**
-     * Revoking an access token also revokes its refresh token, vice
-     * versa.
+     * Revokes either an access token or a refresh token. Revoking a
+     * refresh token also revokes all access token associated with the
+     * refresh token.
      * 
      * RFC 7009
      * Client authentication for confidential client
      * 
      * @param request
      * @param form
+     *            containing
+     *            client_id,
+     *            client_secret (required for confidential clients),
+     *            token,
+     *            token_type (optional)
      * @return 200 if token invalidation is successful or the given
      *         token is invalid
      */
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
index 8282baa..98f3434 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
@@ -9,6 +9,7 @@
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.http.entity.ContentType;
+import org.apache.oltu.oauth2.common.message.types.GrantType;
 import org.junit.Test;
 
 import com.fasterxml.jackson.databind.JsonNode;
@@ -26,10 +27,12 @@
 
 public class OAuth2AccessTokenTest extends SpringJerseyTest {
 
-    private String requestToken () throws KustvaktException {
+    private String clientId = "fCBbQkAyYzI4NzUxMg";
+
+    private JsonNode requestToken () throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("grant_type", "password");
-        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        form.add("client_id", clientId);
         form.add("client_secret", "secret");
         form.add("username", "dory");
         form.add("password", "password");
@@ -41,7 +44,7 @@
 
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        return node.at("/access_token").asText();
+        return node;
     }
 
     @Test
@@ -78,11 +81,10 @@
 
     @Test
     public void testTokenAccessScope () throws KustvaktException, IOException {
-        String accessToken = requestToken();
+        String accessToken = requestToken().at("/access_token").asText();
         testListVCScopeNotAuthorized(accessToken);
         testListVCAccessBearerNotAuthorize(accessToken);
         testSearchWithOAuth2Token(accessToken);
-
     }
 
     private void testListVCScopeNotAuthorized (String accessToken)
@@ -159,7 +161,7 @@
     @Test
     public void testRevokeAccessTokenConfidentialClient ()
             throws KustvaktException {
-        String accessToken = requestToken();
+        String accessToken = requestToken().at("/access_token").asText();
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("token", accessToken);
         form.add("client_id", "fCBbQkAyYzI4NzUxMg");
@@ -171,7 +173,7 @@
                 .entity(form).post(ClientResponse.class);
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        
+
         testSearchWithRevokedToken(accessToken);
     }
 
@@ -186,10 +188,41 @@
         String entity = response.getEntity(String.class);
         assertEquals(ClientResponse.Status.UNAUTHORIZED.getStatusCode(),
                 response.getStatus());
-        
-        JsonNode node = JsonUtils.readTree(entity);
-        assertEquals(StatusCodes.INVALID_ACCESS_TOKEN, node.at("/errors/0/0").asInt());
-        assertEquals("Access token has been revoked", node.at("/errors/0/1").asText());
 
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.INVALID_ACCESS_TOKEN,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Access token has been revoked",
+                node.at("/errors/0/1").asText());
+
+    }
+
+    @Test
+    public void testRevocationAfterRequestRefreshToken ()
+            throws KustvaktException {
+        JsonNode node = requestToken();
+        String accessToken = node.at("/access_token").asText();
+        String refreshToken = node.at("/refresh_token").asText();
+        
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
+        form.add("client_id", clientId);
+        form.add("client_secret", "secret");
+        form.add("refresh_token", refreshToken);
+
+        ClientResponse response = resource().path("oauth2").path("token")
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        node = JsonUtils.readTree(entity);
+        assertNotNull(node.at("/access_token").asText());
+        assertEquals(refreshToken,node.at("/refresh_token").asText());
+        
+        testSearchWithRevokedToken(accessToken);
     }
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java
index 3985fd6..5da5fe9 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/TokenExpiryTest.java
@@ -21,6 +21,13 @@
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
+/**
+ * Before running this test:
+ * set oauth2.access.token.expiry = 3S
+ * 
+ * @author margaretha
+ *
+ */
 public class TokenExpiryTest extends SpringJerseyTest {
 
     @Test
@@ -45,7 +52,7 @@
         Thread.sleep(1000);
 
         testRequestAuthorizationCodeAuthenticationTooOld(token);
-        
+
         Thread.sleep(1500);
         testSearchWithExpiredToken(token);
     }