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 0b11d7ebdf..c9355c8917 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -34,6 +34,7 @@ import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.RefreshToken; import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.AccessCode; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus; @@ -245,11 +246,6 @@ public class TokenService { ClientModel client = authorizeClient(authorizationHeader, form, audit); - if ( (client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) { - audit.error(Errors.NOT_ALLOWED); - return createError("not_allowed", "Bearer-only applications are not allowed to invoke grants/access", Response.Status.FORBIDDEN); - } - if (!realm.isEnabled()) { audit.error(Errors.REALM_DISABLED); return createError("realm_disabled", "Realm is disabled", Response.Status.UNAUTHORIZED); @@ -312,6 +308,9 @@ public class TokenService { @NoCache @Produces(MediaType.APPLICATION_JSON) public Response validateAccessToken(@QueryParam("access_token") String tokenString) { + if (!checkSsl()) { + return createError("https_required", "HTTPS required", Response.Status.FORBIDDEN); + } audit.event(EventType.VALIDATE_ACCESS_TOKEN); AccessToken token = null; try { @@ -423,7 +422,7 @@ public class TokenService { final MultivaluedMap form) { logger.info("--> refreshAccessToken"); if (!checkSsl()) { - throw new NotAcceptableException("HTTPS required"); + return createError("https_required", "HTTPS required", Response.Status.FORBIDDEN); } audit.event(EventType.REFRESH_TOKEN); @@ -716,7 +715,7 @@ public class TokenService { logger.debug("accessRequest <---"); if (!checkSsl()) { - throw new NotAcceptableException("HTTPS required"); + throw new ForbiddenException("HTTPS required"); } audit.event(EventType.CODE_TO_TOKEN); 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 155acb18ef..d3bb9154a0 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 @@ -29,6 +29,10 @@ import org.keycloak.OAuth2Constants; import org.keycloak.audit.Details; import org.keycloak.audit.Errors; import org.keycloak.audit.Event; +import org.keycloak.enums.SslRequired; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.representations.AccessToken; @@ -277,13 +281,7 @@ public class AccessTokenTest { org.keycloak.representations.AccessTokenResponse tokenResponse = null; { - String header = BasicAuthHelper.createHeader("test-app", "password"); - Form form = new Form(); - form.param("username", "test-user@localhost") - .param("password", "password"); - Response response = grantTarget.request() - .header(HttpHeaders.AUTHORIZATION, header) - .post(Entity.form(form)); + Response response = executeGrantAccessTokenRequest(grantTarget); Assert.assertEquals(200, response.getStatus()); tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); response.close(); @@ -320,5 +318,209 @@ public class AccessTokenTest { } + @Test + public void testGrantAccessToken() throws Exception { + Client client = ClientBuilder.newClient(); + UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT); + URI grantUri = TokenService.grantAccessTokenUrl(builder).build("test"); + WebTarget grantTarget = client.target(grantUri); + + { // test checkSsl + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + realm.setSslRequired(SslRequired.ALL); + session.getTransaction().commit(); + session.close(); + } + + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(403, response.getStatus()); + response.close(); + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + realm.setSslRequired(SslRequired.EXTERNAL); + session.getTransaction().commit(); + session.close(); + } + + } + + { // test null username + String header = BasicAuthHelper.createHeader("test-app", "password"); + Form form = new Form(); + form.param("password", "password"); + Response response = grantTarget.request() + .header(HttpHeaders.AUTHORIZATION, header) + .post(Entity.form(form)); + Assert.assertEquals(401, response.getStatus()); + response.close(); + } + + { // test no password + String header = BasicAuthHelper.createHeader("test-app", "password"); + Form form = new Form(); + form.param("username", "test-user@localhost"); + Response response = grantTarget.request() + .header(HttpHeaders.AUTHORIZATION, header) + .post(Entity.form(form)); + Assert.assertEquals(400, response.getStatus()); + response.close(); + } + + { // test bearer-only + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + ApplicationModel clientModel = realm.getApplicationByName("test-app"); + clientModel.setBearerOnly(true); + session.getTransaction().commit(); + session.close(); + } + + + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(400, response.getStatus()); + response.close(); + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + ApplicationModel clientModel = realm.getApplicationByName("test-app"); + clientModel.setBearerOnly(false); + session.getTransaction().commit(); + session.close(); + } + + } + + { // test realm disabled + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + realm.setEnabled(false); + session.getTransaction().commit(); + session.close(); + } + + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(401, response.getStatus()); + response.close(); + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + realm.setEnabled(true); + session.getTransaction().commit(); + session.close(); + } + + } + + { // test application disabled + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + ClientModel clientModel = realm.findClient("test-app"); + clientModel.setEnabled(false); + session.getTransaction().commit(); + session.close(); + } + + + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(400, response.getStatus()); + response.close(); + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + ClientModel clientModel = realm.findClient("test-app"); + clientModel.setEnabled(true); + session.getTransaction().commit(); + session.close(); + } + + } + + { // test user action required + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + UserModel user = session.users().getUserByUsername("test-user@localhost", realm); + user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD); + session.getTransaction().commit(); + session.close(); + } + + + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(400, response.getStatus()); + response.close(); + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + UserModel user = session.users().getUserByUsername("test-user@localhost", realm); + user.removeRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD); + session.getTransaction().commit(); + session.close(); + } + + } + { // test user disabled + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + UserModel user = session.users().getUserByUsername("test-user@localhost", realm); + user.setEnabled(false); + session.getTransaction().commit(); + session.close(); + } + + + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(400, response.getStatus()); + response.close(); + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + UserModel user = session.users().getUserByUsername("test-user@localhost", realm); + user.setEnabled(true); + session.getTransaction().commit(); + session.close(); + } + + } + + + { + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(200, response.getStatus()); + org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); + response.close(); + } + + client.close(); + events.clear(); + + } + + protected Response executeGrantAccessTokenRequest(WebTarget grantTarget) { + String header = BasicAuthHelper.createHeader("test-app", "password"); + Form form = new Form(); + form.param("username", "test-user@localhost") + .param("password", "password"); + return grantTarget.request() + .header(HttpHeaders.AUTHORIZATION, header) + .post(Entity.form(form)); + } + } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java index 361d0c1f84..bc1195a3ea 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java @@ -29,8 +29,12 @@ import org.keycloak.OAuth2Constants; import org.keycloak.audit.Details; import org.keycloak.audit.Errors; import org.keycloak.audit.Event; +import org.keycloak.enums.SslRequired; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.representations.AccessToken; import org.keycloak.representations.RefreshToken; @@ -335,6 +339,89 @@ public class RefreshTokenTest { events.clear(); } + @Test + public void testCheckSsl() throws Exception { + Client client = ClientBuilder.newClient(); + UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT); + URI grantUri = TokenService.grantAccessTokenUrl(builder).build("test"); + WebTarget grantTarget = client.target(grantUri); + builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT); + URI uri = TokenService.refreshUrl(builder).build("test"); + WebTarget refreshTarget = client.target(uri); + + String refreshToken = null; + { + Response response = executeGrantAccessTokenRequest(grantTarget); + Assert.assertEquals(200, response.getStatus()); + org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); + refreshToken = tokenResponse.getRefreshToken(); + response.close(); + } + + { + Response response = executeRefreshToken(refreshTarget, refreshToken); + Assert.assertEquals(200, response.getStatus()); + org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); + refreshToken = tokenResponse.getRefreshToken(); + response.close(); + } + + { // test checkSsl + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + realm.setSslRequired(SslRequired.ALL); + session.getTransaction().commit(); + session.close(); + } + + Response response = executeRefreshToken(refreshTarget, refreshToken); + Assert.assertEquals(403, response.getStatus()); + response.close(); + + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName("test"); + realm.setSslRequired(SslRequired.EXTERNAL); + session.getTransaction().commit(); + session.close(); + } + + } + + { + Response response = executeRefreshToken(refreshTarget, refreshToken); + Assert.assertEquals(200, response.getStatus()); + org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class); + refreshToken = tokenResponse.getRefreshToken(); + response.close(); + } + + + client.close(); + events.clear(); + + } + + protected Response executeRefreshToken(WebTarget refreshTarget, String refreshToken) { + String header = BasicAuthHelper.createHeader("test-app", "password"); + Form form = new Form(); + form.param("refresh_token", refreshToken); + return refreshTarget.request() + .header(HttpHeaders.AUTHORIZATION, header) + .post(Entity.form(form)); + } + + protected Response executeGrantAccessTokenRequest(WebTarget grantTarget) { + String header = BasicAuthHelper.createHeader("test-app", "password"); + Form form = new Form(); + form.param("username", "test-user@localhost") + .param("password", "password"); + return grantTarget.request() + .header(HttpHeaders.AUTHORIZATION, header) + .post(Entity.form(form)); + } + }