This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new fb3794e [SYNCOPE-1585] cxf-rt-rs-security-jose -> nimbus-jose-jwt
fb3794e is described below
commit fb3794e3446b61c452ab452705243ec5a693bda0
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Tue Aug 18 16:48:04 2020 +0200
[SYNCOPE-1585] cxf-rt-rs-security-jose -> nimbus-jose-jwt
---
client/idrepo/console/pom.xml | 10 +-
.../console/panels/AccessTokenDirectoryPanel.java | 19 +-
.../panels/AccessTokenDirectoryPanel.properties | 2 +-
.../AccessTokenDirectoryPanel_fr_CA.properties | 10 +-
.../panels/AccessTokenDirectoryPanel_it.properties | 2 +-
.../panels/AccessTokenDirectoryPanel_ja.properties | 2 +-
.../AccessTokenDirectoryPanel_pt_BR.properties | 2 +-
.../panels/AccessTokenDirectoryPanel_ru.properties | 2 +-
.../client/lib/JWTAuthenticationHandler.java | 1 -
.../syncope/common/lib/to/AccessTokenTO.java | 14 +-
.../common/lib/types/ClientExceptionType.java | 1 +
core/idrepo/logic/pom.xml | 5 -
.../core/persistence/api/entity/AccessToken.java | 4 +-
core/persistence-jpa-json/pom.xml | 5 -
core/persistence-jpa/pom.xml | 5 -
.../persistence/jpa/dao/JPAAccessTokenDAO.java | 2 +-
.../persistence/jpa/entity/JPAAccessToken.java | 10 +-
.../persistence/jpa/outer/AccessTokenTest.java | 2 +-
core/provisioning-java/pom.xml | 5 -
.../java/data/AccessTokenDataBinderImpl.java | 105 +++--
core/spring/pom.xml | 12 +-
.../core/spring/security/AuthDataAccessor.java | 8 +-
.../core/spring/security/JWTAuthentication.java | 8 +-
.../spring/security/JWTAuthenticationFilter.java | 15 +-
.../spring/security/JWTAuthenticationProvider.java | 15 +-
.../core/spring/security/JWTSSOProvider.java | 8 +-
.../core/spring/security/SecurityContext.java | 32 +-
.../spring/security/SyncopeJWTSSOProvider.java | 35 +-
...tureProvider.java => AccessTokenJWSSigner.java} | 71 ++-
.../security/jws/AccessTokenJWSVerifier.java | 87 ++++
.../jws/AccessTokenJwsSignatureVerifier.java | 91 ----
.../apache/syncope/core/logic/SAML2SP4UILogic.java | 30 +-
.../fit/core/reference/CustomJWTSSOProvider.java | 45 +-
.../org/apache/syncope/fit/AbstractITCase.java | 6 +-
.../org/apache/syncope/fit/core/JWTITCase.java | 498 +++++++++------------
pom.xml | 5 -
36 files changed, 559 insertions(+), 615 deletions(-)
diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml
index d1faf21..0b757a7 100644
--- a/client/idrepo/console/pom.xml
+++ b/client/idrepo/console/pom.xml
@@ -64,12 +64,12 @@ under the License.
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-rs-security-jose</artifactId>
- </dependency>
+ <dependency>
+ <groupId>com.nimbusds</groupId>
+ <artifactId>nimbus-jose-jwt</artifactId>
+ </dependency>
- <dependency>
+ <dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
</dependency>
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
index 6d499b1..16e2ef7 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.java
@@ -18,13 +18,14 @@
*/
package org.apache.syncope.client.console.panels;
+import com.nimbusds.jwt.SignedJWT;
import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Date;
import java.util.Iterator;
import java.util.List;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
@@ -98,14 +99,18 @@ public class AccessTokenDirectoryPanel
final String componentId,
final IModel<AccessTokenTO> model) {
- JwsJwtCompactConsumer consumer = new
JwsJwtCompactConsumer(model.getObject().getBody());
- cellItem.add(new Label(componentId,
- SyncopeConsoleSession.get().getDateFormat().format(
- new Date(consumer.getJwtClaims().getIssuedAt()
* 1000))));
+ try {
+ SignedJWT jwt =
SignedJWT.parse(model.getObject().getBody());
+ cellItem.add(new Label(componentId,
+
SyncopeConsoleSession.get().getDateFormat().format(jwt.getJWTClaimsSet().getIssueTime())));
+ } catch (ParseException e) {
+ LOG.error("Could not parse JWT {}",
model.getObject().getBody(), e);
+ cellItem.add(new Label(componentId, StringUtils.EMPTY));
+ }
}
});
- columns.add(new DatePropertyColumn<>(new ResourceModel("expiryTime"),
"expiryTime", "expiryTime"));
+ columns.add(new DatePropertyColumn<>(new
ResourceModel("expirationTime"), "expirationTime", "expirationTime"));
return columns;
}
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
index b55a13f..a46b957 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel.properties
@@ -17,5 +17,5 @@
any.edit=Edit access token ${accessTokenTO.owner}
any.new=New access token
owner=Owner
-expiryTime=Expiry
+expirationTime=Expiry
issuedAt=Start
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_fr_CA.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_fr_CA.properties
index 0479992..e03de3d 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_fr_CA.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_fr_CA.properties
@@ -14,8 +14,8 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-any.edit=Jeton modification acc�s ${accessTokenTO.owner}
-any.new=Jeton nouvel acc�s
-owner=Propri�taire
-expiryTime=Expiration
-issuedAt=D�but
+any.edit=Jeton modification acc\u00e8s ${accessTokenTO.owner}
+any.new=Jeton nouvel acc\u00e8s
+owner=Propri\u00e9taire
+expirationTime=Expiration
+issuedAt=D\u00e9but
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
index c5b59ae..abd2ed8 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_it.properties
@@ -17,5 +17,5 @@
any.edit=Modifica ruolo ${roleTO.key}
any.new=Nuovo token di accesso
owner=Proprietario
-expiryTime=Termine
+expirationTime=Termine
issuedAt=Inizio
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ja.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ja.properties
index 9026cb7..ea5b73e 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ja.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ja.properties
@@ -17,5 +17,5 @@
any.edit=\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3
${accessTokenTO.owner} \u3092\u7de8\u96c6
any.new=\u65b0\u3057\u3044\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3
owner=\u30aa\u30fc\u30ca\u30fc
-expiryTime=\u671f\u9650
+expirationTime=\u671f\u9650
issuedAt=\u958b\u59cb
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
index 125ba73..3424513 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_pt_BR.properties
@@ -17,5 +17,5 @@
any.edit=Alterar fun\u00e7\u00e3o ${roleTO.key}
any.new=Novo token de acesso
owner=Propriet\u00e1rio
-expiryTime=Termo
+expirationTime=Termo
issuedAt=Iniciar
diff --git
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
index 33830e1..d6ff9be 100644
---
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
+++
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AccessTokenDirectoryPanel_ru.properties
@@ -18,5 +18,5 @@
any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c
\u0440\u043e\u043b\u044c ${roleTO.key}
any.new=\u041d\u043e\u0432\u044b\u0439 \u043c\u0430\u0440\u043a\u0435\u0440
\u0434\u043e\u0441\u0442\u0443\u043f\u0430
owner=\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446
-expiryTime=\u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u0435
\u0441\u0440\u043e\u043a\u0430
+expirationTime=\u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u0435
\u0441\u0440\u043e\u043a\u0430
issuedAt=\u041d\u0430\u0447\u0430\u043b\u043e
diff --git
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/JWTAuthenticationHandler.java
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/JWTAuthenticationHandler.java
index 1a3af4c..c6ebf18 100644
---
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/JWTAuthenticationHandler.java
+++
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/JWTAuthenticationHandler.java
@@ -32,5 +32,4 @@ public class JWTAuthenticationHandler implements
AuthenticationHandler {
public String getJwt() {
return jwt;
}
-
}
diff --git
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java
index c8e0784..2080c87 100644
---
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java
+++
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java
@@ -31,7 +31,7 @@ public class AccessTokenTO implements EntityTO {
private String body;
- private Date expiryTime;
+ private Date expirationTime;
private String owner;
@@ -53,12 +53,12 @@ public class AccessTokenTO implements EntityTO {
this.body = body;
}
- public Date getExpiryTime() {
- return Optional.ofNullable(expiryTime).map(time -> new
Date(time.getTime())).orElse(null);
+ public Date getExpirationTime() {
+ return Optional.ofNullable(expirationTime).map(time -> new
Date(time.getTime())).orElse(null);
}
- public void setExpiryTime(final Date expiryTime) {
- this.expiryTime = Optional.ofNullable(expiryTime).map(time -> new
Date(time.getTime())).orElse(null);
+ public void setExpirationTime(final Date expiryTime) {
+ this.expirationTime = Optional.ofNullable(expiryTime).map(time -> new
Date(time.getTime())).orElse(null);
}
public String getOwner() {
@@ -84,7 +84,7 @@ public class AccessTokenTO implements EntityTO {
return new EqualsBuilder().
append(key, other.key).
append(body, other.body).
- append(expiryTime, other.expiryTime).
+ append(expirationTime, other.expirationTime).
append(owner, other.owner).
build();
}
@@ -94,7 +94,7 @@ public class AccessTokenTO implements EntityTO {
return new HashCodeBuilder().
append(key).
append(body).
- append(expiryTime).
+ append(expirationTime).
append(owner).
build();
}
diff --git
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index 8933913..0e170dc 100644
---
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@ -31,6 +31,7 @@ public enum ClientExceptionType {
EntityExists(Response.Status.CONFLICT),
GenericPersistence(Response.Status.BAD_REQUEST),
HasChildren(Response.Status.BAD_REQUEST),
+ InvalidAccessToken(Response.Status.INTERNAL_SERVER_ERROR),
InvalidPrivilege(Response.Status.BAD_REQUEST),
InvalidImplementation(Response.Status.BAD_REQUEST),
InvalidImplementationType(Response.Status.NOT_FOUND),
diff --git a/core/idrepo/logic/pom.xml b/core/idrepo/logic/pom.xml
index cfc1aac..3c2feee 100644
--- a/core/idrepo/logic/pom.xml
+++ b/core/idrepo/logic/pom.xml
@@ -39,11 +39,6 @@ under the License.
<dependencies>
<dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-rs-security-jose</artifactId>
- </dependency>
-
- <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccessToken.java
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccessToken.java
index ee68d6c..d956385 100644
---
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccessToken.java
+++
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccessToken.java
@@ -26,9 +26,9 @@ public interface AccessToken extends ProvidedKeyEntity {
void setBody(String body);
- Date getExpiryTime();
+ Date getExpirationTime();
- void setExpiryTime(Date expiryTime);
+ void setExpirationTime(Date expirationTime);
String getOwner();
diff --git a/core/persistence-jpa-json/pom.xml
b/core/persistence-jpa-json/pom.xml
index e71f5ae..83db64d 100644
--- a/core/persistence-jpa-json/pom.xml
+++ b/core/persistence-jpa-json/pom.xml
@@ -86,11 +86,6 @@ under the License.
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.nimbusds</groupId>
- <artifactId>nimbus-jose-jwt</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<build>
diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml
index 8019673..0d6b810 100644
--- a/core/persistence-jpa/pom.xml
+++ b/core/persistence-jpa/pom.xml
@@ -138,11 +138,6 @@ under the License.
<artifactId>bcprov-jdk15on</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.nimbusds</groupId>
- <artifactId>nimbus-jose-jwt</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<build>
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAccessTokenDAO.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAccessTokenDAO.java
index e7fb7d8..fbe3852 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAccessTokenDAO.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAccessTokenDAO.java
@@ -135,7 +135,7 @@ public class JPAAccessTokenDAO extends
AbstractDAO<AccessToken> implements Acces
public int deleteExpired() {
Query query = entityManager().createQuery(
"DELETE FROM " + JPAAccessToken.class.getSimpleName() + " e "
- + "WHERE e.expiryTime < :now");
+ + "WHERE e.expirationTime < :now");
query.setParameter("now", new Date());
return query.executeUpdate();
}
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccessToken.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccessToken.java
index 514cf12..326c702 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccessToken.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccessToken.java
@@ -44,7 +44,7 @@ public class JPAAccessToken extends AbstractProvidedKeyEntity
implements AccessT
private String body;
@Temporal(TemporalType.TIMESTAMP)
- private Date expiryTime;
+ private Date expirationTime;
@Column(unique = true)
private String owner;
@@ -63,13 +63,13 @@ public class JPAAccessToken extends
AbstractProvidedKeyEntity implements AccessT
}
@Override
- public Date getExpiryTime() {
- return Optional.ofNullable(expiryTime).map(time -> new
Date(time.getTime())).orElse(null);
+ public Date getExpirationTime() {
+ return Optional.ofNullable(expirationTime).map(time -> new
Date(time.getTime())).orElse(null);
}
@Override
- public void setExpiryTime(final Date expiryTime) {
- this.expiryTime = Optional.ofNullable(expiryTime).map(time -> new
Date(time.getTime())).orElse(null);
+ public void setExpirationTime(final Date expirationTime) {
+ this.expirationTime = Optional.ofNullable(expirationTime).map(time ->
new Date(time.getTime())).orElse(null);
}
@Override
diff --git
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AccessTokenTest.java
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AccessTokenTest.java
index 9272302..f36906f 100644
---
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AccessTokenTest.java
+++
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AccessTokenTest.java
@@ -42,7 +42,7 @@ public class AccessTokenTest extends AbstractTest {
AccessToken accessToken = entityFactory.newEntity(AccessToken.class);
accessToken.setKey(UUID.randomUUID().toString());
accessToken.setBody("pointless body");
- accessToken.setExpiryTime(new Date());
+ accessToken.setExpirationTime(new Date());
accessToken.setOwner("bellini");
accessToken = accessTokenDAO.save(accessToken);
diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml
index 9ac7db6..5a2211e 100644
--- a/core/provisioning-java/pom.xml
+++ b/core/provisioning-java/pom.xml
@@ -97,11 +97,6 @@ under the License.
</dependency>
<dependency>
- <groupId>com.nimbusds</groupId>
- <artifactId>nimbus-jose-jwt</artifactId>
- </dependency>
-
- <dependency>
<groupId>org.apache.syncope.core</groupId>
<artifactId>syncope-core-workflow-api</artifactId>
<version>${project.version}</version>
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
index 343dd84..cb9ff1f 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
@@ -18,18 +18,20 @@
*/
package org.apache.syncope.core.provisioning.java.data;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import java.text.ParseException;
+import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.cxf.rs.security.jose.common.JoseType;
-import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
-import org.apache.cxf.rs.security.jose.jwt.JwtToken;
import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
+import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.AccessTokenTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
import org.apache.syncope.core.persistence.api.entity.AccessToken;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
@@ -37,7 +39,7 @@ import
org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.spring.security.DefaultCredentialChecker;
import org.apache.syncope.core.spring.security.SecureRandomUtils;
-import
org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJWSSigner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -51,7 +53,7 @@ public class AccessTokenDataBinderImpl implements
AccessTokenDataBinder {
private String jwtIssuer;
@Autowired
- private AccessTokenJwsSignatureProvider jwsSignatureProvider;
+ private AccessTokenJWSSigner jwsSigner;
@Autowired
private AccessTokenDAO accessTokenDAO;
@@ -74,25 +76,30 @@ public class AccessTokenDataBinderImpl implements
AccessTokenDataBinder {
credentialChecker.checkIsDefaultJWSKeyInUse();
- long currentTime = new Date().getTime() / 1000L;
- long expiryTime = currentTime + 60L * duration;
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(tokenId);
- jwtClaims.setSubject(subject);
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(jwtIssuer);
- jwtClaims.setExpiryTime(expiryTime);
- jwtClaims.setNotBefore(currentTime);
- claims.forEach(jwtClaims::setClaim);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT,
jwsSignatureProvider.getAlgorithm());
- JwtToken token = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token);
-
- String signed = producer.signWith(jwsSignatureProvider);
-
- return Pair.of(signed, new Date(expiryTime * 1000L));
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, (int) duration);
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(tokenId).
+ subject(subject).
+ issueTime(currentTime).
+ issuer(jwtIssuer).
+ expirationTime(expiration.getTime()).
+ notBeforeTime(currentTime);
+ claims.forEach(claimsSet::claim);
+
+ SignedJWT jwt = new SignedJWT(new
JWSHeader(jwsSigner.getJwsAlgorithm()), claimsSet.build());
+ try {
+ jwt.sign(jwsSigner);
+ } catch (JOSEException e) {
+ SyncopeClientException sce =
SyncopeClientException.build(ClientExceptionType.InvalidAccessToken);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ return Pair.of(jwt.serialize(), expiration.getTime());
}
private AccessToken replace(
@@ -108,7 +115,7 @@ public class AccessTokenDataBinderImpl implements
AccessTokenDataBinder {
claims);
accessToken.setBody(generated.getLeft());
- accessToken.setExpiryTime(generated.getRight());
+ accessToken.setExpirationTime(generated.getRight());
accessToken.setOwner(subject);
if (!adminUser.equals(accessToken.getOwner())) {
@@ -132,35 +139,45 @@ public class AccessTokenDataBinderImpl implements
AccessTokenDataBinder {
accessToken.setKey(SecureRandomUtils.generateRandomUUID().toString());
accessToken = replace(subject, claims, authorities, accessToken);
- } else if (replace || accessToken.getExpiryTime() == null ||
accessToken.getExpiryTime().before(new Date())) {
+ } else if (replace || accessToken.getExpirationTime() == null
+ || accessToken.getExpirationTime().before(new Date())) {
+
// AccessToken found, but either replace was requested or it is
expired: update existing
accessToken = replace(subject, claims, authorities, accessToken);
}
- return Pair.of(accessToken.getBody(), accessToken.getExpiryTime());
+ return Pair.of(accessToken.getBody(), accessToken.getExpirationTime());
}
@Override
public Pair<String, Date> update(final AccessToken accessToken, final
byte[] authorities) {
- JwsJwtCompactConsumer consumer = new
JwsJwtCompactConsumer(accessToken.getBody());
-
credentialChecker.checkIsDefaultJWSKeyInUse();
long duration = confParamOps.get(AuthContextUtils.getDomain(),
"jwt.lifetime.minutes", 120L, Long.class);
- long currentTime = new Date().getTime() / 1000L;
- long expiry = currentTime + 60L * duration;
- consumer.getJwtClaims().setExpiryTime(expiry);
- Date expiryDate = new Date(expiry * 1000L);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT,
jwsSignatureProvider.getAlgorithm());
- JwtToken token = new JwtToken(jwsHeaders, consumer.getJwtClaims());
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token);
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, (int) duration);
- String body = producer.signWith(jwsSignatureProvider);
+ SignedJWT jwt;
+ try {
+ JWTClaimsSet.Builder claimsSet =
+ new
JWTClaimsSet.Builder(SignedJWT.parse(accessToken.getBody()).getJWTClaimsSet()).
+ expirationTime(expiration.getTime());
+
+ jwt = new SignedJWT(new JWSHeader(jwsSigner.getJwsAlgorithm()),
claimsSet.build());
+ jwt.sign(jwsSigner);
+ } catch (ParseException | JOSEException e) {
+ SyncopeClientException sce =
SyncopeClientException.build(ClientExceptionType.InvalidAccessToken);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ String body = jwt.serialize();
accessToken.setBody(body);
- // AccessToken stores expiry time in milliseconds, as opposed to
seconds for the JWT tokens.
- accessToken.setExpiryTime(expiryDate);
+ accessToken.setExpirationTime(expiration.getTime());
if (!adminUser.equals(accessToken.getOwner())) {
accessToken.setAuthorities(authorities);
@@ -168,7 +185,7 @@ public class AccessTokenDataBinderImpl implements
AccessTokenDataBinder {
accessTokenDAO.save(accessToken);
- return Pair.of(body, expiryDate);
+ return Pair.of(body, expiration.getTime());
}
@Override
@@ -176,7 +193,7 @@ public class AccessTokenDataBinderImpl implements
AccessTokenDataBinder {
AccessTokenTO accessTokenTO = new AccessTokenTO();
accessTokenTO.setKey(accessToken.getKey());
accessTokenTO.setBody(accessToken.getBody());
- accessTokenTO.setExpiryTime(accessToken.getExpiryTime());
+ accessTokenTO.setExpirationTime(accessToken.getExpirationTime());
accessTokenTO.setOwner(accessToken.getOwner());
return accessTokenTO;
diff --git a/core/spring/pom.xml b/core/spring/pom.xml
index 0d9f953..80dcd6b 100644
--- a/core/spring/pom.xml
+++ b/core/spring/pom.xml
@@ -53,13 +53,13 @@ under the License.
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
</dependency>
-
- <dependency>
- <groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-rs-security-jose</artifactId>
- </dependency>
- <dependency>
+ <dependency>
+ <groupId>com.nimbusds</groupId>
+ <artifactId>nimbus-jose-jwt</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
index 125b5f6..43957ad 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
@@ -368,10 +368,10 @@ public class AuthDataAccessor {
Set<SyncopeGrantedAuthority> authorities;
if (adminUser.equals(authentication.getClaims().getSubject())) {
- AccessToken accessToken =
accessTokenDAO.find(authentication.getClaims().getTokenId());
+ AccessToken accessToken =
accessTokenDAO.find(authentication.getClaims().getJWTID());
if (accessToken == null) {
throw new AuthenticationCredentialsNotFoundException(
- "Could not find an Access Token for JWT " +
authentication.getClaims().getTokenId());
+ "Could not find an Access Token for JWT " +
authentication.getClaims().getJWTID());
}
username = adminUser;
@@ -382,14 +382,14 @@ public class AuthDataAccessor {
if (resolved == null || resolved.getLeft() == null) {
throw new AuthenticationCredentialsNotFoundException(
"Could not find User " +
authentication.getClaims().getSubject()
- + " for JWT " +
authentication.getClaims().getTokenId());
+ + " for JWT " + authentication.getClaims().getJWTID());
}
User user = resolved.getLeft();
username = user.getUsername();
authorities = resolved.getRight() == null ? Set.of() :
resolved.getRight();
LOG.debug("JWT {} issued by {} resolved to User {} with
authorities {}",
- authentication.getClaims().getTokenId(),
+ authentication.getClaims().getJWTID(),
authentication.getClaims().getIssuer(),
username, authorities);
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java
index 846693c..314d2b5 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.spring.security;
+import com.nimbusds.jwt.JWTClaimsSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
@@ -26,7 +27,6 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.springframework.security.core.Authentication;
/**
@@ -38,7 +38,7 @@ public class JWTAuthentication implements Authentication {
private static final long serialVersionUID = -2013733709281305394L;
- private final JwtClaims claims;
+ private final JWTClaimsSet claims;
private final SyncopeAuthenticationDetails details;
@@ -48,12 +48,12 @@ public class JWTAuthentication implements Authentication {
private boolean authenticated = false;
- public JWTAuthentication(final JwtClaims claims, final
SyncopeAuthenticationDetails details) {
+ public JWTAuthentication(final JWTClaimsSet claims, final
SyncopeAuthenticationDetails details) {
this.claims = claims;
this.details = details;
}
- public JwtClaims getClaims() {
+ public JWTClaimsSet getClaims() {
return claims;
}
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java
index e78c494..9a6eef9 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java
@@ -18,7 +18,10 @@
*/
package org.apache.syncope.core.spring.security;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jwt.SignedJWT;
import java.io.IOException;
+import java.text.ParseException;
import java.util.Optional;
import java.util.Set;
import javax.servlet.FilterChain;
@@ -27,8 +30,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.HttpHeaders;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.cxf.rs.security.jose.jws.JwsException;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
@@ -88,14 +89,14 @@ public class JWTAuthenticationFilter extends
BasicAuthenticationFilter {
try {
credentialChecker.checkIsDefaultJWSKeyInUse();
- JwsJwtCompactConsumer consumer = new
JwsJwtCompactConsumer(stringToken);
- JWTSSOProvider jwtSSOProvider =
dataAccessor.getJWTSSOProvider(consumer.getJwtClaims().getIssuer());
- if (!consumer.verifySignatureWith(jwtSSOProvider)) {
+ SignedJWT jwt = SignedJWT.parse(stringToken);
+ JWTSSOProvider jwtSSOProvider =
dataAccessor.getJWTSSOProvider(jwt.getJWTClaimsSet().getIssuer());
+ if (!jwt.verify(jwtSSOProvider)) {
throw new BadCredentialsException("Invalid signature found in
JWT");
}
JWTAuthentication jwtAuthentication =
- new JWTAuthentication(consumer.getJwtClaims(),
authenticationDetailsSource.buildDetails(request));
+ new JWTAuthentication(jwt.getJWTClaimsSet(),
authenticationDetailsSource.buildDetails(request));
AuthContextUtils.callAsAdmin(jwtAuthentication.getDetails().getDomain(), () -> {
Pair<String, Set<SyncopeGrantedAuthority>> authenticated =
dataAccessor.authenticate(jwtAuthentication);
jwtAuthentication.setUsername(authenticated.getLeft());
@@ -105,7 +106,7 @@ public class JWTAuthenticationFilter extends
BasicAuthenticationFilter {
SecurityContextHolder.getContext().setAuthentication(jwtAuthentication);
chain.doFilter(request, response);
- } catch (JwsException e) {
+ } catch (ParseException | JOSEException e) {
SecurityContextHolder.clearContext();
this.authenticationEntryPoint.commence(
request, response, new BadCredentialsException("Invalid
JWT: " + stringToken, e));
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
index 9580070..2a5865d 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java
@@ -18,7 +18,8 @@
*/
package org.apache.syncope.core.spring.security;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
+import com.nimbusds.jwt.JWTClaimsSet;
+import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.CredentialsExpiredException;
@@ -38,17 +39,17 @@ public class JWTAuthenticationProvider implements
AuthenticationProvider {
public Authentication authenticate(final Authentication authentication)
throws AuthenticationException {
JWTAuthentication jwtAuthentication = (JWTAuthentication)
authentication;
- JwtClaims claims = jwtAuthentication.getClaims();
+ JWTClaimsSet claims = jwtAuthentication.getClaims();
Long referenceTime = System.currentTimeMillis();
- Long expiryTime = claims.getExpiryTime();
- if (expiryTime == null || (expiryTime * 1000L) < referenceTime) {
- dataAccessor.removeExpired(claims.getTokenId());
+ Date expiryTime = claims.getExpirationTime();
+ if (expiryTime == null || expiryTime.getTime() < referenceTime) {
+ dataAccessor.removeExpired(claims.getJWTID());
throw new CredentialsExpiredException("JWT is expired");
}
- Long notBefore = claims.getNotBefore();
- if (notBefore == null || (notBefore * 1000L) > referenceTime) {
+ Date notBefore = claims.getNotBeforeTime();
+ if (notBefore == null || notBefore.getTime() > referenceTime) {
throw new CredentialsExpiredException("JWT not valid yet");
}
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTSSOProvider.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTSSOProvider.java
index 9bcdf44..900aabb 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTSSOProvider.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTSSOProvider.java
@@ -18,17 +18,17 @@
*/
package org.apache.syncope.core.spring.security;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jwt.JWTClaimsSet;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.apache.syncope.core.persistence.api.entity.user.User;
/**
* Enables a generic mechanism for JWT validation and subject resolution which
allows to plug in implementations
* recognizing JWT produced by third parties.
*/
-public interface JWTSSOProvider extends JwsSignatureVerifier {
+public interface JWTSSOProvider extends JWSVerifier {
/**
* Gives the identifier for the JWT issuer verified by this instance.
@@ -44,5 +44,5 @@ public interface JWTSSOProvider extends JwsSignatureVerifier {
* @param jwtClaims JWT claims
* @return internal User, with authorities, matching the provided JWT
claims, if found; otherwise null
*/
- Pair<User, Set<SyncopeGrantedAuthority>> resolve(JwtClaims jwtClaims);
+ Pair<User, Set<SyncopeGrantedAuthority>> resolve(JWTClaimsSet jwtClaims);
}
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
index e8c5779..1c71c05 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
@@ -18,10 +18,14 @@
*/
package org.apache.syncope.core.spring.security;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.KeyLengthException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
import org.apache.syncope.core.spring.ApplicationContextProvider;
-import
org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider;
-import
org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJWSSigner;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJWSVerifier;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
@@ -85,20 +89,22 @@ public class SecurityContext implements EnvironmentAware {
@ConditionalOnMissingBean
@Bean
- public AccessTokenJwsSignatureVerifier accessTokenJwsSignatureVerifier() {
- AccessTokenJwsSignatureVerifier verifier = new
AccessTokenJwsSignatureVerifier();
- verifier.setJwsAlgorithm(env.getProperty("jwsAlgorithm",
SignatureAlgorithm.class));
- verifier.setJwsKey(jwsKey());
- return verifier;
+ public AccessTokenJWSVerifier accessTokenJWSVerifier()
+ throws JOSEException, NoSuchAlgorithmException,
InvalidKeySpecException {
+
+ return new AccessTokenJWSVerifier(
+ JWSAlgorithm.parse(env.getProperty("jwsAlgorithm")),
+ jwsKey());
}
@ConditionalOnMissingBean
@Bean
- public AccessTokenJwsSignatureProvider accessTokenJwsSignatureProvider() {
- AccessTokenJwsSignatureProvider provider = new
AccessTokenJwsSignatureProvider();
- provider.setJwsAlgorithm(env.getProperty("jwsAlgorithm",
SignatureAlgorithm.class));
- provider.setJwsKey(jwsKey());
- return provider;
+ public AccessTokenJWSSigner accessTokenJWSSigner()
+ throws KeyLengthException, NoSuchAlgorithmException,
InvalidKeySpecException {
+
+ return new AccessTokenJWSSigner(
+ JWSAlgorithm.parse(env.getProperty("jwsAlgorithm")),
+ jwsKey());
}
@ConditionalOnMissingBean
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
index d813370..2253b4a 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
@@ -19,21 +19,22 @@
package org.apache.syncope.core.spring.security;
import com.fasterxml.jackson.core.type.TypeReference;
-
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.jca.JCAContext;
+import com.nimbusds.jose.util.Base64URL;
+import com.nimbusds.jwt.JWTClaimsSet;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
-import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsVerificationSignature;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.apache.syncope.common.lib.types.CipherAlgorithm;
import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.AccessToken;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import
org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJWSVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -52,7 +53,7 @@ public class SyncopeJWTSSOProvider implements JWTSSOProvider {
private String jwtIssuer;
@Autowired
- private AccessTokenJwsSignatureVerifier delegate;
+ private AccessTokenJWSVerifier delegate;
@Autowired
private UserDAO userDAO;
@@ -66,27 +67,31 @@ public class SyncopeJWTSSOProvider implements
JWTSSOProvider {
}
@Override
- public SignatureAlgorithm getAlgorithm() {
- return delegate.getAlgorithm();
+ public Set<JWSAlgorithm> supportedJWSAlgorithms() {
+ return delegate.supportedJWSAlgorithms();
}
@Override
- public boolean verify(final JwsHeaders headers, final String unsignedText,
final byte[] signature) {
- return delegate.verify(headers, unsignedText, signature);
+ public JCAContext getJCAContext() {
+ return delegate.getJCAContext();
}
@Override
- public JwsVerificationSignature createJwsVerificationSignature(final
JwsHeaders headers) {
- return delegate.createJwsVerificationSignature(headers);
+ public boolean verify(
+ final JWSHeader header,
+ final byte[] signingInput,
+ final Base64URL signature) throws JOSEException {
+
+ return delegate.verify(header, signingInput, signature);
}
@Transactional(readOnly = true)
@Override
- public Pair<User, Set<SyncopeGrantedAuthority>> resolve(final JwtClaims
jwtClaims) {
+ public Pair<User, Set<SyncopeGrantedAuthority>> resolve(final JWTClaimsSet
jwtClaims) {
User user = userDAO.findByUsername(jwtClaims.getSubject());
Set<SyncopeGrantedAuthority> authorities = Set.of();
if (user != null) {
- AccessToken accessToken =
accessTokenDAO.find(jwtClaims.getTokenId());
+ AccessToken accessToken =
accessTokenDAO.find(jwtClaims.getJWTID());
if (accessToken != null && accessToken.getAuthorities() != null) {
try {
authorities = POJOHelper.deserialize(
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureProvider.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSigner.java
similarity index 51%
rename from
core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureProvider.java
rename to
core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSigner.java
index 8bdedf9..b6f7da7 100644
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureProvider.java
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSigner.java
@@ -18,45 +18,38 @@
*/
package org.apache.syncope.core.spring.security.jws;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.KeyLengthException;
+import com.nimbusds.jose.crypto.MACSigner;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.jca.JCAContext;
+import com.nimbusds.jose.util.Base64URL;
import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
+import java.util.Set;
import org.apache.commons.lang3.StringUtils;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
-import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider;
-import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsSignature;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
-import org.apache.cxf.rs.security.jose.jws.PrivateKeyJwsSignatureProvider;
-import org.springframework.beans.factory.InitializingBean;
-public class AccessTokenJwsSignatureProvider implements JwsSignatureProvider,
InitializingBean {
+public class AccessTokenJWSSigner implements JWSSigner {
- private SignatureAlgorithm jwsAlgorithm;
+ private final JWSAlgorithm jwsAlgorithm;
- private String jwsKey;
+ private final JWSSigner delegate;
- private JwsSignatureProvider delegate;
+ public AccessTokenJWSSigner(final JWSAlgorithm jwsAlgorithm, final String
jwsKey)
+ throws KeyLengthException, NoSuchAlgorithmException,
InvalidKeySpecException {
- public void setJwsAlgorithm(final SignatureAlgorithm jwsAlgorithm) {
- this.jwsAlgorithm = jwsAlgorithm;
- }
-
- public void setJwsKey(final String jwsKey) {
- this.jwsKey = jwsKey;
- }
-
- @Override
- public void afterPropertiesSet() throws Exception {
if (jwsAlgorithm == null) {
- throw new IllegalArgumentException("An instance of " +
SignatureAlgorithm.class + " is required");
+ throw new IllegalArgumentException("An instance of " +
JWSAlgorithm.class + " is required");
}
+ this.jwsAlgorithm = jwsAlgorithm;
- if (SignatureAlgorithm.isPublicKeyAlgorithm(jwsAlgorithm)) {
- if (!jwsAlgorithm.getJwaName().startsWith("RS")) {
- throw new IllegalArgumentException(jwsAlgorithm.getJavaName()
+ " not supported.");
- }
-
+ if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm)) {
if (jwsKey == null || jwsKey.indexOf(':') == -1) {
throw new IllegalArgumentException("A key pair is required, in
the 'private:public' format");
}
@@ -64,28 +57,34 @@ public class AccessTokenJwsSignatureProvider implements
JwsSignatureProvider, In
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(StringUtils.substringBefore(jwsKey,
":").getBytes()));
- delegate = new
PrivateKeyJwsSignatureProvider(kf.generatePrivate(keySpecPKCS8), jwsAlgorithm);
- } else {
+ delegate = new RSASSASigner(kf.generatePrivate(keySpecPKCS8));
+ } else if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
if (jwsKey == null) {
throw new IllegalArgumentException("A shared key is required");
}
- delegate = new HmacJwsSignatureProvider(jwsKey.getBytes(),
jwsAlgorithm);
+ delegate = new MACSigner(jwsKey);
+ } else {
+ throw new IllegalArgumentException("Unsupported JWS algorithm: " +
jwsAlgorithm.getName());
}
}
+ public JWSAlgorithm getJwsAlgorithm() {
+ return jwsAlgorithm;
+ }
+
@Override
- public SignatureAlgorithm getAlgorithm() {
- return delegate.getAlgorithm();
+ public Set<JWSAlgorithm> supportedJWSAlgorithms() {
+ return delegate.supportedJWSAlgorithms();
}
@Override
- public byte[] sign(final JwsHeaders headers, final byte[] content) {
- return delegate.sign(headers, content);
+ public JCAContext getJCAContext() {
+ return delegate.getJCAContext();
}
@Override
- public JwsSignature createJwsSignature(final JwsHeaders headers) {
- return delegate.createJwsSignature(headers);
+ public Base64URL sign(final JWSHeader header, final byte[] signingInput)
throws JOSEException {
+ return delegate.sign(header, signingInput);
}
}
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifier.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifier.java
new file mode 100644
index 0000000..6fa2275
--- /dev/null
+++
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifier.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.spring.security.jws;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.MACVerifier;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jose.jca.JCAContext;
+import com.nimbusds.jose.util.Base64URL;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+
+public class AccessTokenJWSVerifier implements JWSVerifier {
+
+ private final JWSVerifier delegate;
+
+ public AccessTokenJWSVerifier(final JWSAlgorithm jwsAlgorithm, final
String jwsKey)
+ throws JOSEException, NoSuchAlgorithmException,
InvalidKeySpecException {
+
+ if (jwsAlgorithm == null) {
+ throw new IllegalArgumentException("An instance of " +
JWSAlgorithm.class + " is required");
+ }
+
+ if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm)) {
+ if (jwsKey == null || jwsKey.indexOf(':') == -1) {
+ throw new IllegalArgumentException("A key pair is required, in
the 'private:public' format");
+ }
+
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(
+
Base64.getDecoder().decode(StringUtils.substringAfter(jwsKey, ":").getBytes()));
+ delegate = new RSASSAVerifier((RSAPublicKey)
kf.generatePublic(keySpecX509));
+ } else if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
+ if (jwsKey == null) {
+ throw new IllegalArgumentException("A shared key is required");
+ }
+
+ delegate = new MACVerifier(jwsKey);
+ } else {
+ throw new IllegalArgumentException("Unsupported JWS algorithm: " +
jwsAlgorithm.getName());
+ }
+ }
+
+ @Override
+ public Set<JWSAlgorithm> supportedJWSAlgorithms() {
+ return delegate.supportedJWSAlgorithms();
+ }
+
+ @Override
+ public JCAContext getJCAContext() {
+ return delegate.getJCAContext();
+ }
+
+ @Override
+ public boolean verify(
+ final JWSHeader header,
+ final byte[] signingInput,
+ final Base64URL signature) throws JOSEException {
+
+ return delegate.verify(header, signingInput, signature);
+ }
+}
diff --git
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureVerifier.java
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureVerifier.java
deleted file mode 100644
index 0a6f6b9..0000000
---
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJwsSignatureVerifier.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.spring.security.jws;
-
-import java.security.KeyFactory;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.Base64;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
-import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier;
-import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
-import org.apache.cxf.rs.security.jose.jws.JwsVerificationSignature;
-import org.apache.cxf.rs.security.jose.jws.PublicKeyJwsSignatureVerifier;
-import org.springframework.beans.factory.InitializingBean;
-
-public class AccessTokenJwsSignatureVerifier implements JwsSignatureVerifier,
InitializingBean {
-
- private SignatureAlgorithm jwsAlgorithm;
-
- private String jwsKey;
-
- private JwsSignatureVerifier delegate;
-
- public void setJwsAlgorithm(final SignatureAlgorithm jwsAlgorithm) {
- this.jwsAlgorithm = jwsAlgorithm;
- }
-
- public void setJwsKey(final String jwsKey) {
- this.jwsKey = jwsKey;
- }
-
- @Override
- public void afterPropertiesSet() throws Exception {
- if (jwsAlgorithm == null) {
- throw new IllegalArgumentException("An instance of " +
SignatureAlgorithm.class + " is required");
- }
-
- if (SignatureAlgorithm.isPublicKeyAlgorithm(jwsAlgorithm)) {
- if (!jwsAlgorithm.getJwaName().startsWith("RS")) {
- throw new IllegalArgumentException(jwsAlgorithm.getJavaName()
+ " not supported.");
- }
-
- if (jwsKey == null || jwsKey.indexOf(':') == -1) {
- throw new IllegalArgumentException("A key pair is required, in
the 'private:public' format");
- }
-
- KeyFactory kf = KeyFactory.getInstance("RSA");
- X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(
-
Base64.getDecoder().decode(StringUtils.substringAfter(jwsKey, ":").getBytes()));
- delegate = new
PublicKeyJwsSignatureVerifier(kf.generatePublic(keySpecX509), jwsAlgorithm);
- } else {
- if (jwsKey == null) {
- throw new IllegalArgumentException("A shared key is required");
- }
-
- delegate = new HmacJwsSignatureVerifier(jwsKey.getBytes(),
jwsAlgorithm);
- }
- }
-
- @Override
- public SignatureAlgorithm getAlgorithm() {
- return delegate.getAlgorithm();
- }
-
- @Override
- public boolean verify(final JwsHeaders headers, final String unsignedText,
final byte[] signature) {
- return delegate.verify(headers, unsignedText, signature);
- }
-
- @Override
- public JwsVerificationSignature createJwsVerificationSignature(final
JwsHeaders headers) {
- return delegate.createJwsVerificationSignature(headers);
- }
-}
diff --git
a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
index 135146a..59978ad 100644
---
a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
+++
b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
@@ -18,9 +18,12 @@
*/
package org.apache.syncope.core.logic;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
+import java.text.ParseException;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
@@ -28,11 +31,8 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
-import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.EntityTO;
@@ -119,9 +119,6 @@ public class SAML2SP4UILogic extends
AbstractTransactionalLogic<EntityTO> {
@Autowired
private AuthDataAccessor authDataAccessor;
- @Resource(name = "accessTokenJwsSignatureVerifier")
- private JwsSignatureVerifier jwsSignatureVerifier;
-
private final Map<String, String> metadataCache = new
ConcurrentHashMap<>();
private static String validateUrl(final String url) {
@@ -425,14 +422,19 @@ public class SAML2SP4UILogic extends
AbstractTransactionalLogic<EntityTO> {
final String spEntityID,
final String urlContext) {
- // 1. fetch the current JWT used for Syncope authentication and
destroy it
- JwsJwtCompactConsumer consumer = new
JwsJwtCompactConsumer(accessToken);
- if (!consumer.verifySignatureWith(jwsSignatureVerifier)) {
- throw new IllegalArgumentException("Invalid signature found in
Access Token");
+ // 1. fetch the current JWT used for Syncope authentication
+ JWTClaimsSet claimsSet;
+ try {
+ SignedJWT jwt = SignedJWT.parse(accessToken);
+ claimsSet = jwt.getJWTClaimsSet();
+ } catch (ParseException e) {
+ SyncopeClientException sce =
SyncopeClientException.build(ClientExceptionType.InvalidAccessToken);
+ sce.getElements().add(e.getMessage());
+ throw sce;
}
// 2. look for SAML2Client
- String idpEntityID = (String)
consumer.getJwtClaims().getClaim(JWT_CLAIM_IDP_ENTITYID);
+ String idpEntityID = (String)
claimsSet.getClaim(JWT_CLAIM_IDP_ENTITYID);
if (idpEntityID == null) {
throw new NotFoundException("No SAML 2.0 IdP information found in
the access token");
}
@@ -444,13 +446,13 @@ public class SAML2SP4UILogic extends
AbstractTransactionalLogic<EntityTO> {
// 3. create LogoutRequest
SAML2Profile saml2Profile = new SAML2Profile();
- saml2Profile.setId((String)
consumer.getJwtClaims().getClaim(JWT_CLAIM_NAMEID_VALUE));
+ saml2Profile.setId((String)
claimsSet.getClaim(JWT_CLAIM_NAMEID_VALUE));
saml2Profile.addAuthenticationAttribute(
SAML2Authenticator.SAML_NAME_ID_FORMAT,
- consumer.getJwtClaims().getClaim(JWT_CLAIM_NAMEID_FORMAT));
+ claimsSet.getClaim(JWT_CLAIM_NAMEID_FORMAT));
saml2Profile.addAuthenticationAttribute(
SAML2Authenticator.SESSION_INDEX,
- consumer.getJwtClaims().getClaim(JWT_CLAIM_SESSIONINDEX));
+ claimsSet.getClaim(JWT_CLAIM_SESSIONINDEX));
SAML2SP4UIContext ctx = new SAML2SP4UIContext(
saml2Client.getConfiguration().getSpLogoutRequestBindingType(), null);
diff --git
a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
index 379d305..8b2137a 100644
---
a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
+++
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java
@@ -18,15 +18,17 @@
*/
package org.apache.syncope.fit.core.reference;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.MACVerifier;
+import com.nimbusds.jose.jca.JCAContext;
+import com.nimbusds.jose.util.Base64URL;
+import com.nimbusds.jwt.JWTClaimsSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
-import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier;
-import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
-import org.apache.cxf.rs.security.jose.jws.JwsVerificationSignature;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
@@ -45,9 +47,14 @@ public class CustomJWTSSOProvider implements JWTSSOProvider {
public static final String ISSUER = "custom-issuer";
- public static final String CUSTOM_KEY = "12345678910987654321";
+ public static final String CUSTOM_KEY =
+
"XW3eTdntLa9Zsz2t4Vm6TNUya8xJEezFS7NVD3ZIZKOMdmSPMfi40rIvyBzXdbqD7TTsp6grcVW3AvRhZnFzZNaLdp6kJ2HXU9X9t2arVK"
+ +
"42bIAp7XOw6aZg8v4OOXReZ9YkuAKtGwKC1JvPMKCz0c28AhJWd3YX5MpG6prXExQpFFVuweA6xTPxf06nYEFSOmKJ9ddJAcIx4Z8qyY"
+ +
"mDJyNscMU8eVVM7aCR9zrCAHnjRZI2i6OnStAEVuqfGL25tK9AUKPVvyWljHNZ6ugXkstF873QaYJTBst1U2Zl9XsZnyeKrFEwwVHipp"
+ +
"vfHwo2xu6VKySyJpZtaqVrjXFqpgFGRwEm890tCm8JhEG6GgJPqcnFHrYC180LqBZSjnNQGvA7eCSFVrABWcWnXDJCIHWbn0Wv153Vf4"
+ +
"ZH75XEEYY53KsOS2T2GAmoqV3Izz7RL8O5dntgNLevl5gZb6MbYFURnQt0vALeObxMmv459FsXinzpAVihriOZWAudpN6Q";
- private final JwsSignatureVerifier delegate;
+ private final JWSVerifier delegate;
@Autowired
private AnySearchDAO searchDAO;
@@ -55,8 +62,8 @@ public class CustomJWTSSOProvider implements JWTSSOProvider {
@Autowired
private AuthDataAccessor authDataAccessor;
- public CustomJWTSSOProvider() {
- delegate = new HmacJwsSignatureVerifier(CUSTOM_KEY.getBytes(),
SignatureAlgorithm.HS512);
+ public CustomJWTSSOProvider() throws JOSEException {
+ delegate = new MACVerifier(CUSTOM_KEY);
}
@Override
@@ -65,23 +72,27 @@ public class CustomJWTSSOProvider implements JWTSSOProvider
{
}
@Override
- public SignatureAlgorithm getAlgorithm() {
- return delegate.getAlgorithm();
+ public Set<JWSAlgorithm> supportedJWSAlgorithms() {
+ return delegate.supportedJWSAlgorithms();
}
@Override
- public boolean verify(final JwsHeaders headers, final String unsignedText,
final byte[] signature) {
- return delegate.verify(headers, unsignedText, signature);
+ public JCAContext getJCAContext() {
+ return delegate.getJCAContext();
}
@Override
- public JwsVerificationSignature createJwsVerificationSignature(final
JwsHeaders headers) {
- return delegate.createJwsVerificationSignature(headers);
+ public boolean verify(
+ final JWSHeader header,
+ final byte[] signingInput,
+ final Base64URL signature) throws JOSEException {
+
+ return delegate.verify(header, signingInput, signature);
}
@Transactional(readOnly = true)
@Override
- public Pair<User, Set<SyncopeGrantedAuthority>> resolve(final JwtClaims
jwtClaims) {
+ public Pair<User, Set<SyncopeGrantedAuthority>> resolve(final JWTClaimsSet
jwtClaims) {
AttrCond userIdCond = new AttrCond();
userIdCond.setSchema("userId");
userIdCond.setType(AttrCond.Type.EQ);
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 192cc9a..7f2202e 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import com.nimbusds.jose.JWSAlgorithm;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@@ -50,7 +51,6 @@ import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.cxf.jaxrs.client.WebClient;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
@@ -243,7 +243,7 @@ public abstract class AbstractITCase {
protected static String JWT_ISSUER;
- protected static SignatureAlgorithm JWS_ALGORITHM;
+ protected static JWSAlgorithm JWS_ALGORITHM;
protected static SyncopeClientFactoryBean clientFactory;
@@ -352,7 +352,7 @@ public abstract class AbstractITCase {
ANONYMOUS_UNAME = props.getProperty("anonymousUser");
ANONYMOUS_KEY = props.getProperty("anonymousKey");
JWT_ISSUER = props.getProperty("jwtIssuer");
- JWS_ALGORITHM =
SignatureAlgorithm.valueOf(props.getProperty("jwsAlgorithm"));
+ JWS_ALGORITHM =
JWSAlgorithm.parse(props.getProperty("jwsAlgorithm"));
JWS_KEY = props.getProperty("jwsKey");
} catch (Exception e) {
LOG.error("Could not read security.properties", e);
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
index cd2ecee..1ebefd7 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/JWTITCase.java
@@ -26,7 +26,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.KeyLengthException;
+import com.nimbusds.jose.crypto.MACSigner;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.jwt.SignedJWT;
import java.security.AccessControlException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -35,19 +46,8 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.ws.rs.core.Response;
-import javax.xml.ws.WebServiceException;
+import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.cxf.rs.security.jose.common.JoseType;
-import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
-import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider;
-import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
-import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
-import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
-import org.apache.cxf.rs.security.jose.jws.NoneJwsSignatureProvider;
-import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
-import org.apache.cxf.rs.security.jose.jwt.JwtToken;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.request.UserCR;
@@ -55,11 +55,11 @@ import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.service.AccessTokenService;
import org.apache.syncope.common.rest.api.service.UserSelfService;
-import
org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider;
-import
org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJWSSigner;
+import org.apache.syncope.core.spring.security.jws.AccessTokenJWSVerifier;
import org.apache.syncope.fit.AbstractITCase;
import org.apache.syncope.fit.core.reference.CustomJWTSSOProvider;
-import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
@@ -67,27 +67,18 @@ import org.junit.jupiter.api.Test;
*/
public class JWTITCase extends AbstractITCase {
- private JwsSignatureProvider jwsSignatureProvider;
+ private static AccessTokenJWSSigner JWS_SIGNER;
- private JwsSignatureVerifier jwsSignatureVerifier;
+ private static AccessTokenJWSVerifier JWS_VERIFIER;
- @BeforeEach
- public void setupVerifier() throws Exception {
- AccessTokenJwsSignatureProvider atjsp = new
AccessTokenJwsSignatureProvider();
- atjsp.setJwsAlgorithm(JWS_ALGORITHM);
- atjsp.setJwsKey(JWS_KEY);
- atjsp.afterPropertiesSet();
- this.jwsSignatureProvider = atjsp;
-
- AccessTokenJwsSignatureVerifier atjsv = new
AccessTokenJwsSignatureVerifier();
- atjsv.setJwsAlgorithm(JWS_ALGORITHM);
- atjsv.setJwsKey(JWS_KEY);
- atjsv.afterPropertiesSet();
- this.jwsSignatureVerifier = atjsv;
+ @BeforeAll
+ public static void setupVerifier() throws Exception {
+ JWS_SIGNER = new AccessTokenJWSSigner(JWS_ALGORITHM, JWS_KEY);
+ JWS_VERIFIER = new AccessTokenJWSVerifier(JWS_ALGORITHM, JWS_KEY);
}
@Test
- public void getJWTToken() throws ParseException {
+ public void getJWTToken() throws ParseException, JOSEException {
// Get the token
SyncopeClient localClient = clientFactory.create(ADMIN_UNAME,
ADMIN_PWD);
AccessTokenService accessTokenService =
localClient.getService(AccessTokenService.class);
@@ -95,39 +86,39 @@ public class JWTITCase extends AbstractITCase {
Response response = accessTokenService.login();
String token = response.getHeaderString(RESTHeaders.TOKEN);
assertNotNull(token);
- String expiry = response.getHeaderString(RESTHeaders.TOKEN_EXPIRE);
- assertNotNull(expiry);
+ String expiration = response.getHeaderString(RESTHeaders.TOKEN_EXPIRE);
+ assertNotNull(expiration);
// Validate the signature
- JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(token);
- assertTrue(consumer.verifySignatureWith(jwsSignatureVerifier));
+ SignedJWT jwt = SignedJWT.parse(token);
+ jwt.verify(JWS_VERIFIER);
+ assertTrue(jwt.verify(JWS_VERIFIER));
Date now = new Date();
// Verify the expiry header matches that of the token
- Long expiryTime = consumer.getJwtClaims().getExpiryTime();
- assertNotNull(expiryTime);
+ Date tokenDate = jwt.getJWTClaimsSet().getExpirationTime();
+ assertNotNull(tokenDate);
SimpleDateFormat dateFormat = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
- Date tokenDate = dateFormat.parse(dateFormat.format(new
Date(expiryTime * 1000L)));
- Date parsedDate = dateFormat.parse(expiry);
+ Date parsedDate = dateFormat.parse(expiration);
assertEquals(tokenDate, parsedDate);
assertTrue(parsedDate.after(now));
// Verify issuedAt
- Long issuedAt = consumer.getJwtClaims().getIssuedAt();
- assertNotNull(issuedAt);
- assertTrue(new Date(issuedAt).before(now));
+ Date issueTime = jwt.getJWTClaimsSet().getIssueTime();
+ assertNotNull(issueTime);
+ assertTrue(issueTime.before(now));
// Validate subject + issuer
- assertEquals(ADMIN_UNAME, consumer.getJwtClaims().getSubject());
- assertEquals(JWT_ISSUER, consumer.getJwtClaims().getIssuer());
+ assertEquals(ADMIN_UNAME, jwt.getJWTClaimsSet().getSubject());
+ assertEquals(JWT_ISSUER, jwt.getJWTClaimsSet().getIssuer());
// Verify NotBefore
- Long notBefore = consumer.getJwtClaims().getNotBefore();
- assertNotNull(notBefore);
- assertTrue(new Date(notBefore).before(now));
+ Date notBeforeTime = jwt.getJWTClaimsSet().getNotBeforeTime();
+ assertNotNull(notBeforeTime);
+ assertTrue(notBeforeTime.before(now));
}
@Test
@@ -151,13 +142,13 @@ public class JWTITCase extends AbstractITCase {
try {
jwtUserSelfService.read();
fail("Failure expected on a modified token");
- } catch (WebServiceException ex) {
- // expected
+ } catch (AccessControlException e) {
+ assertEquals("Invalid signature found in JWT", e.getMessage());
}
}
@Test
- public void tokenValidation() throws ParseException {
+ public void tokenValidation() throws ParseException, JOSEException {
// Get an initial token
SyncopeClient localClient = clientFactory.create(ADMIN_UNAME,
ADMIN_PWD);
AccessTokenService accessTokenService =
localClient.getService(AccessTokenService.class);
@@ -165,30 +156,26 @@ public class JWTITCase extends AbstractITCase {
Response response = accessTokenService.login();
String token = response.getHeaderString(RESTHeaders.TOKEN);
assertNotNull(token);
- JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(token);
- String tokenId = consumer.getJwtClaims().getTokenId();
+ SignedJWT jwt = SignedJWT.parse(token);
+ String tokenId = jwt.getJWTClaimsSet().getJWTID();
// Create a new token using the Id of the first token
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(tokenId);
- jwtClaims.setSubject(ADMIN_UNAME);
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(JWT_ISSUER);
- jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
- jwtClaims.setNotBefore(currentTime);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- String signed = producer.signWith(jwsSignatureProvider);
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, 5);
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(tokenId).
+ subject(ADMIN_UNAME).
+ issueTime(currentTime).
+ issuer(JWT_ISSUER).
+ expirationTime(expiration.getTime()).
+ notBeforeTime(currentTime);
+ jwt = new SignedJWT(new JWSHeader(JWS_SIGNER.getJwsAlgorithm()),
claimsSet.build());
+ jwt.sign(JWS_SIGNER);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
UserSelfService jwtUserSelfService =
jwtClient.getService(UserSelfService.class);
@@ -196,51 +183,46 @@ public class JWTITCase extends AbstractITCase {
}
@Test
- public void invalidIssuer() throws ParseException {
+ public void invalidIssuer() throws ParseException, JOSEException {
// Get an initial token
SyncopeClient localClient = clientFactory.create(ADMIN_UNAME,
ADMIN_PWD);
AccessTokenService accessTokenService =
localClient.getService(AccessTokenService.class);
Response response = accessTokenService.login();
String token = response.getHeaderString(RESTHeaders.TOKEN);
- assertNotNull(token);
- JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(token);
- String tokenId = consumer.getJwtClaims().getTokenId();
+ SignedJWT jwt = SignedJWT.parse(token);
+ String tokenId = jwt.getJWTClaimsSet().getJWTID();
// Create a new token using the Id of the first token
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(tokenId);
- jwtClaims.setSubject(ADMIN_UNAME);
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer("UnknownIssuer");
- jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
- jwtClaims.setNotBefore(currentTime);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- String signed = producer.signWith(jwsSignatureProvider);
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, 5);
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(tokenId).
+ subject(ADMIN_UNAME).
+ issueTime(currentTime).
+ issuer("UnknownIssuer").
+ expirationTime(expiration.getTime()).
+ notBeforeTime(currentTime);
+ jwt = new SignedJWT(new JWSHeader(JWS_SIGNER.getJwsAlgorithm()),
claimsSet.build());
+ jwt.sign(JWS_SIGNER);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
UserSelfService jwtUserSelfService =
jwtClient.getService(UserSelfService.class);
try {
jwtUserSelfService.read();
fail("Failure expected on an invalid issuer");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void expiredToken() throws ParseException {
+ public void expiredToken() throws ParseException, JOSEException {
// Get an initial token
SyncopeClient localClient = clientFactory.create(ADMIN_UNAME,
ADMIN_PWD);
AccessTokenService accessTokenService =
localClient.getService(AccessTokenService.class);
@@ -248,43 +230,35 @@ public class JWTITCase extends AbstractITCase {
Response response = accessTokenService.login();
String token = response.getHeaderString(RESTHeaders.TOKEN);
assertNotNull(token);
- JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(token);
- String tokenId = consumer.getJwtClaims().getTokenId();
+ SignedJWT jwt = SignedJWT.parse(token);
+ String tokenId = jwt.getJWTClaimsSet().getJWTID();
// Create a new token using the Id of the first token
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(tokenId);
- jwtClaims.setSubject(ADMIN_UNAME);
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(JWT_ISSUER);
- jwtClaims.setExpiryTime((now.getTime() - 5000L) / 1000L);
- jwtClaims.setNotBefore(currentTime);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- String signed = producer.signWith(jwsSignatureProvider);
+ Date currentTime = new Date();
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(tokenId).
+ subject(ADMIN_UNAME).
+ issueTime(currentTime).
+ issuer(JWT_ISSUER).
+ expirationTime(new Date(currentTime.getTime() - 5000L)).
+ notBeforeTime(currentTime);
+ jwt = new SignedJWT(new JWSHeader(JWS_SIGNER.getJwsAlgorithm()),
claimsSet.build());
+ jwt.sign(JWS_SIGNER);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
UserSelfService jwtUserSelfService =
jwtClient.getService(UserSelfService.class);
try {
jwtUserSelfService.read();
fail("Failure expected on an expired token");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void notBefore() throws ParseException {
+ public void notBefore() throws ParseException, JOSEException {
// Get an initial token
SyncopeClient localClient = clientFactory.create(ADMIN_UNAME,
ADMIN_PWD);
AccessTokenService accessTokenService =
localClient.getService(AccessTokenService.class);
@@ -292,43 +266,39 @@ public class JWTITCase extends AbstractITCase {
Response response = accessTokenService.login();
String token = response.getHeaderString(RESTHeaders.TOKEN);
assertNotNull(token);
- JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(token);
- String tokenId = consumer.getJwtClaims().getTokenId();
+ SignedJWT jwt = SignedJWT.parse(token);
+ String tokenId = jwt.getJWTClaimsSet().getJWTID();
// Create a new token using the Id of the first token
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(tokenId);
- jwtClaims.setSubject(ADMIN_UNAME);
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(JWT_ISSUER);
- jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
- jwtClaims.setNotBefore(currentTime + 60L);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- String signed = producer.signWith(jwsSignatureProvider);
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, 5);
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(tokenId).
+ subject(ADMIN_UNAME).
+ issueTime(currentTime).
+ issuer(JWT_ISSUER).
+ expirationTime(expiration.getTime()).
+ notBeforeTime(new Date(currentTime.getTime() + 60000L));
+ jwt = new SignedJWT(new JWSHeader(JWS_SIGNER.getJwsAlgorithm()),
claimsSet.build());
+ jwt.sign(JWS_SIGNER);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
UserSelfService jwtUserSelfService =
jwtClient.getService(UserSelfService.class);
try {
jwtUserSelfService.read();
fail("Failure expected on a token that is not valid yet");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void noneSignature() throws ParseException {
+ public void noSignature() throws ParseException {
// Get an initial token
SyncopeClient localClient = clientFactory.create(ADMIN_UNAME,
ADMIN_PWD);
AccessTokenService accessTokenService =
localClient.getService(AccessTokenService.class);
@@ -336,37 +306,25 @@ public class JWTITCase extends AbstractITCase {
Response response = accessTokenService.login();
String token = response.getHeaderString(RESTHeaders.TOKEN);
assertNotNull(token);
- JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(token);
- String tokenId = consumer.getJwtClaims().getTokenId();
+ JWT jwt = SignedJWT.parse(token);
// Create a new token using the Id of the first token
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(tokenId);
- jwtClaims.setSubject(consumer.getJwtClaims().getSubject());
- jwtClaims.setIssuedAt(consumer.getJwtClaims().getIssuedAt());
- jwtClaims.setIssuer(consumer.getJwtClaims().getIssuer());
- jwtClaims.setExpiryTime(consumer.getJwtClaims().getExpiryTime());
- jwtClaims.setNotBefore(consumer.getJwtClaims().getNotBefore());
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT,
SignatureAlgorithm.NONE);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
+ JWTClaimsSet.Builder claimsSet = new
JWTClaimsSet.Builder(jwt.getJWTClaimsSet());
+ jwt = new PlainJWT(claimsSet.build());
+ String bearer = jwt.serialize();
- JwsSignatureProvider noneJwsSignatureProvider = new
NoneJwsSignatureProvider();
- String signed = producer.signWith(noneJwsSignatureProvider);
-
- SyncopeClient jwtClient = clientFactory.create(signed);
+ SyncopeClient jwtClient = clientFactory.create(bearer);
UserSelfService jwtUserSelfService =
jwtClient.getService(UserSelfService.class);
try {
jwtUserSelfService.read();
fail("Failure expected on no signature");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void unknownId() throws ParseException {
+ public void unknownId() throws ParseException, JOSEException {
// Get an initial token
SyncopeClient localClient = clientFactory.create(ADMIN_UNAME,
ADMIN_PWD);
AccessTokenService accessTokenService =
localClient.getService(AccessTokenService.class);
@@ -374,66 +332,46 @@ public class JWTITCase extends AbstractITCase {
Response response = accessTokenService.login();
String token = response.getHeaderString(RESTHeaders.TOKEN);
assertNotNull(token);
+ SignedJWT jwt = SignedJWT.parse(token);
// Create a new token using an unknown Id
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(UUID.randomUUID().toString());
- jwtClaims.setSubject(ADMIN_UNAME);
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(JWT_ISSUER);
- jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
- jwtClaims.setNotBefore(currentTime);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- String signed = producer.signWith(jwsSignatureProvider);
+ JWTClaimsSet.Builder claimsSet = new
JWTClaimsSet.Builder(jwt.getJWTClaimsSet()).
+ jwtID(UUID.randomUUID().toString());
+ jwt = new SignedJWT(new JWSHeader(JWS_SIGNER.getJwsAlgorithm()),
claimsSet.build());
+ jwt.sign(JWS_SIGNER);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
UserSelfService jwtUserSelfService =
jwtClient.getService(UserSelfService.class);
try {
jwtUserSelfService.read();
fail("Failure expected on an unknown id");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void thirdPartyToken() throws ParseException {
- assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+ public void thirdPartyToken() throws ParseException, JOSEException {
+ assumeFalse(JWSAlgorithm.Family.RSA.contains(JWS_ALGORITHM));
// Create a new token
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(UUID.randomUUID().toString());
- jwtClaims.setSubject("[email protected]");
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(CustomJWTSSOProvider.ISSUER);
- jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
- jwtClaims.setNotBefore(currentTime);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- JwsSignatureProvider customSignatureProvider =
- new
HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(),
JWS_ALGORITHM);
- String signed = producer.signWith(customSignatureProvider);
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, 5);
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(UUID.randomUUID().toString()).
+ subject("[email protected]").
+ issueTime(currentTime).
+ issuer(CustomJWTSSOProvider.ISSUER).
+ expirationTime(expiration.getTime()).
+ notBeforeTime(currentTime);
+ SignedJWT jwt = new SignedJWT(new JWSHeader(JWS_ALGORITHM),
claimsSet.build());
+ jwt.sign(new MACSigner(CustomJWTSSOProvider.CUSTOM_KEY));
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
@@ -443,120 +381,110 @@ public class JWTITCase extends AbstractITCase {
}
@Test
- public void thirdPartyTokenUnknownUser() throws ParseException {
- assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+ public void thirdPartyTokenUnknownUser() throws ParseException,
JOSEException {
+ assumeFalse(JWSAlgorithm.Family.RSA.contains(JWS_ALGORITHM));
// Create a new token
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(UUID.randomUUID().toString());
- jwtClaims.setSubject("[email protected]");
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(CustomJWTSSOProvider.ISSUER);
- jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
- jwtClaims.setNotBefore(currentTime);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- JwsSignatureProvider customSignatureProvider =
- new
HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(),
JWS_ALGORITHM);
- String signed = producer.signWith(customSignatureProvider);
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, 5);
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(UUID.randomUUID().toString()).
+ subject("[email protected]").
+ issueTime(currentTime).
+ issuer(CustomJWTSSOProvider.ISSUER).
+ expirationTime(expiration.getTime()).
+ notBeforeTime(currentTime);
+ SignedJWT jwt = new SignedJWT(new
JWSHeader(JWS_SIGNER.getJwsAlgorithm()), claimsSet.build());
+ jwt.sign(JWS_SIGNER);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
try {
jwtClient.self();
fail("Failure expected on an unknown subject");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void thirdPartyTokenUnknownIssuer() throws ParseException {
- assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+ public void thirdPartyTokenUnknownIssuer() throws ParseException,
JOSEException {
+ assumeFalse(JWSAlgorithm.Family.RSA.contains(JWS_ALGORITHM));
// Create a new token
- Date now = new Date();
- long currentTime = now.getTime() / 1000L;
-
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
-
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(UUID.randomUUID().toString());
- jwtClaims.setSubject("[email protected]");
- jwtClaims.setIssuedAt(currentTime);
- jwtClaims.setIssuer(CustomJWTSSOProvider.ISSUER + '_');
- jwtClaims.setExpiryTime(expiry.getTime().getTime() / 1000L);
- jwtClaims.setNotBefore(currentTime);
-
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
-
- JwsSignatureProvider customSignatureProvider =
- new
HmacJwsSignatureProvider(CustomJWTSSOProvider.CUSTOM_KEY.getBytes(),
JWS_ALGORITHM);
- String signed = producer.signWith(customSignatureProvider);
+ Date currentTime = new Date();
+
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, 5);
+
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(UUID.randomUUID().toString()).
+ subject("[email protected]").
+ issueTime(currentTime).
+ issuer(CustomJWTSSOProvider.ISSUER + "_").
+ expirationTime(expiration.getTime()).
+ notBeforeTime(currentTime);
+ SignedJWT jwt = new SignedJWT(new
JWSHeader(JWS_SIGNER.getJwsAlgorithm()), claimsSet.build());
+ jwt.sign(JWS_SIGNER);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
try {
jwtClient.self();
fail("Failure expected on an unknown issuer");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void thirdPartyTokenBadSignature() throws ParseException {
- assumeFalse(SignatureAlgorithm.isPublicKeyAlgorithm(JWS_ALGORITHM));
+ public void thirdPartyTokenBadSignature()
+ throws ParseException, KeyLengthException,
NoSuchAlgorithmException,
+ InvalidKeySpecException, JOSEException {
+
+ assumeFalse(JWSAlgorithm.Family.RSA.contains(JWS_ALGORITHM));
// Create a new token
- Date now = new Date();
+ Date currentTime = new Date();
- Calendar expiry = Calendar.getInstance();
- expiry.setTime(now);
- expiry.add(Calendar.MINUTE, 5);
+ Calendar expiration = Calendar.getInstance();
+ expiration.setTime(currentTime);
+ expiration.add(Calendar.MINUTE, 5);
- JwtClaims jwtClaims = new JwtClaims();
- jwtClaims.setTokenId(UUID.randomUUID().toString());
- jwtClaims.setSubject("[email protected]");
- jwtClaims.setIssuedAt(now.getTime());
- jwtClaims.setIssuer(CustomJWTSSOProvider.ISSUER);
- jwtClaims.setExpiryTime(expiry.getTime().getTime());
- jwtClaims.setNotBefore(now.getTime());
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder().
+ jwtID(UUID.randomUUID().toString()).
+ subject("[email protected]").
+ issueTime(currentTime).
+ issuer(CustomJWTSSOProvider.ISSUER).
+ expirationTime(expiration.getTime()).
+ notBeforeTime(currentTime);
- JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, JWS_ALGORITHM);
- JwtToken jwtToken = new JwtToken(jwsHeaders, jwtClaims);
- JwsJwtCompactProducer producer = new JwsJwtCompactProducer(jwtToken);
+ AccessTokenJWSSigner customJWSSigner =
+ new AccessTokenJWSSigner(JWS_ALGORITHM,
RandomStringUtils.randomAlphanumeric(512));
- JwsSignatureProvider customSignatureProvider =
- new HmacJwsSignatureProvider((CustomJWTSSOProvider.CUSTOM_KEY
+ '_').getBytes(), JWS_ALGORITHM);
- String signed = producer.signWith(customSignatureProvider);
+ SignedJWT jwt = new SignedJWT(new
JWSHeader(customJWSSigner.getJwsAlgorithm()), claimsSet.build());
+ jwt.sign(customJWSSigner);
+ String signed = jwt.serialize();
SyncopeClient jwtClient = clientFactory.create(signed);
try {
jwtClient.self();
fail("Failure expected on a bad signature");
- } catch (AccessControlException ex) {
+ } catch (AccessControlException e) {
// expected
}
}
@Test
- public void issueSYNCOPE1420() {
+ public void issueSYNCOPE1420() throws ParseException {
Long orig = confParamOps.get(SyncopeConstants.MASTER_DOMAIN,
"jwt.lifetime.minutes", null, Long.class);
try {
// set for immediate JWT expiration
@@ -569,10 +497,8 @@ public class JWTITCase extends AbstractITCase {
// login, get JWT with expiryTime
String jwt = clientFactory.create(user.getUsername(),
"password123").getJWT();
- JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(jwt);
- assertTrue(consumer.verifySignatureWith(jwsSignatureVerifier));
- Long expiryTime = consumer.getJwtClaims().getExpiryTime();
- assertNotNull(expiryTime);
+ Date expirationTime =
SignedJWT.parse(jwt).getJWTClaimsSet().getExpirationTime();
+ assertNotNull(expirationTime);
// wait for 1 sec, check that JWT is effectively expired
try {
@@ -580,7 +506,7 @@ public class JWTITCase extends AbstractITCase {
} catch (InterruptedException e) {
// ignore
}
- assertTrue(expiryTime < System.currentTimeMillis());
+ assertTrue(expirationTime.before(new Date()));
// login again, get new JWT
// (even if ExpiredAccessTokenCleanup did not run yet, as it is
scheduled every 5 minutes)
diff --git a/pom.xml b/pom.xml
index b325718..4b1a960 100644
--- a/pom.xml
+++ b/pom.xml
@@ -618,11 +618,6 @@ under the License.
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
- <artifactId>cxf-rt-rs-security-jose</artifactId>
- <version>${cxf.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
<version>${cxf.version}</version>
</dependency>