keycloak-uncached

Merge pull request #1521 from mposolda/master KEYCLOAK-1595

8/4/2015 11:01:17 AM

Details

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) {