Implemented signed OpenID token with default algorithm RSA256.

Change-Id: I5bd5bed5c556e550244e06299bc7e54f53226401
diff --git a/full/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java b/full/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java
index 82bab07..962a14f 100644
--- a/full/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/authentication/APIAuthenticationTest.java
@@ -12,7 +12,7 @@
 import com.nimbusds.jose.JOSEException;
 
 import de.ids_mannheim.korap.config.Attributes;
-import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.constant.TokenType;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -23,7 +23,7 @@
 public class APIAuthenticationTest extends SpringJerseyTest {
 
     @Autowired
-    private KustvaktConfiguration config;
+    private FullConfiguration config;
 
     @Test
     public void testCreateGetTokenContext () throws KustvaktException,
diff --git a/full/src/test/java/de/ids_mannheim/korap/config/ConfigTest.java b/full/src/test/java/de/ids_mannheim/korap/config/ConfigTest.java
index ac3a131..ed263c0 100644
--- a/full/src/test/java/de/ids_mannheim/korap/config/ConfigTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/config/ConfigTest.java
@@ -6,6 +6,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URISyntaxException;
 import java.util.Map;
 import java.util.Properties;
 
@@ -59,7 +60,7 @@
 
     @Test(expected = KustvaktException.class)
     @Ignore
-    public void testBeanOverrideInjection () throws KustvaktException {
+    public void testBeanOverrideInjection () throws KustvaktException, URISyntaxException {
         helper().getContext()
                 .getConfiguration()
                 .setPropertiesAsStream(
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 c4156d6..6bd081a 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
@@ -2,14 +2,29 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URI;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.text.ParseException;
+import java.util.Date;
 
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 
+import org.apache.commons.codec.binary.Base64;
 import org.apache.http.entity.ContentType;
 import org.apache.oltu.oauth2.common.message.types.TokenType;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.MultiValueMap;
@@ -17,6 +32,10 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jwt.SignedJWT;
 import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.UniformInterfaceException;
@@ -24,6 +43,7 @@
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
@@ -33,24 +53,40 @@
 
     @Autowired
     private HttpAuthorizationHandler handler;
+    @Autowired
+    private FullConfiguration config;
 
     private String redirectUri =
             "https://korap.ids-mannheim.de/confidential/redirect";
+    private String username = "dory";
+
+    private static String publicKey;
+
+    @BeforeClass
+    public static void init () throws IOException {
+        InputStream is = OAuth2OpenIdControllerTest.class.getClassLoader()
+                .getResourceAsStream("kustvakt-public.key");
+
+        try (BufferedReader reader =
+                new BufferedReader(new InputStreamReader(is));) {
+            publicKey = reader.readLine();
+        }
+    }
 
     private ClientResponse sendAuthorizationRequest (
             MultivaluedMap<String, String> form) throws KustvaktException {
         return resource().path("oauth2").path("openid").path("authorize")
                 .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
+                        handler.createBasicAuthorizationHeaderValue(username,
                                 "password"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .entity(form).post(ClientResponse.class);
     }
-    
-    private ClientResponse sendTokenRequest (MultivaluedMap<String, String> form)
-            throws KustvaktException {
+
+    private ClientResponse sendTokenRequest (
+            MultivaluedMap<String, String> form) throws KustvaktException {
         return resource().path("oauth2").path("openid").path("token")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE,
@@ -165,33 +201,86 @@
                 node.at("/error_description").asText());
     }
 
-    @Test
-    public void testRequestAuthorizationCodeUnsupportedResponseType ()
+    private void testRequestAuthorizationCodeUnsupportedResponseType (
+            MultivaluedMap<String, String> form, String type)
             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);
+        System.out.println(response.getEntity(String.class));
         URI location = response.getLocation();
-        assertEquals(MediaType.APPLICATION_FORM_URLENCODED, response.getType().toString());
+        assertEquals(MediaType.APPLICATION_FORM_URLENCODED,
+                response.getType().toString());
 
         MultiValueMap<String, String> params =
                 UriComponentsBuilder.fromUri(location).build().getQueryParams();
         assertEquals("invalid_request", params.getFirst("error"));
-        assertEquals("unsupported+response_type%3A+id_token",
+        assertEquals("unsupported+response_type%3A+" + type,
                 params.getFirst("error_description"));
     }
-    
+
+    /**
+     * We don't support implicit grant. Implicit grant allows
+     * response_type:
+     * <ul>
+     * <li>id_token</li>
+     * <li>id_token token</li>
+     * </ul>
+     * 
+     * @throws KustvaktException
+     */
     @Test
-    public void testRequestAccessToken () throws KustvaktException {
+    public void testRequestAuthorizationCodeUnsupportedImplicitFlow ()
+            throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("scope", "openid");
+        form.add("redirect_uri", redirectUri);
+        form.add("response_type", "id_token");
+        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        form.add("nonce", "nonce");
+
+        testRequestAuthorizationCodeUnsupportedResponseType(form, "id_token");
+
+        form.remove("response_type");
+        form.add("response_type", "id_token token");
+        testRequestAuthorizationCodeUnsupportedResponseType(form, "id_token");
+    }
+
+    /**
+     * Hybrid flow is not supported. Hybrid flow allows
+     * response_type:
+     * <ul>
+     * <li>code id_token</li>
+     * <li>code token</li>
+     * <li>code id_token token</li>
+     * </ul>
+     * 
+     * @throws KustvaktException
+     */
+
+    @Test
+    public void testRequestAuthorizationCodeUnsupportedHybridFlow ()
+            throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("scope", "openid");
+        form.add("redirect_uri", redirectUri);
+        form.add("response_type", "code id_token");
+        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        form.add("nonce", "nonce");
+        testRequestAuthorizationCodeUnsupportedResponseType(form, "id_token");
+
+        form.remove("response_type");
+        form.add("response_type", "code token");
+        testRequestAuthorizationCodeUnsupportedResponseType(form, "token");
+    }
+
+    @Test
+    public void testRequestAccessToken ()
+            throws KustvaktException, ParseException, InvalidKeySpecException,
+            NoSuchAlgorithmException, JOSEException {
+        String client_id = "fCBbQkAyYzI4NzUxMg";
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("response_type", "code");
-        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        form.add("client_id", client_id);
         form.add("redirect_uri", redirectUri);
         form.add("scope", "openid");
         form.add("state", "thisIsMyState");
@@ -201,17 +290,17 @@
         MultiValueMap<String, String> params =
                 UriComponentsBuilder.fromUri(location).build().getQueryParams();
         String code = params.getFirst("code");
-        
+
         MultivaluedMap<String, String> tokenForm = new MultivaluedMapImpl();
         tokenForm.add("grant_type", "authorization_code");
         tokenForm.add("redirect_uri", redirectUri);
-        tokenForm.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        tokenForm.add("client_id", client_id);
         tokenForm.add("client_secret", "secret");
         tokenForm.add("code", code);
-        
+
         ClientResponse tokenResponse = sendTokenRequest(tokenForm);
         String entity = tokenResponse.getEntity(String.class);
-//        System.out.println(entity);
+        // System.out.println(entity);
 
         JsonNode node = JsonUtils.readTree(entity);
         assertNotNull(node.at("/access_token").asText());
@@ -219,7 +308,30 @@
         assertEquals(TokenType.BEARER.toString(),
                 node.at("/token_type").asText());
         assertNotNull(node.at("/expires_in").asText());
-        assertNotNull(node.at("/id_token").asText());
+        String id_token = node.at("/id_token").asText();
+        assertNotNull(id_token);
 
+        verifyingIdToken(id_token, username, client_id);
+    }
+
+    private void verifyingIdToken (String id_token, String username,
+            String client_id) throws ParseException, InvalidKeySpecException,
+            NoSuchAlgorithmException, JOSEException {
+        byte[] decodedPuk = Base64.decodeBase64(publicKey);
+        KeySpec keySpec = new X509EncodedKeySpec(decodedPuk);
+        RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
+                .generatePublic(keySpec);
+
+        SignedJWT signedJWT = SignedJWT.parse(id_token);
+        JWSVerifier verifier = new RSASSAVerifier(publicKey);
+        assertTrue(signedJWT.verify(verifier));
+
+        assertEquals(client_id,
+                signedJWT.getJWTClaimsSet().getAudience().get(0));
+        assertEquals(username, signedJWT.getJWTClaimsSet().getSubject());
+        assertEquals(config.getIssuerURI().toString(),
+                signedJWT.getJWTClaimsSet().getIssuer());
+        assertTrue(new Date()
+                .before(signedJWT.getJWTClaimsSet().getExpirationTime()));
     }
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
index 68f1b55..f791244 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
@@ -29,6 +29,7 @@
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.BeansFactory;
+import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.config.JWTSigner;
 import de.ids_mannheim.korap.config.TestHelper;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -49,7 +50,8 @@
 
     @Autowired
     HttpAuthorizationHandler handler;
-    
+    @Autowired
+    FullConfiguration config;
 	private static String[] credentials;
 
 	@Override
@@ -201,8 +203,8 @@
 		String token = node.path("token").asText();
 
 		JWTSigner sign = new JWTSigner(BeansFactory.getKustvaktContext().getConfiguration().getSharedSecret(),
-				BeansFactory.getKustvaktContext().getConfiguration().getIssuer(), -1);
-
+				config.getIssuer(), -1);
+		        //BeansFactory.getKustvaktContext().getConfiguration().getIssuer(), -1);
 		SignedJWT jwt = sign.verifyToken(token);
 
 		while (true) {
diff --git a/full/src/test/resources/kustvakt-private.key b/full/src/test/resources/kustvakt-private.key
new file mode 100644
index 0000000..d419b89
--- /dev/null
+++ b/full/src/test/resources/kustvakt-private.key
@@ -0,0 +1 @@
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAIlVfcPe+PXGph6BX1zU9HQ1kSt0lz2LIGAB+krHcj5oaWeS/4xicvmmGRE5MeJQEMIcijl3OXjdZR7lK1dxn1UUHuZa3ijMnMgDcQz9BuGg+49R5KdSkkMwlVW5Bdt08TmU9teFdQpg+7bsVGKpSuW6yE6wkgo+Wwufw23ULNkjAgMBAAECgYBVq8o3zTm7gH+SmhwWOhaBBAWaeTH7x3WbzsAHtCG1gsb2QMJAHg4hZJdQokBXMKEzpkAoFxL4Lgxt2IJQG2ZL778uiQiy+xHI8VTXBNXmdo+F3hlNzEmJySSSCxYefSSv+DN/yBrOx0heGXR3vbefXey4a6q8RhthCuRfpHmqmQJBALyFdf4Oj4rozi/KI8yiD71+NNR7hHMtepn3YyY0zBXxk2YEwpcPkzBhdDiL6fYJjjoGFnqKLNqlgO8gHx+ET70CQQC6faQiLjUp50wbEAZqLY7Q353k2qTdAX8W9L2lF/79GEA+EJumQ2iWOu9qYqQuSMSKwheY6mdOVWj8yOMiu2pfAkEAll0cr3aNpw3o5tUjmKPqSgnPuWqLShKMJyHaQy75WMdF+ajyS+pwS7ZvLGrsQQF+H2mbpEFxZTN8kz3blRfDQQJBAKADPdm2HBegRkTSMy7XeDrwI+JBWEPpDMr9o9sMA9XWAQk/5s15+Tstxk9Z49VyynDkqKqkNY+Y6UQ8eedLN7ECQQCDiAsbwOe79EpsHdQBOZeNvpWu1x1TxieN0nCAa/zQz8qupHkL/u8VI8csz+s3qOcgxpJqsn58G8eb9Jmk9fGY
\ No newline at end of file
diff --git a/full/src/test/resources/kustvakt-public.key b/full/src/test/resources/kustvakt-public.key
new file mode 100644
index 0000000..087d6f7
--- /dev/null
+++ b/full/src/test/resources/kustvakt-public.key
@@ -0,0 +1 @@
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJVX3D3vj1xqYegV9c1PR0NZErdJc9iyBgAfpKx3I+aGlnkv+MYnL5phkROTHiUBDCHIo5dzl43WUe5StXcZ9VFB7mWt4ozJzIA3EM/QbhoPuPUeSnUpJDMJVVuQXbdPE5lPbXhXUKYPu27FRiqUrlushOsJIKPlsLn8Nt1CzZIwIDAQAB
\ No newline at end of file