Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unneccessary guava dependency #27

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@
<spring-security.version>5.1.5.RELEASE</spring-security.version>
<spring.version>5.1.13.RELEASE</spring.version>
<jackson.version>2.10.0</jackson.version>
<immutables.version>2.7.5</immutables.version>
<immutables.version>2.8.8</immutables.version>
<lombok.version>1.18.8</lombok.version>
<javax.servlet-api.version>3.1.0</javax.servlet-api.version>
<vavr.version>0.10.3</vavr.version>
<vavr-encodings.version>0.6.0</vavr-encodings.version>
<default-immutables.version>1.2.5</default-immutables.version>
<java-jwt.version>3.8.0</java-jwt.version>
<jwks-rsa.version>0.8.1</jwks-rsa.version>
<bcprov-jdk15on.version>1.61</bcprov-jdk15on.version>
<vavr.version>0.10.0</vavr.version>
<slf4j-api.version>1.7.25</slf4j-api.version>
<!-- test -->
<junit.version>4.12</junit.version>
Expand Down Expand Up @@ -364,11 +364,6 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright © 2017 Mercateo AG (http://www.mercateo.com)
*
* Licensed 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 com.mercateo.spring.security.jwt.token.verifier;

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.interfaces.RSAKeyProvider;

class AlgorithmFactory {
private final RSAKeyProvider rsaKeyProvider;

public AlgorithmFactory(RSAKeyProvider rsaKeyProvider) {
this.rsaKeyProvider = rsaKeyProvider;
}

public Algorithm createByName(String algorithmName) throws AlgorithmMismatchException {
switch (algorithmName.toLowerCase()) {
case "rs256":
return Algorithm.RSA256(rsaKeyProvider);
case "rs384":
return Algorithm.RSA384(rsaKeyProvider);
case "rs512":
return Algorithm.RSA512(rsaKeyProvider);
default:
throw new AlgorithmMismatchException(
"The provided Algorithm has to be RSA.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import com.auth0.jwt.interfaces.Clock;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.google.common.annotations.VisibleForTesting;

import lombok.val;

Expand All @@ -47,11 +46,11 @@
@SuppressWarnings("WeakerAccess")
public final class JWTVerifier {
private final Map<String, Object> claims;
private final RSAKeyProvider rsaKeyProvider;
private final AlgorithmFactory algorithmFactory;
private final Clock clock;

JWTVerifier(RSAKeyProvider rsaKeyProvider, Map<String, Object> claims, Clock clock) {
this.rsaKeyProvider = rsaKeyProvider;
JWTVerifier(AlgorithmFactory algorithmFactory, Map<String, Object> claims, Clock clock) {
this.algorithmFactory = algorithmFactory;
this.claims = Collections.unmodifiableMap(claims);
this.clock = clock;
}
Expand All @@ -64,7 +63,7 @@ public final class JWTVerifier {
* @throws IllegalArgumentException if the provided algorithm is null.
*/
public static BaseVerification init(RSAKeyProvider rsaKeyProvider) throws IllegalArgumentException {
return new BaseVerification(rsaKeyProvider);
return new BaseVerification(new AlgorithmFactory(rsaKeyProvider));
}

/**
Expand All @@ -86,25 +85,11 @@ public static BaseVerification init(RSAKeyProvider rsaKeyProvider) throws Illega
*/
public DecodedJWT verify(String token) throws JWTVerificationException {
DecodedJWT jwt = JWT.decode(token);
Algorithm algorithm = getAlgorithm(jwt);
Algorithm algorithm = algorithmFactory.createByName(jwt.getAlgorithm());
algorithm.verify(jwt);
verifyClaims(jwt, claims);
return jwt;
}
@VisibleForTesting
Algorithm getAlgorithm(DecodedJWT jwt) throws AlgorithmMismatchException {
switch (jwt.getAlgorithm().toLowerCase()) {
case "rs256":
return Algorithm.RSA256(rsaKeyProvider);
case "rs384":
return Algorithm.RSA384(rsaKeyProvider);
case "rs512":
return Algorithm.RSA512(rsaKeyProvider);
default:
throw new AlgorithmMismatchException(
"The provided Algorithm has to be RSA.");
}
}

private void verifyClaims(DecodedJWT jwt, Map<String, Object> claims) throws TokenExpiredException,
InvalidClaimException {
Expand Down Expand Up @@ -199,18 +184,18 @@ public Date getToday() {
* The Verification class holds the Claims required by a JWT to be valid.
*/
public static class BaseVerification {
private final RSAKeyProvider rsaKeyProvider;
private final AlgorithmFactory algorithmFactory;

private final Map<String, Object> claims;

private long defaultLeeway;

BaseVerification(RSAKeyProvider rsaKeyProvider) throws IllegalArgumentException {
if (rsaKeyProvider == null) {
BaseVerification(AlgorithmFactory algorithmFactory) throws IllegalArgumentException {
if (algorithmFactory == null) {
throw new IllegalArgumentException("The rsaKeyprovider cannot be null.");
}

this.rsaKeyProvider = rsaKeyProvider;
this.algorithmFactory = algorithmFactory;
this.claims = new HashMap<>();
this.defaultLeeway = 0;
}
Expand Down Expand Up @@ -283,7 +268,7 @@ public JWTVerifier build() {
*/
public JWTVerifier build(Clock clock) {
addLeewayToDateClaims();
return new JWTVerifier(rsaKeyProvider, claims, clock);
return new JWTVerifier(algorithmFactory, claims, clock);
}

private void assertPositive(long leeway) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,60 +15,25 @@
*/
package com.mercateo.spring.security.jwt.token.verifier;

import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import com.auth0.jwk.Jwk;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.mercateo.spring.security.jwt.token.config.JWTConfig;
import com.mercateo.spring.security.jwt.token.keyset.JWTKeyset;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;

@AllArgsConstructor
@Slf4j
public class JWTVerifierFactory {
private final JWTKeyset jwtKeyset;
private final RSAKeyProviderFactory keyProviderFactory;

private final JWTConfig jwtConfig;

private static IllegalStateException map(Throwable cause) {
return new IllegalStateException(cause);
public JWTVerifierFactory(JWTKeyset jwtKeyset, JWTConfig jwtConfig) {
this.keyProviderFactory = new RSAKeyProviderFactory(jwtKeyset);
this.jwtConfig = jwtConfig;
}

public JWTVerifier create() {
final RSAKeyProvider rsaKeyProvider = new RSAKeyProvider() {
@Override
public RSAPublicKey getPublicKeyById(String keyId) {
return jwtKeyset
.getKeysetForId(keyId)
.mapTry(Jwk::getPublicKey)
.map(Key::getEncoded)
.mapTry(JWTVerifierFactory::createKey)
.onFailure(e -> log.error("Error getting public key for id " + keyId, e))
.getOrElseThrow(JWTVerifierFactory::map);
}

@Override
public RSAPrivateKey getPrivateKey() {
return null;
}

@Override
public String getPrivateKeyId() {
return null;
}
};

val verification = JWTVerifier.init(rsaKeyProvider);
val verification = JWTVerifier.init(keyProviderFactory.create());

final int tokenLeeway = jwtConfig.getTokenLeeway();
verification.acceptLeeway(tokenLeeway);
Expand All @@ -80,9 +45,4 @@ public String getPrivateKeyId() {

return verification.build();
}

private static RSAPublicKey createKey(byte[] bytes ) throws NoSuchAlgorithmException, InvalidKeySpecException {
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(
new X509EncodedKeySpec(bytes));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright © 2017 Mercateo AG (http://www.mercateo.com)
*
* Licensed 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 com.mercateo.spring.security.jwt.token.verifier;

import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import com.auth0.jwk.Jwk;
import com.auth0.jwt.interfaces.RSAKeyProvider;
import com.mercateo.spring.security.jwt.token.keyset.JWTKeyset;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AllArgsConstructor
class RSAKeyProviderFactory {

private final JWTKeyset jwtKeyset;

private static IllegalStateException map(Throwable cause) {
return new IllegalStateException(cause);
}

private static RSAPublicKey createKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(
new X509EncodedKeySpec(bytes));
}

public RSAKeyProvider create() {
return new RSAKeyProvider() {
@Override
public RSAPublicKey getPublicKeyById(String keyId) {
return jwtKeyset
.getKeysetForId(keyId)
.mapTry(Jwk::getPublicKey)
.map(Key::getEncoded)
.mapTry(RSAKeyProviderFactory::createKey)
.onFailure(e -> log.error("Error getting public key for id " + keyId, e))
.getOrElseThrow(RSAKeyProviderFactory::map);
}

@Override
public RSAPrivateKey getPrivateKey() {
return null;
}

@Override
public String getPrivateKeyId() {
return null;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright © 2017 Mercateo AG (http://www.mercateo.com)
*
* Licensed 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 com.mercateo.spring.security.jwt.token.verifier;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

import com.auth0.jwk.Jwk;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.mercateo.spring.security.jwt.JWKProvider;
import com.mercateo.spring.security.jwt.token.keyset.JWTKeyset;

import io.vavr.control.Try;

public class AlgorithmFactoryTest {

private AlgorithmFactory uut;

@Before
public void setUp() {
final JWKProvider jwkProvider = new JWKProvider();
String keyId = "4711";
final Jwk jwk = jwkProvider.create(keyId);
JWTKeyset jwks = mock(JWTKeyset.class);
when(jwks.getKeysetForId(keyId)).thenReturn(Try.success(jwk));
assertThat(jwks.getKeysetForId(keyId)).isNotNull();
RSAKeyProviderFactory keyProviderFactory = new RSAKeyProviderFactory(jwks);
uut = new AlgorithmFactory(keyProviderFactory.create());
}

@Test
public void createsRSA256Algorithm() {
Algorithm algo1 = uut.createByName("RS256");
assertEquals("RS256", algo1.getName());
}

@Test
public void createRSA384Algorithm() {
Algorithm algo2 = uut.createByName("RS384");
assertEquals("RS384", algo2.getName());
}

@Test
public void createRSA512Algorithm() {
Algorithm algo3 = uut.createByName("RS512");
assertEquals("RS512", algo3.getName());
}

@Test
public void failsCreatingUnknownAlgorithm() {
assertThatThrownBy(() -> uut.createByName("foo")).isInstanceOf(AlgorithmMismatchException.class);
}
}
Loading