Improved AuthenticationException.

Change-Id: I5c621a1da2df73b3dfb864771d2e9c03bf64942f
diff --git a/core/src/main/java/de/ids_mannheim/korap/config/Attributes.java b/core/src/main/java/de/ids_mannheim/korap/config/Attributes.java
index a43e2f3..03cea54 100644
--- a/core/src/main/java/de/ids_mannheim/korap/config/Attributes.java
+++ b/core/src/main/java/de/ids_mannheim/korap/config/Attributes.java
@@ -4,11 +4,11 @@
 
 	// EM: Use enum for the authentication types
     public static final String AUTHORIZATION = "Authorization";
-    public static final String SESSION_AUTHENTICATION = "session_token";
-    public static final String API_AUTHENTICATION = "api_token";
-    public static final String OAUTH2_AUTHORIZATION = "bearer";
-    public static final String OPENID_AUTHENTICATION = "id_token";
-    public static final String BASIC_AUTHENTICATION = "basic";
+//    public static final String SESSION_AUTHENTICATION = "session_token";
+//    public static final String API_AUTHENTICATION = "api_token";
+//    public static final String OAUTH2_AUTHORIZATION = "bearer";
+//    public static final String OPENID_AUTHENTICATION = "id_token";
+//    public static final String BASIC_AUTHENTICATION = "basic";
 
     public static final String LOCATION = "location"; // location of Client: User.INTERN/EXTERN
     public static final String CORPUS_ACCESS = "corpusAccess"; // User.ALL/PUB/FREE.
@@ -62,7 +62,7 @@
      * token context
      */
     public static final String TOKEN = "token";
-    public static final String AUTHENTICATION_TYPE = "authenticationType";
+    public static final String TOKEN_TYPE = "token_type";
     public static final String TOKEN_EXPIRATION = "expires";
     public static final String TOKEN_CREATION = "tokenCreated";
     public static final String USER_AGENT = "User-Agent";
diff --git a/core/src/main/java/de/ids_mannheim/korap/user/TokenContext.java b/core/src/main/java/de/ids_mannheim/korap/user/TokenContext.java
index 16c2b2f..53dd34e 100644
--- a/core/src/main/java/de/ids_mannheim/korap/user/TokenContext.java
+++ b/core/src/main/java/de/ids_mannheim/korap/user/TokenContext.java
@@ -58,7 +58,7 @@
         m.put(Attributes.TOKEN_EXPIRATION,
                 TimeUtils.format(this.expirationTime));
         m.put(Attributes.TOKEN, this.token);
-        m.put(Attributes.AUTHENTICATION_TYPE, this.authenticationType);
+        m.put(Attributes.TOKEN_TYPE, this.authenticationType);
         return m;
     }
 
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java b/core/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java
index 6c88743..298ee82 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java
@@ -7,6 +7,7 @@
 import javax.ws.rs.core.Response;
 
 import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.config.AuthenticationType;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.interfaces.db.AuditingIface;
@@ -78,21 +79,31 @@
     }
 
 
+//    public WebApplicationException throwAuthenticationException (String message){
+//        return throwAuthenticationException(message, AuthenticationType.BASIC);
+//    }
+    
     //todo:  if exception, make exception message and error code available if not masked!
-    public WebApplicationException throwAuthenticationException (String message) {
+    public WebApplicationException throwAuthenticationException (String message, AuthenticationType authType) {
         return new WebApplicationException(Response
                 .status(Response.Status.UNAUTHORIZED)
                 .header(HttpHeaders.WWW_AUTHENTICATE,
-                        "Basic realm=Kustvakt")
+                        authType.name()+" realm=\"Kustvakt\"")
                 .entity(buildNotification(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
                         "Unauthorized access", message)).build());
     }
 
-    public WebApplicationException throwAuthenticationException (KustvaktException e) {
+    public WebApplicationException throwAuthenticationException (KustvaktException e, 
+            AuthenticationType authType) {
+       return throwAuthenticationException(e, authType.name());
+    }
+    
+    public WebApplicationException throwAuthenticationException (KustvaktException e, 
+            String authType) {
         return new WebApplicationException(Response
                 .status(Response.Status.UNAUTHORIZED)
                 .header(HttpHeaders.WWW_AUTHENTICATE,
-                        "Basic realm=Kustvakt")
+                        authType+" realm=\"Kustvakt\"")
                 .entity(buildNotification(e.getStatusCode(),
                         e.getMessage(), e.getEntity())).build());
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/framework/AuthorizationData.java b/full/src/main/java/de/ids_mannheim/korap/authentication/framework/AuthorizationData.java
index 8f310a6..13715a7 100644
--- a/full/src/main/java/de/ids_mannheim/korap/authentication/framework/AuthorizationData.java
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/framework/AuthorizationData.java
@@ -10,5 +10,7 @@
 
     private String token;
     private AuthenticationType authenticationType;
+    private String username;
+    private String password;
 
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/framework/HttpAuthorizationHandler.java b/full/src/main/java/de/ids_mannheim/korap/authentication/framework/HttpAuthorizationHandler.java
index b5851e3..22bd096 100644
--- a/full/src/main/java/de/ids_mannheim/korap/authentication/framework/HttpAuthorizationHandler.java
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/framework/HttpAuthorizationHandler.java
@@ -3,8 +3,6 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import com.sun.jersey.api.client.ClientResponse.Status;
-
 import de.ids_mannheim.korap.config.AuthenticationType;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
@@ -53,7 +51,13 @@
         return data;
     }
 
-    public void parseToken (AuthorizationData data) throws KustvaktException {
-        String[] credentials = transferEncoding.decodeBase64(data.getToken());        
+    public AuthorizationData parseToken (AuthorizationData data) throws KustvaktException {
+        String[] credentials = transferEncoding.decodeBase64(data.getToken());
+        data.setUsername(credentials[0]);
+        data.setPassword(credentials[1]);
+        return data;
     }
+    
+    
+    
 }
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 bc4f0c9..802a5b2 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
@@ -29,7 +29,6 @@
 
 import de.ids_mannheim.korap.authentication.framework.AuthorizationData;
 import de.ids_mannheim.korap.authentication.framework.HttpAuthorizationHandler;
-import de.ids_mannheim.korap.authentication.framework.TransferEncoding;
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.AuthenticationType;
 import de.ids_mannheim.korap.config.BeansFactory;
@@ -65,9 +64,6 @@
     @Autowired
     private HttpAuthorizationHandler authorizationHandler;
 
-    @Autowired
-    private TransferEncoding transferEncoding;
-    
     private static Boolean DEBUG_LOG = true;
 
     //todo: bootstrap function to transmit certain default configuration settings and examples (example user queries,
@@ -147,11 +143,11 @@
                             "Authorization header"));
         }
         
-        String[] values;
+        AuthorizationData authorizationData;
         try {
-            AuthorizationData authorizationData = authorizationHandler.
+            authorizationData = authorizationHandler.
                     parseAuthorizationHeader(auth.get(0));
-            values = transferEncoding.decodeBase64(authorizationData.getToken());
+            authorizationData = authorizationHandler.parseToken(authorizationData);
            
         }
         catch (KustvaktException e) {
@@ -193,12 +189,10 @@
 //                    secCtx.isSecure() ? "yes" : "no");
         } // DEBUG_LOG        
 
-        // "Invalid syntax for username and password"
-        if (values == null)
-            throw kustvaktResponseHandler.throwit(StatusCodes.ACCESS_DENIED);
-
-        if (values[0].equalsIgnoreCase("null")
-                | values[1].equalsIgnoreCase("null"))
+        if (authorizationData.getUsername() == null || 
+                authorizationData.getUsername().isEmpty() || 
+                authorizationData.getPassword()== null || 
+                authorizationData.getPassword().isEmpty())
             // is actual an invalid request
             throw kustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
 
@@ -212,7 +206,7 @@
         try {
             // User user = controller.authenticate(0, values[0], values[1], attr); Implementation by Hanl
             User user = controller.authenticate(AuthenticationType.LDAP,
-                    values[0], values[1], attr); // Implementation with IdM/LDAP
+                    authorizationData.getUsername(), authorizationData.getPassword(), attr); // Implementation with IdM/LDAP
             // Userdata data = this.controller.getUserData(user, UserDetails.class); // Implem. by Hanl
             // todo: is this necessary?
             //            attr.putAll(data.fields());
@@ -269,28 +263,25 @@
         List<String> auth =
                 headers.getRequestHeader(ContainerRequest.AUTHORIZATION);
 
-        String[] values;
+        AuthorizationData authorizationData;
         try {
-            AuthorizationData authorizationData = authorizationHandler.
+            authorizationData = authorizationHandler.
                     parseAuthorizationHeader(auth.get(0));
-            values = transferEncoding.decodeBase64(authorizationData.getToken());
+            authorizationData = authorizationHandler.parseToken(authorizationData);
            
         }
         catch (KustvaktException e) {
             throw kustvaktResponseHandler.throwit(e);
         }
-        //        authentication = StringUtils.stripTokenType(authentication);
-        //        String[] values = new String(
-        //                DatatypeConverter.parseBase64Binary(authentication)).split(":");
-        //        String[] values = Base64.base64Decode(authentication).split(":");
-
-        // "Invalid syntax for username and password"
 
         // Implementation Hanl mit '|'. 16.02.17/FB
         //if (values[0].equalsIgnoreCase("null")
         //        | values[1].equalsIgnoreCase("null"))
-        if (values[0].equalsIgnoreCase("null")
-                || values[1].equalsIgnoreCase("null"))
+        if (authorizationData.getUsername() == null || 
+                authorizationData.getUsername().isEmpty() || 
+                authorizationData.getPassword()== null || 
+                authorizationData.getPassword().isEmpty())
+            // is actual an invalid request
             throw kustvaktResponseHandler.throwit(StatusCodes.REQUEST_INVALID);
 
         Map<String, Object> attr = new HashMap<>();
@@ -300,7 +291,7 @@
         String contextJson;
         try {
             User user = controller.authenticate(AuthenticationType.SESSION,
-                    values[0], values[1], attr);
+                    authorizationData.getUsername(), authorizationData.getPassword(), attr);
             context = controller.createTokenContext(user, attr,
                     AuthenticationType.SESSION);
             contextJson = context.toJson();
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 34b1875..bffe9d8 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
@@ -279,7 +279,7 @@
         }
         catch (KustvaktException e) {
             throw kustvaktResponseHandler
-                    .throwAuthenticationException(ctx.getUsername());
+                    .throwAuthenticationException(ctx.getUsername(), ctx.getAuthenticationType());
         }
         try {
             return Response.ok(m.toEntity()).build();
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 67ff04a..12e24cd 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
@@ -8,6 +8,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import com.ctc.wstx.util.StringUtil;
 import com.sun.jersey.spi.container.ContainerRequest;
 import com.sun.jersey.spi.container.ContainerRequestFilter;
 import com.sun.jersey.spi.container.ContainerResponseFilter;
@@ -22,6 +23,7 @@
 import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
 import de.ids_mannheim.korap.user.TokenContext;
 import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.StringUtils;
 import de.ids_mannheim.korap.web.utils.KustvaktContext;
 import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
 
@@ -51,13 +53,13 @@
                 cr.getHeaderValue(ContainerRequest.AUTHORIZATION);
         
         AuthorizationData data;
-        String[] userData;
         try {
             data = authorizationHandler.parseAuthorizationHeader(authorization);
-            userData = transferEncoding.decodeBase64(data.getToken());
+            data = authorizationHandler.parseToken(data);
         }
         catch (KustvaktException e) {
-            throw kustvaktResponseHandler.throwAuthenticationException(e);
+            String authType = StringUtils.stripTokenType(authorization);
+            throw kustvaktResponseHandler.throwAuthenticationException(e, authType);
         }
         
         String host = cr.getHeaderValue(ContainerRequest.HOST);
@@ -68,20 +70,20 @@
         try {
             // EM: fix me: AuthenticationType based on header value
             User user = authManager.authenticate(data.getAuthenticationType(),
-                    userData[0], userData[0], attributes);
+                    data.getUsername(), data.getPassword(), attributes);
             if (!user.isAdmin()) {
                 throw kustvaktResponseHandler.throwAuthenticationException(
-                        "Admin authentication failed.");
+                        "Admin authentication failed.", data.getAuthenticationType());
             }
             Map<String, Object> properties = cr.getProperties();
             properties.put("user", user);
         }
         catch (KustvaktException e) {
-            throw kustvaktResponseHandler.throwAuthenticationException(e);
+            throw kustvaktResponseHandler.throwAuthenticationException(e, data.getAuthenticationType());
         }
 
         TokenContext c = new TokenContext();
-        c.setUsername(userData[0]);
+        c.setUsername(data.getUsername());
         c.setAuthenticationType(data.getAuthenticationType());
         // EM: is this secure? Is token context not sent outside Kustvakt?
         c.setToken(data.getToken());
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 61f92e7..2f57c6c 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
@@ -13,8 +13,10 @@
 import de.ids_mannheim.korap.authentication.framework.AuthorizationData;
 import de.ids_mannheim.korap.authentication.framework.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
 import de.ids_mannheim.korap.user.TokenContext;
+import de.ids_mannheim.korap.utils.StringUtils;
 import de.ids_mannheim.korap.web.utils.KustvaktContext;
 import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
 
@@ -53,16 +55,18 @@
 
         if (authorization != null && !authorization.isEmpty()) {
             TokenContext context;
+            AuthorizationData authData;
             try {
-                AuthorizationData data = authorizationHandler
+                authData = authorizationHandler
                         .parseAuthorizationHeader(authorization);
                 context = userController.getTokenStatus(
-                        data.getAuthenticationType(), data.getToken(), host,
+                        authData.getAuthenticationType(), authData.getToken(), host,
                         ua);
             }
             catch (KustvaktException e) {
+                String authType = StringUtils.stripTokenType(authorization);
                 throw kustvaktResponseHandler
-                        .throwAuthenticationException(authorization);
+                        .throwAuthenticationException(e, authType);
             }
             // fixme: give reason why access is not granted?
             if (context != null && context.isValid()
@@ -70,7 +74,9 @@
                             | !context.isSecureRequired()))
                 request.setSecurityContext(new KustvaktContext(context));
             else
-                throw kustvaktResponseHandler.throwAuthenticationException("");
+                throw kustvaktResponseHandler.throwAuthenticationException(
+                        new KustvaktException(StatusCodes.UNAUTHORIZED_OPERATION), 
+                        authData.getAuthenticationType());
         }
         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 b8faf76..e0d1c02 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
@@ -4,6 +4,9 @@
 import com.sun.jersey.spi.container.ContainerRequestFilter;
 import com.sun.jersey.spi.container.ContainerResponseFilter;
 import com.sun.jersey.spi.container.ResourceFilter;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.user.TokenContext;
 import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
 
@@ -25,19 +28,27 @@
 
     @Autowired
     KustvaktResponseHandler kustvaktResponseHandler;
-    
+
     @Override
     public ContainerRequest filter (ContainerRequest request) {
         TokenContext context;
+
         try {
             context = (TokenContext) request.getUserPrincipal();
         }
         catch (UnsupportedOperationException e) {
-            throw kustvaktResponseHandler.throwAuthenticationException("");
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
+                    StatusCodes.UNAUTHORIZED_OPERATION, e.getMessage(), e));
         }
 
-        if(context == null || context.isDemo())
-            throw kustvaktResponseHandler.throwAuthenticationException("");
+        if (context == null || context.isDemo()) {
+            throw kustvaktResponseHandler.throwit(
+                    new KustvaktException(StatusCodes.UNAUTHORIZED_OPERATION,
+                            "Operation is not permitted for user: "
+                                    + context.getUsername(),
+                            context.getUsername()));
+        }
+
 
         return request;
     }
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 95cb077..e5fa7f1 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
@@ -4,6 +4,9 @@
 import com.sun.jersey.spi.container.ContainerRequestFilter;
 import com.sun.jersey.spi.container.ContainerResponseFilter;
 import com.sun.jersey.spi.container.ResourceFilter;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.user.TokenContext;
 import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
 
@@ -34,13 +37,14 @@
             context = (TokenContext) request.getUserPrincipal();
         }
         catch (UnsupportedOperationException e) {
-            throw kustvaktResponseHandler.throwAuthenticationException("");
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
+                    StatusCodes.UNAUTHORIZED_OPERATION, e.getMessage(), e));
         }
 
-        if (context == null || context.isDemo())
-            throw kustvaktResponseHandler
-                    .throwAuthenticationException("Service not available for non-authenticated "
-                            + "or demo account users!");
+        if (context == null || context.isDemo()){
+            new KustvaktException(StatusCodes.UNAUTHORIZED_OPERATION,
+                    "Operation is not permitted for guest users");
+        }
         return request;
     }
 
diff --git a/full/src/main/resources/log4j.properties b/full/src/main/resources/log4j.properties
index 35aad91..308c9ca 100644
--- a/full/src/main/resources/log4j.properties
+++ b/full/src/main/resources/log4j.properties
@@ -4,8 +4,9 @@
 log4j.rootLogger=ERROR, stdout, debugLog
 log4j.logger.log=ERROR, errorLog
 
-log4j.logger.de.ids_mannheim.korap.service.VirtualCorpusService = error, debugLog
-log4j.logger.de.ids_mannheim.korap.web.SearchKrill = debug, debugLog, stdout
+#log4j.logger.de.ids_mannheim.korap.service.VirtualCorpusService = error, debugLog
+#log4j.logger.de.ids_mannheim.korap.web.controller.AuthenticationController = debug, debugLog, stdout
+
 
 # Direct log messages to stdout
 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/service/full/VirtualCorpusServiceTest.java b/full/src/test/java/de/ids_mannheim/korap/web/service/full/VirtualCorpusServiceTest.java
index 1a26e1c..71a425a 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/service/full/VirtualCorpusServiceTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/service/full/VirtualCorpusServiceTest.java
@@ -3,13 +3,17 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
 import org.eclipse.jetty.http.HttpHeaders;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.spi.container.ContainerRequest;
 
 import de.ids_mannheim.korap.authentication.framework.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
@@ -25,7 +29,7 @@
     HttpAuthorizationHandler handler;
     
     @Test
-    @Ignore
+//    @Ignore
     public void testStoreVC () throws KustvaktException {
         String json =
                 "{\"name\": \"new vc\",\"type\": \"PRIVATE\",\"createdBy\": "
@@ -49,8 +53,17 @@
 
         ClientResponse response = resource().path("vc").path("store")
                 .entity(json).post(ClientResponse.class);
+        
+        Set<Entry<String, List<String>>> headers = response.getHeaders().entrySet();
+        
+        for (Entry<String, List<String>> header: headers){
+            if (header.getKey().equals(ContainerRequest.WWW_AUTHENTICATE)){
+                assertEquals("Basic realm=\"Kustvakt\"", header.getValue().get(0));
+            }
+        }
+//        System.out.println(header);
+        
         String entity = response.getEntity(String.class);
-
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.UNAUTHORIZED_OPERATION,
                 node.at("/errors/0/0").asInt());