diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/security/IamTokenEndointSecurityConfig.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/security/IamTokenEndointSecurityConfig.java index 4466db75c..39225929e 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/security/IamTokenEndointSecurityConfig.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/security/IamTokenEndointSecurityConfig.java @@ -15,31 +15,42 @@ */ package it.infn.mw.iam.config.security; +import static java.util.Collections.singletonList; import static org.springframework.http.HttpMethod.OPTIONS; +import java.time.Clock; + +import org.mitre.jwt.signer.service.impl.ClientKeyCacheService; +import org.mitre.oauth2.service.impl.DefaultClientUserDetailsService; import org.mitre.oauth2.web.CorsFilter; +import org.mitre.openid.connect.assertion.JWTBearerClientAssertionTokenEndpointFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter; import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler; import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.context.SecurityContextPersistenceFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +import it.infn.mw.iam.config.IamProperties; +import it.infn.mw.iam.core.oauth.assertion.IAMJWTBearerAuthenticationProvider; @Configuration @Order(-1) public class IamTokenEndointSecurityConfig extends WebSecurityConfigurerAdapter { public static final String TOKEN_ENDPOINT = "/token"; - + @Autowired private CorsFilter corsFilter; @@ -48,7 +59,16 @@ public class IamTokenEndointSecurityConfig extends WebSecurityConfigurerAdapter @Autowired @Qualifier("clientUserDetailsService") - private UserDetailsService userDetailsService; + private DefaultClientUserDetailsService userDetailsService; + + @Autowired + private Clock clock; + + @Autowired + private ClientKeyCacheService validators; + + @Autowired + private IamProperties iamProperties; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { @@ -63,6 +83,20 @@ public ClientCredentialsTokenEndpointFilter ccFilter() throws Exception { return filter; } + @Bean + public JWTBearerClientAssertionTokenEndpointFilter jwtBearerFilter() throws Exception { + + JWTBearerClientAssertionTokenEndpointFilter filter = + new JWTBearerClientAssertionTokenEndpointFilter(new AntPathRequestMatcher(TOKEN_ENDPOINT)); + + IAMJWTBearerAuthenticationProvider authProvider = new IAMJWTBearerAuthenticationProvider(clock, + iamProperties, userDetailsService.getClientDetailsService(), validators); + + filter.setAuthenticationManager(new ProviderManager(singletonList(authProvider))); + + return filter; + } + @Override protected void configure(HttpSecurity http) throws Exception { @@ -78,7 +112,8 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(OPTIONS, TOKEN_ENDPOINT).permitAll() .antMatchers(TOKEN_ENDPOINT).authenticated() .and() - .addFilterBefore(ccFilter(), AbstractPreAuthenticatedProcessingFilter.class) + .addFilterBefore(jwtBearerFilter(), AbstractPreAuthenticatedProcessingFilter.class) + .addFilterBefore(ccFilter(), BasicAuthenticationFilter.class) .addFilterBefore(corsFilter, SecurityContextPersistenceFilter.class) .exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/assertion/IAMJWTBearerAuthenticationProvider.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/assertion/IAMJWTBearerAuthenticationProvider.java new file mode 100644 index 000000000..b89e3aca4 --- /dev/null +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/assertion/IAMJWTBearerAuthenticationProvider.java @@ -0,0 +1,203 @@ +package it.infn.mw.iam.core.oauth.assertion; + +import static java.lang.String.format; +import static java.util.Objects.isNull; + +import java.text.ParseException; +import java.time.Clock; +import java.time.Instant; +import java.util.Date; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import org.mitre.jwt.signer.service.JWTSigningAndValidationService; +import org.mitre.jwt.signer.service.impl.ClientKeyCacheService; +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; +import org.mitre.oauth2.service.ClientDetailsEntityService; +import org.mitre.openid.connect.assertion.JWTBearerAssertionAuthenticationToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import it.infn.mw.iam.config.IamProperties; + +public class IAMJWTBearerAuthenticationProvider implements AuthenticationProvider { + + public static final Logger LOG = + LoggerFactory.getLogger(IAMJWTBearerAuthenticationProvider.class); + + private static final GrantedAuthority ROLE_CLIENT = new SimpleGrantedAuthority("ROLE_CLIENT"); + + private final int CLOCK_SKEW_IN_SECONDS = 300; + + private final Clock clock; + private final ClientDetailsEntityService clientService; + private final ClientKeyCacheService validators; + + private final String TOKEN_ENDPOINT; + + public IAMJWTBearerAuthenticationProvider(Clock clock, IamProperties iamProperties, + ClientDetailsEntityService clientService, ClientKeyCacheService validators) { + + this.clock = clock; + this.clientService = clientService; + this.validators = validators; + + if (iamProperties.getIssuer().endsWith("/")) { + TOKEN_ENDPOINT = iamProperties.getIssuer() + "token"; + } else { + TOKEN_ENDPOINT = iamProperties.getIssuer() + "/token"; + } + + } + + private void clientAuthMethodChecks(ClientDetailsEntity client, SignedJWT jws) { + + if (client.getTokenEndpointAuthMethod() == null + || client.getTokenEndpointAuthMethod().equals(AuthMethod.NONE) + || client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_BASIC) + || client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_POST)) { + + throw new AuthenticationServiceException("Unsupported authentication method."); + } + + JWSAlgorithm alg = jws.getHeader().getAlgorithm(); + + if (client.getTokenEndpointAuthSigningAlg() != null + && !client.getTokenEndpointAuthSigningAlg().equals(alg)) { + invalidBearerAssertion("Invalid signature algorithm: " + alg.getName()); + } + + if (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) { + if (!JWSAlgorithm.Family.RSA.contains(alg) && !JWSAlgorithm.Family.EC.contains(alg)) { + invalidBearerAssertion("Invalid signature algorithm: " + alg.getName()); + } + } else if (client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT)) { + if (!JWSAlgorithm.Family.HMAC_SHA.contains(alg)) { + invalidBearerAssertion("Invalid signature algorithm: " + alg.getName()); + } + } + } + + private void signatureChecks(ClientDetailsEntity client, SignedJWT jws) { + JWSAlgorithm alg = jws.getHeader().getAlgorithm(); + + JWTSigningAndValidationService validator = + Optional.ofNullable(validators.getValidator(client, alg)) + .orElseThrow(() -> new AuthenticationServiceException( + format("Unable to resolve validator for client '%s' and algorithm '%s'", + client.getClientId(), alg.getName()))); + + if (!validator.validateSignature(jws)) { + invalidBearerAssertion("invalid signature"); + } + } + + private void invalidBearerAssertion(String msg) { + throw new AuthenticationServiceException( + String.format("invalid jwt bearer assertion: %s", msg)); + } + + private void assertionChecks(ClientDetailsEntity client, SignedJWT jws) throws ParseException { + + JWTClaimsSet jwtClaims = jws.getJWTClaimsSet(); + + if (isNull(jwtClaims.getIssuer())) { + invalidBearerAssertion("issuer is null"); + } else if (!jwtClaims.getIssuer().equals(client.getClientId())) { + invalidBearerAssertion("issuer does not match client id"); + } + + if (isNull(jwtClaims.getExpirationTime())) { + invalidBearerAssertion("expiration time not set"); + } + + Instant nowSkewed = clock.instant().minusSeconds(CLOCK_SKEW_IN_SECONDS); + + if (Date.from(nowSkewed).after(jwtClaims.getExpirationTime())) { + invalidBearerAssertion("expired assertion token"); + } + + if (!isNull(jwtClaims.getNotBeforeTime())) { + + nowSkewed = clock.instant().plusSeconds(CLOCK_SKEW_IN_SECONDS); + if (Date.from(nowSkewed).before(jwtClaims.getNotBeforeTime())) { + invalidBearerAssertion("assertion is not yet valid"); + } + } + + if (!isNull(jwtClaims.getIssueTime())) { + nowSkewed = clock.instant().plusSeconds(CLOCK_SKEW_IN_SECONDS); + if (Date.from(nowSkewed).before(jwtClaims.getIssueTime())) { + invalidBearerAssertion("assertion was issued in the future"); + } + } + + if (isNull(jwtClaims.getAudience())) { + invalidBearerAssertion("assertion audience is null"); + } else { + if (!jwtClaims.getAudience().contains(TOKEN_ENDPOINT)) { + invalidBearerAssertion("invalid audience"); + } + } + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + + JWTBearerAssertionAuthenticationToken jwtAuth = + (JWTBearerAssertionAuthenticationToken) authentication; + + ClientDetailsEntity client = clientService.loadClientByClientId(jwtAuth.getName()); + + if (isNull(client)) { + throw new UsernameNotFoundException("Unknown client: " + jwtAuth.getName()); + } + + try { + + + final JWT jwt = jwtAuth.getJwt(); + + if (!(jwt instanceof SignedJWT)) { + invalidBearerAssertion("Unsupported JWT type: " + jwt.getClass().getName()); + } + + SignedJWT jws = (SignedJWT) jwt; + + clientAuthMethodChecks(client, jws); + + signatureChecks(client, jws); + + assertionChecks(client, jws); + + Set authorities = new HashSet<>(client.getAuthorities()); + authorities.add(ROLE_CLIENT); + + return new JWTBearerAssertionAuthenticationToken(jwt, authorities); + + } catch (ParseException e) { + throw new AuthenticationServiceException("JWT parse error:" + e.getMessage(), e); + } + } + + + @Override + public boolean supports(Class authentication) { + return JWTBearerAssertionAuthenticationToken.class.isAssignableFrom(authentication); + } + +} diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/common/BaseAccessTokenBuilder.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/common/BaseAccessTokenBuilder.java index 787112491..47b15c951 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/common/BaseAccessTokenBuilder.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/common/BaseAccessTokenBuilder.java @@ -144,9 +144,8 @@ protected JWTClaimsSet.Builder baseJWTSetup(OAuth2AccessTokenEntity token, .subject(subject) .jwtID(UUID.randomUUID().toString()); - if (!authentication.isClientOnly()) { - builder.claim(CLIENT_ID_CLAIM_NAME, token.getClient().getClientId()); - } + + builder.claim(CLIENT_ID_CLAIM_NAME, token.getClient().getClientId()); String audience = null; diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/iam/IamJWTProfileUserinfoHelper.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/iam/IamJWTProfileUserinfoHelper.java index f4cf929b1..da7a5eccb 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/iam/IamJWTProfileUserinfoHelper.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/iam/IamJWTProfileUserinfoHelper.java @@ -41,10 +41,8 @@ public IamJWTProfileUserinfoHelper(IamProperties props, UserInfoService userInfo @Override public UserInfo resolveUserInfo(OAuth2Authentication authentication) { - final String username = authentication.getName(); - UserInfo ui = getUserInfoService().getByUsernameAndClientId(username, - authentication.getOAuth2Request().getClientId()); + UserInfo ui = lookupUserinfo(authentication); if (isNull(ui)) { return null; diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/wlcg/WLCGUserInfoAdapter.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/wlcg/WLCGUserInfoAdapter.java index c82802dfa..b09faf123 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/wlcg/WLCGUserInfoAdapter.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/oauth/profile/wlcg/WLCGUserInfoAdapter.java @@ -34,6 +34,7 @@ public JsonObject toJson() { JsonObject json = super.toJson(); json.remove("groups"); + json.remove("organisation_name"); return json; } diff --git a/iam-login-service/src/main/resources-filtered/application.properties b/iam-login-service/src/main/resources-filtered/application.properties index 0f489abb2..37aa83149 100644 --- a/iam-login-service/src/main/resources-filtered/application.properties +++ b/iam-login-service/src/main/resources-filtered/application.properties @@ -37,7 +37,7 @@ logging.level.org.opensaml.saml2.metadata.provider=INFO #logging.level.org.eclipse.persistence=DEBUG # Test logging -# logging.level.org.springframework.test.web.servlet.result=DEBUG +logging.level.org.springframework.test.web.servlet.result=DEBUG #logging.level.org.apache.jasper.servlet.TldScanner=DEBUG diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/assertions/JWTBearerClientAuthenticationTestSupport.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/assertions/JWTBearerClientAuthenticationTestSupport.java new file mode 100644 index 000000000..e4671428b --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/assertions/JWTBearerClientAuthenticationTestSupport.java @@ -0,0 +1,44 @@ +package it.infn.mw.iam.test.oauth.assertions; + +import static java.util.Collections.singletonList; + +import java.time.Instant; +import java.util.Date; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.MACSigner; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import it.infn.mw.iam.test.oauth.EndpointsTestUtils; + +public class JWTBearerClientAuthenticationTestSupport extends EndpointsTestUtils { + + public static final String CLIENT_ID_SECRET_JWT = "jwt-auth-client_secret_jwt"; + public static final String CLIENT_ID_SECRET_JWT_SECRET = "c8e9eed0-e6e4-4a66-b16e-6f37096356a7"; + public static final String TOKEN_ENDPOINT_AUDIENCE = "http://localhost:8080/token"; + public static final String TOKEN_ENDPOINT = "/token"; + public static final String JWT_BEARER_ASSERTION_TYPE = + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + + public SignedJWT createClientAuthToken(String clientId, Instant expirationTime) + throws JOSEException { + + JWSSigner signer = new MACSigner(CLIENT_ID_SECRET_JWT_SECRET); + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(clientId) + .issuer(clientId) + .expirationTime(Date.from(expirationTime)) + .audience(singletonList(TOKEN_ENDPOINT_AUDIENCE)) + .build(); + + SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet); + + signedJWT.sign(signer); + + return signedJWT; + } + +} diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/assertions/JWTBearerClientAuthenticationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/assertions/JWTBearerClientAuthenticationTests.java new file mode 100644 index 000000000..2e688041d --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/assertions/JWTBearerClientAuthenticationTests.java @@ -0,0 +1,48 @@ +package it.infn.mw.iam.test.oauth.assertions; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.time.Instant; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.transaction.annotation.Transactional; + +import com.nimbusds.jwt.JWT; + +import it.infn.mw.iam.IamLoginService; +import it.infn.mw.iam.test.core.CoreControllerTestSupport; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = {IamLoginService.class, CoreControllerTestSupport.class}) +@WebAppConfiguration +@Transactional +public class JWTBearerClientAuthenticationTests extends JWTBearerClientAuthenticationTestSupport { + + + @Before + public void setup() throws Exception { + buildMockMvc(); + } + + @Test + public void testJwtAuth() throws Exception { + JWT jwt = createClientAuthToken(CLIENT_ID_SECRET_JWT, Instant.now().plusSeconds(600)); + String serializedToken = jwt.serialize(); + + mvc + .perform(post(TOKEN_ENDPOINT).param("client_id", CLIENT_ID_SECRET_JWT) + .param("client_assertion_type", JWT_BEARER_ASSERTION_TYPE) + .param("client_assertion", serializedToken) + .param("grant_type", "client_credentials")) + .andExpect(status().isOk()); + + + } + +} diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/profile/WLCGProfileIntegrationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/profile/WLCGProfileIntegrationTests.java index 4ec3a9147..e740d1eb5 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/profile/WLCGProfileIntegrationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/profile/WLCGProfileIntegrationTests.java @@ -308,14 +308,32 @@ public void testWlcgProfileGroupRequest() throws Exception { @Test public void testWlcgProfileClientCredentials() throws Exception { - mvc + String response = mvc .perform(post("/token") .with(httpBasic(CLIENT_CREDENTIALS_CLIENT_ID, CLIENT_CREDENTIALS_CLIENT_SECRET)) .param("grant_type", CLIENT_CREDENTIALS_GRANT_TYPE) .param("scope", "storage.read:/a-path storage.write:/another-path")) .andExpect(status().isOk()) .andExpect(jsonPath("$.scope", containsString("storage.read:/a-path"))) - .andExpect(jsonPath("$.scope", containsString("storage.write:/another-path"))); + .andExpect(jsonPath("$.scope", containsString("storage.write:/another-path"))) + .andReturn() + .getResponse() + .getContentAsString(); + + DefaultOAuth2AccessToken tokenResponseObject = + mapper.readValue(response, DefaultOAuth2AccessToken.class); + + JWT accessToken = JWTParser.parse(tokenResponseObject.getValue()); + + String scope = accessToken.getJWTClaimsSet().getStringClaim("scope"); + assertThat(scope, containsString("storage.read:/a-path")); + assertThat(scope, containsString("storage.write:/another-path")); + + assertThat(accessToken.getJWTClaimsSet().getStringClaim("client_id"), + is(CLIENT_CREDENTIALS_CLIENT_ID)); + + assertThat(accessToken.getJWTClaimsSet().getSubject(), is(CLIENT_CREDENTIALS_CLIENT_ID)); + } @Test diff --git a/iam-login-service/src/test/resources/client_auth/client-keys.jwk b/iam-login-service/src/test/resources/client_auth/client-keys.jwk new file mode 100644 index 000000000..dff5da18c --- /dev/null +++ b/iam-login-service/src/test/resources/client_auth/client-keys.jwk @@ -0,0 +1,16 @@ +{ + "keys": [ + { + "p": "7FhhHX--icA3yKaihMZVOJbiB2bQcs_p3HLHolTo31v_IADBrXPdxTtDQwtiM7p17yl290o-MaWS-aboa5GNpuYbNImkLDxKqk2YSQIuU_ZebMaq4CXWXVYvgAmWFAvHf6yCYXxJYSt1RqKbm-loYgaqn5K3iSwnWeEO4I9qDcM", + "kty": "RSA", + "q": "6RI38CT9BSnU6N4jY9gUO5_5F_XLKG3RgEUiExk8-6WnGCiIOAsmkeDLjT_o7xqvtx7vLpAYVgU1dtez1rRBug9q1nhTH9gbZ8YOk2XjubWIyAaYje8qmWCviGvQIK2c2QCoFBr6bAoTUs2o_eJQRRtNpPn_JSeEymY6xqcDKWk", + "d": "OANMD3sVYqk5MFTGnZnIhr2RqWYtxzr_wrcj7J8lBK5tlksvZ1bil8sHHsufXejBGLO2Ulok_pMDx-IrSt9gm1U8nkJSNMLX54XlSdi2Q39QIy2pn3nEtT-sWpkIUWRZjczRHJm7gtySWtKmQVUbLnOyowWWxIM7H9P-7bpHXs88R6MDvNhnq2csdNW9Wph0SDqousmp3uFIuKUsjCYwqxse3sIgdsUdDQ7ULdV_DjsxY-HAQ8yE93NeAeBtqPV8fA6y_VLlkR0iSPd4xqhy6SiukwLCexWhRqIREdbz_FI8ZXk3WOerLdfpe5SGKkVbIEjcync-rTBaArjWOEzFQQ", + "e": "AQAB", + "kid": "rsa1", + "qi": "M1CjStmDrWjcOI2T2P8bPj42c4iav4Dyu5x-Mc3LOTUwx2iVToHg5bZHFEf7d2H91y-EUjDzmRopuNk7dV4Fy5QWTflRCYUGvMfrgL-7rUUDBMDhl96ujmAoZrmpvlGcrsae1R_qXduU00FjCNCqGk96vgnmF77lJPNCEB7xq6k", + "dp": "ZTxWHPSjAQ481s3Jv2XVCzBWESWRFBzK54qiyH3mYgZd-a9ZRpri26DO5uDxZ4bvDUqNks9SZKGvmxBLbggizOKztIVgtTH-KYSjPmKYxY46VA2lE-4hLEnGfumcR2nkQmP6VRePtveOfHsafGY2OAby_vcxdqhbEry8SqQjoVE", + "dq": "JtImBxXiHw4MrIzzkBnZpOTMdLU4FY_VKWxadJvrkG7TGi8GIW-aCQpMXUab4desFPBOHo9Zvlo3wYfEKKr1l6whu39nORKh3fMbUmnSOIiIM-kFV_7SNaHpGuv6SrcgPPTjChZER-KVvWEMGN2tSRV3JVeOq_2dHKlSeOwwlqE", + "n": "1y1CP181zqPNPlV1JDM7Xv0QnGswhSTHe8_XPZHxDTJkykpk_1BmgA3ovP62QRE2ORgsv5oSBI_Z_RaOc4Zx2FonjEJF2oBHtBjsAiF-pxGkM5ZPjFNgFTGp1yUUBjFDcEeIGCwPEyYSt93sQIP_0DRbViMUnpyn3xgM_a1dO5brEWR2n1Uqff1yA5NXfLS03qpl2dpH4HFY5-Zs4bvtJykpAOhoHuIQbz-hmxb9MZ3uTAwsx2HiyEJtz-suyTBHO3BM2o8UcCeyfa34ShPB8i86-sf78fOk2KeRIW1Bju3ANmdV3sxL0j29cesxKCZ06u2ZiGR3Srbft8EdLPzf-w" + } + ] +} \ No newline at end of file diff --git a/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql b/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql index 9204ebd85..1c244d9fb 100644 --- a/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql +++ b/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql @@ -25,6 +25,30 @@ INSERT INTO client_details (id, client_id, client_secret, client_name, dynamical (13, 'implicit-flow-client', null, 'Implicit Flow client', false, null, 3600, 600, 600, false, null, false), (14, 'public-dc-client', null, 'Public Device Code client', false, null, 3600, 600, 600, false, null, false); +INSERT INTO client_details (id, client_id, client_secret, client_name, dynamically_registered, + refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection, + token_endpoint_auth_method, require_auth_time, token_endpoint_auth_signing_alg, jwks) VALUES + (15, 'jwt-auth-client_secret_jwt', 'c8e9eed0-e6e4-4a66-b16e-6f37096356a7', 'JWT Bearer Auth Client (client_secret_jwt)', + false, null, 3600, 600, true, 'SECRET_JWT', false, 'HS256', null), + (16, 'jwt-auth-private_key_jwt', 'secret', 'JWT Bearer Auth Client (private_key_jwt)', + false, null, 3600, 600, true,'SECRET_JWT', false, 'RS256', + '{ + "keys": [ + { + "p": "7FhhHX--icA3yKaihMZVOJbiB2bQcs_p3HLHolTo31v_IADBrXPdxTtDQwtiM7p17yl290o-MaWS-aboa5GNpuYbNImkLDxKqk2YSQIuU_ZebMaq4CXWXVYvgAmWFAvHf6yCYXxJYSt1RqKbm-loYgaqn5K3iSwnWeEO4I9qDcM", + "kty": "RSA", + "q": "6RI38CT9BSnU6N4jY9gUO5_5F_XLKG3RgEUiExk8-6WnGCiIOAsmkeDLjT_o7xqvtx7vLpAYVgU1dtez1rRBug9q1nhTH9gbZ8YOk2XjubWIyAaYje8qmWCviGvQIK2c2QCoFBr6bAoTUs2o_eJQRRtNpPn_JSeEymY6xqcDKWk", + "d": "OANMD3sVYqk5MFTGnZnIhr2RqWYtxzr_wrcj7J8lBK5tlksvZ1bil8sHHsufXejBGLO2Ulok_pMDx-IrSt9gm1U8nkJSNMLX54XlSdi2Q39QIy2pn3nEtT-sWpkIUWRZjczRHJm7gtySWtKmQVUbLnOyowWWxIM7H9P-7bpHXs88R6MDvNhnq2csdNW9Wph0SDqousmp3uFIuKUsjCYwqxse3sIgdsUdDQ7ULdV_DjsxY-HAQ8yE93NeAeBtqPV8fA6y_VLlkR0iSPd4xqhy6SiukwLCexWhRqIREdbz_FI8ZXk3WOerLdfpe5SGKkVbIEjcync-rTBaArjWOEzFQQ", + "e": "AQAB", + "kid": "rsa1", + "qi": "M1CjStmDrWjcOI2T2P8bPj42c4iav4Dyu5x-Mc3LOTUwx2iVToHg5bZHFEf7d2H91y-EUjDzmRopuNk7dV4Fy5QWTflRCYUGvMfrgL-7rUUDBMDhl96ujmAoZrmpvlGcrsae1R_qXduU00FjCNCqGk96vgnmF77lJPNCEB7xq6k", + "dp": "ZTxWHPSjAQ481s3Jv2XVCzBWESWRFBzK54qiyH3mYgZd-a9ZRpri26DO5uDxZ4bvDUqNks9SZKGvmxBLbggizOKztIVgtTH-KYSjPmKYxY46VA2lE-4hLEnGfumcR2nkQmP6VRePtveOfHsafGY2OAby_vcxdqhbEry8SqQjoVE", + "dq": "JtImBxXiHw4MrIzzkBnZpOTMdLU4FY_VKWxadJvrkG7TGi8GIW-aCQpMXUab4desFPBOHo9Zvlo3wYfEKKr1l6whu39nORKh3fMbUmnSOIiIM-kFV_7SNaHpGuv6SrcgPPTjChZER-KVvWEMGN2tSRV3JVeOq_2dHKlSeOwwlqE", + "n": "1y1CP181zqPNPlV1JDM7Xv0QnGswhSTHe8_XPZHxDTJkykpk_1BmgA3ovP62QRE2ORgsv5oSBI_Z_RaOc4Zx2FonjEJF2oBHtBjsAiF-pxGkM5ZPjFNgFTGp1yUUBjFDcEeIGCwPEyYSt93sQIP_0DRbViMUnpyn3xgM_a1dO5brEWR2n1Uqff1yA5NXfLS03qpl2dpH4HFY5-Zs4bvtJykpAOhoHuIQbz-hmxb9MZ3uTAwsx2HiyEJtz-suyTBHO3BM2o8UcCeyfa34ShPB8i86-sf78fOk2KeRIW1Bju3ANmdV3sxL0j29cesxKCZ06u2ZiGR3Srbft8EdLPzf-w" + } + ] +}'); + INSERT INTO client_scope (owner_id, scope) VALUES (1, 'openid'), (1, 'profile'),