diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java
index 8279214213..262d07dc1f 100755
--- a/core/src/main/java/org/keycloak/representations/AccessToken.java
+++ b/core/src/main/java/org/keycloak/representations/AccessToken.java
@@ -128,12 +128,12 @@ public class AccessToken extends IDToken {
}
@Override
- public AccessToken expiration(long expiration) {
+ public AccessToken expiration(int expiration) {
return (AccessToken) super.expiration(expiration);
}
@Override
- public AccessToken notBefore(long notBefore) {
+ public AccessToken notBefore(int notBefore) {
return (AccessToken) super.notBefore(notBefore);
}
diff --git a/core/src/main/java/org/keycloak/representations/JsonWebToken.java b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
index 56ac51c3e6..42e9328110 100755
--- a/core/src/main/java/org/keycloak/representations/JsonWebToken.java
+++ b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
@@ -2,6 +2,7 @@ package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
+import org.keycloak.util.Time;
import java.io.Serializable;
@@ -13,9 +14,9 @@ public class JsonWebToken implements Serializable {
@JsonProperty("jti")
protected String id;
@JsonProperty("exp")
- protected long expiration;
+ protected int expiration;
@JsonProperty("nbf")
- protected long notBefore;
+ protected int notBefore;
@JsonProperty("iat")
protected int issuedAt;
@JsonProperty("iss")
@@ -39,26 +40,25 @@ public class JsonWebToken implements Serializable {
}
- public long getExpiration() {
+ public int getExpiration() {
return expiration;
}
- public JsonWebToken expiration(long expiration) {
+ public JsonWebToken expiration(int expiration) {
this.expiration = expiration;
return this;
}
@JsonIgnore
public boolean isExpired() {
- long time = System.currentTimeMillis() / 1000;
- return time > expiration;
+ return Time.currentTime() > expiration;
}
- public long getNotBefore() {
+ public int getNotBefore() {
return notBefore;
}
- public JsonWebToken notBefore(long notBefore) {
+ public JsonWebToken notBefore(int notBefore) {
this.notBefore = notBefore;
return this;
}
@@ -66,7 +66,7 @@ public class JsonWebToken implements Serializable {
@JsonIgnore
public boolean isNotBefore() {
- return (System.currentTimeMillis() / 1000) >= notBefore;
+ return Time.currentTime() >= notBefore;
}
@@ -89,7 +89,7 @@ public class JsonWebToken implements Serializable {
*/
@JsonIgnore
public JsonWebToken issuedNow() {
- issuedAt = (int)(System.currentTimeMillis() / 1000);
+ issuedAt = Time.currentTime();
return this;
}
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
index 7fcc3a006a..1708594cdc 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
@@ -1,6 +1,7 @@
package org.keycloak.representations.adapters.action;
import org.codehaus.jackson.annotate.JsonIgnore;
+import org.keycloak.util.Time;
/**
* Posted to managed client from admin server.
@@ -34,8 +35,7 @@ public abstract class AdminAction {
@JsonIgnore
public boolean isExpired() {
- long time = System.currentTimeMillis() / 1000;
- return time > expiration;
+ return Time.currentTime() > expiration;
}
/**
diff --git a/core/src/main/java/org/keycloak/util/Time.java b/core/src/main/java/org/keycloak/util/Time.java
new file mode 100644
index 0000000000..3b3aef4b31
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/Time.java
@@ -0,0 +1,12 @@
+package org.keycloak.util;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class Time {
+
+ public static int currentTime() {
+ return (int) (System.currentTimeMillis() / 1000);
+ }
+
+}
diff --git a/core/src/test/java/org/keycloak/RSAVerifierTest.java b/core/src/test/java/org/keycloak/RSAVerifierTest.java
index b9e2848537..ac10bbf41c 100755
--- a/core/src/test/java/org/keycloak/RSAVerifierTest.java
+++ b/core/src/test/java/org/keycloak/RSAVerifierTest.java
@@ -9,6 +9,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.representations.AccessToken;
+import org.keycloak.util.Time;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
@@ -145,7 +146,7 @@ public class RSAVerifierTest {
@Test
public void testNotBeforeGood() throws Exception {
- token.notBefore((System.currentTimeMillis() / 1000) - 100);
+ token.notBefore(Time.currentTime() - 100);
String encoded = new JWSBuilder()
.jsonContent(token)
@@ -161,7 +162,7 @@ public class RSAVerifierTest {
@Test
public void testNotBeforeBad() throws Exception {
- token.notBefore((System.currentTimeMillis() / 1000) + 100);
+ token.notBefore(Time.currentTime() + 100);
String encoded = new JWSBuilder()
.jsonContent(token)
@@ -178,7 +179,7 @@ public class RSAVerifierTest {
@Test
public void testExpirationGood() throws Exception {
- token.expiration((System.currentTimeMillis() / 1000) + 100);
+ token.expiration(Time.currentTime() + 100);
String encoded = new JWSBuilder()
.jsonContent(token)
@@ -194,7 +195,7 @@ public class RSAVerifierTest {
@Test
public void testExpirationBad() throws Exception {
- token.expiration((System.currentTimeMillis() / 1000) - 100);
+ token.expiration(Time.currentTime() - 100);
String encoded = new JWSBuilder()
.jsonContent(token)
diff --git a/pom.xml b/pom.xml
index 5a629b786d..00cdfeb9f4 100755
--- a/pom.xml
+++ b/pom.xml
@@ -229,6 +229,13 @@
junit
junit
4.11
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.3
+ test
org.hibernate.javax.persistence
@@ -310,6 +317,7 @@
org.seleniumhq.selenium
selenium-chrome-driver
2.35.0
+ test
org.mongodb
diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
index 79da6b7e8d..51ea351048 100755
--- a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
+++ b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
@@ -6,6 +6,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.representations.AccessToken;
+import org.keycloak.util.Time;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
@@ -25,7 +26,7 @@ public class AccessCodeEntry {
protected String redirectUri;
protected boolean rememberMe;
- protected long expiration;
+ protected int expiration;
protected RealmModel realm;
protected AccessToken token;
protected UserModel user;
@@ -35,7 +36,7 @@ public class AccessCodeEntry {
MultivaluedMap resourceRolesRequested = new MultivaluedHashMap();
public boolean isExpired() {
- return expiration != 0 && (System.currentTimeMillis() / 1000) > expiration;
+ return expiration != 0 && Time.currentTime() > expiration;
}
public String getId() {
@@ -58,11 +59,11 @@ public class AccessCodeEntry {
this.code = code;
}
- public long getExpiration() {
+ public int getExpiration() {
return expiration;
}
- public void setExpiration(long expiration) {
+ public void setExpiration(int expiration) {
this.expiration = expiration;
}
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 b1a473a08c..e3c1e95ee0 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -16,6 +16,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.util.Time;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
@@ -47,7 +48,7 @@ public class AuthenticationManager {
token.subject(user.getId());
token.audience(realm.getName());
if (realm.getCentralLoginLifespan() > 0) {
- token.expiration((System.currentTimeMillis() / 1000) + realm.getCentralLoginLifespan());
+ token.expiration(Time.currentTime() + realm.getCentralLoginLifespan());
}
return token;
}
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index 3c0e74121e..24f3ee41f9 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -14,6 +14,7 @@ import org.keycloak.representations.adapters.action.SessionStats;
import org.keycloak.representations.adapters.action.SessionStatsAction;
import org.keycloak.representations.adapters.action.UserStats;
import org.keycloak.representations.adapters.action.UserStatsAction;
+import org.keycloak.util.Time;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
@@ -44,7 +45,7 @@ public class ResourceAdminManager {
public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users, ResteasyClient client) {
String managementUrl = application.getManagementUrl();
if (managementUrl != null) {
- SessionStatsAction adminAction = new SessionStatsAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, application.getName());
+ SessionStatsAction adminAction = new SessionStatsAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, application.getName());
adminAction.setListUsers(users);
String token = new TokenManager().encodeToken(realm, adminAction);
logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
@@ -91,7 +92,7 @@ public class ResourceAdminManager {
public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user, ResteasyClient client) {
String managementUrl = application.getManagementUrl();
if (managementUrl != null) {
- UserStatsAction adminAction = new UserStatsAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, application.getName(), user.getId());
+ UserStatsAction adminAction = new UserStatsAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, application.getName(), user.getId());
String token = new TokenManager().encodeToken(realm, adminAction);
logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
Response response = client.target(managementUrl).path(AdapterConstants.K_GET_USER_STATS).request().post(Entity.text(token));
@@ -130,7 +131,7 @@ public class ResourceAdminManager {
.build();
try {
- realm.setNotBefore((int)(System.currentTimeMillis()/1000));
+ realm.setNotBefore(Time.currentTime());
List resources = realm.getApplications();
logger.debug("logging out {0} resources ", resources.size());
for (ApplicationModel resource : resources) {
@@ -147,7 +148,7 @@ public class ResourceAdminManager {
.build();
try {
- resource.setNotBefore((int)(System.currentTimeMillis()/1000));
+ resource.setNotBefore(Time.currentTime());
logoutApplication(realm, resource, user, client, resource.getNotBefore());
} finally {
client.close();
@@ -159,7 +160,7 @@ public class ResourceAdminManager {
protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client, int notBefore) {
String managementUrl = resource.getManagementUrl();
if (managementUrl != null) {
- LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), user, notBefore);
+ LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getName(), user, notBefore);
String token = new TokenManager().encodeToken(realm, adminAction);
logger.info("logout user: {0} resource: {1} url: {2}", user, resource.getName(), managementUrl);
Response response = client.target(managementUrl).path(AdapterConstants.K_LOGOUT).request().post(Entity.text(token));
@@ -204,7 +205,7 @@ public class ResourceAdminManager {
if (notBefore <= 0) return false;
String managementUrl = resource.getManagementUrl();
if (managementUrl != null) {
- PushNotBeforeAction adminAction = new PushNotBeforeAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), notBefore);
+ PushNotBeforeAction adminAction = new PushNotBeforeAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getName(), notBefore);
String token = new TokenManager().encodeToken(realm, adminAction);
logger.info("pushRevocation resource: {0} url: {1}", resource.getName(), managementUrl);
Response response = client.target(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).request().post(Entity.text(token));
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index beb916efe6..a9e6bcfea1 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -16,6 +16,7 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.RefreshToken;
+import org.keycloak.util.Time;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
@@ -82,7 +83,7 @@ public class TokenManager {
code.setToken(token);
code.setRealm(realm);
- code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
+ code.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
code.setClient(client);
code.setUser(user);
code.setState(state);
@@ -158,7 +159,7 @@ public class TokenManager {
if (app == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Application no longer exists", "Application no longer exists: " + app.getName());
}
- for (String roleName : refreshToken.getRealmAccess().getRoles()) {
+ for (String roleName : entry.getValue().getRoles()) {
RoleModel role = app.getRole(roleName);
if (role == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown application role: " + roleName);
@@ -257,7 +258,7 @@ public class TokenManager {
token.issuedFor(client.getLoginName());
token.issuer(realm.getName());
if (realm.getAccessTokenLifespan() > 0) {
- token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
+ token.expiration(Time.currentTime() + realm.getAccessTokenLifespan());
}
initClaims(token, claimer, user);
return token;
@@ -274,7 +275,7 @@ public class TokenManager {
token.issuedFor(client.getClientId());
token.issuer(realm.getName());
if (realm.getAccessTokenLifespan() > 0) {
- token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
+ token.expiration(Time.currentTime() + realm.getAccessTokenLifespan());
}
Set allowedOrigins = client.getWebOrigins();
if (allowedOrigins != null) {
@@ -356,7 +357,7 @@ public class TokenManager {
refreshToken = new RefreshToken(accessToken);
refreshToken.id(KeycloakModelUtils.generateId());
refreshToken.issuedNow();
- refreshToken.expiration((System.currentTimeMillis() / 1000) + realm.getRefreshTokenLifespan());
+ refreshToken.expiration(Time.currentTime() + realm.getRefreshTokenLifespan());
return this;
}
@@ -372,7 +373,7 @@ public class TokenManager {
idToken.issuedFor(accessToken.getIssuedFor());
idToken.issuer(accessToken.getIssuer());
if (realm.getAccessTokenLifespan() > 0) {
- idToken.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
+ idToken.expiration(Time.currentTime() + realm.getAccessTokenLifespan());
}
idToken.setPreferredUsername(accessToken.getPreferredUsername());
idToken.setGivenName(accessToken.getGivenName());
@@ -412,8 +413,7 @@ public class TokenManager {
res.setToken(encodedToken);
res.setTokenType("bearer");
if (accessToken.getExpiration() != 0) {
- long time = accessToken.getExpiration() - (System.currentTimeMillis() / 1000);
- res.setExpiresIn(time);
+ res.setExpiresIn(accessToken.getExpiration() - Time.currentTime());
}
}
if (refreshToken != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index b8eb4dba5a..5f5e25289f 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -41,6 +41,7 @@ import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.validation.Validation;
+import org.keycloak.util.Time;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -268,7 +269,7 @@ public class RequiredActionsService {
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user);
accessCode.setRequiredActions(requiredActions);
- accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
+ accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
try {
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
@@ -312,7 +313,7 @@ public class RequiredActionsService {
if (accessCodeEntry.isExpired()) {
logger.debug("getAccessCodeEntry: access code id: {0}", accessCodeEntry.getId());
logger.debug("getAccessCodeEntry access code entry expired: {0}", accessCodeEntry.getExpiration());
- logger.debug("getAccessCodeEntry current time: {0}", (System.currentTimeMillis() / 1000));
+ logger.debug("getAccessCodeEntry current time: {0}", Time.currentTime());
return null;
}
@@ -339,7 +340,7 @@ public class RequiredActionsService {
.createResponse(requiredActions.iterator().next());
} else {
logger.debug("redirectOauth: redirecting to: {0}", accessCode.getRedirectUri());
- accessCode.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
+ accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).redirectAccessCode(accessCode,
accessCode.getState(), accessCode.getRedirectUri());
}
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 b9b16739c5..5a3b239c26 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -28,6 +28,7 @@ import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.OAuthFlows;
import org.keycloak.services.validation.Validation;
import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.Time;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
@@ -622,7 +623,7 @@ public class TokenService {
return redirectAccessDenied(redirect, state);
}
- accessCodeEntry.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
+ accessCodeEntry.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
return oauth.redirectAccessCode(accessCodeEntry, state, redirect);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index c548c78b76..038a9de98c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -25,6 +25,7 @@ import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.util.Time;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
@@ -181,7 +182,7 @@ public class UsersResource {
throw new NotFoundException();
}
// set notBefore so that user will be forced to log in.
- user.setNotBefore((int) (System.currentTimeMillis() / 1000));
+ user.setNotBefore(Time.currentTime());
new ResourceAdminManager().logoutUser(realm, user);
}
@@ -514,7 +515,7 @@ public class UsersResource {
AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user);
accessCode.setRequiredActions(requiredActions);
- accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
+ accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
try {
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index eeb6674d66..0a463e3646 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -37,6 +37,7 @@ import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.TokenService;
+import org.keycloak.util.Time;
import javax.ws.rs.Path;
import javax.ws.rs.core.Cookie;
@@ -127,14 +128,14 @@ public class OAuthFlows {
Set requiredActions = user.getRequiredActions();
if (!requiredActions.isEmpty()) {
accessCode.setRequiredActions(new HashSet(requiredActions));
- accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
+ accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(user)
.createResponse(user.getRequiredActions().iterator().next());
}
if (!isResource
&& (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) {
- accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
+ accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).
setAccessRequest(accessCode.getRealmRolesRequested(), accessCode.getResourceRolesRequested()).
setClient(client).createOAuthGrant();
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index ea61388294..1591c7f5ff 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -236,6 +236,10 @@
junit
junit
+
+ org.hamcrest
+ hamcrest-all
+
org.hibernate.javax.persistence
hibernate-jpa-2.0-api
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 5b262bb1e6..a44991879d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -38,6 +38,7 @@ import org.keycloak.VerificationException;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.RefreshToken;
import org.keycloak.util.BasicAuthHelper;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
@@ -141,6 +142,40 @@ public class OAuthClient {
}
}
+ public AccessTokenResponse doRefreshTokenRequest(String refreshToken, String password) {
+ HttpClient client = new DefaultHttpClient();
+ HttpPost post = new HttpPost(getRefreshTokenUrl());
+
+ List parameters = new LinkedList();
+ if (grantType != null) {
+ parameters.add(new BasicNameValuePair("grant_type", grantType));
+ }
+ if (refreshToken != null) {
+ parameters.add(new BasicNameValuePair("refresh_token", refreshToken));
+ }
+ if (clientId != null && password != null) {
+ String authorization = BasicAuthHelper.createHeader(clientId, password);
+ post.setHeader("Authorization", authorization);
+ }
+ else if (clientId != null) {
+ parameters.add(new BasicNameValuePair("client_id", clientId));
+ }
+
+ UrlEncodedFormEntity formEntity = null;
+ try {
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ post.setEntity(formEntity);
+
+ try {
+ return new AccessTokenResponse(client.execute(post));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to retrieve access token", e);
+ }
+ }
+
public AccessToken verifyToken(String token) {
try {
return RSATokenVerifier.verifyToken(token, realmPublicKey, realm);
@@ -155,6 +190,18 @@ public class OAuthClient {
}
}
+ public RefreshToken verifyRefreshToken(String refreshToken) {
+ try {
+ JWSInput jws = new JWSInput(refreshToken);
+ if (!RSAProvider.verify(jws, realmPublicKey)) {
+ throw new RuntimeException("Invalid refresh token");
+ }
+ return jws.readJsonContent(RefreshToken.class);
+ } catch (Exception e) {
+ throw new RuntimeException("Invalid refresh token", e);
+ }
+ }
+
public String getClientId() {
return clientId;
}
@@ -218,6 +265,11 @@ public class OAuthClient {
return b.build().toString();
}
+ public String getRefreshTokenUrl() {
+ UriBuilder b = UriBuilder.fromUri(baseUrl + "/realms/" + realm + "/tokens/refresh");
+ return b.build().toString();
+ }
+
public OAuthClient realm(String realm) {
this.realm = realm;
return this;
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 b894e2c890..db57c43a62 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
@@ -25,10 +25,7 @@ import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
-import org.keycloak.models.RealmModel;
import org.keycloak.representations.AccessToken;
-import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.pages.LoginPage;
@@ -37,6 +34,10 @@ import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+
/**
* @author Stian Thorgersen
*/
@@ -66,7 +67,7 @@ public class AccessTokenTest {
Assert.assertEquals(200, response.getStatusCode());
- Assert.assertTrue(response.getExpiresIn() <= 600 && response.getExpiresIn() >= 550);
+ Assert.assertThat(response.getExpiresIn(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
Assert.assertEquals("bearer", response.getTokenType());
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
new file mode 100755
index 0000000000..8e70fb8725
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
@@ -0,0 +1,110 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.oauth;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.Time;
+import org.openqa.selenium.WebDriver;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class RefreshTokenTest {
+
+ @ClassRule
+ public static KeycloakRule keycloakRule = new KeycloakRule();
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+
+ @WebResource
+ protected WebDriver driver;
+
+ @WebResource
+ protected OAuthClient oauth;
+
+ @WebResource
+ protected LoginPage loginPage;
+
+ @Test
+ public void refreshTokenRequest() throws Exception {
+ oauth.doLogin("test-user@localhost", "password");
+
+ String code = oauth.getCurrentQuery().get("code");
+
+ AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
+ AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
+ String refreshTokenString = tokenResponse.getRefreshToken();
+ RefreshToken refreshToken = oauth.verifyRefreshToken(refreshTokenString);
+
+ Assert.assertNotNull(refreshTokenString);
+
+ Assert.assertEquals("bearer", tokenResponse.getTokenType());
+
+ Assert.assertThat(token.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
+ Assert.assertThat(refreshToken.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(35950), lessThanOrEqualTo(36000)));
+
+ Thread.sleep(2000);
+
+ AccessTokenResponse response = oauth.doRefreshTokenRequest(refreshTokenString, "password");
+ AccessToken refreshedToken = oauth.verifyToken(response.getAccessToken());
+ RefreshToken refreshedRefreshToken = oauth.verifyRefreshToken(response.getRefreshToken());
+
+ Assert.assertEquals(200, response.getStatusCode());
+
+ Assert.assertThat(response.getExpiresIn(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
+ Assert.assertThat(refreshedToken.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
+
+ Assert.assertThat(refreshedToken.getExpiration() - token.getExpiration(), allOf(greaterThanOrEqualTo(1), lessThanOrEqualTo(3)));
+ Assert.assertThat(refreshedRefreshToken.getExpiration() - refreshToken.getExpiration(), allOf(greaterThanOrEqualTo(1), lessThanOrEqualTo(3)));
+
+ Assert.assertNotEquals(token.getId(), refreshedToken.getId());
+ Assert.assertNotEquals(refreshToken.getId(), refreshedRefreshToken.getId());
+
+ Assert.assertEquals("bearer", response.getTokenType());
+
+ Assert.assertEquals(keycloakRule.getUser("test", "test-user@localhost").getId(), refreshedToken.getSubject());
+ Assert.assertNotEquals("test-user@localhost", refreshedToken.getSubject());
+
+ Assert.assertEquals(1, refreshedToken.getRealmAccess().getRoles().size());
+ Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
+
+ Assert.assertEquals(1, refreshedToken.getResourceAccess(oauth.getClientId()).getRoles().size());
+ Assert.assertTrue(refreshedToken.getResourceAccess(oauth.getClientId()).isUserInRole("customer-user"));
+ }
+
+}
diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json
index ed504aa0f0..a7cf0d49e1 100755
--- a/testsuite/integration/src/test/resources/testrealm.json
+++ b/testsuite/integration/src/test/resources/testrealm.json
@@ -2,9 +2,6 @@
"id": "test",
"realm": "test",
"enabled": true,
- "accessTokenLifespan": 600,
- "accessCodeLifespan": 600,
- "accessCodeLifespanUserAction": 600,
"sslNotRequired": true,
"registrationAllowed": true,
"resetPasswordAllowed": true,