Implemented handler for OpenID authentication error response.

Change-Id: Icc7150e4f880ab38b1ea7be42afe04dd0814af10
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
index b16c8ff..9a76e74 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
@@ -1,5 +1,6 @@
 package de.ids_mannheim.korap.exceptions;
 
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -24,6 +25,7 @@
     private String notification;
     private boolean isNotification;
 //    private TokenType authType;
+    private URI redirectUri;
 
     public KustvaktException (int status) {
         this.statusCode = status;
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index b93892b..b885924 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -113,7 +113,7 @@
     public static final int ACCESS_DENIED = 1003;
 
     /**
-     * 1800 Oauth2
+     * 1800 Oauth2 and OpenID
      */
 
     public static final int OAUTH2_SYSTEM_ERROR = 1800;
@@ -124,8 +124,9 @@
     public static final int CLIENT_AUTHORIZATION_FAILED = 1804;
     public static final int CLIENT_NOT_FOUND = 1805;
     public static final int INVALID_REDIRECT_URI = 1806;
-    public static final int INVALID_SCOPE = 1807;
-    public static final int INVALID_AUTHORIZATION = 1808;
+    public static final int MISSING_REDIRECT_URI = 1807;
+    public static final int INVALID_SCOPE = 1808;
+    public static final int INVALID_AUTHORIZATION = 1809;
     
     public static final int UNSUPPORTED_GRANT_TYPE = 1810;
     
diff --git a/full/Changes b/full/Changes
index 70835d0..ccb3096 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,6 +1,9 @@
 version 0.60.4
 12/06/2018
     - implemented OAuth2 authorization code request with OpenID Authentication (margaretha)
+    - enabled OAuth2 authorization without OpenID authentication using Nimbus library (margaretha)
+    - implemented response handler for OpenID authentication errors in authorization requests (margaretha)
+    - added tests regarding OpenID authentication in authorization requests (margaretha)
     
 version 0.60.3
 06/06/2018
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
index 9f371eb..c0353be 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
@@ -1,7 +1,5 @@
 package de.ids_mannheim.korap.oauth2.oltu.service;
 
-import java.util.Set;
-
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
@@ -15,8 +13,7 @@
 import com.sun.jersey.api.client.ClientResponse.Status;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.oauth2.entity.AccessScope;
-import de.ids_mannheim.korap.oauth2.entity.Authorization;
+import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
 import de.ids_mannheim.korap.oauth2.service.OAuth2AuthorizationService;
 
 /**
@@ -47,17 +44,20 @@
             throws KustvaktException, OAuthSystemException {
 
         String code = oauthIssuer.authorizationCode();
-        Authorization authorization = createAuthorization(username,
-                authzRequest.getClientId(), authzRequest.getResponseType(),
-                authzRequest.getRedirectURI(), authzRequest.getScopes(), code);
+        checkResponseType(authzRequest.getResponseType());
 
-        Set<AccessScope> scopes = authorization.getScopes();
-        String scopeStr = scopeService.convertAccessScopesToString(scopes);
+        String clientId = authzRequest.getClientId();
+        OAuth2Client client = clientService.authenticateClientId(clientId);
+        
+        String redirectUri = authzRequest.getRedirectURI();
+        String verifiedRedirectUri = verifyRedirectUri(client, redirectUri);
+        String scope = createAuthorization(username, authzRequest.getClientId(),
+                redirectUri, authzRequest.getScopes(), code);
 
         OAuthResponse oAuthResponse = OAuthASResponse
                 .authorizationResponse(request, Status.FOUND.getStatusCode())
-                .setCode(code).setScope(scopeStr)
-                .location(authorization.getRedirectURI()).buildQueryMessage();
+                .setCode(code).setScope(scope)
+                .location(verifiedRedirectUri).buildQueryMessage();
         return oAuthResponse.getLocationUri();
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
index 985a7e8..d6f719c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
@@ -3,55 +3,148 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
+import org.apache.commons.validator.routines.UrlValidator;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import com.nimbusds.oauth2.sdk.AuthorizationCode;
 import com.nimbusds.oauth2.sdk.AuthorizationRequest;
+import com.nimbusds.oauth2.sdk.AuthorizationSuccessResponse;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.ResponseMode;
 import com.nimbusds.oauth2.sdk.ResponseType;
 import com.nimbusds.oauth2.sdk.Scope;
-import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.oauth2.sdk.id.State;
 import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
-import de.ids_mannheim.korap.oauth2.entity.Authorization;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
+import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
 import de.ids_mannheim.korap.oauth2.service.OAuth2AuthorizationService;
 
 @Service
-public class OpenIdAuthorizationService
-        extends OAuth2AuthorizationService {
+public class OpenIdAuthorizationService extends OAuth2AuthorizationService {
 
-    public URI requestAuthorizationCode (AuthenticationRequest authRequest,
-            String username) throws KustvaktException {
+    @Autowired
+    private UrlValidator urlValidator;
 
-        ClientID clientId = authRequest.getClientID();
-        URI redirectUri = authRequest.getRedirectionURI();
-        State state = authRequest.getState();
-        Scope scope = authRequest.getScope();
-        ResponseType responseType = authRequest.getResponseType();
+    public void checkRedirectUriParam (Map<String, String> map)
+            throws KustvaktException {
+        if (map.containsKey("redirect_uri")) {
+            String redirect_uri = map.get("redirect_uri");
+            if (redirect_uri != null && !redirect_uri.isEmpty()) {
+                if (!urlValidator.isValid(redirect_uri)) {
+                    throw new KustvaktException(
+                            StatusCodes.INVALID_REDIRECT_URI,
+                            "Invalid redirect URI",
+                            OAuth2Error.INVALID_REQUEST);
+                }
+                return;
+            }
+        }
 
+        throw new KustvaktException(StatusCodes.MISSING_REDIRECT_URI,
+                "redirect_uri is required", OAuth2Error.INVALID_REQUEST);
+    }
+
+    public URI requestAuthorizationCode (Map<String, String> map,
+            String username, boolean isAuthentication)
+            throws KustvaktException, ParseException {
+
+        AuthorizationCode code = new AuthorizationCode();
+        URI redirectUri = null;
+        if (isAuthentication) {
+            AuthenticationRequest authRequest = null;
+            authRequest = AuthenticationRequest.parse(map);
+            redirectUri =
+                    handleAuthenticationRequest(authRequest, code, username);
+            return new AuthenticationSuccessResponse(redirectUri, code, null,
+                    null, authRequest.getState(), null, null).toURI();
+        }
+        else {
+            AuthorizationRequest authzRequest = AuthorizationRequest.parse(map);
+            redirectUri =
+                    handleAuthorizationRequest(authzRequest, code, username);
+            return new AuthorizationSuccessResponse(redirectUri, code, null,
+                    authzRequest.getState(), null).toURI();
+
+        }
+    }
+
+    private URI handleAuthorizationRequest (AuthorizationRequest authzRequest,
+            AuthorizationCode code, String username) throws KustvaktException {
+
+        URI redirectUri = authzRequest.getRedirectionURI();
         String redirectUriStr =
                 (redirectUri != null) ? redirectUri.toString() : null;
-        Set<String> scopeSet =
-                (scope != null) ? new HashSet<>(scope.toStringList()) : null;
-        AuthorizationCode code = new AuthorizationCode();
 
-        Authorization authorization = createAuthorization(username,
-                clientId.toString(), responseType.toString(), redirectUriStr,
-                scopeSet, code.getValue());
+        String clientId = authzRequest.getClientID().getValue();
+        OAuth2Client client = clientService.authenticateClientId(clientId);
+        String verifiedRedirectUri = verifyRedirectUri(client, redirectUriStr);
 
         try {
-            return new AuthenticationSuccessResponse(
-                    new URI(authorization.getRedirectURI()), code, null, null,
-                    state, null, null).toURI();
+            redirectUri = new URI(verifiedRedirectUri);
         }
         catch (URISyntaxException e) {
             throw new KustvaktException(StatusCodes.INVALID_REDIRECT_URI,
-                    "Invalid redirect URI.");
+                    "Invalid redirect URI", OAuth2Error.INVALID_REQUEST);
         }
+
+        try {
+            ResponseType responseType = authzRequest.getResponseType();
+            checkResponseType(responseType.toString());
+
+            Scope scope = authzRequest.getScope();
+            Set<String> scopeSet = (scope != null)
+                    ? new HashSet<>(scope.toStringList()) : null;
+
+            createAuthorization(username, clientId, redirectUriStr, scopeSet,
+                    code.getValue());
+        }
+        catch (KustvaktException e) {
+            e.setRedirectUri(redirectUri);
+            throw e;
+        }
+
+        return redirectUri;
+    }
+
+
+    private URI handleAuthenticationRequest (AuthenticationRequest authRequest,
+            AuthorizationCode code, String username) throws KustvaktException {
+        // TO DO: extra checking for authentication params?
+
+        AuthorizationRequest request = authRequest;
+        return handleAuthorizationRequest(request, code, username);
+    }
+
+    @Override
+    protected void checkResponseType (String responseType)
+            throws KustvaktException {
+        String[] types = responseType.split(" ");
+        for (String type : types) {
+            super.checkResponseType(type);
+        }
+    }
+
+    public State retrieveState (Map<String, String> map) {
+        String stateStr = map.get("state");
+        if (stateStr != null && stateStr.isEmpty()) {
+            return new State(stateStr);
+        }
+        return null;
+    }
+
+    public ResponseMode retrieveResponseMode (Map<String, String> map) {
+        String str = map.get("response_mode");
+        if (str != null && str.isEmpty()) {
+            return new ResponseMode(str);
+        }
+        return null;
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
index 2d64aa6..6bb584f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
@@ -17,14 +17,14 @@
 import de.ids_mannheim.korap.oauth2.entity.Authorization;
 import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
 
-@Service(value="authorizationService")
+@Service(value = "authorizationService")
 public class OAuth2AuthorizationService {
 
     private static Logger jlog =
             LoggerFactory.getLogger(OAuth2AuthorizationService.class);
 
     @Autowired
-    private OAuth2ClientService clientService;
+    protected OAuth2ClientService clientService;
     @Autowired
     protected OAuth2ScopeService scopeService;
 
@@ -40,36 +40,27 @@
      * 
      * @param username
      * @param clientId
-     * @param responseType
      * @param redirectUri
      * @param scopeSet
      * @param code
      * @return
      * @throws KustvaktException
      */
-    public Authorization createAuthorization (String username, String clientId,
-            String responseType, String redirectUri, Set<String> scopeSet,
-            String code) throws KustvaktException {
-
-        checkResponseType(responseType);
-
-        OAuth2Client client = clientService.authenticateClientId(clientId);
-
-        String verifiedRedirectUri = verifyRedirectUri(client, redirectUri);
+    public String createAuthorization (String username, String clientId,
+            String redirectUri, Set<String> scopeSet, String code)
+            throws KustvaktException {
 
         if (scopeSet == null || scopeSet.isEmpty()) {
             scopeSet = config.getDefaultAccessScopes();
         }
         Set<AccessScope> scopes = scopeService.convertToAccessScope(scopeSet);
 
-        Authorization authorization = authorizationDao.storeAuthorizationCode(
-                clientId, username, code, scopes, redirectUri);
-
-        authorization.setRedirectURI(verifiedRedirectUri);
-        return authorization;
+        authorizationDao.storeAuthorizationCode(clientId, username, code,
+                scopes, redirectUri);
+        return String.join(" ", scopeSet);
     }
 
-    public void checkResponseType (String responseType)
+    protected void checkResponseType (String responseType)
             throws KustvaktException {
         if (responseType == null || responseType.isEmpty()) {
             throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
@@ -82,7 +73,8 @@
         }
         else if (!responseType.equals("code")) {
             throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
-                    "unknown response_type", OAuth2Error.INVALID_REQUEST);
+                    "unsupported response_type: " + responseType,
+                    OAuth2Error.INVALID_REQUEST);
         }
     }
 
@@ -114,8 +106,7 @@
             // check if the redirect URI the same as that in DB
             if (!redirectUri.equals(registeredUri)) {
                 throw new KustvaktException(StatusCodes.INVALID_REDIRECT_URI,
-                        redirectUri + " is unknown",
-                        OAuth2Error.INVALID_REQUEST);
+                        "Invalid redirect URI", OAuth2Error.INVALID_REQUEST);
             }
         }
         else {
@@ -126,7 +117,7 @@
                 redirectUri = registeredUri;
             }
             else {
-                throw new KustvaktException(StatusCodes.INVALID_REDIRECT_URI,
+                throw new KustvaktException(StatusCodes.MISSING_REDIRECT_URI,
                         "redirect_uri is required",
                         OAuth2Error.INVALID_REQUEST);
             }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
index e4da82b..e6c825d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
@@ -8,7 +8,6 @@
 
 import org.apache.commons.validator.routines.UrlValidator;
 import org.apache.log4j.Logger;
-import org.apache.oltu.oauth2.as.request.OAuthRequest;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/KustvaktExceptionHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java
similarity index 93%
rename from full/src/main/java/de/ids_mannheim/korap/web/KustvaktExceptionHandler.java
rename to full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java
index 6597608..0d23598 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/KustvaktExceptionHandler.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java
@@ -17,9 +17,9 @@
  * @author margaretha
  *
  */
-public class KustvaktExceptionHandler extends CoreResponseHandler {
+public class KustvaktResponseHandler extends CoreResponseHandler {
 
-    public KustvaktExceptionHandler (AuditingIface iface) {
+    public KustvaktResponseHandler (AuditingIface iface) {
         super(iface);
     }
 
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 f134c79..f6a223c 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
@@ -34,7 +34,7 @@
  * @author margaretha
  *
  */
-public class OAuth2ResponseHandler extends KustvaktExceptionHandler {
+public class OAuth2ResponseHandler extends KustvaktResponseHandler {
 
     public OAuth2ResponseHandler (AuditingIface iface) {
         super(iface);
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/OpenIdResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/OpenIdResponseHandler.java
new file mode 100644
index 0000000..4c50d75
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/OpenIdResponseHandler.java
@@ -0,0 +1,141 @@
+package de.ids_mannheim.korap.web;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import org.springframework.stereotype.Service;
+
+import com.nimbusds.oauth2.sdk.AuthorizationErrorResponse;
+import com.nimbusds.oauth2.sdk.ErrorObject;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.ResponseMode;
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.oauth2.sdk.token.BearerTokenError;
+import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
+import net.minidev.json.JSONObject;
+
+@Service
+public class OpenIdResponseHandler extends KustvaktResponseHandler {
+
+    final static Map<String, ErrorObject> errorObjectMap = new HashMap<>();
+    {
+        errorObjectMap.put(OAuth2Error.ACCESS_DENIED,
+                com.nimbusds.oauth2.sdk.OAuth2Error.ACCESS_DENIED);
+        errorObjectMap.put(OAuth2Error.INVALID_CLIENT,
+                com.nimbusds.oauth2.sdk.OAuth2Error.INVALID_CLIENT);
+        errorObjectMap.put(OAuth2Error.INVALID_GRANT,
+                com.nimbusds.oauth2.sdk.OAuth2Error.INVALID_GRANT);
+        errorObjectMap.put(OAuth2Error.INVALID_REQUEST,
+                com.nimbusds.oauth2.sdk.OAuth2Error.INVALID_REQUEST);
+        errorObjectMap.put(OAuth2Error.INVALID_SCOPE,
+                com.nimbusds.oauth2.sdk.OAuth2Error.INVALID_SCOPE);
+        errorObjectMap.put(OAuth2Error.SERVER_ERROR,
+                com.nimbusds.oauth2.sdk.OAuth2Error.SERVER_ERROR);
+        errorObjectMap.put(OAuth2Error.TEMPORARILY_UNAVAILABLE,
+                com.nimbusds.oauth2.sdk.OAuth2Error.TEMPORARILY_UNAVAILABLE);
+        errorObjectMap.put(OAuth2Error.UNAUTHORIZED_CLIENT,
+                com.nimbusds.oauth2.sdk.OAuth2Error.UNAUTHORIZED_CLIENT);
+        errorObjectMap.put(OAuth2Error.UNSUPPORTED_GRANT_TYPE,
+                com.nimbusds.oauth2.sdk.OAuth2Error.UNSUPPORTED_GRANT_TYPE);
+        errorObjectMap.put(OAuth2Error.UNSUPPORTED_RESPONSE_TYPE,
+                com.nimbusds.oauth2.sdk.OAuth2Error.UNSUPPORTED_RESPONSE_TYPE);
+    }
+
+    final static Map<String, ErrorObject> tokenErrorObjectMap = new HashMap<>();
+    {
+        errorObjectMap.put(OAuth2Error.INSUFFICIENT_SCOPE,
+                BearerTokenError.INSUFFICIENT_SCOPE);
+        errorObjectMap.put(OAuth2Error.INVALID_TOKEN,
+                BearerTokenError.INVALID_TOKEN);
+    }
+
+    public OpenIdResponseHandler (AuditingIface iface) {
+        super(iface);
+    }
+
+    /**
+     * According to OpenID connect core 1.0 specification, all
+     * authentication errors must be represented through
+     * AuthenticationErrorResponse. Moreover, for authorization code
+     * flow, the error response parameters must be added to the
+     * redirect URI as query parameters, unless a different response
+     * mode was specified.
+     * 
+     * {@link AuthorizationErrorResponse} defines specific
+     * {@link ErrorObject}s regarding OAUTH2 errors.
+     * {@link AuthenticationErrorResponse} defines additional
+     * ErrorObjects regarding OpenID connect authenticaition errors.
+     * 
+     * @param e
+     *            a {@link KustvaktException}
+     * @param isAuthentication
+     * @param redirectURI
+     * @param state
+     * @param responseMode
+     * @return a redirect uri with error response parameters as part
+     *         of query parameters
+     */
+    public Response createAuthorizationErrorResponse (KustvaktException e,
+            boolean isAuthentication, URI redirectURI, State state,
+            ResponseMode responseMode) {
+
+        ErrorObject errorObject = createErrorObject(e);
+        errorObject = errorObject.setDescription(e.getMessage());
+        if (redirectURI == null) {
+            return Response.status(errorObject.getHTTPStatusCode())
+                    .entity(errorObject.toJSONObject()).build();
+        }
+
+        URI uri = null;
+        if (isAuthentication) {
+            uri = new AuthenticationErrorResponse(redirectURI, errorObject,
+                    state, responseMode).toURI();
+        }
+        else {
+            uri = new AuthorizationErrorResponse(redirectURI, errorObject,
+                    state, responseMode).toURI();
+        }
+
+        ResponseBuilder builder = Response.temporaryRedirect(uri);
+        return builder.build();
+    }
+
+    private ErrorObject createErrorObject (KustvaktException e) {
+        String errorCode = e.getEntity();
+
+        ErrorObject errorObject = errorObjectMap.get(errorCode);
+        if (errorObject == null) {
+            errorObject = new ErrorObject(e.getEntity(), e.getMessage());
+        }
+        return errorObject;
+    }
+
+    public Response createAuthorizationErrorResponse (ParseException e,
+            boolean isAuthentication, State state) {
+        ErrorObject errorObject = e.getErrorObject();
+        if (errorObject == null) {
+            errorObject = com.nimbusds.oauth2.sdk.OAuth2Error.INVALID_REQUEST;
+            if (e.getMessage() != null) {
+                errorObject = errorObject.setDescription(e.getMessage());
+            }
+        }
+
+        JSONObject json = errorObject.toJSONObject();
+        if (state != null) {
+            json.put("state", state.getValue());
+        }
+
+        return Response.status(errorObject.getHTTPStatusCode()).entity(json)
+                .build();
+
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java
index 9bb3ac8..1b37937 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/AdminController.java
@@ -26,7 +26,7 @@
 import de.ids_mannheim.korap.server.KustvaktServer;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.AdminFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 
@@ -49,7 +49,7 @@
     private AuditingIface auditingController;
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     // EM: not documented and tested, not sure what the purpose of the service is
     @GET
@@ -75,7 +75,7 @@
             integer_limit = Integer.valueOf(limit);
         }
         catch (NumberFormatException | NullPointerException e) {
-            throw kustvaktExceptionHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT);
+            throw kustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT);
         }
         String result="";
         try {
@@ -84,7 +84,7 @@
                     until_date, dayOnly, integer_limit));
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         // limit number of records to return
         return Response.ok(result).build();
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java
index 5ddd8d1..595ae81 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/AnnotationController.java
@@ -22,7 +22,7 @@
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.service.AnnotationService;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.DemoUserFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 
@@ -39,7 +39,7 @@
 public class AnnotationController {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     @Autowired
     private AnnotationService annotationService;
@@ -71,7 +71,7 @@
     @Consumes(MediaType.APPLICATION_JSON)
     public List<FoundryDto> getFoundryDescriptions (String json) {
         if (json == null || json.isEmpty()) {
-            throw kustvaktExceptionHandler
+            throw kustvaktResponseHandler
                     .throwit(new KustvaktException(StatusCodes.MISSING_PARAMETER,
                             "Missing a json string.", ""));
         }
@@ -81,7 +81,7 @@
             node = JsonUtils.readTree(json);
         }
         catch (KustvaktException e1) {
-            throw kustvaktExceptionHandler.throwit(e1);
+            throw kustvaktResponseHandler.throwit(e1);
         }
 
         String language;
@@ -94,7 +94,7 @@
                 language = "en";
             }
             else if (!(language.equals("en") || language.equals("de"))) {
-                throw kustvaktExceptionHandler.throwit(
+                throw kustvaktResponseHandler.throwit(
                         new KustvaktException(StatusCodes.UNSUPPORTED_VALUE,
                                 "Unsupported value:", language));
             }
@@ -105,16 +105,16 @@
             codes = JsonUtils.convert(node.get("codes"), List.class);
         }
         catch (IOException | NullPointerException e) {
-            throw kustvaktExceptionHandler.throwit(new KustvaktException(
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
                     StatusCodes.INVALID_ARGUMENT, "Bad argument:", json));
         }
         if (codes == null) {
-            throw kustvaktExceptionHandler.throwit(
+            throw kustvaktResponseHandler.throwit(
                     new KustvaktException(StatusCodes.MISSING_ATTRIBUTE,
                             "Missing attribute:", "codes"));
         }
         else if (codes.isEmpty()) {
-            throw kustvaktExceptionHandler
+            throw kustvaktResponseHandler
                     .throwit(new KustvaktException(StatusCodes.NO_RESULT_FOUND,
                             "No result found.", "codes:[]"));
         }
@@ -123,7 +123,7 @@
             return annotationService.getFoundryDtos(codes, language);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java
index 73181f4..94c6a7f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/AuthenticationController.java
@@ -44,7 +44,7 @@
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.KustvaktLogger;
 import de.ids_mannheim.korap.utils.ServiceInfo;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.DemoUserFilter;
@@ -63,7 +63,7 @@
 public class AuthenticationController {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
     
     @Autowired
     private HttpAuthorizationHandler authorizationHandler;
@@ -102,7 +102,7 @@
             return Response.ok(JsonUtils.toJSON(m)).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -121,7 +121,7 @@
             return Response.ok(ctx.toJson()).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
     
@@ -158,7 +158,7 @@
             return Response.ok(context.toJson()).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -178,7 +178,7 @@
         List<String> auth =
                 headers.getRequestHeader(ContainerRequest.AUTHORIZATION);
         if (auth == null || auth.isEmpty()) {
-            throw kustvaktExceptionHandler
+            throw kustvaktResponseHandler
                     .throwit(new KustvaktException(StatusCodes.MISSING_PARAMETER,
                             "Authorization header is missing.",
                             "Authorization header"));
@@ -197,7 +197,7 @@
            
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         if (DEBUG_LOG == true) {
@@ -240,7 +240,7 @@
                 authorizationData.getPassword()== null || 
                 authorizationData.getPassword().isEmpty())
             // is actual an invalid request
-            throw kustvaktExceptionHandler.throwit(StatusCodes.REQUEST_INVALID);
+            throw kustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
 
         Map<String, Object> attr = new HashMap<>();
         if (scopes != null && !scopes.isEmpty())
@@ -268,14 +268,14 @@
 //                    Attributes.API_AUTHENTICATION);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         try {
             return Response.ok(context.toJson()).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -318,7 +318,7 @@
            
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         // Implementation Hanl mit '|'. 16.02.17/FB
@@ -329,7 +329,7 @@
                 authorizationData.getPassword()== null || 
                 authorizationData.getPassword().isEmpty())
             // is actual an invalid request
-            throw kustvaktExceptionHandler.throwit(StatusCodes.REQUEST_INVALID);
+            throw kustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
 
         Map<String, Object> attr = new HashMap<>();
         attr.put(Attributes.HOST, host);
@@ -348,7 +348,7 @@
             jlog.debug(contextJson);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().entity(contextJson).build();
     }
@@ -382,13 +382,13 @@
             context = controller.createTokenContext(user, attr, null);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         try {
             return Response.ok().entity(context.toJson()).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -406,7 +406,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Logout Exception: {}", e.string());
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java
index aafb569..79ee0e3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/DocumentController.java
@@ -23,7 +23,7 @@
 import de.ids_mannheim.korap.resources.Document;
 import de.ids_mannheim.korap.server.KustvaktServer;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.AdminFilter;
 
 /**
@@ -40,7 +40,7 @@
 public class DocumentController {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     private static Logger jlog =
             LoggerFactory.getLogger(DocumentController.class);
@@ -57,7 +57,7 @@
             this.documentDao.storeResource(doc, null);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -78,7 +78,7 @@
             return Response.ok(JsonUtils.toJSON(docs)).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java
index 1a1ef0e..32663f5 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/KustvaktController.java
@@ -26,7 +26,7 @@
 
     private static Logger jlog = LoggerFactory.getLogger(UserController.class);
     @Autowired
-    private CoreResponseHandler kustvaktExceptionHandler;
+    private CoreResponseHandler kustvaktResponseHandler;
 
 
     @Path("info")
@@ -39,7 +39,7 @@
             return Response.ok(JsonUtils.toJSON(m)).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
index c87516f..d3605de 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
@@ -5,6 +5,7 @@
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -19,12 +20,14 @@
 import org.springframework.stereotype.Controller;
 
 import com.nimbusds.oauth2.sdk.ParseException;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+import com.nimbusds.oauth2.sdk.ResponseMode;
+import com.nimbusds.oauth2.sdk.id.State;
 import com.sun.jersey.spi.container.ResourceFilters;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.oauth2.openid.service.OpenIdAuthorizationService;
 import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.web.OpenIdResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.utils.MapUtils;
@@ -35,6 +38,8 @@
 
     @Autowired
     private OpenIdAuthorizationService authzService;
+    @Autowired
+    private OpenIdResponseHandler openIdResponseHandler;
 
     /**
      * Required parameters for OpenID authentication requests:
@@ -51,23 +56,31 @@
      * Other parameters:
      * 
      * <ul>
-     * <li>state (recommended): Opaque value used to maintain state between the request and the 
-     * callback.</li>
-     * <li>response_mode (optional) : mechanism to be used for returning parameters</li>
-     * <li>nonce (optional): String value used to associate a Client session with an ID Token, 
+     * <li>state (recommended): Opaque value used to maintain state
+     * between the request and the callback.</li>
+     * <li>response_mode (optional) : mechanism to be used for
+     * returning parameters</li>
+     * <li>nonce (optional): String value used to associate a Client
+     * session with an ID Token,
      * and to mitigate replay attacks. </li>
-     * <li>display (optional):  specifies how the Authorization Server displays the authentication 
-     * and consent user interface pages</li>
-     * <li>prompt (optional): specifies if the Authorization Server prompts the End-User 
-     * for reauthentication and consent. Defined values: none, login, consent, select_account </li>
+     * <li>display (optional): specifies how the Authorization Server
+     * displays the authentication and consent user interface
+     * pages. Options: page (default), popup, touch, wap. This
+     * parameter is more relevant for Kalamar. </li>
+     * <li>prompt (optional): specifies if the Authorization Server
+     * prompts the End-User for reauthentication and consent. Defined
+     * values: none, login, consent, select_account </li>
      * <li>max_age (optional): maximum Authentication Age.</li>
-     * <li>ui_locales (optional): preferred languages and scripts for the user interface 
-     * represented as a space-separated list of BCP47 [RFC5646] </li>
-     * <li>id_token_hint (optional): ID Token previously issued by the Authorization Server 
-     * being passed as a hint</li>
-     * <li>login_hint (optional): hint to the Authorization Server about the login identifier 
-     * the End-User might use to log in</li>
-     * <li>acr_values (optional): requested Authentication Context Class Reference values. </li>
+     * <li>ui_locales (optional): preferred languages and scripts for
+     * the user interface represented as a space-separated list of
+     * BCP47 [RFC5646] </li>
+     * <li>id_token_hint (optional): ID Token previously issued by the
+     * Authorization Server being passed as a hint</li>
+     * <li>login_hint (optional): hint to the Authorization Server
+     * about the login identifier the End-User might use to log
+     * in</li>
+     * <li>acr_values (optional): requested Authentication Context
+     * Class Reference values. </li>
      * </ul>
      * 
      * @see OpenID Connect Core 1.0 specification
@@ -87,30 +100,37 @@
             @Context SecurityContext context,
             MultivaluedMap<String, String> form) {
 
-        Map<String, String> map = MapUtils.toMap(form);
-        AuthenticationRequest authRequest = null;
-        try {
-            authRequest = AuthenticationRequest.parse(map);
-        }
-        catch (ParseException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-            return null;
-        }
-
         TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
         String username = tokenContext.getUsername();
 
+        Map<String, String> map = MapUtils.toMap(form);
+        State state = authzService.retrieveState(map);
+        ResponseMode responseMode = authzService.retrieveResponseMode(map);
+
+        boolean isAuthentication = false;
+        if (map.containsKey("scope") && map.get("scope").contains("openid")) {
+            isAuthentication = true;
+        }
+
         URI uri = null;
         try {
-            uri = authzService.requestAuthorizationCode(authRequest, username);
-            // System.out.println(uri.toString());
+            if (isAuthentication) {
+                authzService.checkRedirectUriParam(map);
+            }
+            uri = authzService.requestAuthorizationCode(map, username,
+                    isAuthentication);
+        }
+        catch (ParseException e) {
+            return openIdResponseHandler.createAuthorizationErrorResponse(e,
+                    isAuthentication, state);
         }
         catch (KustvaktException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
+            return openIdResponseHandler.createAuthorizationErrorResponse(e,
+                    isAuthentication, e.getRedirectUri(), state, responseMode);
         }
+
         ResponseBuilder builder = Response.temporaryRedirect(uri);
         return builder.build();
     }
+
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
index 9ef6de4..2d76a18 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
@@ -75,7 +75,7 @@
 public class OAuthController {
 
     @Autowired
-    private CoreResponseHandler kustvaktExceptionHandler;
+    private CoreResponseHandler kustvaktResponseHandler;
     
     private OAuth2Handler handler;
     @Autowired
@@ -160,7 +160,7 @@
             json = JsonUtils.toJSON(Scopes.mapScopes(scopes, data));
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         // json format with scope callback parameter
         // todo: add other scopes as well!
@@ -185,7 +185,7 @@
             return Response.ok(JsonUtils.toJSON(auths)).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -233,7 +233,7 @@
 //                user.addUserData(data);
             }
             catch (KustvaktException e) {
-                throw kustvaktExceptionHandler.throwit(e);
+                throw kustvaktResponseHandler.throwit(e);
             }
 
             // register response according to response_type
@@ -309,7 +309,7 @@
                     this.handler.authorize(codeInfo, user);
                 }
                 catch (KustvaktException e) {
-                    throw kustvaktExceptionHandler.throwit(e);
+                    throw kustvaktResponseHandler.throwit(e);
                 }
                 builder.setParam(OAuth.OAUTH_RESPONSE_TYPE,
                         ResponseType.CODE.toString());
@@ -345,7 +345,7 @@
                                 new_context.getToken());
                     }
                     catch (KustvaktException e) {
-                        throw kustvaktExceptionHandler.throwit(e);
+                        throw kustvaktResponseHandler.throwit(e);
                     }
                 }
                 response = builder.buildBodyMessage();
@@ -530,7 +530,7 @@
                     }
                 }
                 catch (KustvaktException e) {
-                    throw kustvaktExceptionHandler.throwit(e);
+                    throw kustvaktResponseHandler.throwit(e);
                 }
                 // todo: errors for invalid scopes or different scopes then during authorization request?
                 //todo ??
@@ -560,7 +560,7 @@
                             oauthRequest.getPassword(), attr);
                 }
                 catch (KustvaktException e) {
-                    throw kustvaktExceptionHandler.throwit(e);
+                    throw kustvaktResponseHandler.throwit(e);
                 }
 
                 try {
@@ -582,7 +582,7 @@
 
                 }
                 catch (KustvaktException e) {
-                    throw kustvaktExceptionHandler.throwit(e);
+                    throw kustvaktResponseHandler.throwit(e);
                 }
             }
 
@@ -607,7 +607,7 @@
                     builder.setParam(c.getTokenType().displayName(), c.getToken());
                 }
                 catch (KustvaktException e) {
-                    throw kustvaktExceptionHandler.throwit(e);
+                    throw kustvaktResponseHandler.throwit(e);
                 }
             }
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
index c6a3a97..830a76f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
@@ -82,7 +82,7 @@
             LoggerFactory.getLogger(SearchController.class);
 
     @Autowired
-    private CoreResponseHandler kustvaktExceptionHandler;
+    private CoreResponseHandler kustvaktResponseHandler;
     @Autowired
     private SearchKrill searchKrill;
     private ResourceCache resourceHandler;
@@ -215,7 +215,7 @@
             result = graphDBhandler.getResponse("distCollo", "q", query);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok(result).build();
     }
@@ -323,7 +323,7 @@
             cquery.setBaseQuery(ss.toJSON());
         }
         catch (KustvaktException e1) {
-            throw kustvaktExceptionHandler.throwit(e1);
+            throw kustvaktResponseHandler.throwit(e1);
         }
 
         String query = "";
@@ -343,7 +343,7 @@
                 }
             }
             catch (KustvaktException e) {
-                throw kustvaktExceptionHandler.throwit(e);
+                throw kustvaktResponseHandler.throwit(e);
             }
         }
 
@@ -399,7 +399,7 @@
             // jsonld = this.processor.processQuery(jsonld, user);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         jlog.info("Serialized search: {}", jsonld);
 
@@ -434,7 +434,7 @@
         catch (KustvaktException e) {
             jlog.error("Failed retrieving user in the search service: {}",
                     e.string());
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         QuerySerializer serializer = new QuerySerializer();
@@ -451,7 +451,7 @@
             jlog.info("the serialized query {}", query);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         String result = doSearch(eng, query, pageLength, meta);
@@ -496,7 +496,7 @@
             MetaQueryBuilder meta, boolean raw) {
 
         if (raw) {
-            throw kustvaktExceptionHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
+            throw kustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
                     "raw not supported!", null);
         }
 
@@ -510,7 +510,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Failed searching in Neo4J: {}", e.string());
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
     }
@@ -535,7 +535,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered: {}", e.string());
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         VirtualCollection tmp = resourceHandler.getCache(cache.getId(),
@@ -548,7 +548,7 @@
                 cache.setStats(JsonUtils.convertToClass(stats, Map.class));
             }
             catch (KustvaktException e) {
-                throw kustvaktExceptionHandler.throwit(e);
+                throw kustvaktResponseHandler.throwit(e);
             }
             resourceHandler.cache(cache);
         }
@@ -562,7 +562,7 @@
             return Response.ok(JsonUtils.toJSON(vals)).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -599,7 +599,7 @@
         catch (KustvaktException e) {
             jlog.error("Failed getting user in the matchInfo service: {}",
                     e.string());
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         CorpusAccess corpusAccess = user.getCorpusAccess();
@@ -642,7 +642,7 @@
         }
         catch (Exception e) {
             jlog.error("Exception in the MatchInfo service encountered!", e);
-            throw kustvaktExceptionHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
+            throw kustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
                     e.getMessage(), "");
         }
         jlog.debug("MatchInfo results: " + results);
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
index ebce268..65d30bd 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
@@ -44,7 +44,7 @@
     private static Logger jlog =
             LoggerFactory.getLogger(StatisticController.class);
     @Autowired
-    private CoreResponseHandler kustvaktExceptionHandler;
+    private CoreResponseHandler kustvaktResponseHandler;
     @Autowired
     private SearchKrill searchKrill;
 
@@ -68,7 +68,7 @@
             @QueryParam("corpusQuery") String corpusQuery) {
 
         if (corpusQuery == null || corpusQuery.isEmpty()) {
-            throw kustvaktExceptionHandler
+            throw kustvaktResponseHandler
                     .throwit(new KustvaktException(StatusCodes.MISSING_PARAMETER,
                             "Parameter corpusQuery is missing.",
                             "corpusQuery"));
@@ -82,12 +82,12 @@
             json = builder.toJSON();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         String stats = searchKrill.getStatistics(json);
         if (stats.contains("-1"))
-            throw kustvaktExceptionHandler.throwit(StatusCodes.NO_RESULT_FOUND);
+            throw kustvaktResponseHandler.throwit(StatusCodes.NO_RESULT_FOUND);
         jlog.debug("Stats: " + stats);
         return Response.ok(stats).build();
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java
index 2cda085..ed9f97a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserController.java
@@ -52,7 +52,7 @@
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.StringUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.DemoUserFilter;
@@ -71,7 +71,7 @@
 public class UserController {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     private static Logger jlog = LoggerFactory.getLogger(UserController.class);
     @Autowired
@@ -104,7 +104,7 @@
             user = controller.createUserAccount(values, true);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         URIParam uri = user.getField(URIParam.class);
         if (uri.hasValues()) {
@@ -123,13 +123,13 @@
                 return Response.ok(JsonUtils.toJSON(object)).build();
             }
             catch (KustvaktException e) {
-                throw kustvaktExceptionHandler.throwit(e);
+                throw kustvaktResponseHandler.throwit(e);
             }
         }
         else {
             jlog.error("Failed creating confirmation and expiry tokens.");
             // EM: why illegal argument when uri fragment/param is self-generated 
-            throw kustvaktExceptionHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
+            throw kustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
                     "failed to validate uri parameter",
                     "confirmation fragment");
         }
@@ -157,7 +157,7 @@
             //            controller.updateAccount(user);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -169,10 +169,10 @@
     public Response confirmRegistration (@QueryParam("uri") String uritoken,
             @Context Locale locale, @QueryParam("user") String username) {
         if (uritoken == null || uritoken.isEmpty())
-            throw kustvaktExceptionHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
+            throw kustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
                     "parameter missing", "uri parameter");
         if (username == null || username.isEmpty())
-            throw kustvaktExceptionHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
+            throw kustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
                     "parameter missing", "Username");
 
         try {
@@ -180,7 +180,7 @@
         }
         catch (KustvaktException e) {
             e.printStackTrace();
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -198,7 +198,7 @@
             node = JsonUtils.readTree(json);
         }
         catch (KustvaktException e1) {
-            throw kustvaktExceptionHandler.throwit(e1);
+            throw kustvaktResponseHandler.throwit(e1);
         }
         StringBuilder builder = new StringBuilder();
         String username, email;
@@ -227,7 +227,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Eoxception encountered!", e.string());
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         ObjectNode obj = JsonUtils.createObjectNode();
@@ -237,7 +237,7 @@
             return Response.ok(JsonUtils.toJSON(obj)).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -282,7 +282,7 @@
             return Response.ok(m.toEntity()).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -303,7 +303,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered!", e);
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok(result).build();
     }
@@ -339,7 +339,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered!", e);
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         return Response.ok().build();
@@ -365,7 +365,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered: {}", e.string());
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok(result).build();
     }
@@ -396,7 +396,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered!", e);
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -452,13 +452,13 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered!", e);
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         try {
             return Response.ok(JsonUtils.toJSON(add)).build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -475,7 +475,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered!", e);
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -499,7 +499,7 @@
         }
         catch (KustvaktException e) {
             jlog.error("Exception encountered!", e);
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok(queryStr).build();
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index 910225d..ec633fb 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -26,7 +26,7 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.service.UserGroupService;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
@@ -50,7 +50,7 @@
 public class UserGroupController {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
     @Autowired
     private UserGroupService service;
 
@@ -73,7 +73,7 @@
             return service.retrieveUserGroup(context.getUsername());
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -101,7 +101,7 @@
                     context.getUsername(), status);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -123,7 +123,7 @@
             return service.searchById(context.getUsername(), groupId);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
     }
@@ -159,7 +159,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -181,7 +181,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -205,7 +205,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -229,7 +229,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -257,7 +257,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -285,7 +285,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -308,7 +308,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -335,7 +335,7 @@
             return Response.ok().build();
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index b6b1b15..f8198e5 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -28,7 +28,7 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.service.VirtualCorpusService;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
@@ -55,7 +55,7 @@
 public class VirtualCorpusController {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
     @Autowired
     private VirtualCorpusService service;
 
@@ -80,7 +80,7 @@
             service.storeVC(vc, context.getUsername());
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -108,7 +108,7 @@
             service.editVC(vc, context.getUsername());
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -130,7 +130,7 @@
             return service.searchVCById(context.getUsername(), vcId);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -157,7 +157,7 @@
             return service.listVCByUser(context.getUsername(), createdBy);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -178,7 +178,7 @@
             return service.listOwnerVC(context.getUsername());
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -207,7 +207,7 @@
             return service.listVCByType(context.getUsername(), createdBy, type);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -229,7 +229,7 @@
             service.deleteVC(context.getUsername(), vcId);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -254,7 +254,7 @@
             service.shareVC(context.getUsername(), vcId, groupId);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -275,7 +275,7 @@
             service.deleteVCAccess(accessId, context.getUsername());
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
         return Response.ok().build();
     }
@@ -303,7 +303,7 @@
             return service.listVCAccessByVC(context.getUsername(), vcId);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 
@@ -327,7 +327,7 @@
             return service.listVCAccessByGroup(context.getUsername(), groupId);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
index e56831a..7e1a88b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
@@ -27,7 +27,7 @@
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.user.User;
 import de.ids_mannheim.korap.utils.StringUtils;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 
 /**
  * @author hanl, margaretha
@@ -46,7 +46,7 @@
     private AuthenticationManagerIface authManager;
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     @Autowired
     private HttpAuthorizationHandler authorizationHandler;
@@ -62,7 +62,7 @@
             data = authorizationHandler.parseBasicToken(data);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         String host = cr.getHeaderValue(ContainerRequest.HOST);
@@ -82,7 +82,7 @@
             properties.put("user", user);
         }
         catch (KustvaktException e) {
-            throw kustvaktExceptionHandler.throwit(e);
+            throw kustvaktResponseHandler.throwit(e);
         }
 
         TokenContext c = new TokenContext();
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
index 51f8644..bdcc0cf 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
@@ -19,7 +19,7 @@
 import de.ids_mannheim.korap.security.context.KustvaktContext;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.utils.TimeUtils;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 
 /**
  * @author hanl, margaretha
@@ -38,7 +38,7 @@
     private AuthenticationManagerIface authenticationManager;
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     @Override
     public ContainerRequest filter (ContainerRequest request) {
@@ -96,7 +96,7 @@
                 request.setSecurityContext(new KustvaktContext(context));
             }
             catch (KustvaktException e) {
-                throw kustvaktExceptionHandler.throwit(e);
+                throw kustvaktResponseHandler.throwit(e);
             }
         }
         return request;
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
index b1f0e69..8375b62 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
@@ -13,7 +13,7 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.security.context.TokenContext;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 
 /**
  * @author hanl
@@ -27,7 +27,7 @@
 public class BlockingFilter implements ContainerRequestFilter, ResourceFilter {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     @Override
     public ContainerRequest filter (ContainerRequest request) {
@@ -37,12 +37,12 @@
             context = (TokenContext) request.getUserPrincipal();
         }
         catch (UnsupportedOperationException e) {
-            throw kustvaktExceptionHandler.throwit(new KustvaktException(
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
                     StatusCodes.UNSUPPORTED_OPERATION, e.getMessage(), e));
         }
 
         if (context == null || context.isDemo()) {
-            throw kustvaktExceptionHandler.throwit(new KustvaktException(
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
                     StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: guest", "guest"));
         }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java
index eda335a..47cd086 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java
@@ -13,7 +13,7 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.security.context.TokenContext;
-import de.ids_mannheim.korap.web.KustvaktExceptionHandler;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
 
 /**
  * EM: pretty much identical to {@link BlockingFilter}, should be deleted? 
@@ -30,7 +30,7 @@
         implements ContainerRequestFilter, ResourceFilter {
 
     @Autowired
-    private KustvaktExceptionHandler kustvaktExceptionHandler;
+    private KustvaktResponseHandler kustvaktResponseHandler;
 
     @Override
     public ContainerRequest filter (ContainerRequest request) {
@@ -39,12 +39,12 @@
             context = (TokenContext) request.getUserPrincipal();
         }
         catch (UnsupportedOperationException e) {
-            throw kustvaktExceptionHandler.throwit(new KustvaktException(
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
                     StatusCodes.UNSUPPORTED_OPERATION, e.getMessage(), e));
         }
 
         if (context == null || context.isDemo()) {
-            throw kustvaktExceptionHandler.throwit(
+            throw kustvaktResponseHandler.throwit(
                     new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                             "Operation is not permitted for guest users"));
         }
diff --git a/full/src/main/resources/default-config.xml b/full/src/main/resources/default-config.xml
index e3910dc..e06aa8b 100644
--- a/full/src/main/resources/default-config.xml
+++ b/full/src/main/resources/default-config.xml
@@ -192,7 +192,7 @@
 		<constructor-arg ref="kustvakt_db" />
 	</bean>
 
-	<bean id="kustvaktExceptionHandler" class="de.ids_mannheim.korap.web.KustvaktExceptionHandler">
+	<bean id="kustvaktResponseHandler" class="de.ids_mannheim.korap.web.kustvaktResponseHandler">
 		<constructor-arg index="0" name="iface" ref="kustvakt_auditing" />
 	</bean>
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index eda15fc..bd66b74 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -2,7 +2,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
 import java.net.URI;
 
@@ -14,6 +13,8 @@
 import org.apache.oltu.oauth2.common.message.types.TokenType;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
@@ -55,13 +56,17 @@
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("response_type", "code");
         form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        form.add("state", "thisIsMyState");
 
         ClientResponse response = requestAuthorizationConfidentialClient(form);
 
         assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
                 response.getStatus());
         URI redirectUri = response.getLocation();
-        assertTrue(redirectUri.getQuery().startsWith("code"));
+        MultiValueMap<String, String> params = UriComponentsBuilder
+                .fromUri(redirectUri).build().getQueryParams();
+        assertNotNull(params.getFirst("code"));
+        assertEquals("thisIsMyState", params.getFirst("state"));
     }
 
     @Test
@@ -80,7 +85,7 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
                 node.at("/error").asText());
-        assertEquals(redirectUri + " is unknown",
+        assertEquals("Invalid redirect URI",
                 node.at("/error_description").asText());
     }
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
index da84a06..fc35235 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
@@ -1,7 +1,7 @@
 package de.ids_mannheim.korap.web.controller;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
 
 import java.net.URI;
 
@@ -10,7 +10,10 @@
 import org.apache.http.entity.ContentType;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
 import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
@@ -21,26 +24,20 @@
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
+import de.ids_mannheim.korap.utils.JsonUtils;
 
 public class OAuth2OpenIdControllerTest extends SpringJerseyTest {
 
     @Autowired
     private HttpAuthorizationHandler handler;
 
-    @Test
-    public void testAuthorize () throws UniformInterfaceException,
-            ClientHandlerException, KustvaktException {
+    private String redirectUri =
+            "https://korap.ids-mannheim.de/confidential/redirect";
 
-        String redirectUri =
-                "https://korap.ids-mannheim.de/confidential/redirect";
-        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("response_type", "code");
-        form.add("scope", "openid");
-        form.add("redirect_uri", redirectUri);
-        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
-
-        ClientResponse response = resource().path("oauth2").path("openid")
-                .path("authorize")
+    private ClientResponse sendAuthorizationRequest (
+            MultivaluedMap<String, String> form) throws KustvaktException {
+        return resource().path("oauth2").path("openid").path("authorize")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue("dory",
                                 "password"))
@@ -48,12 +45,134 @@
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .entity(form).post(ClientResponse.class);
-
-        URI location = response.getLocation();
-
-        assertEquals(redirectUri, location.getScheme() + "://"
-                + location.getHost() + location.getPath());
-        assertTrue(location.getQuery().startsWith("code"));
     }
 
+    @Test
+    public void testRequestAuthorizationCode ()
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("response_type", "code");
+        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+
+        testRequestAuthorizationCodeWithoutOpenID(form, redirectUri);
+        form.add("scope", "openid");
+
+        testRequestAuthorizationCodeMissingRedirectUri(form);
+        testRequestAuthorizationCodeInvalidRedirectUri(form);
+        form.add("redirect_uri", redirectUri);
+
+        form.add("state", "thisIsMyState");
+
+        ClientResponse response = sendAuthorizationRequest(form);
+        URI location = response.getLocation();
+        assertEquals(redirectUri, location.getScheme() + "://"
+                + location.getHost() + location.getPath());
+
+        MultiValueMap<String, String> params =
+                UriComponentsBuilder.fromUri(location).build().getQueryParams();
+        assertNotNull(params.getFirst("code"));
+        assertEquals("thisIsMyState", params.getFirst("state"));
+    }
+
+    private void testRequestAuthorizationCodeWithoutOpenID (
+            MultivaluedMap<String, String> form, String redirectUri)
+            throws KustvaktException {
+        ClientResponse response = sendAuthorizationRequest(form);
+        URI location = response.getLocation();
+        // System.out.println(location.toString());
+        assertEquals(redirectUri, location.getScheme() + "://"
+                + location.getHost() + location.getPath());
+    }
+
+    private void testRequestAuthorizationCodeMissingRedirectUri (
+            MultivaluedMap<String, String> form) throws KustvaktException {
+        ClientResponse response = sendAuthorizationRequest(form);
+        String entity = response.getEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
+        assertEquals("redirect_uri is required",
+                node.at("/error_description").asText());
+    }
+
+    private void testRequestAuthorizationCodeInvalidRedirectUri (
+            MultivaluedMap<String, String> form) throws KustvaktException {
+        form.add("redirect_uri", "blah");
+        ClientResponse response = sendAuthorizationRequest(form);
+        String entity = response.getEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
+        assertEquals("Invalid redirect URI",
+                node.at("/error_description").asText());
+
+        form.remove("redirect_uri");
+    }
+
+    @Test
+    public void testRequestAuthorizationCodeMissingClientID ()
+            throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("scope", "openid");
+        form.add("redirect_uri", redirectUri);
+
+        // error response is represented in JSON because redirect URI
+        // cannot be verified without client id
+        // Besides client_id is a mandatory parameter in a normal
+        // OAuth2 authorization request, thus it is checked first,
+        // before redirect_uri. see
+        // com.nimbusds.oauth2.sdk.AuthorizationRequest
+
+        ClientResponse response = sendAuthorizationRequest(form);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
+        assertEquals("Invalid request: Missing \"client_id\" parameter",
+                node.at("/error_description").asText());
+
+    }
+
+    @Test
+    public void testRequestAuthorizationCodeMissingResponseType ()
+            throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("scope", "openid");
+        form.add("redirect_uri", redirectUri);
+        form.add("client_id", "blah");
+
+        // client_id has not been verified yet
+        // MUST NOT automatically redirect the user-agent to the
+        // invalid redirection URI.
+
+        ClientResponse response = sendAuthorizationRequest(form);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(OAuth2Error.INVALID_REQUEST, node.at("/error").asText());
+        assertEquals("Invalid request: Missing \"response_type\" parameter",
+                node.at("/error_description").asText());
+    }
+
+    @Test
+    public void testRequestAuthorizationCodeUnsupportedResponseType ()
+            throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("scope", "openid");
+        form.add("redirect_uri", redirectUri);
+        // we don't support implicit grant
+        form.add("response_type", "id_token token");
+        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        form.add("nonce", "nonce");
+
+        ClientResponse response = sendAuthorizationRequest(form);
+        URI location = response.getLocation();
+        // System.out.println(location);
+
+        MultiValueMap<String, String> params =
+                UriComponentsBuilder.fromUri(location).build().getQueryParams();
+        assertEquals("invalid_request", params.getFirst("error"));
+        assertEquals("unsupported+response_type%3A+id_token",
+                params.getFirst("error_description"));
+    }
 }
diff --git a/full/src/test/resources/test-config.xml b/full/src/test/resources/test-config.xml
index 75edbc4..6094435 100644
--- a/full/src/test/resources/test-config.xml
+++ b/full/src/test/resources/test-config.xml
@@ -193,7 +193,7 @@
 		<constructor-arg ref="kustvakt_db" />
 	</bean>
 
-	<bean id="kustvaktExceptionHandler" class="de.ids_mannheim.korap.web.KustvaktExceptionHandler">
+	<bean id="kustvaktResponseHandler" class="de.ids_mannheim.korap.web.KustvaktResponseHandler">
 		<constructor-arg index="0" name="iface" ref="kustvakt_auditing" />
 	</bean>