Updated token response using Nimbus (#650)

Change-Id: Id33f3f4422da63f24d430e4f8d0a6618edf8ec2e
diff --git a/full/Changes b/full/Changes
index 3973646..f1e63b9 100644
--- a/full/Changes
+++ b/full/Changes
@@ -20,6 +20,7 @@
 - Fixed clearing cache
 - Fix JettyServerTest, init package, and some java docs.
 - Make scope extraction more flexible.
+- Updated token response using Nimbus (#650)
 
 
 # version 0.71.1
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java
index 70c9a0b..0af4df4 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2TokenService.java
@@ -12,13 +12,10 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.oltu.oauth2.as.response.OAuthASResponse;
-import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
-import org.apache.oltu.oauth2.common.message.OAuthResponse;
-import org.apache.oltu.oauth2.common.message.types.TokenType;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import com.nimbusds.oauth2.sdk.AccessTokenResponse;
 import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
 import com.nimbusds.oauth2.sdk.AuthorizationGrant;
 import com.nimbusds.oauth2.sdk.GrantType;
@@ -26,11 +23,8 @@
 import com.nimbusds.oauth2.sdk.ResourceOwnerPasswordCredentialsGrant;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.TokenRequest;
-import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
-import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
-import com.nimbusds.oauth2.sdk.auth.ClientSecretPost;
-import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
+import com.nimbusds.oauth2.sdk.token.Tokens;
 import com.unboundid.ldap.sdk.LDAPException;
 
 import de.ids_mannheim.korap.authentication.AuthenticationManager;
@@ -50,11 +44,7 @@
 import de.ids_mannheim.korap.oauth2.entity.Authorization;
 import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
 import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
-import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeAllTokenSuperRequest;
-import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenRequest;
-import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenSuperRequest;
 import jakarta.persistence.NoResultException;
-import jakarta.ws.rs.core.Response.Status;
 
 /**
  * OAuth2TokenService manages business logic related to OAuth2
@@ -103,7 +93,6 @@
      *            client_secret, required if client_secret was issued
      *            for the client in client registration.
      * @return an authorization
-     * @throws OAuthSystemException
      * @throws KustvaktException
      */
     protected Authorization retrieveAuthorization (
@@ -149,9 +138,9 @@
         return authenticationTime;
     }
 
-    public OAuthResponse requestAccessToken (
+    public AccessTokenResponse requestAccessToken (
             TokenRequest tokenRequest, String clientId, String clientSecret)
-            throws KustvaktException, OAuthSystemException {
+            throws KustvaktException {
 
         AuthorizationGrant authGrant = tokenRequest.getAuthorizationGrant();
         GrantType grantType = authGrant.getType();
@@ -215,12 +204,11 @@
      * @param clientSecret
      * @return if successful, a new access token
      * @throws KustvaktException
-     * @throws OAuthSystemException
      */
-    private OAuthResponse requestAccessTokenWithRefreshToken (
+    private AccessTokenResponse requestAccessTokenWithRefreshToken (
             String refreshTokenStr, Set<String> requestScopes, String clientId,
             String clientSecret)
-            throws KustvaktException, OAuthSystemException {
+            throws KustvaktException {
 
         if (refreshTokenStr == null || refreshTokenStr.isEmpty()) {
             throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
@@ -292,13 +280,12 @@
      *            client id, required
      * @param clientSecret
      *            client secret, required
-     * @return an {@link OAuthResponse}
-     * @throws OAuthSystemException
+     * @return an {@link AccessTokenResponse}
      * @throws KustvaktException
      */
-    private OAuthResponse requestAccessTokenWithAuthorizationCode (String code,
+    private AccessTokenResponse requestAccessTokenWithAuthorizationCode (String code,
             String redirectionURI, String clientId, String clientSecret)
-            throws OAuthSystemException, KustvaktException {
+            throws KustvaktException {
         Authorization authorization = retrieveAuthorization(code, redirectionURI,
                 clientId, clientSecret);
 
@@ -336,13 +323,12 @@
      *            password, required
      * @param scopes
      *            authorization scopes, optional
-     * @return an {@link OAuthResponse}
+     * @return an {@link AccessTokenResponse}
      * @throws KustvaktException
-     * @throws OAuthSystemException
      */
-    private OAuthResponse requestAccessTokenWithPassword (String clientId,
+    private AccessTokenResponse requestAccessTokenWithPassword (String clientId,
             String clientSecret, String username, String password,
-            Set<String> scopes) throws KustvaktException, OAuthSystemException {
+            Set<String> scopes) throws KustvaktException {
 
         OAuth2Client client =
                 clientService.authenticateClient(clientId, clientSecret);
@@ -390,13 +376,12 @@
      *            client_secret parameter, required
      * @param scopes
      *            authorization scopes, optional
-     * @return an {@link OAuthResponse}
+     * @return an {@link AccessTokenResponse}
      * @throws KustvaktException
-     * @throws OAuthSystemException
      */
-    protected OAuthResponse requestAccessTokenWithClientCredentials (
+    protected AccessTokenResponse requestAccessTokenWithClientCredentials (
             String clientId, String clientSecret, Set<String> scopes)
-            throws KustvaktException, OAuthSystemException {
+            throws KustvaktException {
 
         if (clientSecret == null || clientSecret.isEmpty()) {
             throw new KustvaktException(
@@ -428,7 +413,7 @@
     }
 
     /**
-     * Creates an OAuthResponse containing an access token of type
+     * Creates an OAuth response containing an access token of type
      * Bearer. By default, MD generator is used to generates access
      * token of 128 bit values, represented in hexadecimal comprising
      * 32 bytes. The generated value is subsequently encoded in
@@ -451,67 +436,59 @@
      *            the user authentication time
      * @param client
      *            an OAuth2Client
-     * @return an {@link OAuthResponse}
-     * @throws OAuthSystemException
+     * @return an {@link AccessTokenResponse}
      * @throws KustvaktException
      */
-    private OAuthResponse createsAccessTokenResponse (Set<String> scopes,
+    private AccessTokenResponse createsAccessTokenResponse (Set<String> scopes,
             Set<AccessScope> accessScopes, String clientId, String userId,
             ZonedDateTime authenticationTime, OAuth2Client client)
-            throws OAuthSystemException, KustvaktException {
+            throws KustvaktException {
 
         String random = randomGenerator.createRandomCode();
         random += randomGenerator.createRandomCode();
         
-        if (clientService.isPublicClient(client)){
+        if (clientService.isPublicClient(client)) {
             // refresh token == null, getAccessTokenLongExpiry
-            return createsAccessTokenResponse(scopes, accessScopes, clientId,
-                    userId, authenticationTime);
-            }
+            return createsAccessTokenResponse(null, scopes, accessScopes,
+                    clientId, userId, authenticationTime);
+        }
         else {
             // refresh token != null, getAccessTokenExpiry
             // default refresh token expiry: 365 days in seconds
             RefreshToken refreshToken = refreshDao.storeRefreshToken(random,
                     userId, authenticationTime, client, accessScopes);
-            return createsAccessTokenResponse(scopes, accessScopes, clientId,
-                    userId, authenticationTime, refreshToken);
+            return createsAccessTokenResponse(refreshToken, scopes,
+                    accessScopes, clientId, userId, authenticationTime);
         }
     }
 
-    private OAuthResponse createsAccessTokenResponse (Set<String> scopes,
+    private AccessTokenResponse createsAccessTokenResponse (
+            RefreshToken refreshToken, Set<String> scopes,
             Set<AccessScope> accessScopes, String clientId, String userId,
-            ZonedDateTime authenticationTime, RefreshToken refreshToken)
-            throws OAuthSystemException, KustvaktException {
+            ZonedDateTime authenticationTime) throws KustvaktException {
 
         String accessToken = randomGenerator.createRandomCode();
         accessToken +=randomGenerator.createRandomCode();
         tokenDao.storeAccessToken(accessToken, refreshToken, accessScopes,
                 userId, clientId, authenticationTime);
 
-        return OAuthASResponse.tokenResponse(Status.OK.getStatusCode())
-                .setAccessToken(accessToken)
-                .setTokenType(TokenType.BEARER.toString())
-                .setExpiresIn(String.valueOf(config.getAccessTokenExpiry()))
-                .setRefreshToken(refreshToken.getToken())
-                .setScope(String.join(" ", scopes)).buildJSONMessage();
-    }
-    
-    private OAuthResponse createsAccessTokenResponse (Set<String> scopes,
-            Set<AccessScope> accessScopes, String clientId, String userId,
-            ZonedDateTime authenticationTime)
-            throws OAuthSystemException, KustvaktException {
+        Tokens tokens = null;
+        if (refreshToken !=null) {
+            BearerAccessToken bearerToken = new BearerAccessToken(accessToken,
+                    (long) config.getAccessTokenExpiry(), Scope.parse(scopes));
+            com.nimbusds.oauth2.sdk.token.RefreshToken rf =
+                    new com.nimbusds.oauth2.sdk.token.RefreshToken(
+                            refreshToken.getToken());
+            tokens = new Tokens(bearerToken, rf);
+        }
+        else {
+              BearerAccessToken bearerToken = new BearerAccessToken(accessToken,
+              (long) config.getAccessTokenLongExpiry(), Scope.parse(scopes));
+              tokens = new Tokens(bearerToken, null);
+        }
+        return new AccessTokenResponse(tokens);
+    }        
 
-        String accessToken = randomGenerator.createRandomCode();
-        accessToken +=randomGenerator.createRandomCode();
-        tokenDao.storeAccessToken(accessToken, null, accessScopes,
-                userId, clientId, authenticationTime);
-
-        return OAuthASResponse.tokenResponse(Status.OK.getStatusCode())
-                .setAccessToken(accessToken)
-                .setTokenType(TokenType.BEARER.toString())
-                .setExpiresIn(String.valueOf(config.getAccessTokenLongExpiry()))
-                .setScope(String.join(" ", scopes)).buildJSONMessage();
-    }
 
     public void revokeToken (String clientId, String clientSecret,
             String token, String tokenType) throws KustvaktException {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java
index 077383f..9ece449 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java
@@ -9,7 +9,13 @@
 import org.apache.oltu.oauth2.common.message.OAuthResponse;
 import org.apache.oltu.oauth2.common.message.OAuthResponse.OAuthErrorResponseBuilder;
 
+import com.nimbusds.oauth2.sdk.AccessTokenResponse;
+import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse;
+import com.nimbusds.oauth2.sdk.ErrorObject;
+import com.nimbusds.oauth2.sdk.ErrorResponse;
 import com.nimbusds.oauth2.sdk.OAuth2Error;
+import com.nimbusds.oauth2.sdk.TokenErrorResponse;
+import com.nimbusds.oauth2.sdk.id.State;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
@@ -69,9 +75,12 @@
         catch (OAuthSystemException e1) {
             throwit(e1, state);
         }
-        Response r = createResponse(oAuthResponse);
+        Response r = createResponse(oAuthResponse.getResponseStatus(),
+                oAuthResponse.getBody(), oAuthResponse.getLocationUri());
         return new WebApplicationException(r);
     }
+    
+    
 
     @Override
     public WebApplicationException throwit (KustvaktException e){
@@ -136,6 +145,19 @@
         }
         return ex;
     }
+    
+    private ErrorResponse createErrorResponse (
+            KustvaktException e, String statusCode, String state){
+        ErrorResponse r = null;
+        
+        if (e.getRedirectUri()!=null) {
+            ErrorObject eo = new ErrorObject(statusCode, e.getMessage());
+            State s = new State(state);
+            r = new AuthorizationErrorResponse(e.getRedirectUri(), eo, s, null);
+        }
+        
+        return r;
+    }
 
     /**
      * RFC 6749 regarding authorization error response:
@@ -156,19 +178,18 @@
      * @param oAuthResponse
      * @return
      */
-    public Response createResponse (OAuthResponse oAuthResponse) {
+    public Response createResponse (int status, String body, String uri) {
         ResponseBuilder builder =
-                Response.status(oAuthResponse.getResponseStatus());
-        builder.entity(oAuthResponse.getBody());
+                Response.status(status);
+        builder.entity(body);
         builder.header(HttpHeaders.CACHE_CONTROL, "no-store");
         builder.header(HttpHeaders.PRAGMA, "no-store");
 
-        if (oAuthResponse.getResponseStatus() == Status.UNAUTHORIZED
+        if (status == Status.UNAUTHORIZED
                 .getStatusCode()) {
             builder.header(HttpHeaders.WWW_AUTHENTICATE,
                     "Basic realm=\"Kustvakt\"");
         }
-        String uri = oAuthResponse.getLocationUri();
         if (uri != null && !uri.isEmpty()) {
             try {
                 builder.location(new URI(uri));
@@ -186,4 +207,28 @@
         ResponseBuilder builder = Response.temporaryRedirect(locationUri);
         return builder.build();
     }
+    
+    public Response createResponse (AccessTokenResponse tokenResponse) {
+        String jsonString = tokenResponse.toJSONObject().toJSONString();
+        return createResponse(Status.OK, jsonString);
+    }
+
+    public Response createResponse (TokenErrorResponse tokenResponse,
+            Status status) {
+        String jsonString = tokenResponse.toJSONObject().toJSONString();
+        return createResponse(status, jsonString);
+    }
+
+    private Response createResponse (Status status, Object entity) {
+        ResponseBuilder builder = Response.status(status);
+        builder.entity(entity);
+        builder.header(HttpHeaders.CACHE_CONTROL, "no-store");
+        builder.header(HttpHeaders.PRAGMA, "no-store");
+
+        if (status == Status.UNAUTHORIZED) {
+            builder.header(HttpHeaders.WWW_AUTHENTICATE,
+                    "Basic realm=\"Kustvakt\"");
+        }
+        return builder.build();
+    }
 }
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 a74d241..e8d7b10 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
@@ -5,11 +5,10 @@
 import java.time.ZonedDateTime;
 import java.util.List;
 
-import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
-import org.apache.oltu.oauth2.common.message.OAuthResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 
+import com.nimbusds.oauth2.sdk.AccessTokenResponse;
 import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse;
 import com.nimbusds.oauth2.sdk.AuthorizationGrant;
 import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
@@ -17,13 +16,11 @@
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.TokenRequest;
-import com.nimbusds.oauth2.sdk.TokenRevocationRequest;
 import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
 import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
 import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
 import com.nimbusds.oauth2.sdk.auth.ClientSecretPost;
 import com.nimbusds.oauth2.sdk.id.ClientID;
-import com.nimbusds.oauth2.sdk.token.Token;
 
 import de.ids_mannheim.korap.constant.OAuth2Scope;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -267,7 +264,6 @@
             @FormParam("client_secret") String clientSecret,
             MultivaluedMap<String, String> form) {
 
-        OAuthResponse oAuthResponse = null;
         try {
             URI requestURI;
             UriBuilder builder = UriBuilder.fromPath(
@@ -317,8 +313,9 @@
                         Scope.parse(form.getFirst("scope")));
                 }
             
-                oAuthResponse = tokenService.requestAccessToken(tokenRequest,
+                AccessTokenResponse r = tokenService.requestAccessToken(tokenRequest,
                         clientId, clientSecret);
+                return responseHandler.createResponse(r);
             }
             catch (ParseException | IllegalArgumentException e) {
                 throw new KustvaktException(StatusCodes.INVALID_REQUEST,
@@ -329,11 +326,6 @@
         catch (KustvaktException e) {
             throw responseHandler.throwit(e);
         }
-        catch (OAuthSystemException e) {
-            throw responseHandler.throwit(e);
-        }
-        
-        return responseHandler.createResponse(oAuthResponse);
     }
 
     /**