diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java index eebf18d8c8..3cf324238a 100755 --- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java +++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java @@ -5,6 +5,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.constants.AdapterConstants; import org.keycloak.events.EventBuilder; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; import org.keycloak.representations.AccessTokenResponse; @@ -51,7 +52,13 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider { @POST @Path(AdapterConstants.K_LOGOUT) public Response backchannelLogout(String input) { - JWSInput token = new JWSInput(input); + JWSInput token = null; + try { + token = new JWSInput(input); + } catch (JWSInputException e) { + logger.warn("Failed to verify logout request"); + return Response.status(400).build(); + } PublicKey key = getExternalIdpKey(); if (key != null) { if (!verify(token, key)) { diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java index 4d5b45ed7f..6938c11147 100755 --- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java +++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java @@ -29,6 +29,7 @@ import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.models.ClientSessionModel; import org.keycloak.models.RealmModel; @@ -282,40 +283,41 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider 3) throw new IllegalArgumentException("Parsing error"); - encodedHeader = parts[0]; - encodedContent = parts[1]; - encodedSignatureInput = encodedHeader + '.' + encodedContent; + public JWSInput(String wire) throws JWSInputException { try { + this.wireString = wire; + String[] parts = wire.split("\\."); + if (parts.length < 2 || parts.length > 3) throw new IllegalArgumentException("Parsing error"); + encodedHeader = parts[0]; + encodedContent = parts[1]; + encodedSignatureInput = encodedHeader + '.' + encodedContent; content = Base64Url.decode(encodedContent); if (parts.length > 2) { encodedSignature = parts[2]; @@ -37,8 +37,8 @@ public class JWSInput { } byte[] headerBytes = Base64Url.decode(encodedHeader); header = JsonSerialization.readValue(headerBytes, JWSHeader.class); - } catch (Exception e) { - throw new RuntimeException(e); + } catch (Throwable t) { + throw new JWSInputException(t); } } @@ -80,8 +80,12 @@ public class JWSInput { return header.getAlgorithm().getProvider().verify(this, key); } - public T readJsonContent(Class type) throws IOException { - return JsonSerialization.readValue(content, type); + public T readJsonContent(Class type) throws JWSInputException { + try { + return JsonSerialization.readValue(content, type); + } catch (IOException e) { + throw new JWSInputException(e); + } } public String readContentAsString() { diff --git a/core/src/main/java/org/keycloak/jose/jws/JWSInputException.java b/core/src/main/java/org/keycloak/jose/jws/JWSInputException.java new file mode 100644 index 0000000000..930a0b6f6d --- /dev/null +++ b/core/src/main/java/org/keycloak/jose/jws/JWSInputException.java @@ -0,0 +1,18 @@ +package org.keycloak.jose.jws; + +/** + * @author Stian Thorgersen + */ +public class JWSInputException extends Exception { + + public JWSInputException(String s) { + super(s); + } + + public JWSInputException() { + } + + public JWSInputException(Throwable throwable) { + super(throwable); + } +} diff --git a/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java b/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java index 59e25ee737..dc0e4e3124 100755 --- a/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java +++ b/core/src/main/java/org/keycloak/jose/jws/crypto/RSAProvider.java @@ -64,7 +64,7 @@ public class RSAProvider implements SignatureProvider { verifier.update(input.getEncodedSignatureInput().getBytes("UTF-8")); return verifier.verify(input.getSignature()); } catch (Exception e) { - throw new RuntimeException(e); + return false; } } diff --git a/core/src/main/java/org/keycloak/util/TokenUtil.java b/core/src/main/java/org/keycloak/util/TokenUtil.java index 10b6c20ae6..94f6a73b69 100644 --- a/core/src/main/java/org/keycloak/util/TokenUtil.java +++ b/core/src/main/java/org/keycloak/util/TokenUtil.java @@ -4,6 +4,7 @@ import java.io.IOException; import org.keycloak.OAuth2Constants; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.representations.RefreshToken; /** @@ -41,11 +42,15 @@ public class TokenUtil { * @param decodedToken * @return */ - public static RefreshToken getRefreshToken(byte[] decodedToken) throws IOException { - return JsonSerialization.readValue(decodedToken, RefreshToken.class); + public static RefreshToken getRefreshToken(byte[] decodedToken) throws JWSInputException { + try { + return JsonSerialization.readValue(decodedToken, RefreshToken.class); + } catch (IOException e) { + throw new JWSInputException(e); + } } - public static RefreshToken getRefreshToken(String refreshToken) throws IOException { + public static RefreshToken getRefreshToken(String refreshToken) throws JWSInputException { byte[] encodedContent = new JWSInput(refreshToken).getContent(); return getRefreshToken(encodedContent); } @@ -56,13 +61,9 @@ public class TokenUtil { * @param refreshToken * @return */ - public static boolean isOfflineToken(String refreshToken) { - try { - RefreshToken token = getRefreshToken(refreshToken); - return token.getType().equals(TOKEN_TYPE_OFFLINE); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } + public static boolean isOfflineToken(String refreshToken) throws JWSInputException { + RefreshToken token = getRefreshToken(refreshToken); + return token.getType().equals(TOKEN_TYPE_OFFLINE); } } 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 1143968f81..2583ec4124 100755 --- 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 @@ -23,6 +23,7 @@ import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.ServerRequest; import org.keycloak.adapters.spi.LogoutError; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.RefreshToken; import org.keycloak.util.JsonSerialization; @@ -49,40 +50,44 @@ public class OfflineAccessPortalServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + if (req.getRequestURI().endsWith("/login")) { + storeToken(req); + req.getRequestDispatcher("/WEB-INF/pages/loginCallback.jsp").forward(req, resp); + return; + } - if (req.getRequestURI().endsWith("/login")) { - storeToken(req); - req.getRequestDispatcher("/WEB-INF/pages/loginCallback.jsp").forward(req, resp); - return; + String refreshToken = RefreshTokenDAO.loadToken(); + String refreshTokenInfo; + boolean savedTokenAvailable; + if (refreshToken == null) { + refreshTokenInfo = "No token saved in database. Please login first"; + savedTokenAvailable = false; + } else { + RefreshToken refreshTokenDecoded = null; + 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; + } + req.setAttribute("tokenInfo", refreshTokenInfo); + req.setAttribute("savedTokenAvailable", savedTokenAvailable); + + String customers; + if (req.getRequestURI().endsWith("/loadCustomers")) { + customers = loadCustomers(req, refreshToken); + } else { + customers = ""; + } + req.setAttribute("customers", customers); + + req.getRequestDispatcher("/WEB-INF/pages/view.jsp").forward(req, resp); + } catch (JWSInputException e) { + throw new ServletException(e); } - - String refreshToken = RefreshTokenDAO.loadToken(); - String refreshTokenInfo; - boolean savedTokenAvailable; - if (refreshToken == null) { - refreshTokenInfo = "No token saved in database. Please login first"; - savedTokenAvailable = false; - } else { - 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; - } - req.setAttribute("tokenInfo", refreshTokenInfo); - req.setAttribute("savedTokenAvailable", savedTokenAvailable); - - String customers; - if (req.getRequestURI().endsWith("/loadCustomers")) { - customers = loadCustomers(req, refreshToken); - } else { - customers = ""; - } - req.setAttribute("customers", customers); - - req.getRequestDispatcher("/WEB-INF/pages/view.jsp").forward(req, resp); } - private void storeToken(HttpServletRequest req) throws IOException { + private void storeToken(HttpServletRequest req) throws IOException, JWSInputException { RefreshableKeycloakSecurityContext ctx = (RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName()); String refreshToken = ctx.getRefreshToken(); 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 b1bd12406e..b385592bb8 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 @@ -9,6 +9,7 @@ import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.common.VerificationException; import org.keycloak.constants.AdapterConstants; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.representations.AccessToken; import org.keycloak.representations.IDToken; import org.keycloak.common.util.KeycloakUriBuilder; @@ -58,10 +59,10 @@ public class CookieTokenStore { AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false, true); IDToken idToken; if (idTokenString != null && idTokenString.length() > 0) { - JWSInput input = new JWSInput(idTokenString); try { + JWSInput input = new JWSInput(idTokenString); idToken = input.readJsonContent(IDToken.class); - } catch (IOException e) { + } catch (JWSInputException e) { throw new VerificationException(e); } } else { diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java index f41f80f202..cc6b1882f9 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java @@ -11,6 +11,7 @@ import org.keycloak.common.VerificationException; import org.keycloak.constants.AdapterConstants; import org.keycloak.enums.TokenStore; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.IDToken; @@ -313,10 +314,10 @@ public class OAuthRequestAuthenticator { try { token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); if (idTokenString != null) { - JWSInput input = new JWSInput(idTokenString); try { + JWSInput input = new JWSInput(idTokenString); idToken = input.readJsonContent(IDToken.class); - } catch (IOException e) { + } catch (JWSInputException e) { throw new VerificationException(); } } diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java index 2588c407c1..a332f83e14 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java @@ -3,6 +3,7 @@ package org.keycloak.adapters; import org.jboss.logging.Logger; import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.UserSessionManagement; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.representations.VersionRepresentation; import org.keycloak.constants.AdapterConstants; import org.keycloak.jose.jws.JWSInput; @@ -178,18 +179,17 @@ public class PreAuthActionsHandler { return null; } - JWSInput input = new JWSInput(token); - boolean verified = false; try { - verified = RSAProvider.verify(input, deployment.getRealmKey()); - } catch (Exception ignore) { + JWSInput input = new JWSInput(token); + if (RSAProvider.verify(input, deployment.getRealmKey())) { + return input; + } + } catch (JWSInputException ignore) { } - if (!verified) { - log.warn("admin request failed, unable to verify token"); - facade.getResponse().sendError(403, "no token"); - return null; - } - return input; + + log.warn("admin request failed, unable to verify token"); + facade.getResponse().sendError(403, "no token"); + return null; } diff --git a/integration/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java b/integration/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java index c468f8d839..652860bc17 100644 --- a/integration/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java +++ b/integration/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java @@ -8,6 +8,7 @@ import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.ServerRequest; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.IDToken; @@ -195,10 +196,10 @@ public class KeycloakInstalled { token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); if (idTokenString != null) { - JWSInput input = new JWSInput(idTokenString); try { + JWSInput input = new JWSInput(idTokenString); idToken = input.readJsonContent(IDToken.class); - } catch (IOException e) { + } catch (JWSInputException e) { throw new VerificationException(); } } diff --git a/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java b/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java index 9c54ab1051..be92dea9b3 100755 --- a/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java +++ b/integration/servlet-oauth-client/src/main/java/org/keycloak/servlet/ServletOAuthClient.java @@ -9,6 +9,7 @@ import org.keycloak.adapters.ServerRequest; import org.keycloak.adapters.spi.AuthenticationError; import org.keycloak.adapters.spi.LogoutError; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.IDToken; import org.keycloak.common.util.KeycloakUriBuilder; @@ -153,10 +154,10 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient { public static IDToken extractIdToken(String idToken) { if (idToken == null) return null; - JWSInput input = new JWSInput(idToken); try { + JWSInput input = new JWSInput(idToken); return input.readJsonContent(IDToken.class); - } catch (IOException e) { + } catch (JWSInputException e) { throw new RuntimeException(e); } } diff --git a/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java index af9b6b562b..d3e4d4ab25 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java +++ b/model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java @@ -1,6 +1,7 @@ package org.keycloak.models.utils; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.models.OTPPolicy; import org.keycloak.models.PasswordPolicy; @@ -73,11 +74,11 @@ public class CredentialValidation { } public static boolean validPasswordToken(RealmModel realm, UserModel user, String encodedPasswordToken) { - JWSInput jws = new JWSInput(encodedPasswordToken); - if (!RSAProvider.verify(jws, realm.getPublicKey())) { - return false; - } try { + JWSInput jws = new JWSInput(encodedPasswordToken); + if (!RSAProvider.verify(jws, realm.getPublicKey())) { + return false; + } PasswordToken passwordToken = jws.readJsonContent(PasswordToken.class); if (!passwordToken.getRealm().equals(realm.getName())) { return false; @@ -89,7 +90,7 @@ public class CredentialValidation { return false; } return true; - } catch (IOException e) { + } catch (JWSInputException e) { return false; } } 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 32a9a6df24..304e6af282 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -9,6 +9,7 @@ import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; @@ -195,44 +196,46 @@ public class TokenManager { } public RefreshToken verifyRefreshToken(RealmModel realm, String encodedRefreshToken) throws OAuthErrorException { - JWSInput jws = new JWSInput(encodedRefreshToken); - RefreshToken refreshToken = null; try { + JWSInput jws = new JWSInput(encodedRefreshToken); + RefreshToken refreshToken = null; if (!RSAProvider.verify(jws, realm.getPublicKey())) { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token"); } refreshToken = jws.readJsonContent(RefreshToken.class); - } catch (Exception e) { + + if (refreshToken.getExpiration() != 0 && refreshToken.isExpired()) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh token expired"); + } + + if (refreshToken.getIssuedAt() < realm.getNotBefore()) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token"); + } + return refreshToken; + } catch (JWSInputException e) { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", e); } - if (refreshToken.getExpiration() != 0 && refreshToken.isExpired()) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh token expired"); - } - - if (refreshToken.getIssuedAt() < realm.getNotBefore()) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token"); - } - return refreshToken; } public IDToken verifyIDToken(RealmModel realm, String encodedIDToken) throws OAuthErrorException { - JWSInput jws = new JWSInput(encodedIDToken); - IDToken idToken = null; try { + JWSInput jws = new JWSInput(encodedIDToken); + IDToken idToken; if (!RSAProvider.verify(jws, realm.getPublicKey())) { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid IDToken"); } idToken = jws.readJsonContent(IDToken.class); - } catch (IOException e) { + + if (idToken.isExpired()) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "IDToken expired"); + } + + if (idToken.getIssuedAt() < realm.getNotBefore()) { + throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale IDToken"); + } + return idToken; + } catch (JWSInputException e) { throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid IDToken", e); } - if (idToken.isExpired()) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "IDToken expired"); - } - - if (idToken.getIssuedAt() < realm.getNotBefore()) { - throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale IDToken"); - } - return idToken; } public AccessToken createClientAccessToken(KeycloakSession session, Set requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession, ClientSessionModel clientSession) { diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java index 241bcff57e..4d54d5fa21 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java @@ -3,6 +3,7 @@ package org.keycloak.services.clientregistration; import org.keycloak.common.util.Time; import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.models.ClientInitialAccessModel; import org.keycloak.models.ClientModel; @@ -44,7 +45,7 @@ public class ClientRegistrationTokenUtils { JWSInput input; try { input = new JWSInput(token); - } catch (Exception e) { + } catch (JWSInputException e) { throw new ForbiddenException(e); } @@ -55,7 +56,7 @@ public class ClientRegistrationTokenUtils { JsonWebToken jwt; try { jwt = input.readJsonContent(JsonWebToken.class); - } catch (IOException e) { + } catch (JWSInputException e) { throw new ForbiddenException(e); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java index a7bead8ba5..1ac5dbab8f 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java @@ -9,6 +9,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.UnauthorizedException; import org.keycloak.common.ClientConnection; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.models.AdminRoles; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; @@ -137,11 +138,11 @@ public class AdminRoot { protected AdminAuth authenticateRealmAdminRequest(HttpHeaders headers) { String tokenString = authManager.extractAuthorizationHeaderToken(headers); if (tokenString == null) throw new UnauthorizedException("Bearer"); - JWSInput input = new JWSInput(tokenString); AccessToken token; try { + JWSInput input = new JWSInput(tokenString); token = input.readJsonContent(AccessToken.class); - } catch (IOException e) { + } catch (JWSInputException e) { throw new UnauthorizedException("Bearer token format error"); } String realmName = token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1); 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 e191871615..1b6cbd626c 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 @@ -38,6 +38,7 @@ import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.events.Event; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; @@ -806,26 +807,15 @@ public class AccessTokenTest { } } - private IDToken getIdToken(org.keycloak.representations.AccessTokenResponse tokenResponse) throws VerificationException { + private IDToken getIdToken(org.keycloak.representations.AccessTokenResponse tokenResponse) throws JWSInputException { JWSInput input = new JWSInput(tokenResponse.getIdToken()); - IDToken idToken = null; - try { - idToken = input.readJsonContent(IDToken.class); - } catch (IOException e) { - throw new VerificationException(); - } - return idToken; + return input.readJsonContent(IDToken.class); } - private AccessToken getAccessToken(org.keycloak.representations.AccessTokenResponse tokenResponse) throws VerificationException { + private AccessToken getAccessToken(org.keycloak.representations.AccessTokenResponse tokenResponse) throws JWSInputException { JWSInput input = new JWSInput(tokenResponse.getToken()); - AccessToken idToken = null; - try { - idToken = input.readJsonContent(AccessToken.class); - } catch (IOException e) { - throw new VerificationException(); - } - return idToken; + return input.readJsonContent(AccessToken.class); + } protected Response executeGrantAccessTokenRequest(WebTarget grantTarget) { 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 219fa4f325..884d9f0efc 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 @@ -25,6 +25,7 @@ import org.keycloak.events.Errors; import org.keycloak.events.Event; import org.keycloak.events.EventType; import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.RealmModel; @@ -645,7 +646,12 @@ public class OfflineTokenTest { StringBuilder response = new StringBuilder("Offline token servlet
");
             RefreshableKeycloakSecurityContext ctx = (RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
             String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(ctx.getToken());
-            RefreshToken refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
+            RefreshToken refreshToken = null;
+            try {
+                refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
+            } catch (JWSInputException e) {
+                throw new IOException(e);
+            }
             String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
 
             response = response.append(accessTokenPretty)
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 15f7f9305c..237348962d 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
@@ -115,6 +115,14 @@ public class RefreshTokenTest {
 
     }
 
+    @Test
+    public void invalidRefreshToken() throws Exception {
+        AccessTokenResponse response = oauth.doRefreshTokenRequest("invalid", "password");
+        Assert.assertEquals(400, response.getStatusCode());
+        Assert.assertEquals("invalid_grant", response.getError());
+        events.clear();
+    }
+
     @Test
     public void refreshTokenRequest() throws Exception {
         oauth.doLogin("test-user@localhost", "password");