}
dependency 'org.json:json:20231013'
dependency 'com.tngtech.java:junit-dataprovider:1.13.1'
- dependencySet(group: 'io.jsonwebtoken', version: '0.11.5') {
+ dependencySet(group: 'io.jsonwebtoken', version: '0.12.3') {
entry 'jjwt-api'
entry 'jjwt-impl'
entry 'jjwt-jackson'
package org.sonar.server.authentication;
import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.impl.DefaultClaims;
+import io.jsonwebtoken.ClaimsBuilder;
+import io.jsonwebtoken.impl.DefaultClaimsBuilder;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
@Rule
public DbTester db = DbTester.create();
- private DbClient dbClient = db.getDbClient();
- private DbSession dbSession = db.getSession();
- private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
- private ArgumentCaptor<JwtSerializer.JwtSession> jwtArgumentCaptor = ArgumentCaptor.forClass(JwtSerializer.JwtSession.class);
- private HttpRequest request = mock(HttpRequest.class);
- private HttpResponse response = mock(HttpResponse.class);
- private HttpSession httpSession = mock(HttpSession.class);
- private System2 system2 = spy(System2.INSTANCE);
- private MapSettings settings = new MapSettings();
- private JwtSerializer jwtSerializer = mock(JwtSerializer.class);
- private JwtCsrfVerifier jwtCsrfVerifier = mock(JwtCsrfVerifier.class);
+ private final DbClient dbClient = db.getDbClient();
+ private final DbSession dbSession = db.getSession();
+ private final ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
+ private final ArgumentCaptor<JwtSerializer.JwtSession> jwtArgumentCaptor = ArgumentCaptor.forClass(JwtSerializer.JwtSession.class);
+ private final HttpRequest request = mock(HttpRequest.class);
+ private final HttpResponse response = mock(HttpResponse.class);
+ private final HttpSession httpSession = mock(HttpSession.class);
+ private final System2 system2 = spy(System2.INSTANCE);
+ private final MapSettings settings = new MapSettings();
+ private final JwtSerializer jwtSerializer = mock(JwtSerializer.class);
+ private final JwtCsrfVerifier jwtCsrfVerifier = mock(JwtCsrfVerifier.class);
private JwtHttpHandler underTest = new JwtHttpHandler(system2, dbClient, settings.asConfig(), jwtSerializer, jwtCsrfVerifier);
UserDto user = db.users().insertUser();
addJwtCookie();
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, NOW);
+ Claims claims = createTokenBuilder(sessionToken, NOW).build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
assertThat(underTest.validateToken(request, response)).isPresent();
addJwtCookie();
// Token was created 10 days ago and refreshed 6 minutes ago
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, TEN_DAYS_AGO);
- claims.put("lastRefreshTime", SIX_MINUTES_AGO);
+ Claims claims = createTokenBuilder(sessionToken, TEN_DAYS_AGO)
+ .add("lastRefreshTime", SIX_MINUTES_AGO)
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
assertThat(underTest.validateToken(request, response)).isPresent();
addJwtCookie();
// Token was created 10 days ago and refreshed 4 minutes ago
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, TEN_DAYS_AGO);
- claims.put("lastRefreshTime", FOUR_MINUTES_AGO);
+ Claims claims = createTokenBuilder(sessionToken, TEN_DAYS_AGO)
+ .add("lastRefreshTime", FOUR_MINUTES_AGO)
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
assertThat(underTest.validateToken(request, response)).isPresent();
addJwtCookie();
// Token was created 4 months ago, refreshed 4 minutes ago, and it expired in 5 minutes
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, NOW - (4L * 30 * 24 * 60 * 60 * 1000));
- claims.put("lastRefreshTime", FOUR_MINUTES_AGO);
+ Claims claims = createTokenBuilder(sessionToken, NOW - (4L * 30 * 24 * 60 * 60 * 1000))
+ .add("lastRefreshTime", FOUR_MINUTES_AGO)
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
assertThat(underTest.validateToken(request, response)).isEmpty();
addJwtCookie();
UserDto user = addUser(false);
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, NOW);
+ Claims claims = createTokenBuilder(sessionToken, NOW).build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
assertThat(underTest.validateToken(request, response)).isEmpty();
UserDto user = db.users().insertUser();
addJwtCookie();
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, NOW);
- claims.put("xsrfToken", CSRF_STATE);
+ Claims claims = createTokenBuilder(sessionToken, NOW)
+ .add("xsrfToken", CSRF_STATE)
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.validateToken(request, response);
UserDto user = db.users().insertUser();
addJwtCookie();
// No SessionToken in DB
- Claims claims = createToken("ABCD", user.getUuid(), NOW, IN_FIVE_MINUTES);
- claims.put("lastRefreshTime", SIX_MINUTES_AGO);
+ Claims claims = createTokenBuilder("ABCD", user.getUuid(), NOW, IN_FIVE_MINUTES)
+ .add("lastRefreshTime", SIX_MINUTES_AGO)
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.validateToken(request, response);
// In SessionToken, the expiration date is expired...
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(FOUR_MINUTES_AGO));
// ...whereas in the cookie, the expiration date is not expired
- Claims claims = createToken(sessionToken.getUuid(), user.getUuid(), NOW, IN_FIVE_MINUTES);
- claims.put("lastRefreshTime", SIX_MINUTES_AGO);
+ Claims claims = createTokenBuilder(sessionToken.getUuid(), user.getUuid(), NOW, IN_FIVE_MINUTES)
+ .add("lastRefreshTime", SIX_MINUTES_AGO)
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.validateToken(request, response);
addJwtCookie();
// Token was created 10 days ago and refreshed 6 minutes ago
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, TEN_DAYS_AGO);
- claims.put("xsrfToken", "CSRF_STATE");
+ Claims claims = createTokenBuilder(sessionToken, TEN_DAYS_AGO)
+ .add("xsrfToken", "CSRF_STATE")
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.validateToken(request, response);
addJwtCookie();
UserDto user = db.users().insertUser();
SessionTokenDto sessionToken = db.users().insertSessionToken(user, st -> st.setExpirationDate(IN_FIVE_MINUTES));
- Claims claims = createToken(sessionToken, TEN_DAYS_AGO);
- claims.put("lastRefreshTime", FOUR_MINUTES_AGO);
+ Claims claims = createTokenBuilder(sessionToken, TEN_DAYS_AGO)
+ .add("lastRefreshTime", FOUR_MINUTES_AGO)
+ .build();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.removeToken(request, response);
return cookie;
}
- private Claims createToken(SessionTokenDto sessionToken, long createdAt) {
- return createToken(sessionToken.getUuid(), sessionToken.getUserUuid(), createdAt, sessionToken.getExpirationDate());
+ private ClaimsBuilder createTokenBuilder(SessionTokenDto sessionToken, long createdAt) {
+ return createTokenBuilder(sessionToken.getUuid(), sessionToken.getUserUuid(), createdAt, sessionToken.getExpirationDate());
}
- private Claims createToken(String uuid, String userUuid, long createdAt, long expiredAt) {
- DefaultClaims claims = new DefaultClaims();
- claims.setId(uuid);
- claims.setSubject(userUuid);
- claims.setIssuedAt(new Date(createdAt));
- claims.setExpiration(new Date(expiredAt));
- claims.put("lastRefreshTime", createdAt);
- return claims;
+ private ClaimsBuilder createTokenBuilder(String uuid, String userUuid, long createdAt, long expiredAt) {
+ return new DefaultClaimsBuilder()
+ .id(uuid)
+ .subject(userUuid)
+ .issuedAt(new Date(createdAt))
+ .expiration(new Date(expiredAt))
+ .add("lastRefreshTime", createdAt);
}
}
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
+import io.jsonwebtoken.security.MacAlgorithm;
import io.jsonwebtoken.security.SignatureException;
import java.util.Base64;
import java.util.Collections;
import org.sonar.server.authentication.event.AuthenticationException;
import static com.google.common.base.Preconditions.checkNotNull;
-import static io.jsonwebtoken.impl.crypto.MacProvider.generateKey;
import static java.util.Objects.requireNonNull;
import static org.sonar.process.ProcessProperties.Property.AUTH_JWT_SECRET;
@ServerSide
public class JwtSerializer implements Startable {
- private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
+ private static final MacAlgorithm SIGNATURE_ALGORITHM = Jwts.SIG.HS256;
+ private static final String HS256_JCA_NAME = "HmacSHA256";
static final String LAST_REFRESH_TIME_PARAM = "lastRefreshTime";
String encode(JwtSession jwtSession) {
checkIsStarted();
return Jwts.builder()
- .addClaims(jwtSession.getProperties())
+ .claims(jwtSession.getProperties())
.claim(LAST_REFRESH_TIME_PARAM, system2.now())
- .setId(jwtSession.getSessionTokenUuid())
- .setSubject(jwtSession.getUserLogin())
- .setIssuedAt(new Date(system2.now()))
- .setExpiration(new Date(jwtSession.getExpirationTime()))
+ .id(jwtSession.getSessionTokenUuid())
+ .subject(jwtSession.getUserLogin())
+ .issuedAt(new Date(system2.now()))
+ .expiration(new Date(jwtSession.getExpirationTime()))
.signWith(secretKey, SIGNATURE_ALGORITHM)
.compact();
}
checkIsStarted();
Claims claims = null;
try {
- claims = Jwts.parserBuilder()
- .setSigningKey(secretKey)
+ claims = Jwts.parser()
+ .verifyWith(secretKey)
.build()
- .parseClaimsJws(token)
- .getBody();
+ .parseSignedClaims(token)
+ .getPayload();
requireNonNull(claims.getId(), "Token id hasn't been found");
requireNonNull(claims.getSubject(), "Token subject hasn't been found");
requireNonNull(claims.getExpiration(), "Token expiration date hasn't been found");
String refresh(Claims token, long expirationTime) {
checkIsStarted();
return Jwts.builder()
- .setClaims(token)
+ .claims(token)
.claim(LAST_REFRESH_TIME_PARAM, system2.now())
- .setExpiration(new Date(expirationTime))
+ .expiration(new Date(expirationTime))
.signWith(secretKey, SIGNATURE_ALGORITHM)
.compact();
}
private static SecretKey generateSecretKey() {
- return generateKey(SIGNATURE_ALGORITHM);
+ return SIGNATURE_ALGORITHM.key().build();
}
private static SecretKey decodeSecretKeyProperty(String base64SecretKey) {
byte[] decodedKey = Base64.getDecoder().decode(base64SecretKey);
- return new SecretKeySpec(decodedKey, 0, decodedKey.length, SIGNATURE_ALGORITHM.getJcaName());
+ return new SecretKeySpec(decodedKey, 0, decodedKey.length, HS256_JCA_NAME);
}
private void checkIsStarted() {
import com.google.common.collect.ImmutableMap;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.impl.DefaultClaims;
+import io.jsonwebtoken.impl.DefaultClaimsBuilder;
+import io.jsonwebtoken.security.MacAlgorithm;
import java.util.Base64;
import java.util.Date;
import java.util.Optional;
import org.sonar.server.authentication.JwtSerializer.JwtSession;
import org.sonar.server.authentication.event.AuthenticationException;
-import static io.jsonwebtoken.SignatureAlgorithm.HS256;
import static org.apache.commons.lang.time.DateUtils.addMinutes;
import static org.apache.commons.lang.time.DateUtils.addYears;
import static org.assertj.core.api.Assertions.assertThat;
public class JwtSerializerTest {
+ private static final MacAlgorithm SIGNATURE_ALGORITHM = Jwts.SIG.HS256;
+
private static final String A_SECRET_KEY = "HrPSavOYLNNrwTY+SOqpChr7OwvbR/zbDLdVXRN0+Eg=";
private static final String USER_LOGIN = "john";
private static final String SESSION_TOKEN_UUID = "ABCD";
-
- private MapSettings settings = new MapSettings();
- private System2 system2 = System2.INSTANCE;
- private JwtSerializer underTest = new JwtSerializer(settings.asConfig(), system2);
+ private final MapSettings settings = new MapSettings();
+ private final System2 system2 = System2.INSTANCE;
+ private final JwtSerializer underTest = new JwtSerializer(settings.asConfig(), system2);
@Test
public void generate_token() {
underTest.start();
String token = Jwts.builder()
- .setId("123")
- .setIssuedAt(new Date(system2.now()))
- .setExpiration(addMinutes(new Date(), -20))
- .signWith(decodeSecretKey(A_SECRET_KEY), HS256)
+ .id("123")
+ .issuedAt(new Date(system2.now()))
+ .expiration(addMinutes(new Date(), -20))
+ .signWith(decodeSecretKey(A_SECRET_KEY), SIGNATURE_ALGORITHM)
.compact();
assertThat(underTest.decode(token)).isEmpty();
underTest.start();
String token = Jwts.builder()
- .setId("123")
- .setSubject(USER_LOGIN)
- .setIssuedAt(new Date(system2.now()))
- .setExpiration(addMinutes(new Date(), 20))
- .signWith(decodeSecretKey("LyWgHktP0FuHB2K+kMs3KWMCJyFHVZDdDSqpIxAMVaQ="), HS256)
+ .id("123")
+ .subject(USER_LOGIN)
+ .issuedAt(new Date(system2.now()))
+ .expiration(addMinutes(new Date(), 20))
+ .signWith(decodeSecretKey("LyWgHktP0FuHB2K+kMs3KWMCJyFHVZDdDSqpIxAMVaQ="), SIGNATURE_ALGORITHM)
.compact();
assertThat(underTest.decode(token)).isEmpty();
underTest.start();
String token = Jwts.builder()
- .setId("123")
- .setSubject(USER_LOGIN)
- .setIssuedAt(new Date(system2.now()))
- .setExpiration(addMinutes(new Date(), 20))
+ .id("123")
+ .subject(USER_LOGIN)
+ .issuedAt(new Date(system2.now()))
+ .expiration(addMinutes(new Date(), 20))
.compact();
assertThat(underTest.decode(token)).isEmpty();
underTest.start();
String token = Jwts.builder()
- .setSubject(USER_LOGIN)
- .setIssuer("sonarqube")
- .setIssuedAt(new Date(system2.now()))
- .setExpiration(addMinutes(new Date(), 20))
- .signWith(decodeSecretKey(A_SECRET_KEY), HS256)
+ .subject(USER_LOGIN)
+ .issuer("sonarqube")
+ .issuedAt(new Date(system2.now()))
+ .expiration(addMinutes(new Date(), 20))
+ .signWith(decodeSecretKey(A_SECRET_KEY), SIGNATURE_ALGORITHM)
.compact();
assertThatThrownBy(() -> underTest.decode(token))
underTest.start();
String token = Jwts.builder()
- .setId("123")
- .setIssuer("sonarqube")
- .setIssuedAt(new Date(system2.now()))
- .setExpiration(addMinutes(new Date(), 20))
- .signWith(HS256, decodeSecretKey(A_SECRET_KEY))
+ .id("123")
+ .issuer("sonarqube")
+ .issuedAt(new Date(system2.now()))
+ .expiration(addMinutes(new Date(), 20))
+ .signWith(decodeSecretKey(A_SECRET_KEY), SIGNATURE_ALGORITHM)
.compact();
assertThatThrownBy(() -> underTest.decode(token))
underTest.start();
String token = Jwts.builder()
- .setId("123")
- .setIssuer("sonarqube")
- .setSubject(USER_LOGIN)
- .setIssuedAt(new Date(system2.now()))
- .signWith(decodeSecretKey(A_SECRET_KEY), HS256)
+ .id("123")
+ .issuer("sonarqube")
+ .subject(USER_LOGIN)
+ .issuedAt(new Date(system2.now()))
+ .signWith(decodeSecretKey(A_SECRET_KEY), SIGNATURE_ALGORITHM)
.compact();
assertThatThrownBy(() -> underTest.decode(token))
underTest.start();
String token = Jwts.builder()
- .setId("123")
- .setSubject(USER_LOGIN)
- .setExpiration(addMinutes(new Date(), 20))
- .signWith(decodeSecretKey(A_SECRET_KEY), HS256)
+ .id("123")
+ .subject(USER_LOGIN)
+ .expiration(addMinutes(new Date(), 20))
+ .signWith(decodeSecretKey(A_SECRET_KEY), SIGNATURE_ALGORITHM)
.compact();
assertThatThrownBy(() -> underTest.decode(token))
underTest.start();
assertThat(underTest.getSecretKey()).isNotNull();
- assertThat(underTest.getSecretKey().getAlgorithm()).isEqualTo(HS256.getJcaName());
+ assertThat(underTest.getSecretKey().getAlgorithm()).isEqualTo("HmacSHA256");
}
@Test
// Expired in 10 minutes
Date expiredAt = addMinutes(new Date(), 10);
Date lastRefreshDate = addMinutes(new Date(), -4);
- Claims token = new DefaultClaims()
+ Claims token = new DefaultClaimsBuilder()
.setId("id")
- .setSubject("subject")
- .setIssuer("sonarqube")
- .setIssuedAt(createdAt)
- .setExpiration(expiredAt);
- token.put("lastRefreshTime", lastRefreshDate.getTime());
- token.put("key", "value");
+ .subject("subject")
+ .issuer("sonarqube")
+ .issuedAt(createdAt)
+ .expiration(expiredAt)
+ .add("lastRefreshTime", lastRefreshDate.getTime())
+ .add("key", "value")
+ .build();
// Refresh the token with a higher expiration time
String encodedToken = underTest.refresh(token, addMinutes(new Date(), 20).getTime());
@Test
public void encode_fail_when_not_started() {
- assertThatThrownBy(() -> underTest.encode(new JwtSession(USER_LOGIN, SESSION_TOKEN_UUID, addMinutes(new Date(), 10).getTime())))
+ JwtSession jwtSession = new JwtSession(USER_LOGIN, SESSION_TOKEN_UUID, addMinutes(new Date(), 10).getTime());
+ assertThatThrownBy(() -> underTest.encode(jwtSession))
.isInstanceOf(NullPointerException.class)
.hasMessage("org.sonar.server.authentication.JwtSerializer not started");
}
@Test
public void refresh_fail_when_not_started() {
- assertThatThrownBy(() -> underTest.refresh(new DefaultClaims(), addMinutes(new Date(), 10).getTime()))
+ Claims claims = new DefaultClaimsBuilder().build();
+ long time = addMinutes(new Date(), 10).getTime();
+
+ assertThatThrownBy(() -> underTest.refresh(claims, time))
.isInstanceOf(NullPointerException.class)
.hasMessage("org.sonar.server.authentication.JwtSerializer not started");
}
private SecretKey decodeSecretKey(String encodedKey) {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
- return new SecretKeySpec(decodedKey, 0, decodedKey.length, HS256.getJcaName());
+ return new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA256");
}
private void setSecretKey(String s) {