Skip to content

Commit

Permalink
Merge pull request #300 from holashchand/issue-969-1.0
Browse files Browse the repository at this point in the history
[LTS-1.0] Fixed issuer not matching problem
  • Loading branch information
srprasanna authored Mar 21, 2024
2 parents 8a10773 + e51370c commit 1dca387
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 8 deletions.
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ services:
- search_providerName=${SEARCH_PROVIDER_NAME-dev.sunbirdrc.registry.service.NativeSearchService}
- sunbird_sso_realm=${KEYCLOAK_REALM-sunbird-rc}
- sunbird_sso_url=${sunbird_sso_url-http://keycloak:8080/auth}
- oauth2_resource_uri=${oauth2_resource_uri-http://keycloak:8080/auth/realms/sunbird-rc}
- oauth2_resource_roles_path=${oauth2_resource_roles_path-realm_access.roles}
- OAUTH2_RESOURCES_0_URI=${oauth2_resource_uri-http://keycloak:8080/auth/realms/sunbird-rc}
- OAUTH2_RESOURCES_0_PROPERTIES_ROLES_PATH=${oauth2_resource_roles_path-realm_access.roles}
- identity_provider=${identity_provider-dev.sunbirdrc.auth.keycloak.KeycloakProviderImpl}
- sunbird_sso_admin_client_id=${KEYCLOAK_ADMIN_CLIENT_ID-admin-api}
- sunbird_sso_client_id=${KEYCLOAK_CLIENT_ID-registry-frontend}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package dev.sunbirdrc.registry.authorization;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.Collections;
import java.util.Map;

final class CustomJwtDecoderProviderConfigurationUtils {
private static final String OIDC_METADATA_PATH = "/.well-known/openid-configuration";
private static final RestTemplate rest = new RestTemplate();
private static final ParameterizedTypeReference<Map<String, Object>> typeReference =
new ParameterizedTypeReference<Map<String, Object>>() {};

static Map<String, Object> getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) {
return getConfiguration(oidcIssuerLocation, oidc(URI.create(oidcIssuerLocation)));
}

static String getIssuer(Map<String, Object> configuration) {
String metadataIssuer = "(unavailable)";
if (configuration.containsKey("issuer")) {
metadataIssuer = configuration.get("issuer").toString();
}
return metadataIssuer;
}

private static Map<String, Object> getConfiguration(String issuer, URI... uris) {
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " +
"\"" + issuer + "\"";
for (URI uri : uris) {
try {
RequestEntity<Void> request = RequestEntity.get(uri).build();
ResponseEntity<Map<String, Object>> response = rest.exchange(request, typeReference);
Map<String, Object> configuration = response.getBody();

if (configuration.get("jwks_uri") == null) {
throw new IllegalArgumentException("The public JWK set URI must not be null");
}

return configuration;
} catch (IllegalArgumentException e) {
throw e;
} catch (RuntimeException e) {
if (!(e instanceof HttpClientErrorException &&
((HttpClientErrorException) e).getStatusCode().is4xxClientError())) {
throw new IllegalArgumentException(errorMessage, e);
}
// else try another endpoint
}
}
throw new IllegalArgumentException(errorMessage);
}

private static URI oidc(URI issuer) {
return UriComponentsBuilder.fromUri(issuer)
.replacePath(issuer.getPath() + OIDC_METADATA_PATH)
.build(Collections.emptyMap());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dev.sunbirdrc.registry.authorization;

import java.util.Map;

import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.util.Assert;

import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;

final class CustomJwtDecoders {

public static TenantJwtDecoder fromOidcIssuerLocation(String oidcIssuerLocation) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map<String, Object> configuration = CustomJwtDecoderProviderConfigurationUtils.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
return withProviderConfiguration(configuration);
}

private static TenantJwtDecoder withProviderConfiguration(Map<String, Object> configuration) {
String metadataIssuer = CustomJwtDecoderProviderConfigurationUtils.getIssuer(configuration);
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(metadataIssuer);
NimbusJwtDecoder jwtDecoder = withJwkSetUri(configuration.get("jwks_uri").toString()).build();
jwtDecoder.setJwtValidator(jwtValidator);
return TenantJwtDecoder.from(jwtDecoder, metadataIssuer);
}

private CustomJwtDecoders() {}
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import dev.sunbirdrc.registry.authorization.pojos.OAuth2Configuration;
import dev.sunbirdrc.registry.authorization.pojos.OAuth2Resources;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
Expand Down Expand Up @@ -61,10 +59,11 @@ protected void configure(HttpSecurity http) throws Exception {

}

private void addManager(Map<String, AuthenticationManager> authenticationManagers, OAuth2Resources issuer) {
JwtAuthenticationProvider authenticationProvider = new JwtAuthenticationProvider(JwtDecoders.fromOidcIssuerLocation(issuer.getUri()));
authenticationProvider.setJwtAuthenticationConverter(new CustomJwtAuthenticationConverter(issuer.getProperties()));
authenticationManagers.put(issuer.getUri(), authenticationProvider::authenticate);
private void addManager(Map<String, AuthenticationManager> authenticationManagers, OAuth2Resources auth2Resources) {
TenantJwtDecoder tenantJwtDecoder = CustomJwtDecoders.fromOidcIssuerLocation(auth2Resources.getUri());
JwtAuthenticationProvider authenticationProvider = new JwtAuthenticationProvider(tenantJwtDecoder);
authenticationProvider.setJwtAuthenticationConverter(new CustomJwtAuthenticationConverter(auth2Resources.getProperties()));
authenticationManagers.put(tenantJwtDecoder.getIssuer(), authenticationProvider::authenticate);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.sunbirdrc.registry.authorization;

import lombok.Getter;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException;

public final class TenantJwtDecoder implements JwtDecoder {
JwtDecoder jwtDecoder;
@Getter
String issuer;
private TenantJwtDecoder(JwtDecoder jwtDecoder, String issuer) {
this.jwtDecoder = jwtDecoder;
this.issuer = issuer;
}

@Override
public Jwt decode(String token) throws JwtException {
return this.jwtDecoder.decode(token);
}

public static TenantJwtDecoder from(JwtDecoder jwtDecoder, String issuer) {
return new TenantJwtDecoder(jwtDecoder, issuer);
}
}

0 comments on commit 1dca387

Please sign in to comment.