keycloak-aplcache

Details

diff --git a/core/src/main/java/org/keycloak/util/BasicAuthHelper.java b/core/src/main/java/org/keycloak/util/BasicAuthHelper.java
new file mode 100755
index 0000000..8806542
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/BasicAuthHelper.java
@@ -0,0 +1,44 @@
+package org.keycloak.util;
+
+import net.iharder.Base64;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class BasicAuthHelper
+{
+    public static String createHeader(String username, String password)
+    {
+        StringBuffer buf = new StringBuffer(username);
+        buf.append(':').append(password);
+        try
+        {
+            return "Basic " + Base64.encodeBytes(buf.toString().getBytes("UTF-8"));
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String[] parseHeader(String header)
+    {
+        if (header.length() < 6) return null;
+        String type = header.substring(0, 5);
+        type = type.toLowerCase();
+        if (!type.equalsIgnoreCase("Basic")) return null;
+        String val = header.substring(6);
+        try {
+            val = new String(Base64.decode(val.getBytes()));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        String[] split = val.split(":");
+        if (split.length != 2) return null;
+        return split;
+    }
+}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/TokenGrantRequest.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/TokenGrantRequest.java
index da49e88..4f3d2ff 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/TokenGrantRequest.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/TokenGrantRequest.java
@@ -10,6 +10,7 @@ import org.apache.http.message.BasicNameValuePair;
 import org.keycloak.adapters.config.RealmConfiguration;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.util.BasicAuthHelper;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.KeycloakUriBuilder;
 import org.keycloak.util.StreamUtil;
@@ -62,11 +63,16 @@ public class TokenGrantRequest {
         }
         formparams.add(new BasicNameValuePair("grant_type", "authorization_code"));
         formparams.add(new BasicNameValuePair("code", code));
-        formparams.add(new BasicNameValuePair("client_id", client_id));
         formparams.add(new BasicNameValuePair("redirect_uri", redirectUri));
         HttpResponse response = null;
         UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
         HttpPost post = new HttpPost(codeUrl);
+        String clientSecret = credentials.get(CredentialRepresentation.SECRET);
+        if (clientSecret != null) {
+            String authorization = BasicAuthHelper.createHeader(client_id, clientSecret);
+            post.setHeader("Authorization", authorization);
+        }
+
         post.setEntity(form);
         response = client.execute(post);
         int status = response.getStatusLine().getStatusCode();
diff --git a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
old mode 100644
new mode 100755
index 5222953..077e852
--- a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
+++ b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js
@@ -111,11 +111,11 @@ var Keycloak = function (options) {
         var prompt = window.oauth.prompt;
 
         if (code) {
-            var params = 'code=' + code + '&client_id=' + encodeURIComponent(options.clientId) + '&secret=' + encodeURIComponent(options.clientSecret);
+            var params = 'code=' + code;
             var url = getRealmUrl() + '/tokens/access/codes';
 
             var req = new XMLHttpRequest();
-            req.open('POST', url, true);
+            req.open('POST', url, true, options.clientId, options.clientSecret);
             req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
 
             req.onreadystatechange = function () {
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 5805ba4..08abe26 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -245,6 +245,7 @@ public class AuthenticationManager {
         }
     }
 
+
     public AuthenticationStatus authenticateForm(RealmModel realm, UserModel user, MultivaluedMap<String, String> formData) {
         if (user == null) {
             logger.debug("Not Authenticated! Incorrect user name");
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 3263715..7279f45 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -28,9 +28,11 @@ import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.services.resources.flows.OAuthFlows;
 import org.keycloak.services.validation.Validation;
+import org.keycloak.util.BasicAuthHelper;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.NotAcceptableException;
 import javax.ws.rs.NotAuthorizedException;
 import javax.ws.rs.POST;
@@ -326,7 +328,7 @@ public class TokenService {
     @Path("access/codes")
     @POST
     @Produces("application/json")
-    public Response accessCodeToToken(final MultivaluedMap<String, String> formData) {
+    public Response accessCodeToToken(@HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader, final MultivaluedMap<String, String> formData) {
         logger.debug("accessRequest <---");
 
         if (!checkSsl()) {
@@ -337,23 +339,17 @@ public class TokenService {
             throw new NotAuthorizedException("Realm not enabled");
         }
 
-        String code = formData.getFirst("code");
-        if (code == null) {
-            logger.debug("code not specified");
-            Map<String, String> error = new HashMap<String, String>();
-            error.put("error", "invalid_request");
-            error.put("error_description", "code not specified");
-            return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
-
+        if (authorizationHeader == null) {
+            throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
         }
-        String client_id = formData.getFirst("client_id");
-        if (client_id == null) {
-            logger.debug("client_id not specified");
-            Map<String, String> error = new HashMap<String, String>();
-            error.put("error", "invalid_request");
-            error.put("error_description", "client_id not specified");
-            return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+
+        String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
+        if (usernameSecret == null) {
+            throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
         }
+
+        String client_id = usernameSecret[0];
+        String clientSecret = usernameSecret[1];
         UserModel client = realm.getUser(client_id);
         if (client == null) {
             logger.debug("Could not find user");
@@ -371,13 +367,22 @@ public class TokenService {
             return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
         }
 
-        AuthenticationStatus status = authManager.authenticateForm(realm, client, formData);
-        if (status != AuthenticationStatus.SUCCESS) {
+        if (!realm.validateSecret(client, clientSecret)) {
             Map<String, String> error = new HashMap<String, String>();
             error.put("error", "unauthorized_client");
             return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
         }
 
+        String code = formData.getFirst("code");
+        if (code == null) {
+            logger.debug("code not specified");
+            Map<String, String> error = new HashMap<String, String>();
+            error.put("error", "invalid_request");
+            error.put("error_description", "code not specified");
+            return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+
+        }
+
         JWSInput input = new JWSInput(code);
         boolean verifiedCode = false;
         try {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index a68ffad..e92b223 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -35,6 +35,7 @@ import org.json.JSONObject;
 import org.junit.Assert;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.VerificationException;
+import org.keycloak.util.BasicAuthHelper;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.representations.SkeletonKeyScope;
 import org.keycloak.representations.SkeletonKeyToken;
@@ -121,11 +122,12 @@ public class OAuthClient {
         if (redirectUri != null) {
             parameters.add(new BasicNameValuePair("redirect_uri", redirectUri));
         }
-        if (clientId != null) {
-            parameters.add(new BasicNameValuePair("client_id", clientId));
+        if (clientId != null && password != null) {
+            String authorization = BasicAuthHelper.createHeader(clientId, password);
+            post.setHeader("Authorization", authorization);
         }
-        if (password != null) {
-            parameters.add(new BasicNameValuePair("secret", password));
+        else if (clientId != null) {
+            parameters.add(new BasicNameValuePair("client_id", clientId));
         }
 
         UrlEncodedFormEntity formEntity = null;