KEYCLOAK-378 KEYCLOAK-379 KEYCLOAK-381 Fix refresh token if token contains app roles. Changed long time fields in AccessCode and AccessToken to int
This commit is contained in:
parent
2ebc32793a
commit
f9aaa16cfe
19 changed files with 244 additions and 52 deletions
|
@ -128,12 +128,12 @@ public class AccessToken extends IDToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessToken expiration(long expiration) {
|
public AccessToken expiration(int expiration) {
|
||||||
return (AccessToken) super.expiration(expiration);
|
return (AccessToken) super.expiration(expiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessToken notBefore(long notBefore) {
|
public AccessToken notBefore(int notBefore) {
|
||||||
return (AccessToken) super.notBefore(notBefore);
|
return (AccessToken) super.notBefore(notBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.representations;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
import org.codehaus.jackson.annotate.JsonProperty;
|
import org.codehaus.jackson.annotate.JsonProperty;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@ -13,9 +14,9 @@ public class JsonWebToken implements Serializable {
|
||||||
@JsonProperty("jti")
|
@JsonProperty("jti")
|
||||||
protected String id;
|
protected String id;
|
||||||
@JsonProperty("exp")
|
@JsonProperty("exp")
|
||||||
protected long expiration;
|
protected int expiration;
|
||||||
@JsonProperty("nbf")
|
@JsonProperty("nbf")
|
||||||
protected long notBefore;
|
protected int notBefore;
|
||||||
@JsonProperty("iat")
|
@JsonProperty("iat")
|
||||||
protected int issuedAt;
|
protected int issuedAt;
|
||||||
@JsonProperty("iss")
|
@JsonProperty("iss")
|
||||||
|
@ -39,26 +40,25 @@ public class JsonWebToken implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getExpiration() {
|
public int getExpiration() {
|
||||||
return expiration;
|
return expiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonWebToken expiration(long expiration) {
|
public JsonWebToken expiration(int expiration) {
|
||||||
this.expiration = expiration;
|
this.expiration = expiration;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
long time = System.currentTimeMillis() / 1000;
|
return Time.currentTime() > expiration;
|
||||||
return time > expiration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getNotBefore() {
|
public int getNotBefore() {
|
||||||
return notBefore;
|
return notBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonWebToken notBefore(long notBefore) {
|
public JsonWebToken notBefore(int notBefore) {
|
||||||
this.notBefore = notBefore;
|
this.notBefore = notBefore;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ public class JsonWebToken implements Serializable {
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isNotBefore() {
|
public boolean isNotBefore() {
|
||||||
return (System.currentTimeMillis() / 1000) >= notBefore;
|
return Time.currentTime() >= notBefore;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ public class JsonWebToken implements Serializable {
|
||||||
*/
|
*/
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public JsonWebToken issuedNow() {
|
public JsonWebToken issuedNow() {
|
||||||
issuedAt = (int)(System.currentTimeMillis() / 1000);
|
issuedAt = Time.currentTime();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.representations.adapters.action;
|
package org.keycloak.representations.adapters.action;
|
||||||
|
|
||||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posted to managed client from admin server.
|
* Posted to managed client from admin server.
|
||||||
|
@ -34,8 +35,7 @@ public abstract class AdminAction {
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
long time = System.currentTimeMillis() / 1000;
|
return Time.currentTime() > expiration;
|
||||||
return time > expiration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
12
core/src/main/java/org/keycloak/util/Time.java
Normal file
12
core/src/main/java/org/keycloak/util/Time.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package org.keycloak.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class Time {
|
||||||
|
|
||||||
|
public static int currentTime() {
|
||||||
|
return (int) (System.currentTimeMillis() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.jose.jws.JWSBuilder;
|
import org.keycloak.jose.jws.JWSBuilder;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -145,7 +146,7 @@ public class RSAVerifierTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotBeforeGood() throws Exception {
|
public void testNotBeforeGood() throws Exception {
|
||||||
token.notBefore((System.currentTimeMillis() / 1000) - 100);
|
token.notBefore(Time.currentTime() - 100);
|
||||||
|
|
||||||
String encoded = new JWSBuilder()
|
String encoded = new JWSBuilder()
|
||||||
.jsonContent(token)
|
.jsonContent(token)
|
||||||
|
@ -161,7 +162,7 @@ public class RSAVerifierTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotBeforeBad() throws Exception {
|
public void testNotBeforeBad() throws Exception {
|
||||||
token.notBefore((System.currentTimeMillis() / 1000) + 100);
|
token.notBefore(Time.currentTime() + 100);
|
||||||
|
|
||||||
String encoded = new JWSBuilder()
|
String encoded = new JWSBuilder()
|
||||||
.jsonContent(token)
|
.jsonContent(token)
|
||||||
|
@ -178,7 +179,7 @@ public class RSAVerifierTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExpirationGood() throws Exception {
|
public void testExpirationGood() throws Exception {
|
||||||
token.expiration((System.currentTimeMillis() / 1000) + 100);
|
token.expiration(Time.currentTime() + 100);
|
||||||
|
|
||||||
String encoded = new JWSBuilder()
|
String encoded = new JWSBuilder()
|
||||||
.jsonContent(token)
|
.jsonContent(token)
|
||||||
|
@ -194,7 +195,7 @@ public class RSAVerifierTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExpirationBad() throws Exception {
|
public void testExpirationBad() throws Exception {
|
||||||
token.expiration((System.currentTimeMillis() / 1000) - 100);
|
token.expiration(Time.currentTime() - 100);
|
||||||
|
|
||||||
String encoded = new JWSBuilder()
|
String encoded = new JWSBuilder()
|
||||||
.jsonContent(token)
|
.jsonContent(token)
|
||||||
|
|
8
pom.xml
8
pom.xml
|
@ -229,6 +229,13 @@
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.11</version>
|
<version>4.11</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest-all</artifactId>
|
||||||
|
<version>1.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate.javax.persistence</groupId>
|
<groupId>org.hibernate.javax.persistence</groupId>
|
||||||
|
@ -310,6 +317,7 @@
|
||||||
<groupId>org.seleniumhq.selenium</groupId>
|
<groupId>org.seleniumhq.selenium</groupId>
|
||||||
<artifactId>selenium-chrome-driver</artifactId>
|
<artifactId>selenium-chrome-driver</artifactId>
|
||||||
<version>2.35.0</version>
|
<version>2.35.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedHashMap;
|
import javax.ws.rs.core.MultivaluedHashMap;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
@ -25,7 +26,7 @@ public class AccessCodeEntry {
|
||||||
protected String redirectUri;
|
protected String redirectUri;
|
||||||
protected boolean rememberMe;
|
protected boolean rememberMe;
|
||||||
|
|
||||||
protected long expiration;
|
protected int expiration;
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected AccessToken token;
|
protected AccessToken token;
|
||||||
protected UserModel user;
|
protected UserModel user;
|
||||||
|
@ -35,7 +36,7 @@ public class AccessCodeEntry {
|
||||||
MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedHashMap<String, RoleModel>();
|
MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedHashMap<String, RoleModel>();
|
||||||
|
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return expiration != 0 && (System.currentTimeMillis() / 1000) > expiration;
|
return expiration != 0 && Time.currentTime() > expiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -58,11 +59,11 @@ public class AccessCodeEntry {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getExpiration() {
|
public int getExpiration() {
|
||||||
return expiration;
|
return expiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpiration(long expiration) {
|
public void setExpiration(int expiration) {
|
||||||
this.expiration = expiration;
|
this.expiration = expiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
import org.keycloak.services.resources.RealmsResource;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.Cookie;
|
import javax.ws.rs.core.Cookie;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
@ -47,7 +48,7 @@ public class AuthenticationManager {
|
||||||
token.subject(user.getId());
|
token.subject(user.getId());
|
||||||
token.audience(realm.getName());
|
token.audience(realm.getName());
|
||||||
if (realm.getCentralLoginLifespan() > 0) {
|
if (realm.getCentralLoginLifespan() > 0) {
|
||||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getCentralLoginLifespan());
|
token.expiration(Time.currentTime() + realm.getCentralLoginLifespan());
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.keycloak.representations.adapters.action.SessionStats;
|
||||||
import org.keycloak.representations.adapters.action.SessionStatsAction;
|
import org.keycloak.representations.adapters.action.SessionStatsAction;
|
||||||
import org.keycloak.representations.adapters.action.UserStats;
|
import org.keycloak.representations.adapters.action.UserStats;
|
||||||
import org.keycloak.representations.adapters.action.UserStatsAction;
|
import org.keycloak.representations.adapters.action.UserStatsAction;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -44,7 +45,7 @@ public class ResourceAdminManager {
|
||||||
public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users, ResteasyClient client) {
|
public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users, ResteasyClient client) {
|
||||||
String managementUrl = application.getManagementUrl();
|
String managementUrl = application.getManagementUrl();
|
||||||
if (managementUrl != null) {
|
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);
|
adminAction.setListUsers(users);
|
||||||
String token = new TokenManager().encodeToken(realm, adminAction);
|
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||||
logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
|
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) {
|
public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user, ResteasyClient client) {
|
||||||
String managementUrl = application.getManagementUrl();
|
String managementUrl = application.getManagementUrl();
|
||||||
if (managementUrl != null) {
|
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);
|
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||||
logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
|
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));
|
Response response = client.target(managementUrl).path(AdapterConstants.K_GET_USER_STATS).request().post(Entity.text(token));
|
||||||
|
@ -130,7 +131,7 @@ public class ResourceAdminManager {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
realm.setNotBefore((int)(System.currentTimeMillis()/1000));
|
realm.setNotBefore(Time.currentTime());
|
||||||
List<ApplicationModel> resources = realm.getApplications();
|
List<ApplicationModel> resources = realm.getApplications();
|
||||||
logger.debug("logging out {0} resources ", resources.size());
|
logger.debug("logging out {0} resources ", resources.size());
|
||||||
for (ApplicationModel resource : resources) {
|
for (ApplicationModel resource : resources) {
|
||||||
|
@ -147,7 +148,7 @@ public class ResourceAdminManager {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resource.setNotBefore((int)(System.currentTimeMillis()/1000));
|
resource.setNotBefore(Time.currentTime());
|
||||||
logoutApplication(realm, resource, user, client, resource.getNotBefore());
|
logoutApplication(realm, resource, user, client, resource.getNotBefore());
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
|
@ -159,7 +160,7 @@ public class ResourceAdminManager {
|
||||||
protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client, int notBefore) {
|
protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client, int notBefore) {
|
||||||
String managementUrl = resource.getManagementUrl();
|
String managementUrl = resource.getManagementUrl();
|
||||||
if (managementUrl != null) {
|
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);
|
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||||
logger.info("logout user: {0} resource: {1} url: {2}", user, resource.getName(), managementUrl);
|
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));
|
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;
|
if (notBefore <= 0) return false;
|
||||||
String managementUrl = resource.getManagementUrl();
|
String managementUrl = resource.getManagementUrl();
|
||||||
if (managementUrl != null) {
|
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);
|
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||||
logger.info("pushRevocation resource: {0} url: {1}", resource.getName(), managementUrl);
|
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));
|
Response response = client.target(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).request().post(Entity.text(token));
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.IDToken;
|
import org.keycloak.representations.IDToken;
|
||||||
import org.keycloak.representations.RefreshToken;
|
import org.keycloak.representations.RefreshToken;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedHashMap;
|
import javax.ws.rs.core.MultivaluedHashMap;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
@ -82,7 +83,7 @@ public class TokenManager {
|
||||||
|
|
||||||
code.setToken(token);
|
code.setToken(token);
|
||||||
code.setRealm(realm);
|
code.setRealm(realm);
|
||||||
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
|
code.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
||||||
code.setClient(client);
|
code.setClient(client);
|
||||||
code.setUser(user);
|
code.setUser(user);
|
||||||
code.setState(state);
|
code.setState(state);
|
||||||
|
@ -158,7 +159,7 @@ public class TokenManager {
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Application no longer exists", "Application no longer exists: " + app.getName());
|
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);
|
RoleModel role = app.getRole(roleName);
|
||||||
if (role == null) {
|
if (role == null) {
|
||||||
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown application role: " + roleName);
|
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.issuedFor(client.getLoginName());
|
||||||
token.issuer(realm.getName());
|
token.issuer(realm.getName());
|
||||||
if (realm.getAccessTokenLifespan() > 0) {
|
if (realm.getAccessTokenLifespan() > 0) {
|
||||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
|
token.expiration(Time.currentTime() + realm.getAccessTokenLifespan());
|
||||||
}
|
}
|
||||||
initClaims(token, claimer, user);
|
initClaims(token, claimer, user);
|
||||||
return token;
|
return token;
|
||||||
|
@ -274,7 +275,7 @@ public class TokenManager {
|
||||||
token.issuedFor(client.getClientId());
|
token.issuedFor(client.getClientId());
|
||||||
token.issuer(realm.getName());
|
token.issuer(realm.getName());
|
||||||
if (realm.getAccessTokenLifespan() > 0) {
|
if (realm.getAccessTokenLifespan() > 0) {
|
||||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
|
token.expiration(Time.currentTime() + realm.getAccessTokenLifespan());
|
||||||
}
|
}
|
||||||
Set<String> allowedOrigins = client.getWebOrigins();
|
Set<String> allowedOrigins = client.getWebOrigins();
|
||||||
if (allowedOrigins != null) {
|
if (allowedOrigins != null) {
|
||||||
|
@ -356,7 +357,7 @@ public class TokenManager {
|
||||||
refreshToken = new RefreshToken(accessToken);
|
refreshToken = new RefreshToken(accessToken);
|
||||||
refreshToken.id(KeycloakModelUtils.generateId());
|
refreshToken.id(KeycloakModelUtils.generateId());
|
||||||
refreshToken.issuedNow();
|
refreshToken.issuedNow();
|
||||||
refreshToken.expiration((System.currentTimeMillis() / 1000) + realm.getRefreshTokenLifespan());
|
refreshToken.expiration(Time.currentTime() + realm.getRefreshTokenLifespan());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +373,7 @@ public class TokenManager {
|
||||||
idToken.issuedFor(accessToken.getIssuedFor());
|
idToken.issuedFor(accessToken.getIssuedFor());
|
||||||
idToken.issuer(accessToken.getIssuer());
|
idToken.issuer(accessToken.getIssuer());
|
||||||
if (realm.getAccessTokenLifespan() > 0) {
|
if (realm.getAccessTokenLifespan() > 0) {
|
||||||
idToken.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
|
idToken.expiration(Time.currentTime() + realm.getAccessTokenLifespan());
|
||||||
}
|
}
|
||||||
idToken.setPreferredUsername(accessToken.getPreferredUsername());
|
idToken.setPreferredUsername(accessToken.getPreferredUsername());
|
||||||
idToken.setGivenName(accessToken.getGivenName());
|
idToken.setGivenName(accessToken.getGivenName());
|
||||||
|
@ -412,8 +413,7 @@ public class TokenManager {
|
||||||
res.setToken(encodedToken);
|
res.setToken(encodedToken);
|
||||||
res.setTokenType("bearer");
|
res.setTokenType("bearer");
|
||||||
if (accessToken.getExpiration() != 0) {
|
if (accessToken.getExpiration() != 0) {
|
||||||
long time = accessToken.getExpiration() - (System.currentTimeMillis() / 1000);
|
res.setExpiresIn(accessToken.getExpiration() - Time.currentTime());
|
||||||
res.setExpiresIn(time);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (refreshToken != null) {
|
if (refreshToken != null) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.keycloak.services.managers.TokenManager;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.services.resources.flows.Flows;
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
import org.keycloak.services.validation.Validation;
|
import org.keycloak.services.validation.Validation;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
@ -268,7 +269,7 @@ public class RequiredActionsService {
|
||||||
|
|
||||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user);
|
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user);
|
||||||
accessCode.setRequiredActions(requiredActions);
|
accessCode.setRequiredActions(requiredActions);
|
||||||
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
|
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
|
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
|
||||||
|
@ -312,7 +313,7 @@ public class RequiredActionsService {
|
||||||
if (accessCodeEntry.isExpired()) {
|
if (accessCodeEntry.isExpired()) {
|
||||||
logger.debug("getAccessCodeEntry: access code id: {0}", accessCodeEntry.getId());
|
logger.debug("getAccessCodeEntry: access code id: {0}", accessCodeEntry.getId());
|
||||||
logger.debug("getAccessCodeEntry access code entry expired: {0}", accessCodeEntry.getExpiration());
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,7 +340,7 @@ public class RequiredActionsService {
|
||||||
.createResponse(requiredActions.iterator().next());
|
.createResponse(requiredActions.iterator().next());
|
||||||
} else {
|
} else {
|
||||||
logger.debug("redirectOauth: redirecting to: {0}", accessCode.getRedirectUri());
|
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,
|
return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).redirectAccessCode(accessCode,
|
||||||
accessCode.getState(), accessCode.getRedirectUri());
|
accessCode.getState(), accessCode.getRedirectUri());
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.services.resources.flows.Flows;
|
||||||
import org.keycloak.services.resources.flows.OAuthFlows;
|
import org.keycloak.services.resources.flows.OAuthFlows;
|
||||||
import org.keycloak.services.validation.Validation;
|
import org.keycloak.services.validation.Validation;
|
||||||
import org.keycloak.util.BasicAuthHelper;
|
import org.keycloak.util.BasicAuthHelper;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.BadRequestException;
|
import javax.ws.rs.BadRequestException;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
@ -622,7 +623,7 @@ public class TokenService {
|
||||||
return redirectAccessDenied(redirect, state);
|
return redirectAccessDenied(redirect, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
accessCodeEntry.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
|
accessCodeEntry.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
||||||
return oauth.redirectAccessCode(accessCodeEntry, state, redirect);
|
return oauth.redirectAccessCode(accessCodeEntry, state, redirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.services.managers.ResourceAdminManager;
|
||||||
import org.keycloak.services.managers.TokenManager;
|
import org.keycloak.services.managers.TokenManager;
|
||||||
import org.keycloak.services.resources.flows.Flows;
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
import org.keycloak.services.resources.flows.Urls;
|
import org.keycloak.services.resources.flows.Urls;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.BadRequestException;
|
import javax.ws.rs.BadRequestException;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
@ -181,7 +182,7 @@ public class UsersResource {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
// set notBefore so that user will be forced to log in.
|
// 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);
|
new ResourceAdminManager().logoutUser(realm, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +515,7 @@ public class UsersResource {
|
||||||
|
|
||||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user);
|
AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user);
|
||||||
accessCode.setRequiredActions(requiredActions);
|
accessCode.setRequiredActions(requiredActions);
|
||||||
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
|
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
|
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.keycloak.services.managers.AccessCodeEntry;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.TokenManager;
|
import org.keycloak.services.managers.TokenManager;
|
||||||
import org.keycloak.services.resources.TokenService;
|
import org.keycloak.services.resources.TokenService;
|
||||||
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.core.Cookie;
|
import javax.ws.rs.core.Cookie;
|
||||||
|
@ -127,14 +128,14 @@ public class OAuthFlows {
|
||||||
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
||||||
if (!requiredActions.isEmpty()) {
|
if (!requiredActions.isEmpty()) {
|
||||||
accessCode.setRequiredActions(new HashSet<UserModel.RequiredAction>(requiredActions));
|
accessCode.setRequiredActions(new HashSet<UserModel.RequiredAction>(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)
|
return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(user)
|
||||||
.createResponse(user.getRequiredActions().iterator().next());
|
.createResponse(user.getRequiredActions().iterator().next());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isResource
|
if (!isResource
|
||||||
&& (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) {
|
&& (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()).
|
return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).
|
||||||
setAccessRequest(accessCode.getRealmRolesRequested(), accessCode.getResourceRolesRequested()).
|
setAccessRequest(accessCode.getRealmRolesRequested(), accessCode.getResourceRolesRequested()).
|
||||||
setClient(client).createOAuthGrant();
|
setClient(client).createOAuthGrant();
|
||||||
|
|
|
@ -236,6 +236,10 @@
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest-all</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate.javax.persistence</groupId>
|
<groupId>org.hibernate.javax.persistence</groupId>
|
||||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.keycloak.VerificationException;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.representations.RefreshToken;
|
||||||
import org.keycloak.util.BasicAuthHelper;
|
import org.keycloak.util.BasicAuthHelper;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.WebDriver;
|
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<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||||
|
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) {
|
public AccessToken verifyToken(String token) {
|
||||||
try {
|
try {
|
||||||
return RSATokenVerifier.verifyToken(token, realmPublicKey, realm);
|
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() {
|
public String getClientId() {
|
||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
@ -218,6 +265,11 @@ public class OAuthClient {
|
||||||
return b.build().toString();
|
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) {
|
public OAuthClient realm(String realm) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -25,10 +25,7 @@ import org.junit.Assert;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.representations.AccessToken;
|
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;
|
||||||
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
@ -37,6 +34,10 @@ import org.keycloak.testsuite.rule.WebResource;
|
||||||
import org.keycloak.testsuite.rule.WebRule;
|
import org.keycloak.testsuite.rule.WebRule;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
|
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
|
@ -66,7 +67,7 @@ public class AccessTokenTest {
|
||||||
|
|
||||||
Assert.assertEquals(200, response.getStatusCode());
|
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());
|
Assert.assertEquals("bearer", response.getTokenType());
|
||||||
|
|
||||||
|
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,9 +2,6 @@
|
||||||
"id": "test",
|
"id": "test",
|
||||||
"realm": "test",
|
"realm": "test",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"accessTokenLifespan": 600,
|
|
||||||
"accessCodeLifespan": 600,
|
|
||||||
"accessCodeLifespanUserAction": 600,
|
|
||||||
"sslNotRequired": true,
|
"sslNotRequired": true,
|
||||||
"registrationAllowed": true,
|
"registrationAllowed": true,
|
||||||
"resetPasswordAllowed": true,
|
"resetPasswordAllowed": true,
|
||||||
|
|
Loading…
Reference in a new issue