keycloak-memoizeit
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java 3(+2 -1)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/QueryParamterTokenRequestAuthenticator.java 54(+54 -0)
Details
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
index 420ae93..a58a05a 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
@@ -35,7 +35,8 @@ public class OIDCAuthenticationError implements AuthenticationError {
CODE_TO_TOKEN_FAILURE,
INVALID_TOKEN,
STALE_TOKEN,
- NO_AUTHORIZATION_HEADER
+ NO_AUTHORIZATION_HEADER,
+ NO_QUERY_PARAMETER_ACCESS_TOKEN
}
private Reason reason;
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/QueryParamterTokenRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/QueryParamterTokenRequestAuthenticator.java
new file mode 100644
index 0000000..5ee6662
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/QueryParamterTokenRequestAuthenticator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+
+/**
+ * @author <a href="mailto:froehlich.ch@gmail.com">Christian Froehlich</a>
+ * @version $Revision: 1 $
+ */
+public class QueryParamterTokenRequestAuthenticator extends BearerTokenRequestAuthenticator {
+ public static final String ACCESS_TOKEN = "access_token";
+ protected Logger log = Logger.getLogger(QueryParamterTokenRequestAuthenticator.class);
+
+ public QueryParamterTokenRequestAuthenticator(KeycloakDeployment deployment) {
+ super(deployment);
+ }
+
+ public AuthOutcome authenticate(HttpFacade exchange) {
+ tokenString = null;
+ tokenString = getAccessTokenFromQueryParamter(exchange);
+ if (tokenString == null || tokenString.trim().isEmpty()) {
+ challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.NO_QUERY_PARAMETER_ACCESS_TOKEN, null, null);
+ return AuthOutcome.NOT_ATTEMPTED;
+ }
+ return (authenticateToken(exchange, tokenString));
+ }
+
+ String getAccessTokenFromQueryParamter(HttpFacade exchange) {
+ try {
+ if (exchange != null && exchange.getRequest() != null) {
+ return exchange.getRequest().getQueryParamValue(ACCESS_TOKEN);
+ }
+ } catch (Exception ignore) {
+ }
+ return null;
+ }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
index 2cd1261..c59087c 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
@@ -61,7 +61,7 @@ public abstract class RequestAuthenticator {
if (log.isTraceEnabled()) {
log.trace("try bearer");
}
-
+
AuthOutcome outcome = bearer.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = bearer.getChallenge();
@@ -74,12 +74,28 @@ public abstract class RequestAuthenticator {
return AuthOutcome.AUTHENTICATED;
}
+ QueryParamterTokenRequestAuthenticator queryParamAuth = createQueryParamterTokenRequestAuthenticator();
+ if (log.isTraceEnabled()) {
+ log.trace("try query paramter auth");
+ }
+
+ outcome = queryParamAuth.authenticate(facade);
+ if (outcome == AuthOutcome.FAILED) {
+ challenge = queryParamAuth.getChallenge();
+ log.debug("QueryParamAuth auth FAILED");
+ return AuthOutcome.FAILED;
+ } else if (outcome == AuthOutcome.AUTHENTICATED) {
+ log.debug("QueryParamAuth AUTHENTICATED");
+ completeAuthentication(queryParamAuth, "KEYCLOAK");
+ return AuthOutcome.AUTHENTICATED;
+ }
+
if (deployment.isEnableBasicAuth()) {
BasicAuthRequestAuthenticator basicAuth = createBasicAuthAuthenticator();
if (log.isTraceEnabled()) {
log.trace("try basic auth");
}
-
+
outcome = basicAuth.authenticate(facade);
if (outcome == AuthOutcome.FAILED) {
challenge = basicAuth.getChallenge();
@@ -150,6 +166,10 @@ public abstract class RequestAuthenticator {
return new BasicAuthRequestAuthenticator(deployment);
}
+ protected QueryParamterTokenRequestAuthenticator createQueryParamterTokenRequestAuthenticator() {
+ return new QueryParamterTokenRequestAuthenticator(deployment);
+ }
+
protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, tokenStore, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, oauth.getToken()), session);
@@ -158,10 +178,12 @@ public abstract class RequestAuthenticator {
}
protected abstract void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
+
protected abstract void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method);
/**
* After code is received, we change the session id if possible to guard against https://www.owasp.org/index.php/Session_Fixation
+ *
* @param create
* @return
*/
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 15b48fa..a602ffd 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -203,4 +203,17 @@ public class AdapterTest {
testStrategy.testAccountManagementSessionsLogout();
}
+ /**
+ * KEYCLOAK-1733
+ */
+ @Test
+ public void testNullQueryParameterAccessToken() throws Exception {
+ testStrategy.testNullQueryParameterAccessToken();
+ }
+
+ @Test
+ public void testRestCallWithAccessTokenAsQueryParameter() throws Exception {
+ testStrategy.testRestCallWithAccessTokenAsQueryParameter();
+
+ }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index c5790dc..507c0fe 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -17,34 +17,29 @@
package org.keycloak.testsuite.adapter;
import org.apache.http.conn.params.ConnManagerParams;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.junit.Assert;
import org.junit.rules.ExternalResource;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.OIDCAuthenticationError;
-import org.keycloak.common.Version;
-import org.keycloak.representations.VersionRepresentation;
import org.keycloak.admin.client.Keycloak;
+import org.keycloak.common.Version;
+import org.keycloak.common.util.Time;
import org.keycloak.constants.AdapterConstants;
-import org.keycloak.models.ClientModel;
-import org.keycloak.models.Constants;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
+import org.keycloak.models.*;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.VersionRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
+import org.keycloak.testsuite.KeycloakServer;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountSessionsPage;
import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.rule.AbstractKeycloakRule;
-import org.keycloak.testsuite.rule.ErrorServlet;
-import org.keycloak.testsuite.rule.KeycloakRule;
-import org.keycloak.testsuite.rule.WebResource;
-import org.keycloak.testsuite.rule.WebRule;
-import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.*;
import org.keycloak.util.BasicAuthHelper;
-import org.keycloak.common.util.Time;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.client.Client;
@@ -59,7 +54,6 @@ import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-import org.keycloak.representations.idm.UserRepresentation;
/**
* Tests Undertow Adapter
@@ -144,7 +138,8 @@ public class AdapterTestStrategy extends ExternalResource {
System.out.println("insecure: ");
System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getPageSource().contains("Insecure Page"));
- if (System.getProperty("insecure.user.principal.unsupported") == null) Assert.assertTrue(driver.getPageSource().contains("UserPrincipal"));
+ if (System.getProperty("insecure.user.principal.unsupported") == null)
+ Assert.assertTrue(driver.getPageSource().contains("UserPrincipal"));
// test logout
@@ -385,6 +380,26 @@ public class AdapterTestStrategy extends ExternalResource {
}
/**
+ * KEYCLOAK-1733
+ *
+ * @throws Exception
+ */
+ public void testNullQueryParameterAccessToken() throws Exception {
+ Client client = ClientBuilder.newClient();
+ WebTarget target = client.target(APP_SERVER_BASE_URL + "/customer-db/");
+ Response response = target.request().get();
+ Assert.assertEquals(401, response.getStatus());
+ response.close();
+
+ target = client.target(APP_SERVER_BASE_URL + "/customer-db?access_token=");
+ response = target.request().get();
+ Assert.assertEquals(401, response.getStatus());
+ response.close();
+
+ client.close();
+ }
+
+ /**
* KEYCLOAK-1368
* @throws Exception
*/
@@ -406,7 +421,7 @@ public class AdapterTestStrategy extends ExternalResource {
Assert.assertTrue(errorPageResponse.contains("Error Page"));
response.close();
Assert.assertNotNull(ErrorServlet.authError);
- OIDCAuthenticationError error = (OIDCAuthenticationError)ErrorServlet.authError;
+ OIDCAuthenticationError error = (OIDCAuthenticationError) ErrorServlet.authError;
Assert.assertEquals(OIDCAuthenticationError.Reason.NO_BEARER_TOKEN, error.getReason());
ErrorServlet.authError = null;
@@ -422,7 +437,7 @@ public class AdapterTestStrategy extends ExternalResource {
Assert.assertTrue(errorPageResponse.contains("Error Page"));
response.close();
Assert.assertNotNull(ErrorServlet.authError);
- error = (OIDCAuthenticationError)ErrorServlet.authError;
+ error = (OIDCAuthenticationError) ErrorServlet.authError;
Assert.assertEquals(OIDCAuthenticationError.Reason.INVALID_TOKEN, error.getReason());
client.close();
@@ -464,8 +479,8 @@ public class AdapterTestStrategy extends ExternalResource {
String header = BasicAuthHelper.createHeader("customer-portal", "password");
Form form = new Form();
form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD)
- .param("username", "monkey@redhat.com")
- .param("password", "password");
+ .param("username", "monkey@redhat.com")
+ .param("password", "password");
Response response = target.request()
.header(HttpHeaders.AUTHORIZATION, header)
.post(Entity.form(form));
@@ -496,7 +511,6 @@ public class AdapterTestStrategy extends ExternalResource {
}
-
public void testAuthenticated() throws Exception {
// test login to customer-portal which does a bearer request to customer-db
driver.navigate().to(APP_SERVER_BASE_URL + "/secure-portal");
@@ -522,6 +536,53 @@ public class AdapterTestStrategy extends ExternalResource {
}
/**
+ * KEYCLOAK-1733
+ *
+ * @throws Exception
+ */
+ public void testRestCallWithAccessTokenAsQueryParameter() throws Exception {
+ String accessToken = getAccessToken();
+ Client client = ClientBuilder.newClient();
+ try {
+ // test without token
+ Response response = client.target(APP_SERVER_BASE_URL + "/customer-db").request().get();
+ Assert.assertEquals(401, response.getStatus());
+ response.close();
+ // test with access_token as QueryParamter
+ response = client.target(APP_SERVER_BASE_URL + "/customer-db").queryParam("access_token", accessToken).request().get();
+ Assert.assertEquals(200, response.getStatus());
+ response.close();
+ } finally {
+ client.close();
+ }
+ }
+
+ private String getAccessToken() throws JSONException {
+ String tokenUrl = AUTH_SERVER_URL + "/realms/demo/protocol/openid-connect/token";
+
+ Client client = ClientBuilder.newClient();
+ try {
+ WebTarget webTarget = client.target(tokenUrl);
+
+ Form form = new Form();
+ form.param("grant_type", "password");
+ form.param("client_id", "customer-portal-public");
+ form.param("username", "bburke@redhat.com");
+ form.param("password", "password");
+ Response response = webTarget.request().post(Entity.form(form));
+
+ Assert.assertEquals(200, response.getStatus());
+
+ JSONObject jsonObject = new JSONObject(response.readEntity(String.class));
+ System.out.println(jsonObject);
+ response.close();
+ return jsonObject.getString("access_token");
+ } finally {
+ client.close();
+ }
+ }
+
+ /**
* KEYCLOAK-732
*
* @throws Throwable
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm.json b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
index 70dc85a..aaac871 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
@@ -157,6 +157,12 @@
]
},
{
+ "name": "customer-portal-public",
+ "enabled": true,
+ "publicClient": true,
+ "directAccessGrantsEnabled": true
+ },
+ {
"name": "product-portal",
"enabled": true,
"adminUrl": "http://localhost:8081/product-portal",