Implemented refreshing OAuth2 access token
Change-Id: I559ba31a96965d5fc2594935f179b7b3f1f2d66a
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index 2166813..d69dabd 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -134,15 +134,15 @@
public static final int MISSING_REDIRECT_URI = 1807;
public static final int INVALID_SCOPE = 1808;
public static final int INVALID_AUTHORIZATION = 1809;
+ public static final int INVALID_REFRESH_TOKEN = 1810;
- public static final int UNSUPPORTED_GRANT_TYPE = 1810;
- public static final int UNSUPPORTED_AUTHENTICATION_METHOD = 1811;
+ public static final int UNSUPPORTED_GRANT_TYPE = 1811;
+ public static final int UNSUPPORTED_AUTHENTICATION_METHOD = 1812;
- public static final int ID_TOKEN_CLAIM_ERROR = 1812;
- public static final int ID_TOKEN_SIGNING_FAILED = 1813;
- public static final int USER_REAUTHENTICATION_REQUIRED = 1814;
+ public static final int ID_TOKEN_CLAIM_ERROR = 1813;
+ public static final int ID_TOKEN_SIGNING_FAILED = 1814;
+ public static final int USER_REAUTHENTICATION_REQUIRED = 1815;
-
/**
* 1900 User account and logins
*/
diff --git a/full/Changes b/full/Changes
index 1715ded..d23bcc5 100644
--- a/full/Changes
+++ b/full/Changes
@@ -11,6 +11,7 @@
- Fixed authentication time in authentication controller (margaretha)
- Added OAuth2 access token tests (margaretha)
- Updated maven surefire setting for faster test suite runtime (margaretha)
+ - Implemented refreshing OAuth2 access token (margaretha)
version 0.60.4
05/07/2018
diff --git a/full/src/main/java/de/ids_mannheim/korap/encryption/KustvaktEncryption.java b/full/src/main/java/de/ids_mannheim/korap/encryption/KustvaktEncryption.java
index 2ae1a32..735546c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/encryption/KustvaktEncryption.java
+++ b/full/src/main/java/de/ids_mannheim/korap/encryption/KustvaktEncryption.java
@@ -179,7 +179,7 @@
@Override
public String createRandomNumber (Object ... obj) {
final byte[] rNumber = SecureRGenerator
- .getNextSecureRandom(SecureRGenerator.CORPUS_RANDOM_SIZE);
+ .getNextSecureRandom(SecureRGenerator.ID_RANDOM_SIZE);
if (obj.length == 0) {
obj = new Object[1];
obj[0] = rNumber;
@@ -245,8 +245,8 @@
private static final String SHA1_PRNG = "SHA1PRNG";
protected static final int DEFAULT_RANDOM_SIZE = 128;
protected static final int TOKEN_RANDOM_SIZE = 128;
- protected static final int USERID_RANDOM_SIZE = 64;
- protected static final int CORPUS_RANDOM_SIZE = 48;
+ protected static final int ID_RANDOM_SIZE = 128;
+ protected static final int CORPUS_RANDOM_SIZE = 64;
private static final char[] HEX_DIGIT = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'z', 'x',
'h', 'q', 'w' };
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
index 76daaab..9e96654 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessTokenDao.java
@@ -17,6 +17,7 @@
import de.ids_mannheim.korap.config.KustvaktCacheable;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.entity.AccessScope;
import de.ids_mannheim.korap.oauth2.entity.AccessToken;
import de.ids_mannheim.korap.oauth2.entity.AccessToken_;
@@ -50,14 +51,20 @@
entityManager.persist(accessToken);
}
- public void storeAccessToken (String token, Set<AccessScope> scopes,
- String userId, String clientId, ZonedDateTime authenticationTime)
- throws KustvaktException {
+ public void storeAccessToken (String token, String refreshToken,
+ Set<AccessScope> scopes, String userId, String clientId,
+ ZonedDateTime authenticationTime) throws KustvaktException {
+ ParameterChecker.checkStringValue(token, "access token");
+ ParameterChecker.checkStringValue(refreshToken, "refresh token");
ParameterChecker.checkObjectValue(scopes, "scopes");
+// ParameterChecker.checkStringValue(userId, "username");
+ ParameterChecker.checkStringValue(clientId, "client_id");
ParameterChecker.checkObjectValue(authenticationTime,
"authentication time");
+
AccessToken accessToken = new AccessToken();
accessToken.setToken(token);
+ accessToken.setRefreshToken(refreshToken);
accessToken.setScopes(scopes);
accessToken.setUserId(userId);
accessToken.setClientId(clientId);
@@ -88,7 +95,42 @@
}
catch (NoResultException e) {
throw new KustvaktException(StatusCodes.INVALID_ACCESS_TOKEN,
- "Access token is not found");
+ "Access token is not found", OAuth2Error.INVALID_TOKEN);
}
}
+
+ public AccessToken retrieveAccessTokenByRefreshToken (String refreshToken)
+ throws KustvaktException {
+
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<AccessToken> query =
+ builder.createQuery(AccessToken.class);
+ Root<AccessToken> root = query.from(AccessToken.class);
+ query.select(root);
+ query.where(builder.equal(root.get(AccessToken_.refreshToken),
+ refreshToken));
+ Query q = entityManager.createQuery(query);
+ try {
+ AccessToken token = (AccessToken) q.getSingleResult();
+ return token;
+ }
+ catch (NoResultException e) {
+ throw new KustvaktException(StatusCodes.INVALID_REFRESH_TOKEN,
+ "Refresh token is not found", OAuth2Error.INVALID_GRANT);
+ }
+
+ }
+
+ public AccessToken updateAccessToken (AccessToken accessToken)
+ throws KustvaktException {
+ ParameterChecker.checkObjectValue(accessToken, "access token");
+ AccessToken cachedToken =
+ (AccessToken) this.getCacheValue(accessToken.getId());
+ if (cachedToken != null) {
+ this.removeCacheEntry(cachedToken);
+ }
+
+ accessToken = entityManager.merge(accessToken);
+ return accessToken;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java
index ce7cfd8..e9aa596 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessToken.java
@@ -23,7 +23,7 @@
@Setter
@Entity
@Table(name = "oauth2_access_token")
-public class AccessToken implements Serializable{
+public class AccessToken implements Serializable {
private static final long serialVersionUID = 8452701765986475302L;
@@ -41,11 +41,15 @@
private boolean isRevoked;
@Column(name = "user_auth_time", updatable = false)
private ZonedDateTime userAuthenticationTime;
-
-// @OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.REMOVE)
-// @JoinColumn(name="authorization_id")
-// private Authorization authorization;
-
+ @Column(name = "refresh_token", updatable = false)
+ private String refreshToken;
+ @Column(name = "is_refresh_revoked")
+ private boolean isRefreshTokenRevoked;
+
+ // @OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.REMOVE)
+ // @JoinColumn(name="authorization_id")
+ // private Authorization authorization;
+
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "oauth2_access_token_scope",
joinColumns = @JoinColumn(name = "token_id",
@@ -55,5 +59,5 @@
uniqueConstraints = @UniqueConstraint(
columnNames = { "token_id", "scope_id" }))
private Set<AccessScope> scopes;
-
+
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
index 3a7a294..b05ed06 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
@@ -6,6 +6,7 @@
import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.codec.binary.Base64;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
@@ -70,8 +71,11 @@
String scope, code;
try {
- code = oauthIssuer.authorizationCode();
checkResponseType(authzRequest.getResponseType());
+
+ code = oauthIssuer.authorizationCode();
+// code = Base64.encodeBase64String(code.getBytes());
+
scope = createAuthorization(username, authzRequest.getClientId(),
redirectUri, authzRequest.getScopes(), code,
authenticationTime, null);
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
index e089ece..e8ad63b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
@@ -6,6 +6,7 @@
import javax.ws.rs.core.Response.Status;
+import org.apache.commons.codec.binary.Base64;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.request.AbstractOAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
@@ -22,6 +23,7 @@
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
import de.ids_mannheim.korap.oauth2.entity.AccessScope;
+import de.ids_mannheim.korap.oauth2.entity.AccessToken;
import de.ids_mannheim.korap.oauth2.entity.Authorization;
import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
import de.ids_mannheim.korap.oauth2.service.OAuth2TokenService;
@@ -56,6 +58,11 @@
oAuthRequest.getClientId(), oAuthRequest.getClientSecret(),
oAuthRequest.getScopes());
}
+ else if (grantType.equals(GrantType.REFRESH_TOKEN.toString())) {
+ return requestAccessTokenWithRefreshToken(
+ oAuthRequest.getRefreshToken(), oAuthRequest.getScopes(),
+ oAuthRequest.getClientId(), oAuthRequest.getClientSecret());
+ }
else {
throw new KustvaktException(StatusCodes.UNSUPPORTED_GRANT_TYPE,
grantType + " is not supported.",
@@ -64,6 +71,53 @@
}
+ private OAuthResponse requestAccessTokenWithRefreshToken (
+ String refreshToken, Set<String> scopes, String clientId,
+ String clientSecret)
+ throws KustvaktException, OAuthSystemException {
+
+ if (refreshToken == null || refreshToken.isEmpty()) {
+ throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
+ "Missing parameters: refresh_token",
+ OAuth2Error.INVALID_REQUEST);
+ }
+
+ clientService.authenticateClient(clientId, clientSecret);
+ AccessToken accessToken =
+ tokenDao.retrieveAccessTokenByRefreshToken(refreshToken);
+
+ if (!clientId.equals(accessToken.getClientId())) {
+ throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
+ "Client " + clientId + "is not authorized",
+ OAuth2Error.INVALID_CLIENT);
+ }
+
+ if (accessToken.isRefreshTokenRevoked()) {
+ throw new KustvaktException(StatusCodes.INVALID_REFRESH_TOKEN,
+ "Refresh token has been revoked.",
+ OAuth2Error.INVALID_GRANT);
+ }
+ else if (ZonedDateTime.now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE))
+ .isAfter(accessToken.getCreatedDate()
+ .plusSeconds(config.getRefreshTokenExpiry()))) {
+ throw new KustvaktException(StatusCodes.INVALID_REFRESH_TOKEN,
+ "Refresh token is expired.", OAuth2Error.INVALID_GRANT);
+ }
+
+ Set<AccessScope> requestedScopes = accessToken.getScopes();
+ if (scopes != null && !scopes.isEmpty()) {
+ requestedScopes = scopeService.verifyRefreshScope(scopes,
+ accessToken.getScopes());
+ }
+
+ accessToken.setRefreshTokenRevoked(true);
+ accessToken = tokenDao.updateAccessToken(accessToken);
+
+ return createsAccessTokenResponse(scopes, requestedScopes, clientId,
+ accessToken.getUserId(),
+ accessToken.getUserAuthenticationTime());
+ }
+
/**
* Issues an access token for the specified client if the
* authorization code is valid and client successfully
@@ -197,12 +251,28 @@
}
/**
- * Creates an OAuthResponse containing an access token and a
- * refresh token with type Bearer.
+ * Creates an OAuthResponse containing an access token of type
+ * Bearer. By default, MD generator is used to generates access
+ * token of 128 bit values, represented in hexadecimal comprising
+ * 32 bytes. The generated value is subsequently encoded in
+ * Base64.
*
+ * <br /><br />
+ * Additionally, a refresh token is issued. It can be used to
+ * request a new access token without requiring user
+ * reauthentication.
+ *
+ * @param scopes
+ * a set of access token scopes in String
+ * @param accessScopes
+ * a set of access token scopes in {@link AccessScope}
+ * @param clientId
+ * a client id
+ * @param userId
+ * a user id
* @param authenticationTime
- *
- * @return an OAuthResponse containing an access token
+ * the user authentication time
+ * @return an {@link OAuthResponse}
* @throws OAuthSystemException
* @throws KustvaktException
*/
@@ -212,17 +282,19 @@
throws OAuthSystemException, KustvaktException {
String accessToken = oauthIssuer.accessToken();
- // String refreshToken = oauthIssuer.refreshToken();
+// accessToken = Base64.encodeBase64String(accessToken.getBytes());
- tokenDao.storeAccessToken(accessToken, accessScopes, userId, clientId,
- authenticationTime);
+ String refreshToken = oauthIssuer.refreshToken();
+// refreshToken = Base64.encodeBase64String(refreshToken.getBytes());
+
+ tokenDao.storeAccessToken(accessToken, refreshToken, accessScopes,
+ userId, clientId, authenticationTime);
return OAuthASResponse.tokenResponse(Status.OK.getStatusCode())
.setAccessToken(accessToken)
.setTokenType(TokenType.BEARER.toString())
.setExpiresIn(String.valueOf(config.getTokenTTL()))
- // .setRefreshToken(refreshToken)
+ .setRefreshToken(refreshToken)
.setScope(String.join(" ", scopes)).buildJSONMessage();
}
-
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
index 91a51bf..45735ed 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
@@ -176,12 +176,15 @@
AccessToken accessToken =
new BearerAccessToken(config.getTokenTTL(), scope);
+ RefreshToken refreshToken = new RefreshToken();
+
tokenDao.storeAccessToken(accessToken.getValue(),
+ refreshToken.getValue(),
scopeService.convertToAccessScope(scopeSet), username,
clientIdStr, authenticationTime);
- return createsAccessTokenResponse(accessToken, scope, clientIdStr,
- username, authenticationTime, null);
+ return createsAccessTokenResponse(accessToken, refreshToken, scope,
+ clientIdStr, username, authenticationTime, null);
}
private AccessTokenResponse requestAccessTokenWithAuthorizationCode (
@@ -226,23 +229,25 @@
Scope scope = new Scope(scopeArray);
AccessToken accessToken =
new BearerAccessToken(config.getTokenTTL(), scope);
- tokenDao.storeAccessToken(accessToken.getValue(), scopes,
- authorization.getUserId(), authorization.getClientId(),
+ RefreshToken refreshToken = new RefreshToken();
+
+ tokenDao.storeAccessToken(accessToken.getValue(),
+ refreshToken.getValue(), scopes, authorization.getUserId(),
+ authorization.getClientId(),
authorization.getUserAuthenticationTime());
- return createsAccessTokenResponse(accessToken, scope,
+ return createsAccessTokenResponse(accessToken, refreshToken, scope,
authorization.getClientId(), authorization.getUserId(),
authorization.getUserAuthenticationTime(),
authorization.getNonce());
}
private AccessTokenResponse createsAccessTokenResponse (
- AccessToken accessToken, Scope scope, String clientId,
- String userId, ZonedDateTime userAuthenticationTime, String nonce)
+ AccessToken accessToken, RefreshToken refreshToken, Scope scope,
+ String clientId, String userId,
+ ZonedDateTime userAuthenticationTime, String nonce)
throws KustvaktException {
- RefreshToken refreshToken = new RefreshToken();
-
if (scope.contains("openid")) {
JWTClaimsSet claims = createIdTokenClaims(clientId, userId,
userAuthenticationTime, nonce);
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
index 6f1ce49..c820a30 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
@@ -30,7 +30,7 @@
@Autowired
private AdminDao adminDao;
-
+
/**
* Converts a set of scope strings to a set of {@link AccessScope}
*
@@ -100,4 +100,29 @@
}
}
}
+
+ /**
+ * Verify scopes given in a refresh request. The scopes must not
+ * include other scopes than those authorized in the original
+ * access token issued together with the refresh token.
+ *
+ * @param requestScopes
+ * requested scopes
+ * @param originalScopes
+ * authorized scopes
+ * @return a set of requested {@link AccessScope}
+ * @throws KustvaktException
+ */
+ public Set<AccessScope> verifyRefreshScope (Set<String> requestScopes,
+ Set<AccessScope> originalScopes) throws KustvaktException {
+ Set<AccessScope> requestedScopes = convertToAccessScope(requestScopes);
+ for (AccessScope scope : requestedScopes) {
+ if (!originalScopes.contains(scope)) {
+ throw new KustvaktException(StatusCodes.INVALID_SCOPE,
+ "Scope " + scope.getId() + " is not authorized.",
+ OAuth2Error.INVALID_SCOPE);
+ }
+ }
+ return requestedScopes;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index aff9e19..4ab46b4 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -75,8 +75,7 @@
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response requestAuthorizationCode (
@Context HttpServletRequest request,
- @Context SecurityContext context,
- @FormParam("state") String state,
+ @Context SecurityContext context, @FormParam("state") String state,
MultivaluedMap<String, String> form) {
TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
@@ -159,8 +158,9 @@
MultivaluedMap<String, String> form) {
try {
+ boolean grantTypeExist = grantType != null && !grantType.isEmpty();
AbstractOAuthTokenRequest oAuthRequest = null;
- if (grantType != null && !grantType.isEmpty() && grantType
+ if (grantTypeExist && grantType
.equals(GrantType.CLIENT_CREDENTIALS.toString())) {
oAuthRequest = new OAuthTokenRequest(
new FormRequestWrapper(request, form));
diff --git a/full/src/main/resources/db/new-mysql/V1.4__oauth2_tables.sql b/full/src/main/resources/db/new-mysql/V1.4__oauth2_tables.sql
index b8df487..4c35fca 100644
--- a/full/src/main/resources/db/new-mysql/V1.4__oauth2_tables.sql
+++ b/full/src/main/resources/db/new-mysql/V1.4__oauth2_tables.sql
@@ -59,6 +59,8 @@
created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_revoked BOOLEAN DEFAULT 0,
user_auth_time TIMESTAMP NULL,
+ refresh_token VARCHAR(255) DEFAULT NULL,
+ is_refresh_revoked BOOLEAN DEFAULT 0,
FOREIGN KEY (client_id)
REFERENCES oauth2_client(id)
);
diff --git a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
index 2310c12..dff4df8 100644
--- a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
@@ -64,6 +64,8 @@
created_date TIMESTAMP DEFAULT (datetime('now','localtime')),
is_revoked BOOLEAN DEFAULT 0,
user_auth_time TIMESTAMP NOT NULL,
+ refresh_token VARCHAR(255) DEFAULT NULL,
+ is_refresh_revoked BOOLEAN DEFAULT 0,
FOREIGN KEY (client_id)
REFERENCES oauth2_client(id)
);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index a0b143b..7fb5d4c 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -8,8 +8,10 @@
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response.Status;
+import org.apache.commons.codec.binary.Base64;
import org.apache.http.entity.ContentType;
import org.apache.oltu.oauth2.common.error.OAuthError;
+import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.TokenType;
import org.junit.Test;
import org.springframework.util.MultiValueMap;
@@ -316,7 +318,6 @@
JsonNode node = JsonUtils.readTree(entity);
assertNotNull(node.at("/access_token").asText());
- assertNotNull(node.at("/refresh_token").asText());
assertEquals(TokenType.BEARER.toString(),
node.at("/token_type").asText());
assertNotNull(node.at("/expires_in").asText());
@@ -420,21 +421,30 @@
@Test
public void testRequestTokenPasswordGrantPublic ()
throws KustvaktException {
+ String clientId = "iBr3LsTCxOj7D2o0A5m";
MultivaluedMap<String, String> form = new MultivaluedMapImpl();
form.add("grant_type", "password");
form.add("username", "dory");
form.add("password", "password");
- form.add("client_id", "iBr3LsTCxOj7D2o0A5m");
+ form.add("client_id", clientId);
ClientResponse response = requestToken(form);
String entity = response.getEntity(String.class);
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
JsonNode node = JsonUtils.readTree(entity);
assertNotNull(node.at("/access_token").asText());
- assertNotNull(node.at("/refresh_token").asText());
assertEquals(TokenType.BEARER.toString(),
node.at("/token_type").asText());
assertNotNull(node.at("/expires_in").asText());
+
+ String refreshToken = node.at("/refresh_token").asText();
+ testRequestRefreshTokenInvalidScope(clientId, refreshToken);
+ testRequestRefreshTokenPublicClient(clientId, refreshToken);
+ testRequestRefreshTokenInvalidClient(refreshToken);
+ testRequestRefreshTokenInvalidRefreshToken(clientId);
+ testRevokedRefreshToken(clientId, refreshToken);
}
@Test
@@ -538,4 +548,101 @@
node.get("error").asText());
}
+ private void testRequestRefreshTokenInvalidScope (String clientId,
+ String refreshToken) throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
+ form.add("client_id", clientId);
+ form.add("refresh_token", refreshToken);
+ form.add("scope", "search serialize_query");
+
+ ClientResponse response = resource().path("oauth2").path("token")
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).post(ClientResponse.class);
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuth2Error.INVALID_SCOPE, node.at("/error").asText());
+ }
+
+ private void testRequestRefreshTokenPublicClient (String clientId,
+ String refreshToken) throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
+ form.add("client_id", clientId);
+ form.add("refresh_token", refreshToken);
+
+ ClientResponse response = resource().path("oauth2").path("token")
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).post(ClientResponse.class);
+
+ String entity = response.getEntity(String.class);
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+ JsonNode node = JsonUtils.readTree(entity);
+ assertNotNull(node.at("/access_token").asText());
+ assertNotNull(node.at("/refresh_token").asText());
+ assertEquals(TokenType.BEARER.toString(),
+ node.at("/token_type").asText());
+ assertNotNull(node.at("/expires_in").asText());
+ }
+
+ private void testRequestRefreshTokenInvalidClient (String refreshToken)
+ throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
+ form.add("client_id", "8bIDtZnH6NvRkW2Fq");
+ form.add("refresh_token", refreshToken);
+
+ ClientResponse response = resource().path("oauth2").path("token")
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).post(ClientResponse.class);
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuth2Error.INVALID_CLIENT, node.at("/error").asText());
+ }
+
+ private void testRequestRefreshTokenInvalidRefreshToken (String clientId)
+ throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
+ form.add("client_id", clientId);
+ form.add("refresh_token", "Lia8s8w8tJeZSBlaQDrYV8ion3l");
+
+ ClientResponse response = resource().path("oauth2").path("token")
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).post(ClientResponse.class);
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuth2Error.INVALID_GRANT, node.at("/error").asText());
+ }
+
+ private void testRevokedRefreshToken (String clientId, String refreshToken)
+ throws KustvaktException {
+ MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+ form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
+ form.add("client_id", clientId);
+ form.add("refresh_token", refreshToken);
+
+ ClientResponse response = resource().path("oauth2").path("token")
+ .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+ .header(HttpHeaders.CONTENT_TYPE,
+ ContentType.APPLICATION_FORM_URLENCODED)
+ .entity(form).post(ClientResponse.class);
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuth2Error.INVALID_GRANT, node.at("/error").asText());
+ }
+
}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
index 1bea9d3..eb716ab 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
@@ -372,7 +372,6 @@
ClientResponse tokenResponse = sendTokenRequest(tokenForm);
String entity = tokenResponse.getEntity(String.class);
-
JsonNode node = JsonUtils.readTree(entity);
assertNotNull(node.at("/access_token").asText());
assertNotNull(node.at("/refresh_token").asText());