From 7816f053a63ae80b36251d8561a59f5f423452d1 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 2 Oct 2015 11:05:46 +0200 Subject: [PATCH] KEYCLOAK-1856 KEYCLOAK-1860 Fix onoffswitchvalue directive --- .../java/org/keycloak/RSATokenVerifier.java | 12 +++++++-- .../representations/RefreshToken.java | 5 ++-- .../{RefreshTokenUtil.java => TokenUtil.java} | 10 ++++--- .../java/org/keycloak/RSAVerifierTest.java | 4 ++- .../example/OfflineAccessPortalServlet.java | 11 +++----- .../theme/base/admin/resources/js/app.js | 15 +++++++---- .../partials/client-offline-sessions.html | 2 +- .../realm-identity-provider-oidc.html | 4 +-- .../realm-identity-provider-saml.html | 12 ++++----- .../resources/templates/kc-tabs-client.html | 2 +- .../keycloak/adapters/CookieTokenStore.java | 2 +- .../keycloak/protocol/oidc/TokenManager.java | 14 +++++----- .../oidc/endpoints/UserInfoEndpoint.java | 2 +- .../services/managers/AppAuthManager.java | 2 +- .../managers/AuthenticationManager.java | 10 ++++--- .../org/keycloak/testsuite/AssertEvents.java | 10 +++---- .../testsuite/adapter/CustomerServlet.java | 10 +++++++ .../testsuite/oauth/OfflineTokenTest.java | 26 +++++++++---------- 18 files changed, 89 insertions(+), 64 deletions(-) rename core/src/main/java/org/keycloak/util/{RefreshTokenUtil.java => TokenUtil.java} (85%) diff --git a/core/src/main/java/org/keycloak/RSATokenVerifier.java b/core/src/main/java/org/keycloak/RSATokenVerifier.java index 9bd544ddee..80a4cd64a6 100755 --- a/core/src/main/java/org/keycloak/RSATokenVerifier.java +++ b/core/src/main/java/org/keycloak/RSATokenVerifier.java @@ -3,6 +3,7 @@ package org.keycloak; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.representations.AccessToken; +import org.keycloak.util.TokenUtil; import java.io.IOException; import java.security.PublicKey; @@ -13,10 +14,10 @@ import java.security.PublicKey; */ public class RSATokenVerifier { public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl) throws VerificationException { - return verifyToken(tokenString, realmKey, realmUrl, true); + return verifyToken(tokenString, realmKey, realmUrl, true, true); } - public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive) throws VerificationException { + public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException { JWSInput input = null; try { input = new JWSInput(tokenString); @@ -42,6 +43,13 @@ public class RSATokenVerifier { throw new VerificationException("Token audience doesn't match domain. Token issuer is " + token.getIssuer() + ", but URL from configuration is " + realmUrl); } + + if (checkTokenType) { + String type = token.getType(); + if (type == null || !type.equalsIgnoreCase(TokenUtil.TOKEN_TYPE_BEARER)) { + throw new VerificationException("Token type is incorrect. Expected '" + TokenUtil.TOKEN_TYPE_BEARER + "' but was '" + type + "'"); + } + } if (checkActive && !token.isActive()) { throw new VerificationException("Token is not active."); } diff --git a/core/src/main/java/org/keycloak/representations/RefreshToken.java b/core/src/main/java/org/keycloak/representations/RefreshToken.java index ff1ce6862b..39c7c4629a 100755 --- a/core/src/main/java/org/keycloak/representations/RefreshToken.java +++ b/core/src/main/java/org/keycloak/representations/RefreshToken.java @@ -1,7 +1,6 @@ package org.keycloak.representations; -import org.codehaus.jackson.annotate.JsonProperty; -import org.keycloak.util.RefreshTokenUtil; +import org.keycloak.util.TokenUtil; import java.util.HashMap; import java.util.Map; @@ -13,7 +12,7 @@ import java.util.Map; public class RefreshToken extends AccessToken { private RefreshToken() { - type(RefreshTokenUtil.TOKEN_TYPE_REFRESH); + type(TokenUtil.TOKEN_TYPE_REFRESH); } /** diff --git a/core/src/main/java/org/keycloak/util/RefreshTokenUtil.java b/core/src/main/java/org/keycloak/util/TokenUtil.java similarity index 85% rename from core/src/main/java/org/keycloak/util/RefreshTokenUtil.java rename to core/src/main/java/org/keycloak/util/TokenUtil.java index 85c04a007f..0d103a6b9b 100644 --- a/core/src/main/java/org/keycloak/util/RefreshTokenUtil.java +++ b/core/src/main/java/org/keycloak/util/TokenUtil.java @@ -9,11 +9,15 @@ import org.keycloak.representations.RefreshToken; /** * @author Marek Posolda */ -public class RefreshTokenUtil { +public class TokenUtil { - public static final String TOKEN_TYPE_REFRESH = "REFRESH"; + public static final String TOKEN_TYPE_BEARER = "Bearer"; - public static final String TOKEN_TYPE_OFFLINE = "OFFLINE"; + public static final String TOKEN_TYPE_ID = "ID"; + + public static final String TOKEN_TYPE_REFRESH = "Refresh"; + + public static final String TOKEN_TYPE_OFFLINE = "Offline"; public static boolean isOfflineTokenRequested(String scopeParam) { if (scopeParam == null) { diff --git a/core/src/test/java/org/keycloak/RSAVerifierTest.java b/core/src/test/java/org/keycloak/RSAVerifierTest.java index e1eb84693b..acdbb323be 100755 --- a/core/src/test/java/org/keycloak/RSAVerifierTest.java +++ b/core/src/test/java/org/keycloak/RSAVerifierTest.java @@ -10,6 +10,7 @@ import org.junit.Test; import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.representations.AccessToken; import org.keycloak.util.Time; +import org.keycloak.util.TokenUtil; import javax.security.auth.x500.X500Principal; import java.io.IOException; @@ -71,7 +72,8 @@ public class RSAVerifierTest { public void initTest() { token = new AccessToken(); - token.subject("CN=Client") + token.type(TokenUtil.TOKEN_TYPE_BEARER) + .subject("CN=Client") .issuer("http://localhost:8080/auth/realm") .addAccess("service").addRole("admin"); } diff --git a/examples/demo-template/offline-access-app/src/main/java/org/keycloak/example/OfflineAccessPortalServlet.java b/examples/demo-template/offline-access-app/src/main/java/org/keycloak/example/OfflineAccessPortalServlet.java index b23357469d..354a778fc4 100644 --- a/examples/demo-template/offline-access-app/src/main/java/org/keycloak/example/OfflineAccessPortalServlet.java +++ b/examples/demo-template/offline-access-app/src/main/java/org/keycloak/example/OfflineAccessPortalServlet.java @@ -3,7 +3,6 @@ package org.keycloak.example; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Date; import java.util.List; import javax.security.cert.X509Certificate; @@ -22,12 +21,10 @@ import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.ServerRequest; -import org.keycloak.constants.ServiceUrlConstants; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.RefreshToken; import org.keycloak.util.JsonSerialization; -import org.keycloak.util.KeycloakUriBuilder; -import org.keycloak.util.RefreshTokenUtil; +import org.keycloak.util.TokenUtil; import org.keycloak.util.StreamUtil; import org.keycloak.util.Time; import org.keycloak.util.UriUtils; @@ -64,7 +61,7 @@ public class OfflineAccessPortalServlet extends HttpServlet { refreshTokenInfo = "No token saved in database. Please login first"; savedTokenAvailable = false; } else { - RefreshToken refreshTokenDecoded = RefreshTokenUtil.getRefreshToken(refreshToken); + RefreshToken refreshTokenDecoded = TokenUtil.getRefreshToken(refreshToken); String exp = (refreshTokenDecoded.getExpiration() == 0) ? "NEVER" : Time.toDate(refreshTokenDecoded.getExpiration()).toString(); refreshTokenInfo = String.format("

Type: %s

ID: %s

Expires: %s

", refreshTokenDecoded.getType(), refreshTokenDecoded.getId(), exp); savedTokenAvailable = true; @@ -89,8 +86,8 @@ public class OfflineAccessPortalServlet extends HttpServlet { RefreshTokenDAO.saveToken(refreshToken); - RefreshToken refreshTokenDecoded = RefreshTokenUtil.getRefreshToken(refreshToken); - Boolean isOfflineToken = refreshTokenDecoded.getType().equals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE); + RefreshToken refreshTokenDecoded = TokenUtil.getRefreshToken(refreshToken); + Boolean isOfflineToken = refreshTokenDecoded.getType().equals(TokenUtil.TOKEN_TYPE_OFFLINE); req.setAttribute("isOfflineToken", isOfflineToken); } diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js index a965325548..8af3ef8d0f 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js @@ -1535,12 +1535,13 @@ module.directive('onoffswitchstring', function() { }); /** - * Directive for presenting an ON-OFF switch for checkbox. + * Directive for presenting an ON-OFF switch for checkbox. The directive expects the true-value or false-value to be string like 'true' or 'false', not boolean true/false. * This directive provides some additional capabilities to the default onoffswitch such as: * - * - Specific scope to specify the value. Instead of just true or false. + * - Specific scope to specify the value. Instead of just 'true' or 'false' you can use any other values. For example: true-value="'foo'" false-value="'bar'" . + * But 'true'/'false' are defaults if true-value and false-value are not specified * - * Usage: + * Usage: */ module.directive('onoffswitchvalue', function() { return { @@ -1549,7 +1550,8 @@ module.directive('onoffswitchvalue', function() { scope: { name: '@', id: '@', - value: '=', + trueValue: '@', + falseValue: '@', ngModel: '=', ngDisabled: '=', kcOnText: '@onText', @@ -1557,7 +1559,7 @@ module.directive('onoffswitchvalue', function() { }, // TODO - The same code acts differently when put into the templateURL. Find why and move the code there. //templateUrl: "templates/kc-switch.html", - template: "
", + template: "
", compile: function(element, attrs) { /* We don't want to propagate basic attributes to the root element of directive. Id should be passed to the @@ -1566,6 +1568,9 @@ module.directive('onoffswitchvalue', function() { element.removeAttr('name'); element.removeAttr('id'); + if (!attrs.trueValue) { attrs.trueValue = "'true'"; } + if (!attrs.falseValue) { attrs.falseValue = "'false'"; } + if (!attrs.onText) { attrs.onText = "ON"; } if (!attrs.offText) { attrs.offText = "OFF"; } diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html index c8afb022a6..a5df7b6870 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html @@ -14,7 +14,7 @@
- Total number of active offline tokens for this client. + Total number of offline tokens for this client. diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html index 4654a587f0..f3726226c0 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html @@ -106,7 +106,7 @@
- +
@@ -166,7 +166,7 @@
- +
Enable/disable signature validation of external IDP signatures.
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html index 53878269a9..478137c863 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html @@ -100,7 +100,7 @@
- +
@@ -117,21 +117,21 @@
- +
Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
- +
Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
- +
Indicates whether the identity provider expects signed a AuthnRequest.
@@ -150,14 +150,14 @@
- +
Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.
- +
Enable/disable signature validation of SAML responses.
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html index f033aac42c..e16406db08 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html @@ -29,7 +29,7 @@
  • Offline Access View offline sessions for this client. Allows you to see which users retrieve offline token and when they retrieve it. - To revoke all tokens for the client, go to Revocation tab and set new not before value. + To revoke all tokens for the client, go to Revocation tab and set not before value to now.
  • diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java index 73d58bcf94..40604a4f07 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java @@ -54,7 +54,7 @@ public class CookieTokenStore { try { // Skip check if token is active now. It's supposed to be done later by the caller - AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false); + AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false, true); IDToken idToken; if (idTokenString != null && idTokenString.length() > 0) { JWSInput input = new JWSInput(idTokenString); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java index ee8e9e836a..8716d73f34 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -33,7 +33,7 @@ import org.keycloak.services.ErrorResponseException; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.offline.OfflineTokenUtils; -import org.keycloak.util.RefreshTokenUtil; +import org.keycloak.util.TokenUtil; import org.keycloak.util.Time; import javax.ws.rs.core.HttpHeaders; @@ -96,7 +96,7 @@ public class TokenManager { UserSessionModel userSession = null; ClientSessionModel clientSession = null; - if (RefreshTokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType())) { + if (TokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType())) { clientSession = OfflineTokenUtils.findOfflineClientSession(session, realm, user, oldToken.getClientSession(), oldToken.getSessionState()); if (clientSession != null) { @@ -168,12 +168,12 @@ public class TokenManager { .generateIDToken(); // Don't generate refresh token again if refresh was triggered with offline token - if (!refreshToken.getType().equals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE)) { + if (!refreshToken.getType().equals(TokenUtil.TOKEN_TYPE_OFFLINE)) { responseBuilder.generateRefreshToken(); } AccessTokenResponse res = responseBuilder.build(); - return new RefreshResult(res, RefreshTokenUtil.TOKEN_TYPE_OFFLINE.equals(refreshToken.getType())); + return new RefreshResult(res, TokenUtil.TOKEN_TYPE_OFFLINE.equals(refreshToken.getType())); } public RefreshToken verifyRefreshToken(RealmModel realm, String encodedRefreshToken) throws OAuthErrorException { @@ -385,6 +385,7 @@ public class TokenManager { AccessToken token = new AccessToken(); if (clientSession != null) token.clientSession(clientSession.getId()); token.id(KeycloakModelUtils.generateId()); + token.type(TokenUtil.TOKEN_TYPE_BEARER); token.subject(user.getId()); token.audience(client.getClientId()); token.issuedNow(); @@ -487,7 +488,7 @@ public class TokenManager { } String scopeParam = clientSession.getNote(OIDCLoginProtocol.SCOPE_PARAM); - boolean offlineTokenRequested = RefreshTokenUtil.isOfflineTokenRequested(scopeParam); + boolean offlineTokenRequested = TokenUtil.isOfflineTokenRequested(scopeParam); if (offlineTokenRequested) { if (!OfflineTokenUtils.isOfflineTokenAllowed(realm, clientSession)) { event.error(Errors.NOT_ALLOWED); @@ -495,7 +496,7 @@ public class TokenManager { } refreshToken = new RefreshToken(accessToken); - refreshToken.type(RefreshTokenUtil.TOKEN_TYPE_OFFLINE); + refreshToken.type(TokenUtil.TOKEN_TYPE_OFFLINE); OfflineTokenUtils.persistOfflineSession(session, realm, clientSession, userSession); } else { refreshToken = new RefreshToken(accessToken); @@ -512,6 +513,7 @@ public class TokenManager { } idToken = new IDToken(); idToken.id(KeycloakModelUtils.generateId()); + idToken.type(TokenUtil.TOKEN_TYPE_ID); idToken.subject(accessToken.getSubject()); idToken.audience(client.getClientId()); idToken.issuedNow(); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java index 71c30e42a2..096252a2f7 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java @@ -119,7 +119,7 @@ public class UserInfoEndpoint { AccessToken token = null; try { - token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), true); + token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), true, true); } catch (VerificationException e) { throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Token invalid: " + e.getMessage(), Status.FORBIDDEN); } diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java index 457388ba50..0156fb6803 100755 --- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java @@ -42,7 +42,7 @@ public class AppAuthManager extends AuthenticationManager { public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) { String tokenString = extractAuthorizationHeaderToken(headers); if (tokenString == null) return null; - AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, tokenString, headers); + AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, true, tokenString, headers); return authResult; } 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 5f1f9bfba7..73d274edc9 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -68,6 +68,7 @@ import java.util.List; import java.util.Locale; import java.util.Set; import org.keycloak.freemarker.LocaleHelper; +import org.keycloak.util.TokenUtil; /** * Stateless object that manages authentication @@ -115,7 +116,7 @@ public class AuthenticationManager { Cookie cookie = headers.getCookies().get(KEYCLOAK_IDENTITY_COOKIE); if (cookie == null) return; String tokenString = cookie.getValue(); - AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), false); + AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), false, false); UserSessionModel cookieSession = session.sessions().getUserSession(realm, token.getSessionState()); if (cookieSession == null || !cookieSession.getId().equals(userSession.getId())) return; expireIdentityCookie(realm, uriInfo, connection); @@ -383,7 +384,7 @@ public class AuthenticationManager { } String tokenString = cookie.getValue(); - AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, tokenString, session.getContext().getRequestHeaders()); + AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, tokenString, session.getContext().getRequestHeaders()); if (authResult == null) { expireIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection()); return null; @@ -601,9 +602,10 @@ public class AuthenticationManager { } - protected static AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString, HttpHeaders headers) { + protected static AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, boolean checkTokenType, + String tokenString, HttpHeaders headers) { try { - AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), checkActive); + AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), checkActive, checkTokenType); if (checkActive) { if (!token.isActive() || token.getIssuedAt() < realm.getNotBefore()) { logger.debug("identity cookie expired"); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java index 77b6d19f6f..cda71acf6f 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java @@ -9,7 +9,6 @@ import org.junit.rules.TestRule; import org.junit.runners.model.Statement; import org.keycloak.Config; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; -import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; import org.keycloak.constants.ServiceAccountConstants; import org.keycloak.events.admin.AdminEvent; import org.keycloak.events.Details; @@ -24,13 +23,10 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.protocol.oidc.OIDCLoginProtocolService; -import org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.rule.KeycloakRule; -import org.keycloak.util.RefreshTokenUtil; +import org.keycloak.util.TokenUtil; import java.util.HashMap; import java.util.HashSet; @@ -157,7 +153,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory { .detail(Details.CODE_ID, codeId) .detail(Details.TOKEN_ID, isUUID()) .detail(Details.REFRESH_TOKEN_ID, isUUID()) - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_REFRESH) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH) .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID) .session(sessionId); } @@ -166,7 +162,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory { return expect(EventType.REFRESH_TOKEN) .detail(Details.TOKEN_ID, isUUID()) .detail(Details.REFRESH_TOKEN_ID, refreshTokenId) - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_REFRESH) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH) .detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID()) .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID) .session(sessionId); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CustomerServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CustomerServlet.java index 9fa1a476ef..0b32146076 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CustomerServlet.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/CustomerServlet.java @@ -2,6 +2,7 @@ package org.keycloak.testsuite.adapter; import org.junit.Assert; import org.keycloak.KeycloakSecurityContext; +import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -44,6 +45,15 @@ public class CustomerServlet extends HttpServlet { Response response = target.request().get(); Assert.assertEquals(401, response.getStatus()); response.close(); + + // Assert not possible to authenticate with refresh token + RefreshableKeycloakSecurityContext refreshableContext = (RefreshableKeycloakSecurityContext) context; + response = target.request() + .header(HttpHeaders.AUTHORIZATION, "Bearer " + refreshableContext.getRefreshToken()) + .get(); + Assert.assertEquals(401, response.getStatus()); + response.close(); + String html = target.request() .header(HttpHeaders.AUTHORIZATION, "Bearer " + context.getTokenString()) .get(String.class); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java index 8eab300ede..a8c3d9c531 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java @@ -43,7 +43,7 @@ import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; import org.keycloak.util.JsonSerialization; -import org.keycloak.util.RefreshTokenUtil; +import org.keycloak.util.TokenUtil; import org.keycloak.util.Time; import org.keycloak.util.UriUtils; import org.openqa.selenium.WebDriver; @@ -221,10 +221,10 @@ public class OfflineTokenTest { events.expectCodeToToken(codeId, sessionId) .client("offline-client") - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE) .assertEvent(); - Assert.assertEquals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); + Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, sessionId, userId); @@ -278,7 +278,7 @@ public class OfflineTokenTest { .client("offline-client") .user(userId) .removeDetail(Details.UPDATED_REFRESH_TOKEN_ID) - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE) .assertEvent(); Assert.assertNotEquals(oldToken.getId(), refreshEvent.getDetails().get(Details.TOKEN_ID)); @@ -302,14 +302,14 @@ public class OfflineTokenTest { .detail(Details.RESPONSE_TYPE, "token") .detail(Details.TOKEN_ID, token.getId()) .detail(Details.REFRESH_TOKEN_ID, offlineToken.getId()) - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE) .detail(Details.USERNAME, "test-user@localhost") .removeDetail(Details.CODE_ID) .removeDetail(Details.REDIRECT_URI) .removeDetail(Details.CONSENT) .assertEvent(); - Assert.assertEquals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); + Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId); @@ -333,11 +333,11 @@ public class OfflineTokenTest { .session(token.getSessionState()) .detail(Details.TOKEN_ID, token.getId()) .detail(Details.REFRESH_TOKEN_ID, offlineToken.getId()) - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE) .detail(Details.USERNAME, ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client") .assertEvent(); - Assert.assertEquals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); + Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId); @@ -356,7 +356,7 @@ public class OfflineTokenTest { .session(token2.getSessionState()) .detail(Details.TOKEN_ID, token2.getId()) .detail(Details.REFRESH_TOKEN_ID, offlineToken2.getId()) - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE) .detail(Details.USERNAME, ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client") .assertEvent(); @@ -371,7 +371,7 @@ public class OfflineTokenTest { .user(serviceAccountUserId) .removeDetail(Details.UPDATED_REFRESH_TOKEN_ID) .removeDetail(Details.TOKEN_ID) - .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE) + .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE) .assertEvent(); // Refresh with new offline token is ok @@ -389,7 +389,7 @@ public class OfflineTokenTest { loginPage.login("test-user@localhost", "password"); Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri)); - Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), RefreshTokenUtil.TOKEN_TYPE_OFFLINE); + Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), TokenUtil.TOKEN_TYPE_OFFLINE); Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getExpiration(), 0); String accessTokenId = OfflineTokenServlet.tokenInfo.accessToken.getId(); @@ -422,7 +422,7 @@ public class OfflineTokenTest { loginPage.login("test-user@localhost", "password"); Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri)); - Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), RefreshTokenUtil.TOKEN_TYPE_OFFLINE); + Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), TokenUtil.TOKEN_TYPE_OFFLINE); // Assert refresh works with increased time Time.setOffset(9999); @@ -480,7 +480,7 @@ public class OfflineTokenTest { oauthGrantPage.accept(); Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri)); - Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), RefreshTokenUtil.TOKEN_TYPE_OFFLINE); + Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), TokenUtil.TOKEN_TYPE_OFFLINE); accountAppPage.open(); AccountApplicationsPage.AppEntry offlineClient = accountAppPage.getApplications().get("offline-client");