diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
index 0626f1c..5a1f8ee 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
@@ -22,16 +22,23 @@ import java.util.Map;
public class AuthorizeClientUtil {
public static ClientModel authorizeClient(String authorizationHeader, MultivaluedMap<String, String> formData, EventBuilder event, RealmModel realm) {
- String client_id;
- String clientSecret;
+ String client_id = null;
+ String clientSecret = null;
if (authorizationHeader != null) {
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
- if (usernameSecret == null) {
- throw new UnauthorizedException("Bad Authorization header", Response.status(401).header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + realm.getName() + "\"").build());
+ if (usernameSecret != null) {
+ client_id = usernameSecret[0];
+ clientSecret = usernameSecret[1];
+ } else {
+
+ // Don't send 401 if client_id parameter was sent in request. For example IE may automatically send "Authorization: Negotiate" in XHR requests even for public clients
+ if (!formData.containsKey(OAuth2Constants.CLIENT_ID)) {
+ throw new UnauthorizedException("Bad Authorization header", Response.status(401).header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + realm.getName() + "\"").build());
+ }
}
- client_id = usernameSecret[0];
- clientSecret = usernameSecret[1];
- } else {
+ }
+
+ if (client_id == null) {
client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
clientSecret = formData.getFirst("client_secret");
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 4c62dd8..cbfc76f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -21,12 +21,19 @@
*/
package org.keycloak.testsuite.oauth;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.VerificationException;
+import org.keycloak.constants.AdapterConstants;
import org.keycloak.enums.SslRequired;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
@@ -46,6 +53,7 @@ import org.keycloak.protocol.oidc.mappers.RoleNameMapper;
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
+import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
@@ -68,9 +76,11 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -719,6 +729,52 @@ public class AccessTokenTest {
}
+ // KEYCLOAK-1595 Assert that public client is able to retrieve token even if header "Authorization: Negotiate something" was used (parameter client_id has preference in this case)
+ @Test
+ public void testAuthorizationNegotiateHeaderIgnored() throws Exception {
+ keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ ClientModel client = new ClientManager(manager).createClient(appRealm, "sample-public-client");
+ client.addRedirectUri("http://localhost:8081/app/auth");
+ client.setEnabled(true);
+ client.setPublicClient(true);
+ }
+ });
+
+ oauth.clientId("sample-public-client");
+ oauth.doLogin("test-user@localhost", "password");
+ Event loginEvent = events.expectLogin().client("sample-public-client").assertEvent();
+
+ String sessionId = loginEvent.getSessionId();
+ String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+ String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+ CloseableHttpClient client = new DefaultHttpClient();
+ try {
+ HttpPost post = new HttpPost(oauth.getAccessTokenUrl());
+
+ List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+ parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.AUTHORIZATION_CODE));
+ parameters.add(new BasicNameValuePair(OAuth2Constants.CODE, code));
+ parameters.add(new BasicNameValuePair(OAuth2Constants.REDIRECT_URI, oauth.getRedirectUri()));
+ parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, oauth.getClientId()));
+ post.setHeader("Authorization", "Negotiate something-which-will-be-ignored");
+
+ UrlEncodedFormEntity formEntity = null;
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ post.setEntity(formEntity);
+
+ AccessTokenResponse response = new AccessTokenResponse(client.execute(post));
+ Assert.assertEquals(200, response.getStatusCode());
+ AccessToken token = oauth.verifyToken(response.getAccessToken());
+ events.expectCodeToToken(codeId, sessionId).client("sample-public-client").assertEvent();
+ } finally {
+ oauth.closeClient(client);
+ }
+ }
+
private IDToken getIdToken(org.keycloak.representations.AccessTokenResponse tokenResponse) throws VerificationException {
JWSInput input = new JWSInput(tokenResponse.getIdToken());
IDToken idToken = null;
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 abfe3c3..b06e433 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -306,7 +306,7 @@ public class OAuthClient {
}
}
- private void closeClient(CloseableHttpClient client) {
+ public void closeClient(CloseableHttpClient client) {
try {
client.close();
} catch (IOException ioe) {