Updated RefreshToken implementations with separate DB tables. Allows
multiple access tokens per refresh token.
Change-Id: I5227191ed38a2f5260e1249d548f893a49108dce
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 f701777..b0785cd 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
@@ -11,7 +11,6 @@
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,7 +26,7 @@
import de.ids_mannheim.korap.oauth2.entity.AccessScope;
import de.ids_mannheim.korap.oauth2.entity.AccessToken;
import de.ids_mannheim.korap.oauth2.entity.AccessToken_;
-import de.ids_mannheim.korap.oauth2.entity.Authorization;
+import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
import de.ids_mannheim.korap.utils.ParameterChecker;
@Repository
@@ -43,23 +42,7 @@
super("access_token", "key:access_token");
}
- @Deprecated
- public void storeAccessToken (Authorization authorization, String token)
- throws KustvaktException {
- ParameterChecker.checkObjectValue(authorization, "Authorization");
- ParameterChecker.checkStringValue(token, "accessToken");
-
- AccessToken accessToken = new AccessToken();
- // accessToken.setAuthorization(authorization);
- accessToken.setUserId(authorization.getUserId());
- accessToken.setToken(token);
- accessToken.setScopes(authorization.getScopes());
- accessToken.setUserAuthenticationTime(
- authorization.getUserAuthenticationTime());
- entityManager.persist(accessToken);
- }
-
- public void storeAccessToken (String token, String refreshToken,
+ public void storeAccessToken (String token, RefreshToken refreshToken,
Set<AccessScope> scopes, String userId, String clientId,
ZonedDateTime authenticationTime) throws KustvaktException {
ParameterChecker.checkStringValue(token, "access token");
@@ -77,8 +60,6 @@
accessToken.setCreatedDate(now);
accessToken
.setExpiryDate(now.plusSeconds(config.getAccessTokenExpiry()));
- accessToken.setRefreshTokenExpiryDate(
- now.plusSeconds(config.getRefreshTokenExpiry()));
accessToken.setToken(token);
accessToken.setRefreshToken(refreshToken);
accessToken.setScopes(scopes);
@@ -128,57 +109,6 @@
}
@SuppressWarnings("unchecked")
- public List<AccessToken> retrieveAccessTokenByRefreshToken (
- String refreshToken) throws KustvaktException {
-
- ParameterChecker.checkStringValue(refreshToken, "refresh_token");
- CriteriaBuilder builder = entityManager.getCriteriaBuilder();
- CriteriaQuery<AccessToken> query =
- builder.createQuery(AccessToken.class);
- Root<AccessToken> root = query.from(AccessToken.class);
- Predicate condition = builder.equal(root.get(AccessToken_.refreshToken),
- refreshToken);
-
- query.select(root);
- query.where(condition);
- query.orderBy(builder.desc(root.get(AccessToken_.createdDate)));
-
- Query q = entityManager.createQuery(query);
- return q.getResultList();
- }
-
- public AccessToken retrieveAccessTokenByAnynomousToken (String token)
- throws KustvaktException {
- ParameterChecker.checkObjectValue(token, "token");
- AccessToken accessToken = (AccessToken) this.getCacheValue(token);
- if (accessToken != null) {
- return accessToken;
- }
-
- CriteriaBuilder builder = entityManager.getCriteriaBuilder();
- CriteriaQuery<AccessToken> query =
- builder.createQuery(AccessToken.class);
-
- Root<AccessToken> root = query.from(AccessToken.class);
- Predicate condition = builder.or(
- builder.equal(root.get(AccessToken_.token), token),
- builder.equal(root.get(AccessToken_.refreshToken), token));
-
- query.select(root);
- query.where(condition);
- Query q = entityManager.createQuery(query);
-
- try {
- accessToken = (AccessToken) q.getSingleResult();
- return accessToken;
- }
- catch (NoResultException e) {
- throw new KustvaktException(StatusCodes.INVALID_ACCESS_TOKEN,
- "Access token is not found", OAuth2Error.INVALID_TOKEN);
- }
- }
-
- @SuppressWarnings("unchecked")
public List<AccessToken> retrieveAccessTokenByClientId (String clientId) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<AccessToken> query =
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/RefreshTokenDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/RefreshTokenDao.java
new file mode 100644
index 0000000..e98c627
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/RefreshTokenDao.java
@@ -0,0 +1,94 @@
+package de.ids_mannheim.korap.oauth2.dao;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.FullConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.entity.AccessScope;
+import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
+import de.ids_mannheim.korap.oauth2.entity.RefreshToken_;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+@Repository
+@Transactional
+public class RefreshTokenDao {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+ @Autowired
+ private FullConfiguration config;
+
+ public RefreshToken storeRefreshToken (String refreshToken, String userId,
+ ZonedDateTime userAuthenticationTime, String clientId,
+ Set<AccessScope> scopes) throws KustvaktException {
+ ParameterChecker.checkStringValue(refreshToken, "refresh token");
+ // ParameterChecker.checkStringValue(userId, "username");
+ ParameterChecker.checkStringValue(clientId, "client_id");
+ ParameterChecker.checkObjectValue(scopes, "scopes");
+
+ ZonedDateTime now =
+ ZonedDateTime.now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE));
+
+ RefreshToken token = new RefreshToken();
+ token.setToken(refreshToken);
+ token.setUserId(userId);
+ token.setUserAuthenticationTime(userAuthenticationTime);
+ token.setClientId(clientId);
+ token.setCreatedDate(now);
+ token.setExpiryDate(now.plusSeconds(config.getRefreshTokenExpiry()));
+ token.setScopes(scopes);
+
+ entityManager.persist(token);
+ return token;
+ }
+
+ public RefreshToken retrieveRefreshToken (String token)
+ throws KustvaktException {
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<RefreshToken> query =
+ builder.createQuery(RefreshToken.class);
+ Root<RefreshToken> root = query.from(RefreshToken.class);
+ query.select(root);
+ query.where(builder.equal(root.get(RefreshToken_.token), token));
+ Query q = entityManager.createQuery(query);
+ return (RefreshToken) q.getSingleResult();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<RefreshToken> retrieveRefreshTokenByClientId (String clientId)
+ throws KustvaktException {
+ ParameterChecker.checkStringValue(clientId, "client_id");
+
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<RefreshToken> query =
+ builder.createQuery(RefreshToken.class);
+ Root<RefreshToken> root = query.from(RefreshToken.class);
+ query.select(root);
+ query.where(builder.equal(root.get(RefreshToken_.clientId), clientId));
+ Query q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
+
+ public RefreshToken updateRefreshToken (RefreshToken token)
+ throws KustvaktException {
+ ParameterChecker.checkObjectValue(token, "refresh_token");
+
+ token = entityManager.merge(token);
+ return token;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
index b26a90a..8fb2e0d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
@@ -38,6 +38,9 @@
@ManyToMany(mappedBy = "scopes", fetch = FetchType.LAZY)
private List<AccessToken> accessTokens;
+
+ @ManyToMany(mappedBy = "scopes", fetch = FetchType.LAZY)
+ private List<RefreshToken> refreshTokens;
@Override
public String toString () {
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 e7509d0..ba381de 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
@@ -13,6 +13,7 @@
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@@ -43,12 +44,6 @@
private boolean isRevoked;
@Column(name = "user_auth_time", updatable = false)
private ZonedDateTime userAuthenticationTime;
- @Column(name = "refresh_token", updatable = false)
- private String refreshToken;
- @Column(name = "refresh_expiry_date", updatable = false)
- private ZonedDateTime refreshTokenExpiryDate;
- @Column(name = "is_refresh_revoked")
- private boolean isRefreshTokenRevoked;
// @OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.REMOVE)
// @JoinColumn(name="authorization_id")
@@ -64,4 +59,7 @@
columnNames = { "token_id", "scope_id" }))
private Set<AccessScope> scopes;
+ @ManyToOne(fetch=FetchType.LAZY)
+ @JoinColumn(name="refresh_token")
+ private RefreshToken refreshToken;
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/RefreshToken.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/RefreshToken.java
new file mode 100644
index 0000000..a43a493
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/RefreshToken.java
@@ -0,0 +1,126 @@
+package de.ids_mannheim.korap.oauth2.entity;
+
+import java.time.ZonedDateTime;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+@Entity
+@Table(name = "oauth2_refresh_token")
+public class RefreshToken {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int id;
+ private String token;
+ @Column(name = "created_date", updatable = false)
+ private ZonedDateTime createdDate;
+ @Column(name = "expiry_date", updatable = false)
+ private ZonedDateTime expiryDate;
+ @Column(name = "user_id")
+ private String userId;
+ @Column(name = "client_id")
+ private String clientId;
+ @Column(name = "user_auth_time", updatable = false)
+ private ZonedDateTime userAuthenticationTime;
+ @Column(name = "is_revoked")
+ private boolean isRevoked;
+
+ @OneToMany(fetch = FetchType.EAGER, mappedBy = "refreshToken")
+ private Set<AccessToken> accessTokens;
+
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(name = "oauth2_refresh_token_scope",
+ joinColumns = @JoinColumn(name = "token_id",
+ referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "scope_id",
+ referencedColumnName = "id"),
+ uniqueConstraints = @UniqueConstraint(
+ columnNames = { "token_id", "scope_id" }))
+ private Set<AccessScope> scopes;
+
+ public String getToken () {
+ return token;
+ }
+
+ public void setToken (String token) {
+ this.token = token;
+ }
+
+ public ZonedDateTime getCreatedDate () {
+ return createdDate;
+ }
+
+ public void setCreatedDate (ZonedDateTime createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ public ZonedDateTime getExpiryDate () {
+ return expiryDate;
+ }
+
+ public void setExpiryDate (ZonedDateTime expiryDate) {
+ this.expiryDate = expiryDate;
+ }
+
+ public String getUserId () {
+ return userId;
+ }
+
+ public void setUserId (String userId) {
+ this.userId = userId;
+ }
+
+ public String getClientId () {
+ return clientId;
+ }
+
+ public void setClientId (String clientId) {
+ this.clientId = clientId;
+ }
+
+ public ZonedDateTime getUserAuthenticationTime () {
+ return userAuthenticationTime;
+ }
+
+ public void setUserAuthenticationTime (
+ ZonedDateTime userAuthenticationTime) {
+ this.userAuthenticationTime = userAuthenticationTime;
+ }
+
+ public boolean isRevoked () {
+ return isRevoked;
+ }
+
+ public void setRevoked (boolean isRevoked) {
+ this.isRevoked = isRevoked;
+ }
+
+ public Set<AccessToken> getAccessTokens () {
+ return accessTokens;
+ }
+
+ public void setAccessTokens (Set<AccessToken> accessTokens) {
+ this.accessTokens = accessTokens;
+ }
+
+ public Set<AccessScope> getScopes () {
+ return scopes;
+ }
+
+ public void setScopes (Set<AccessScope> scopes) {
+ this.scopes = scopes;
+ }
+
+}
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 7730b1e..fb7b541 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
@@ -3,9 +3,9 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
+import javax.persistence.NoResultException;
import javax.ws.rs.core.Response.Status;
import org.apache.oltu.oauth2.as.request.AbstractOAuthTokenRequest;
@@ -23,10 +23,12 @@
import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
+import de.ids_mannheim.korap.oauth2.dao.RefreshTokenDao;
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.entity.RefreshToken;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenRequest;
import de.ids_mannheim.korap.oauth2.service.OAuth2TokenService;
@@ -38,6 +40,8 @@
@Autowired
private AccessTokenDao tokenDao;
+ @Autowired
+ private RefreshTokenDao refreshDao;
public OAuthResponse requestAccessToken (
AbstractOAuthTokenRequest oAuthRequest)
@@ -82,7 +86,7 @@
* Client authentication is done using the given client
* credentials.
*
- * @param refreshToken
+ * @param refreshTokenStr
* @param scopes
* @param clientId
* @param clientSecret
@@ -91,58 +95,52 @@
* @throws OAuthSystemException
*/
private OAuthResponse requestAccessTokenWithRefreshToken (
- String refreshToken, Set<String> scopes, String clientId,
+ String refreshTokenStr, Set<String> scopes, String clientId,
String clientSecret)
throws KustvaktException, OAuthSystemException {
- if (refreshToken == null || refreshToken.isEmpty()) {
+ if (refreshTokenStr == null || refreshTokenStr.isEmpty()) {
throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
"Missing parameters: refresh_token",
OAuth2Error.INVALID_REQUEST);
}
clientService.authenticateClient(clientId, clientSecret);
- List<AccessToken> accessTokenList =
- tokenDao.retrieveAccessTokenByRefreshToken(refreshToken);
- if (accessTokenList.isEmpty()) {
+
+ RefreshToken refreshToken;
+ try {
+ refreshToken = refreshDao.retrieveRefreshToken(refreshTokenStr);
+ }
+ catch (NoResultException e) {
throw new KustvaktException(StatusCodes.INVALID_REFRESH_TOKEN,
"Refresh token is not found", OAuth2Error.INVALID_GRANT);
}
- AccessToken latestAccessToken = accessTokenList.get(0);
- AccessToken origin = accessTokenList.get(accessTokenList.size() - 1);
-
- if (!clientId.equals(latestAccessToken.getClientId())) {
+ if (!clientId.equals(refreshToken.getClientId())) {
throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
"Client " + clientId + "is not authorized",
OAuth2Error.INVALID_CLIENT);
}
-
- if (latestAccessToken.isRefreshTokenRevoked()) {
+ else if (refreshToken.isRevoked()) {
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(origin.getRefreshTokenExpiryDate())) {
+ .isAfter(refreshToken.getExpiryDate())) {
throw new KustvaktException(StatusCodes.INVALID_REFRESH_TOKEN,
"Refresh token is expired.", OAuth2Error.INVALID_GRANT);
}
- Set<AccessScope> requestedScopes = latestAccessToken.getScopes();
+ Set<AccessScope> requestedScopes = refreshToken.getScopes();
if (scopes != null && !scopes.isEmpty()) {
- requestedScopes = scopeService.verifyRefreshScope(scopes,
- latestAccessToken.getScopes());
- }
-
- if (!latestAccessToken.isRevoked()) {
- latestAccessToken.setRevoked(true);
- tokenDao.updateAccessToken(latestAccessToken);
+ requestedScopes =
+ scopeService.verifyRefreshScope(scopes, requestedScopes);
}
return createsAccessTokenResponse(scopes, requestedScopes, clientId,
- latestAccessToken.getUserId(),
- latestAccessToken.getUserAuthenticationTime(), refreshToken);
+ refreshToken.getUserId(),
+ refreshToken.getUserAuthenticationTime(), refreshToken);
}
/**
@@ -310,17 +308,19 @@
ZonedDateTime authenticationTime)
throws OAuthSystemException, KustvaktException {
- String refreshToken = randomGenerator.createRandomCode();
+ String random = randomGenerator.createRandomCode();
+ RefreshToken refreshToken = refreshDao.storeRefreshToken(random, userId,
+ authenticationTime, clientId, accessScopes);
return createsAccessTokenResponse(scopes, accessScopes, clientId,
userId, authenticationTime, refreshToken);
}
private OAuthResponse createsAccessTokenResponse (Set<String> scopes,
Set<AccessScope> accessScopes, String clientId, String userId,
- ZonedDateTime authenticationTime, String refreshToken)
+ ZonedDateTime authenticationTime, RefreshToken refreshToken)
throws OAuthSystemException, KustvaktException {
- String accessToken = randomGenerator.createRandomCode();
+ String accessToken = randomGenerator.createRandomCode();
tokenDao.storeAccessToken(accessToken, refreshToken, accessScopes,
userId, clientId, authenticationTime);
@@ -328,7 +328,7 @@
.setAccessToken(accessToken)
.setTokenType(TokenType.BEARER.toString())
.setExpiresIn(String.valueOf(config.getAccessTokenExpiry()))
- .setRefreshToken(refreshToken)
+ .setRefreshToken(refreshToken.getToken())
.setScope(String.join(" ", scopes)).buildJSONMessage();
}
@@ -368,17 +368,23 @@
}
private boolean revokeRefreshToken (String token) throws KustvaktException {
- List<AccessToken> accessTokenList =
- tokenDao.retrieveAccessTokenByRefreshToken(token);
- if (accessTokenList.isEmpty()) {
+ RefreshToken refreshToken = null;
+ try {
+ refreshToken = refreshDao.retrieveRefreshToken(token);
+ }
+ catch (NoResultException e) {
return false;
}
+ refreshToken.setRevoked(true);
+ refreshDao.updateRefreshToken(refreshToken);
+
+ Set<AccessToken> accessTokenList = refreshToken.getAccessTokens();
for (AccessToken accessToken : accessTokenList) {
accessToken.setRevoked(true);
- accessToken.setRefreshTokenRevoked(true);
tokenDao.updateAccessToken(accessToken);
}
+
return true;
}
}
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 a17b4c8..15570e1 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
@@ -47,6 +47,7 @@
import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
+import de.ids_mannheim.korap.oauth2.dao.RefreshTokenDao;
import de.ids_mannheim.korap.oauth2.entity.AccessScope;
import de.ids_mannheim.korap.oauth2.entity.Authorization;
import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
@@ -73,6 +74,8 @@
@Autowired
private AccessTokenDao tokenDao;
+ @Autowired
+ private RefreshTokenDao refreshDao;
public AccessTokenResponse requestAccessToken (TokenRequest tokenRequest)
throws KustvaktException {
@@ -177,10 +180,11 @@
new BearerAccessToken(config.getAccessTokenExpiry(), scope);
RefreshToken refreshToken = new RefreshToken();
-
- tokenDao.storeAccessToken(accessToken.getValue(),
- refreshToken.getValue(),
- scopeService.convertToAccessScope(scopeSet), username,
+ Set<AccessScope> scopes = scopeService.convertToAccessScope(scopeSet);
+ de.ids_mannheim.korap.oauth2.entity.RefreshToken rt =
+ refreshDao.storeRefreshToken(refreshToken.getValue(), username,
+ authenticationTime, clientId.getValue(), scopes);
+ tokenDao.storeAccessToken(accessToken.getValue(), rt, scopes, username,
clientIdStr, authenticationTime);
return createsAccessTokenResponse(accessToken, refreshToken, scope,
@@ -230,10 +234,14 @@
AccessToken accessToken =
new BearerAccessToken(config.getAccessTokenExpiry(), scope);
RefreshToken refreshToken = new RefreshToken();
+ de.ids_mannheim.korap.oauth2.entity.RefreshToken rt =
+ refreshDao.storeRefreshToken(refreshToken.getValue(),
+ authorization.getUserId(),
+ authorization.getUserAuthenticationTime(),
+ authorization.getClientId(), scopes);
- tokenDao.storeAccessToken(accessToken.getValue(),
- refreshToken.getValue(), scopes, authorization.getUserId(),
- authorization.getClientId(),
+ tokenDao.storeAccessToken(accessToken.getValue(), rt, scopes,
+ authorization.getUserId(), authorization.getClientId(),
authorization.getUserAuthenticationTime());
return createsAccessTokenResponse(accessToken, refreshToken, scope,
@@ -265,7 +273,6 @@
}
}
-
private String[] extractClientCredentials (
ClientAuthentication clientAuthentication)
throws KustvaktException {
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
index 6e8803a..f077e33 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
@@ -21,11 +21,13 @@
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
import de.ids_mannheim.korap.oauth2.dao.OAuth2ClientDao;
+import de.ids_mannheim.korap.oauth2.dao.RefreshTokenDao;
import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientDto;
import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientInfoDto;
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.entity.RefreshToken;
import de.ids_mannheim.korap.oauth2.interfaces.AuthorizationDaoInterface;
import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
@@ -54,6 +56,8 @@
@Autowired
private AccessTokenDao tokenDao;
@Autowired
+ private RefreshTokenDao refreshDao;
+ @Autowired
private AuthorizationDaoInterface authorizationDao;
@Autowired
private AdminDao adminDao;
@@ -207,9 +211,15 @@
tokenDao.retrieveAccessTokenByClientId(clientId);
for (AccessToken token : tokens) {
token.setRevoked(true);
- token.setRefreshTokenRevoked(true);
tokenDao.updateAccessToken(token);
}
+
+ List<RefreshToken> refreshTokens =
+ refreshDao.retrieveRefreshTokenByClientId(clientId);
+ for (RefreshToken token : refreshTokens) {
+ token.setRevoked(true);
+ refreshDao.updateRefreshToken(token);
+ }
}
public OAuth2ClientDto resetSecret (String clientId, String clientSecret,
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 ac082a9..99e7f68 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
@@ -85,10 +85,10 @@
TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
String username = tokenContext.getUsername();
ZonedDateTime authTime = tokenContext.getAuthenticationTime();
-
+
try {
scopeService.verifyScope(tokenContext, OAuth2Scope.AUTHORIZE);
-
+
HttpServletRequest requestWithForm =
new FormRequestWrapper(request, form);
OAuth2AuthorizationRequest authzRequest =
@@ -108,7 +108,6 @@
}
}
-
/**
* Grants a client an access token, namely a string used in
* authenticated requests representing user authorization for
@@ -117,9 +116,8 @@
*
* <br /><br />
*
- * Clients may refreshing access token using this endpoint. This
- * request will revoke all access token associated with the given
- * refresh token, and grants a new access token. The refresh token
+ * Clients may request refresh access token using this endpoint.
+ * This request will grants a new access token. The refresh token
* is not changed and can be used until it expires.
*
* <br /><br />
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
deleted file mode 100644
index 2c43acc..0000000
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthController.java
+++ /dev/null
@@ -1,631 +0,0 @@
-package de.ids_mannheim.korap.web.controller;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.HeaderParam;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
-
-import org.apache.oltu.oauth2.as.issuer.MD5Generator;
-import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
-import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
-import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
-import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
-import org.apache.oltu.oauth2.as.response.OAuthASResponse;
-import org.apache.oltu.oauth2.common.OAuth;
-import org.apache.oltu.oauth2.common.error.OAuthError;
-import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
-import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
-import org.apache.oltu.oauth2.common.message.OAuthResponse;
-import org.apache.oltu.oauth2.common.message.types.GrantType;
-import org.apache.oltu.oauth2.common.message.types.ResponseType;
-import org.apache.oltu.oauth2.common.utils.OAuthUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import com.sun.jersey.spi.container.ContainerRequest;
-import com.sun.jersey.spi.container.ResourceFilters;
-
-import de.ids_mannheim.korap.config.Attributes;
-import de.ids_mannheim.korap.config.AuthCodeInfo;
-import de.ids_mannheim.korap.config.ClientInfo;
-import de.ids_mannheim.korap.config.KustvaktConfiguration;
-import de.ids_mannheim.korap.config.Scopes;
-import de.ids_mannheim.korap.constant.AuthenticationMethod;
-import de.ids_mannheim.korap.constant.TokenType;
-import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.handlers.OAuth2Handler;
-import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
-import de.ids_mannheim.korap.security.context.TokenContext;
-import de.ids_mannheim.korap.user.User;
-import de.ids_mannheim.korap.user.UserDetails;
-import de.ids_mannheim.korap.user.Userdata;
-import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.utils.StringUtils;
-import de.ids_mannheim.korap.web.CoreResponseHandler;
-import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
-import de.ids_mannheim.korap.web.filter.BlockingFilter;
-import de.ids_mannheim.korap.web.filter.DemoUserFilter;
-import de.ids_mannheim.korap.web.filter.PiwikFilter;
-import de.ids_mannheim.korap.web.utils.FormRequestWrapper;
-
-/**
- * @author hanl
- * @date 07/06/2014
- */
-//todo: only allow oauth2 access_token requests GET methods?
-//todo: allow refresh tokens
-//@Path("/oauth2")
-@Deprecated
-public class OAuthController {
-
- @Autowired
- private CoreResponseHandler kustvaktResponseHandler;
-
- private OAuth2Handler handler;
- @Autowired
- private AuthenticationManagerIface controller;
-
-// private EncryptionIface crypto;
- @Autowired
- private KustvaktConfiguration config;
-
-
- public OAuthController () {
-// this.handler = new OAuth2Handler(BeansFactory.getKustvaktContext()
-// .getPersistenceClient());
-// this.controller = BeansFactory.getKustvaktContext()
-// .getAuthenticationManager();
-// this.crypto = BeansFactory.getKustvaktContext().getEncryption();
- }
-
-
-// @POST
-// @Path("unregister")
-// @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
-// public Response unregisterClient (@Context SecurityContext context,
-// @HeaderParam("Host") String host,
-// @QueryParam("client_secret") String secret,
-// @QueryParam("client_id") String client_id) {
-// ClientInfo info = new ClientInfo(client_id, secret);
-// info.setUrl(host);
-// TokenContext ctx = (TokenContext) context.getUserPrincipal();
-// try {
-// this.handler.getPersistenceHandler().removeClient(info,
-// this.controller.getUser(ctx.getUsername()));
-// }
-// catch (KustvaktException e) {
-// throw kustvaktResponseHandler.throwit(e);
-// }
-// return Response.ok().build();
-// }
-
-
-// @POST
-// @Path("register")
-// @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
-// public Response registerClient (@Context SecurityContext context,
-// @HeaderParam("Host") String host,
-// @QueryParam("redirect_url") String rurl) {
-// ClientInfo info = new ClientInfo(crypto.createRandomNumber(),
-// crypto.createToken());
-// info.setUrl(host);
-// if (rurl == null)
-// throw kustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT,
-// "Missing parameter!", "redirect_url");
-// info.setRedirect_uri(rurl);
-// TokenContext ctx = (TokenContext) context.getUserPrincipal();
-// String json = "";
-// try {
-// User user = this.controller.getUser(ctx.getUsername());
-// this.handler.getPersistenceHandler().registerClient(info, user);
-// json = info.toJSON();
-// }
-// catch (KustvaktException e) {
-// throw kustvaktResponseHandler.throwit(e);
-// }
-// return Response.ok(json).build();
-// }
-
-
- @GET
- @Path("info")
- @ResourceFilters({ AuthenticationFilter.class, DemoUserFilter.class, PiwikFilter.class })
- public Response getStatus (@Context SecurityContext context,
- @QueryParam("scope") String scopes) {
- TokenContext ctx = (TokenContext) context.getUserPrincipal();
- Userdata data;
- String json= "";
- try {
- User user = this.controller.getUser(ctx.getUsername());
- data = this.controller.getUserData(user, UserDetails.class);
- Set<String> base_scope = StringUtils.toSet(scopes, " ");
- base_scope.retainAll(StringUtils.toSet(scopes));
- scopes = StringUtils.toString(base_scope);
- json = JsonUtils.toJSON(Scopes.mapScopes(scopes, data));
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- // json format with scope callback parameter
- // todo: add other scopes as well!
- return Response.ok(json).build();
- }
-
-
- @GET
- @Path("authorizations")
- @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
- public Response getAuthorizations (@Context SecurityContext context,
- @HeaderParam(ContainerRequest.USER_AGENT) String agent,
- @HeaderParam(ContainerRequest.HOST) String host) {
- // works on all tokens, but access to native apps cannot be revoked!
- // check secret and id and retrieve access tokens
- TokenContext ctx = (TokenContext) context.getUserPrincipal();
- try {
- User user = this.controller.getUser(ctx.getUsername());
- Collection auths = this.handler.getPersistenceHandler().getAuthorizedClients(user.getId());
- if (auths.isEmpty())
- return Response.noContent().build();
- return Response.ok(JsonUtils.toJSON(auths)).build();
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- }
-
-
- // todo: scopes for access_token are defined here
- // todo: if user already has an access token registered for client and application, then redirect to token endpoint to retrieve that token
- // todo: demo account should be disabled for this function --> if authentication failed, client must redirect to login url (little login window)
- @POST
- @Path("authorize")
- @Consumes("application/x-www-form-urlencoded")
- @Produces("application/json")
- @ResourceFilters({ BlockingFilter.class })
- public Response authorize (@Context HttpServletRequest request,
- @Context SecurityContext context,
- @HeaderParam(ContainerRequest.USER_AGENT) String agent,
- @HeaderParam(ContainerRequest.HOST) String host,
- MultivaluedMap<String, String> form) throws OAuthSystemException,
- URISyntaxException {
- // user needs to be authenticated to this service!
- TokenContext c = (TokenContext) context.getUserPrincipal();
-
- try {
- OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(
- new FormRequestWrapper(request, form));
- OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(
- new MD5Generator());
- User user;
-
- Map<String, Object> attr = new HashMap<>();
- attr.put(Attributes.HOST, host);
- attr.put(Attributes.USER_AGENT, agent);
- attr.put(Attributes.USERNAME, c.getUsername());
- // also extractable via authorization header
- attr.put(Attributes.CLIENT_ID, oauthRequest.getClientId());
- attr.put(Attributes.CLIENT_SECRET, oauthRequest.getClientSecret());
- StringBuilder scopes = new StringBuilder();
- for (String scope : oauthRequest.getScopes())
- scopes.append(scope + " ");
- attr.put(Attributes.SCOPE, scopes.toString());
-
- try {
- user = controller.getUser(c.getUsername());
- // EM: not in the new DB
-// Userdata data = controller.getUserData(user, UserDetails.class);
-// user.addUserData(data);
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
-
- // register response according to response_type
- String responseType = oauthRequest
- .getParam(OAuth.OAUTH_RESPONSE_TYPE);
-
- final String authorizationCode = oauthIssuerImpl
- .authorizationCode();
- ClientInfo info = this.handler.getPersistenceHandler()
- .getClient(oauthRequest.getClientId());
-
- if (info == null
- || !info.getClient_secret().equals(
- oauthRequest.getClientSecret())) {
- OAuthResponse res = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
- .setError(OAuthError.CodeResponse.UNAUTHORIZED_CLIENT)
- .setErrorDescription("Unauthorized client!\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
-
- if (!info.getRedirect_uri().contains(oauthRequest.getRedirectURI())) {
- OAuthResponse res = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
- .setError(OAuthError.CodeResponse.INVALID_REQUEST)
- .setErrorDescription("Unauthorized redirect!\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
-
- String accessToken = this.handler.getPersistenceHandler().getToken(
- oauthRequest.getClientId(), user.getId());
-
- //todo: test correct redirect and parameters
- if (accessToken != null) {
- // fixme: correct status code?
- OAuthASResponse.OAuthResponseBuilder builder = OAuthASResponse
- .status(HttpServletResponse.SC_FOUND);
- final OAuthResponse response = builder
- .location("/oauth2/token")
- .setParam(OAuth.OAUTH_CLIENT_ID,
- oauthRequest.getClientId())
- .setParam(OAuth.OAUTH_CLIENT_SECRET,
- oauthRequest.getClientSecret())
- .buildQueryMessage();
- return Response.status(response.getResponseStatus())
- .location(new URI(response.getLocationUri())).build();
- }
-
- final OAuthResponse response;
- String redirectURI = oauthRequest.getRedirectURI();
- if (OAuthUtils.isEmpty(redirectURI)) {
- throw new WebApplicationException(
- Response.status(HttpServletResponse.SC_BAD_REQUEST)
- .entity("OAuth callback url needs to be provided by client!!!\n")
- .build());
- }
-
- if (responseType.equals(ResponseType.CODE.toString())) {
- OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse
- .authorizationResponse(request,
- HttpServletResponse.SC_FOUND);
- builder.location(redirectURI);
-
- try {
- AuthCodeInfo codeInfo = new AuthCodeInfo(
- info.getClient_id(), authorizationCode);
- codeInfo.setScopes(StringUtils.toString(
- oauthRequest.getScopes(), " "));
- this.handler.authorize(codeInfo, user);
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- builder.setParam(OAuth.OAUTH_RESPONSE_TYPE,
- ResponseType.CODE.toString());
- builder.setCode(authorizationCode);
- response = builder.buildBodyMessage();
-
- }
- else if (responseType.contains(ResponseType.TOKEN.toString())) {
- OAuthASResponse.OAuthTokenResponseBuilder builder = OAuthASResponse
- .tokenResponse(HttpServletResponse.SC_OK);
- builder.setParam(OAuth.OAUTH_RESPONSE_TYPE,
- ResponseType.TOKEN.toString());
- builder.location(redirectURI);
-
- String token = oauthIssuerImpl.accessToken();
- String refresh = oauthIssuerImpl.refreshToken();
-
- this.handler.getPersistenceHandler().addToken(token, refresh, user.getId(),
- oauthRequest.getClientId(),
- StringUtils.toString(oauthRequest.getScopes(), " "),
- config.getLongTokenTTL());
- builder.setAccessToken(token);
- builder.setRefreshToken(refresh);
- builder.setExpiresIn(String.valueOf(config.getLongTokenTTL()));
-
- // skips authorization code type and returns id_token and access token directly
- if (oauthRequest.getScopes().contains("openid")) {
- try {
- // EM: MH uses APIAuthentication to create api token
- TokenContext new_context = this.controller
- .createTokenContext(user, attr, null);
- builder.setParam(new_context.getTokenType().displayName(),
- new_context.getToken());
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- }
- response = builder.buildBodyMessage();
- }
- else {
- OAuthResponse res = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
- .setError(
- OAuthError.CodeResponse.UNSUPPORTED_RESPONSE_TYPE)
- .setErrorDescription("Unsupported Response type!\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
- //
- // String redirectURI = oauthRequest.getRedirectURI();
- //
- // // enables state parameter to disable cross-site scripting attacks
- // final OAuthResponse response = builder.location(redirectURI)
- // .buildQueryMessage();
- // if (OAuthUtils.isEmpty(redirectURI)) {
- // throw new WebApplicationException(
- // Response.status(HttpServletResponse.SC_BAD_REQUEST)
- // .entity("OAuth callback url needs to be provided by client!!!\n")
- // .build());
- // }
-
- return Response.status(response.getResponseStatus())
- .location(new URI(response.getLocationUri())).build();
- }
- catch (OAuthProblemException e) {
- final Response.ResponseBuilder responseBuilder = Response
- .status(HttpServletResponse.SC_BAD_REQUEST);
- String redirectUri = e.getRedirectUri();
-
- if (OAuthUtils.isEmpty(redirectUri))
- throw new WebApplicationException(
- responseBuilder
- .entity("OAuth callback url needs to be provided by client!!!\n")
- .build());
-
- final OAuthResponse response = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
- .location(redirectUri).buildQueryMessage();
- final URI location = new URI(response.getLocationUri());
- return responseBuilder.location(location).build();
- }
- catch (OAuthSystemException | URISyntaxException | KustvaktException e) {
- e.printStackTrace();
- }
- return Response.noContent().build();
- }
-
-
- @POST
- @Path("revoke")
- public Response revokeToken (@Context HttpServletRequest request,
- @Context SecurityContext context,
- @HeaderParam(ContainerRequest.USER_AGENT) String agent,
- @HeaderParam(ContainerRequest.HOST) String host)
- throws OAuthSystemException, URISyntaxException {
- TokenContext ctx = (TokenContext) context.getUserPrincipal();
- try {
-
- if (!this.handler.getPersistenceHandler().revokeToken(ctx.getToken())) {
- OAuthResponse res = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
- .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
- .setErrorDescription("Invalid access token!\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
-
- }
- catch (KustvaktException e) {
- e.printStackTrace();
- // fixme: do something
- /**
- * final Response.ResponseBuilder responseBuilder =
- * Response
- * .status(HttpServletResponse.SC_FOUND);
- * String redirectUri = e.getRedirectUri();
- *
- * final OAuthResponse response = OAuthASResponse
- * .errorResponse(HttpServletResponse.SC_FOUND).error(e)
- * .location(redirectUri).buildQueryMessage();
- * final URI location = new
- * URI(response.getLocationUri());
- * return responseBuilder.location(location).build();
- */
- }
-
- return Response.ok().build();
- }
-
-
- @POST
- @Consumes("application/x-www-form-urlencoded")
- @Produces("application/json")
- @Path("token")
- public Response requestToken (@Context HttpServletRequest request,
- @HeaderParam(ContainerRequest.USER_AGENT) String agent,
- @HeaderParam(ContainerRequest.HOST) String host, MultivaluedMap form)
- throws OAuthSystemException {
- boolean openid_valid = false;
- User user = null;
- OAuthTokenRequest oauthRequest;
- OAuthASResponse.OAuthTokenResponseBuilder builder = OAuthASResponse
- .tokenResponse(HttpServletResponse.SC_OK);
-
- OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
- ClientInfo info;
- try {
- oauthRequest = new OAuthTokenRequest(new FormRequestWrapper(
- request, form));
-
- if ((info = this.handler.getPersistenceHandler().getClient(oauthRequest.getClientId())) == null) {
- OAuthResponse res = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
- .setError(OAuthError.TokenResponse.INVALID_CLIENT)
- .setErrorDescription("Invalid client id!\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
- else if (!info.getClient_secret().equals(
- oauthRequest.getClientSecret())) {
- OAuthResponse res = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
- .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
- .setErrorDescription("Invalid client secret!\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
-
- Map<String, Object> attr = new HashMap<>();
- attr.put(Attributes.HOST, host);
- attr.put(Attributes.USER_AGENT, agent);
- attr.put(Attributes.SCOPE,
- StringUtils.toString(oauthRequest.getScopes(), " "));
-
- // support code (for external clients only) and password grant type
- // password grant at this point is only allowed with trusted clients (korap frontend)
- if (oauthRequest.getGrantType().equalsIgnoreCase(
- GrantType.AUTHORIZATION_CODE.toString())) {
- // validate auth code
- AuthCodeInfo codeInfo;
- try {
- //can this be joined with the simple retrieval of access tokens?
- // partially yes: auth code can be valid, even though no access token exists
- // --> zero result set
- codeInfo = this.handler.getAuthorization(oauthRequest
- .getCode());
- if (codeInfo == null) {
- OAuthResponse res = OAuthASResponse
- .errorResponse(
- HttpServletResponse.SC_UNAUTHORIZED)
- .setError(
- OAuthError.TokenResponse.INVALID_REQUEST)
- .setErrorDescription(
- "Invalid authorization code\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
- else {
- openid_valid = codeInfo.getScopes().contains("openid");
- String accessToken = oauthIssuerImpl.accessToken();
- String refreshToken = oauthIssuerImpl.refreshToken();
- // auth code posesses the user reference. native apps access_tokens are directly associated with the user
- this.handler
- .addToken(oauthRequest.getCode(), accessToken,
- refreshToken, config.getTokenTTL());
-
- builder.setTokenType(TokenType.BEARER.displayName());
- builder.setExpiresIn(String.valueOf(config
- .getLongTokenTTL()));
- builder.setAccessToken(accessToken);
- builder.setRefreshToken(refreshToken);
- }
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- // todo: errors for invalid scopes or different scopes then during authorization request?
- //todo ??
- attr.put(Attributes.SCOPE, codeInfo.getScopes());
-
- }
- else if (oauthRequest.getGrantType().equalsIgnoreCase(
- GrantType.PASSWORD.toString())) {
- //fixme: via https; as basic auth header and only if client is native!
- if (!info.isConfidential()) {
- OAuthResponse res = OAuthASResponse
- .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
- .setError(
- OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
- .setErrorDescription(
- "Grant type not supported for client!\n")
- .buildJSONMessage();
- return Response.status(res.getResponseStatus())
- .entity(res.getBody()).build();
- }
-
- openid_valid = true;
- try {
- // EM: MH uses database
- user = controller.authenticate(AuthenticationMethod.DATABASE,
- oauthRequest.getUsername(),
- oauthRequest.getPassword(), attr);
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
-
- try {
- String accessToken = this.handler.getPersistenceHandler().getToken(
- oauthRequest.getClientId(), user.getId());
- if (accessToken == null) {
- String refresh = oauthIssuerImpl.refreshToken();
- accessToken = oauthIssuerImpl.accessToken();
- this.handler.getPersistenceHandler().addToken(accessToken, refresh, user
- .getId(), oauthRequest.getClientId(),
- StringUtils.toString(oauthRequest.getScopes(),
- " "), config.getLongTokenTTL());
- builder.setRefreshToken(refresh);
- }
- builder.setTokenType(TokenType.BEARER.displayName());
- builder.setExpiresIn(String.valueOf(config
- .getLongTokenTTL()));
- builder.setAccessToken(accessToken);
-
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- }
-
- if (openid_valid
- && oauthRequest.getScopes().contains(
- Scopes.Scope.openid.toString())) {
- try {
- if (user == null)
- // EM: MH uses database
- user = controller.authenticate(AuthenticationMethod.DATABASE,
- oauthRequest.getUsername(),
- oauthRequest.getPassword(), attr);
- Userdata data = controller.getUserData(user,
- UserDetails.class);
- user.addUserData(data);
-
- attr.put(Attributes.CLIENT_SECRET,
- oauthRequest.getClientSecret());
- TokenContext c = controller.createTokenContext(user, attr,TokenType.ID_TOKEN);
- //Attributes.OPENID_AUTHENTICATION);
-
- builder.setParam(c.getTokenType().displayName(), c.getToken());
- }
- catch (KustvaktException e) {
- throw kustvaktResponseHandler.throwit(e);
- }
- }
-
- OAuthResponse r = builder.buildJSONMessage();
- return Response.status(r.getResponseStatus()).entity(r.getBody())
- .build();
- }
- catch (OAuthProblemException ex) {
- OAuthResponse r = OAuthResponse.errorResponse(401).error(ex)
- .buildJSONMessage();
- return Response.status(r.getResponseStatus()).entity(r.getBody())
- .build();
- }
- catch (OAuthSystemException e) {
- e.printStackTrace();
- // todo: throw error
- }
- return Response.noContent().build();
- }
-
-}
diff --git a/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql b/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql
index 0d518a3..01e5099 100644
--- a/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql
+++ b/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql
@@ -48,6 +48,6 @@
-- "This is a test super public client.");
INSERT INTO oauth2_access_token(token,user_id,created_date,
-expiry_date, refresh_expiry_date, user_auth_time)
+expiry_date, user_auth_time)
VALUES("fia0123ikBWn931470H8s5gRqx7Moc4p","marlin","2018-05-30 16:25:50",
-"2018-05-31 16:25:50", "2018-08-30 16:25:50", "2018-05-30 16:23:10");
+"2018-05-31 16:25:50", "2018-05-30 16:23:10");
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 10e069d..d5688b8 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
@@ -20,37 +20,39 @@
REFERENCES oauth2_client_url(url_hashcode)
);
-CREATE TABLE IF NOT EXISTS oauth2_authorization (
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
- code VARCHAR(255) NOT NULL,
- client_id VARCHAR(100) NOT NULL,
- user_id VARCHAR(100) NOT NULL,
- redirect_uri TEXT DEFAULT NULL,
- created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- expiry_date TIMESTAMP NULL,
- is_revoked BOOLEAN DEFAULT 0,
- total_attempts INTEGER DEFAULT 0,
- user_auth_time TIMESTAMP NULL,
- nonce TEXT DEFAULT NULL,
- FOREIGN KEY (client_id)
- REFERENCES oauth2_client(id),
- UNIQUE INDEX authorization_index(code, client_id)
-);
-
CREATE TABLE IF NOT EXISTS oauth2_access_scope (
id VARCHAR(255) PRIMARY KEY NOT NULL
);
-CREATE TABLE IF NOT EXISTS oauth2_authorization_scope (
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
- authorization_id INTEGER NOT NULL,
- scope_id VARCHAR(100) NOT NULL,
- FOREIGN KEY (authorization_id)
- REFERENCES oauth2_authorization(id),
- FOREIGN KEY (scope_id)
- REFERENCES oauth2_access_scope(id),
- UNIQUE INDEX authorization_scope_index(authorization_id, scope_id)
-);
+-- authorization tables are not needed if using cache
+
+--CREATE TABLE IF NOT EXISTS oauth2_authorization (
+-- id INTEGER PRIMARY KEY AUTO_INCREMENT,
+-- code VARCHAR(255) NOT NULL,
+-- client_id VARCHAR(100) NOT NULL,
+-- user_id VARCHAR(100) NOT NULL,
+-- redirect_uri TEXT DEFAULT NULL,
+-- created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+-- expiry_date TIMESTAMP NULL,
+-- is_revoked BOOLEAN DEFAULT 0,
+-- total_attempts INTEGER DEFAULT 0,
+-- user_auth_time TIMESTAMP NULL,
+-- nonce TEXT DEFAULT NULL,
+-- FOREIGN KEY (client_id)
+-- REFERENCES oauth2_client(id),
+-- UNIQUE INDEX authorization_index(code, client_id)
+--);
+--
+--CREATE TABLE IF NOT EXISTS oauth2_authorization_scope (
+-- id INTEGER PRIMARY KEY AUTO_INCREMENT,
+-- authorization_id INTEGER NOT NULL,
+-- scope_id VARCHAR(100) NOT NULL,
+-- FOREIGN KEY (authorization_id)
+-- REFERENCES oauth2_authorization(id),
+-- FOREIGN KEY (scope_id)
+-- REFERENCES oauth2_access_scope(id),
+-- UNIQUE INDEX authorization_scope_index(authorization_id, scope_id)
+--);
CREATE TABLE IF NOT EXISTS oauth2_access_token (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
@@ -62,10 +64,10 @@
is_revoked BOOLEAN DEFAULT 0,
user_auth_time TIMESTAMP NULL,
refresh_token VARCHAR(255) DEFAULT NULL,
- refresh_expiry_date TIMESTAMP NULL,
- is_refresh_revoked BOOLEAN DEFAULT 0,
FOREIGN KEY (client_id)
- REFERENCES oauth2_client(id)
+ REFERENCES oauth2_client(id),
+ FOREIGN KEY (refresh_token)
+ REFERENCES oauth2_refresh_token(id)
);
CREATE TABLE oauth2_access_token_scope (
@@ -74,40 +76,20 @@
CONSTRAINT primary_key PRIMARY KEY (token_id, scope_id)
);
---
----- status 1 = valid, 0 = revoked, -1 = disabled
---create table if not exists oauth2_access_token (
---id INTEGER PRIMARY KEY AUTO_INCREMENT,
---access_token VARCHAR(300),
---auth_code VARCHAR(250),
---client_id VARCHAR(100),
---user_id INTEGER,
----- make boolean --
---status INTEGER DEFAULT 1,
----- in case of code authorization, should match auth code scopes!
----- use scopes for levelaccess descriptor level[rw],level[r]
---scopes VARCHAR(350),
---expiration TIMESTAMP,
---FOREIGN KEY (user_id)
---REFERENCES korap_users(id)
---ON DELETE CASCADE,
---FOREIGN KEY (client_id)
---REFERENCES oauth2_client(client_id)
---ON DELETE CASCADE
---);
---
---
----- also scopes?
---create table if not exists oauth2_refresh_token (
---id INTEGER PRIMARY KEY AUTO_INCREMENT,
---client_id VARCHAR(100),
---user_id INTEGER,
---expiration TIMESTAMP,
---scopes VARCHAR(350),
---FOREIGN KEY (user_id)
---REFERENCES korap_users(id)
---ON DELETE CASCADE,
---FOREIGN KEY (client_id)
---REFERENCES oauth2_client(client_id)
---ON DELETE CASCADE
---);
\ No newline at end of file
+CREATE TABLE IF NOT EXISTS oauth2_refresh_token (
+ id INTEGER PRIMARY KEY AUTO_INCREMENT,
+ token VARCHAR(255) NOT NULL,
+ user_id VARCHAR(100) DEFAULT NULL,
+ client_id VARCHAR(100) DEFAULT NULL,
+ created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ expiry_date TIMESTAMP NULL,
+ is_revoked BOOLEAN DEFAULT 0,
+ FOREIGN KEY (client_id)
+ REFERENCES oauth2_client(id)
+);
+
+CREATE TABLE oauth2_refresh_token_scope (
+ token_id INTEGER NOT NULL,
+ scope_id VARCHAR(100) NOT NULL,
+ CONSTRAINT primary_key PRIMARY KEY (token_id, scope_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 45fa1f4..f11cac8 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
@@ -22,40 +22,42 @@
CREATE UNIQUE INDEX client_id_index on oauth2_client(id);
-CREATE TABLE IF NOT EXISTS oauth2_authorization (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- code VARCHAR(255) NOT NULL,
- client_id VARCHAR(100) NOT NULL,
- user_id VARCHAR(100) NOT NULL,
- redirect_uri TEXT DEFAULT NULL,
- created_date TIMESTAMP NOT NULL,
- expiry_date TIMESTAMP NOT NULL,
- is_revoked BOOLEAN DEFAULT 0,
- total_attempts INTEGER DEFAULT 0,
- user_auth_time TIMESTAMP NOT NULL,
- nonce TEXT DEFAULT NULL,
- FOREIGN KEY (client_id)
- REFERENCES oauth2_client(id)
-);
-
-CREATE UNIQUE INDEX authorization_index on oauth2_authorization(code, client_id);
-
CREATE TABLE IF NOT EXISTS oauth2_access_scope (
id VARCHAR(255) PRIMARY KEY NOT NULL
);
-CREATE TABLE IF NOT EXISTS oauth2_authorization_scope (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- authorization_id INTEGER NOT NULL,
- scope_id VARCHAR(100) NOT NULL,
- FOREIGN KEY (authorization_id)
- REFERENCES oauth2_authorization(id),
- FOREIGN KEY (scope_id)
- REFERENCES oauth2_access_scope(id)
-);
+-- authorization tables are not needed if using cache
-CREATE UNIQUE INDEX authorization_scope_index on
- oauth2_authorization_scope(authorization_id, scope_id);
+--CREATE TABLE IF NOT EXISTS oauth2_authorization (
+-- id INTEGER PRIMARY KEY AUTOINCREMENT,
+-- code VARCHAR(255) NOT NULL,
+-- client_id VARCHAR(100) NOT NULL,
+-- user_id VARCHAR(100) NOT NULL,
+-- redirect_uri TEXT DEFAULT NULL,
+-- created_date TIMESTAMP NOT NULL,
+-- expiry_date TIMESTAMP NOT NULL,
+-- is_revoked BOOLEAN DEFAULT 0,
+-- total_attempts INTEGER DEFAULT 0,
+-- user_auth_time TIMESTAMP NOT NULL,
+-- nonce TEXT DEFAULT NULL,
+-- FOREIGN KEY (client_id)
+-- REFERENCES oauth2_client(id)
+--);
+--
+--CREATE UNIQUE INDEX authorization_index on oauth2_authorization(code, client_id);
+--
+--CREATE TABLE IF NOT EXISTS oauth2_authorization_scope (
+-- id INTEGER PRIMARY KEY AUTOINCREMENT,
+-- authorization_id INTEGER NOT NULL,
+-- scope_id VARCHAR(100) NOT NULL,
+-- FOREIGN KEY (authorization_id)
+-- REFERENCES oauth2_authorization(id),
+-- FOREIGN KEY (scope_id)
+-- REFERENCES oauth2_access_scope(id)
+--);
+--
+--CREATE UNIQUE INDEX authorization_scope_index on
+-- oauth2_authorization_scope(authorization_id, scope_id);
CREATE TABLE IF NOT EXISTS oauth2_access_token (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -67,10 +69,10 @@
is_revoked BOOLEAN DEFAULT 0,
user_auth_time TIMESTAMP NOT NULL,
refresh_token VARCHAR(255) DEFAULT NULL,
- refresh_expiry_date TIMESTAMP NOT NULL,
- is_refresh_revoked BOOLEAN DEFAULT 0,
FOREIGN KEY (client_id)
REFERENCES oauth2_client(id)
+ FOREIGN KEY (refresh_token)
+ REFERENCES oauth2_refresh_token(id)
);
CREATE TABLE oauth2_access_token_scope (
@@ -78,3 +80,22 @@
scope_id VARCHAR(100) NOT NULL,
primary key (token_id, scope_id)
);
+
+CREATE TABLE IF NOT EXISTS oauth2_refresh_token (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ token VARCHAR(255) NOT NULL,
+ user_id VARCHAR(100) DEFAULT NULL,
+ user_auth_time TIMESTAMP NOT NULL,
+ client_id VARCHAR(100) DEFAULT NULL,
+ created_date TIMESTAMP NOT NULL,
+ expiry_date TIMESTAMP NULL,
+ is_revoked BOOLEAN DEFAULT 0,
+ FOREIGN KEY (client_id)
+ REFERENCES oauth2_client(id)
+);
+
+CREATE TABLE oauth2_refresh_token_scope (
+ token_id INTEGER NOT NULL,
+ scope_id VARCHAR(100) NOT NULL,
+ CONSTRAINT primary_key PRIMARY KEY (token_id, scope_id)
+);
diff --git a/full/src/main/resources/default-config.xml b/full/src/main/resources/default-config.xml
index f9ce666..ac243e3 100644
--- a/full/src/main/resources/default-config.xml
+++ b/full/src/main/resources/default-config.xml
@@ -141,6 +141,10 @@
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory}</prop>
<prop key="hibernate.jdbc.time_zone">${hibernate.jdbc.time_zone}</prop>
<!-- <prop key="net.sf.ehcache.configurationResourceName">classpath:ehcache.xml</prop> -->
+
+ <prop key="connection.autoReconnect">true</prop>
+ <prop key="connection.autoReconnectForPools">true</prop>
+ <prop key="connection.is-connection-validation-required">true</prop>
</props>
</property>
</bean>