From 23a6354fc708bd89301bf2cac0619bbebb431f4f Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik Date: Mon, 8 Nov 2021 07:35:53 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Sign=20refresh=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add signature to the refresh_token JWT. Previously it has been missing it. --- .../DefaultOAuth2ProviderTokenService.java | 37 ++++++++++++++++--- ...TestDefaultOAuth2ProviderTokenService.java | 15 ++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/perun-oidc-server/src/main/java/cz/muni/ics/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java b/perun-oidc-server/src/main/java/cz/muni/ics/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java index 0f4beb0d1..0665ed5ab 100644 --- a/perun-oidc-server/src/main/java/cz/muni/ics/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java +++ b/perun-oidc-server/src/main/java/cz/muni/ics/oauth2/service/impl/DefaultOAuth2ProviderTokenService.java @@ -20,13 +20,23 @@ */ package cz.muni.ics.oauth2.service.impl; +import static cz.muni.ics.oauth2.service.IntrospectionResultAssembler.SCOPE; +import static cz.muni.ics.oauth2.service.IntrospectionResultAssembler.SCOPE_SEPARATOR; import static cz.muni.ics.openid.connect.request.ConnectRequestParameters.CODE_CHALLENGE; import static cz.muni.ics.openid.connect.request.ConnectRequestParameters.CODE_CHALLENGE_METHOD; import static cz.muni.ics.openid.connect.request.ConnectRequestParameters.CODE_VERIFIER; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.nimbusds.jose.JOSEObjectType; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.SignedJWT; +import cz.muni.ics.jwt.signer.service.JWTSigningAndValidationService; import cz.muni.ics.oauth2.model.OAuth2AccessTokenEntity; import cz.muni.ics.oauth2.model.OAuth2RefreshTokenEntity; import cz.muni.ics.oauth2.repository.OAuth2TokenRepository; +import cz.muni.ics.openid.connect.config.ConfigurationPropertiesBean; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -101,6 +111,12 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi @Autowired private ApprovedSiteService approvedSiteService; + @Autowired + private JWTSigningAndValidationService jwtService; + + @Autowired + private ConfigurationPropertiesBean configBean; + @Override public Set getAllAccessTokensForUser(String userName) { return tokenRepository.getAccessTokensByUserName(userName); @@ -262,7 +278,6 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi OAuth2RefreshTokenEntity refreshToken = new OAuth2RefreshTokenEntity(); //refreshTokenFactory.createNewRefreshToken(); JWTClaimsSet.Builder refreshClaims = new JWTClaimsSet.Builder(); - // make it expire if necessary if (client.getRefreshTokenValiditySeconds() != null) { Date expiration = new Date(System.currentTimeMillis() + (client.getRefreshTokenValiditySeconds() * 1000L)); @@ -272,19 +287,29 @@ public class DefaultOAuth2ProviderTokenService implements OAuth2TokenEntityServi // set a random identifier refreshClaims.jwtID(UUID.randomUUID().toString()); + refreshClaims.issuer(configBean.getIssuer()); - // TODO: add issuer fields, signature to JWT + String audience = client.getClientId(); + if (!Strings.isNullOrEmpty(audience)) { + refreshClaims.audience(Lists.newArrayList(audience)); + } - PlainJWT refreshJwt = new PlainJWT(refreshClaims.build()); - refreshToken.setJwt(refreshJwt); + JWTClaimsSet claims = refreshClaims.build(); + + JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm(); + JWSHeader header = new JWSHeader(signingAlg, JOSEObjectType.JWT, null, null, null, null, null, null, null, null, + jwtService.getDefaultSignerKeyId(), true, null, null); + SignedJWT signed = new SignedJWT(header, claims); + + jwtService.signJwt(signed); + refreshToken.setJwt(signed); //Add the authentication refreshToken.setAuthenticationHolder(authHolder); refreshToken.setClient(client); // save the token first so that we can set it to a member of the access token (NOTE: is this step necessary?) - OAuth2RefreshTokenEntity savedRefreshToken = tokenRepository.saveRefreshToken(refreshToken); - return savedRefreshToken; + return tokenRepository.saveRefreshToken(refreshToken); } @Override diff --git a/perun-oidc-server/src/test/java/cz/muni/ics/oauth2/service/impl/TestDefaultOAuth2ProviderTokenService.java b/perun-oidc-server/src/test/java/cz/muni/ics/oauth2/service/impl/TestDefaultOAuth2ProviderTokenService.java index 5136263a0..a68a83c3b 100644 --- a/perun-oidc-server/src/test/java/cz/muni/ics/oauth2/service/impl/TestDefaultOAuth2ProviderTokenService.java +++ b/perun-oidc-server/src/test/java/cz/muni/ics/oauth2/service/impl/TestDefaultOAuth2ProviderTokenService.java @@ -17,9 +17,11 @@ *******************************************************************************/ package cz.muni.ics.oauth2.service.impl; +import cz.muni.ics.jwt.signer.service.JWTSigningAndValidationService; import cz.muni.ics.oauth2.model.OAuth2AccessTokenEntity; import cz.muni.ics.oauth2.model.OAuth2RefreshTokenEntity; import cz.muni.ics.oauth2.repository.OAuth2TokenRepository; +import cz.muni.ics.openid.connect.config.ConfigurationPropertiesBean; import java.util.Date; import java.util.HashSet; import java.util.Set; @@ -50,6 +52,7 @@ import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import static com.google.common.collect.Sets.newHashSet; +import static com.nimbusds.jose.JWSAlgorithm.RS256; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; @@ -91,6 +94,7 @@ public class TestDefaultOAuth2ProviderTokenService { private OAuth2AccessTokenEntity accessToken; private String refreshTokenValue = "refresh_token_value"; private String userName = "6a50ac11786d402a9591d3e592ac770f"; + private final String issuer = "https://issuer.com/oidc/"; private TokenRequest tokenRequest; // for use when refreshing access tokens @@ -114,6 +118,12 @@ public class TestDefaultOAuth2ProviderTokenService { @Mock private SystemScopeService scopeService; + @Mock + private ConfigurationPropertiesBean configBean; + + @Mock + private JWTSigningAndValidationService jwtService; + @InjectMocks private DefaultOAuth2ProviderTokenService service; @@ -219,6 +229,11 @@ public class TestDefaultOAuth2ProviderTokenService { } }); + when(configBean.getIssuer()).thenReturn(issuer); + + when(jwtService.getDefaultSigningAlgorithm()).thenReturn(RS256); + String keyId = "kid1"; + when(jwtService.getDefaultSignerKeyId()).thenReturn(keyId); } /**