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 0000000000..88065424b9
--- /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 Bill Burke
+ * @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 da49e8853d..4f3d2ffff6 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 5222953f3d..077e852f1a
--- 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 5805ba4071..08abe26844 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 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 32637153f9..7279f45116 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 formData) {
+ public Response accessCodeToToken(@HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader, final MultivaluedMap 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 error = new HashMap();
- 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[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
+ if (usernameSecret == 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 error = new HashMap();
- 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 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 error = new HashMap();
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 error = new HashMap();
+ 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 a68ffad59f..e92b223fde 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;