Skip to content

Commit b2ccb87

Browse files
committed
multiple passport brokers
1 parent 855ca11 commit b2ccb87

File tree

6 files changed

+50
-53
lines changed

6 files changed

+50
-53
lines changed

src/main/java/bio/overture/ego/controller/AuthController.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ public class AuthController {
6464
private final GoogleTokenService googleTokenService;
6565
private final TokenSigner tokenSigner;
6666
private final RefreshContextService refreshContextService;
67-
68-
private final String PASSPORT_CLIENT_NAME = "passport";
67+
private final String GA4GH_PASSPORT_SCOPE = "ga4gh_passport_v1";
6968

7069
@Autowired
7170
public AuthController(
@@ -128,8 +127,11 @@ public ResponseEntity<String> user(
128127

129128
val user = (CustomOAuth2User) authentication.getPrincipal();
130129

131-
val passportJwtToken = (authentication.getAuthorizedClientRegistrationId().equals(PASSPORT_CLIENT_NAME)) ?
132-
passportService.getPassportToken(((CustomOAuth2User) authentication.getPrincipal()).getAccessToken()) :
130+
131+
val passportJwtToken = (user.getClaim(GA4GH_PASSPORT_SCOPE) != null) ?
132+
passportService.getPassportToken(
133+
authentication.getAuthorizedClientRegistrationId(),
134+
user.getAccessToken()) :
133135
null;
134136

135137
String token =
@@ -143,7 +145,8 @@ public ResponseEntity<String> user(
143145
ProviderType.resolveProviderType(
144146
authentication.getAuthorizedClientRegistrationId()))
145147
.build(),
146-
passportJwtToken);
148+
passportJwtToken,
149+
authentication.getAuthorizedClientRegistrationId());
147150

148151
val outgoingRefreshContext = refreshContextService.createInitialRefreshContext(token);
149152
val cookie =

src/main/java/bio/overture/ego/service/PassportService.java

+21-29
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@
2626
import lombok.val;
2727
import org.apache.commons.codec.binary.Base64;
2828
import org.springframework.beans.factory.annotation.Autowired;
29-
import org.springframework.beans.factory.annotation.Value;
3029
import org.springframework.context.annotation.Configuration;
3130
import org.springframework.http.HttpHeaders;
3231
import org.springframework.http.HttpMethod;
32+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
33+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
3334
import org.springframework.stereotype.Service;
3435
import org.springframework.transaction.annotation.Transactional;
3536
import org.springframework.util.LinkedMultiValueMap;
@@ -52,20 +53,12 @@ public class PassportService {
5253

5354
@Autowired private CacheUtil cacheUtil;
5455

56+
@Autowired private ClientRegistrationRepository clientRegistrationRepository;
57+
5558
private final String REQUESTED_TOKEN_TYPE = "urn:ga4gh:params:oauth:token-type:passport";
5659
private final String SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
5760
private final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
5861

59-
@Value("${spring.security.oauth2.client.registration.passport.clientId}")
60-
private String clientId;
61-
62-
@Value("${spring.security.oauth2.client.registration.passport.clientSecret}")
63-
private String clientSecret;
64-
65-
@Value("${spring.security.oauth2.client.provider.passport.issuer-uri}")
66-
private String passportIssuerUri;
67-
68-
6962

7063
@Autowired
7164
public PassportService(
@@ -74,14 +67,14 @@ public PassportService(
7467
this.visaPermissionService = visaPermissionService;
7568
}
7669

77-
public List<VisaPermission> getPermissions(String authToken)
70+
public List<VisaPermission> getPermissions(String authToken, String providerType)
7871
throws JsonProcessingException, ParseException, JwkException {
7972
// Validates passport auth token
80-
isValidPassport(authToken);
73+
isValidPassport(authToken, providerType);
8174
// Parses passport JWT token
8275
Passport parsedPassport = parsePassport(authToken);
8376
// Fetches visas for parsed passport
84-
List<PassportVisa> visas = getVisas(parsedPassport);
77+
List<PassportVisa> visas = getVisas(parsedPassport, providerType);
8578
// Fetches visa permissions for extracted visas
8679
List<VisaPermission> visaPermissions = getVisaPermissions(visas);
8780
// removes deduplicates from visaPermissions
@@ -90,22 +83,22 @@ public List<VisaPermission> getPermissions(String authToken)
9083
}
9184

9285
// Validates passport token based on public key
93-
private void isValidPassport(@NonNull String authToken)
94-
throws ParseException, JwkException, JsonProcessingException {
86+
private void isValidPassport(@NonNull String authToken, @NonNull String providerType)
87+
throws JwkException {
9588
DecodedJWT jwt = JWT.decode(authToken);
96-
Jwk jwk = cacheUtil.getPassportBrokerPublicKey().get(jwt.getKeyId());
89+
Jwk jwk = cacheUtil.getPassportBrokerPublicKey(providerType).get(jwt.getKeyId());
9790
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
9891
algorithm.verify(jwt);
9992
}
10093

10194
// Extracts Visas from parsed passport object
102-
private List<PassportVisa> getVisas(Passport passport) {
95+
private List<PassportVisa> getVisas(Passport passport, @NonNull String providerType) {
10396
List<PassportVisa> visas = new ArrayList<>();
10497
passport.getGa4ghPassportV1().stream()
10598
.forEach(
10699
visaJwt -> {
107100
try {
108-
visaService.isValidVisa(visaJwt);
101+
visaService.isValidVisa(visaJwt, providerType);
109102
PassportVisa visa = visaService.parseVisa(visaJwt);
110103
if (visa != null) {
111104
visas.add(visa);
@@ -134,8 +127,8 @@ private List<VisaPermission> getVisaPermissions(List<PassportVisa> visas) {
134127
return visaPermissions;
135128
}
136129

137-
public Set<Scope> extractScopes(@NonNull String passportJwtToken) throws ParseException, JwkException, JsonProcessingException {
138-
val resolvedPermissions = getPermissions(passportJwtToken);
130+
public Set<Scope> extractScopes(@NonNull String passportJwtToken, @NonNull String providerType) throws ParseException, JwkException, JsonProcessingException {
131+
val resolvedPermissions = getPermissions(passportJwtToken, providerType);
139132
val output = mapToSet(resolvedPermissions, AbstractPermissionService::buildScope);
140133
if (output.isEmpty()) {
141134
output.add(Scope.defaultScope());
@@ -162,19 +155,18 @@ private List<VisaPermission> deDupeVisaPermissions(List<VisaPermission> visaPerm
162155
return permissionsSet.stream().collect(Collectors.toList());
163156
}
164157

165-
public String getPassportToken(String accessToken) {
158+
public String getPassportToken(String providerId, String accessToken) {
166159

167160
if (accessToken == null || accessToken.isEmpty()) return null;
168161

169-
val params = passportTokenParams(accessToken);
162+
val clientRegistration = clientRegistrationRepository.findByRegistrationId(providerId);
170163

171164
val uri = UriComponentsBuilder
172-
.fromUriString(passportIssuerUri)
173-
.path("/token")
174-
.queryParams(params)
165+
.fromUriString(clientRegistration.getProviderDetails().getTokenUri())
166+
.queryParams(passportTokenParams(accessToken))
175167
.toUriString();
176168

177-
val passportToken = getTemplate(clientId, clientSecret)
169+
val passportToken = getTemplate(clientRegistration)
178170
.exchange(uri,
179171
HttpMethod.POST,
180172
null,
@@ -186,7 +178,7 @@ public String getPassportToken(String accessToken) {
186178
null;
187179
}
188180

189-
private RestTemplate getTemplate(String clientId, String clientSecret) {
181+
private RestTemplate getTemplate(ClientRegistration clientRegistration) {
190182
RestTemplate restTemplate = new RestTemplate();
191183
restTemplate
192184
.getInterceptors()
@@ -195,7 +187,7 @@ private RestTemplate getTemplate(String clientId, String clientSecret) {
195187
x.getHeaders()
196188
.set(
197189
HttpHeaders.AUTHORIZATION,
198-
"Basic " + getBasicAuthHeader(clientId, clientSecret));
190+
"Basic " + getBasicAuthHeader(clientRegistration.getClientId(), clientRegistration.getClientSecret()));
199191
return z.execute(x, y);
200192
});
201193
return restTemplate;

src/main/java/bio/overture/ego/service/TokenService.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,12 @@ public ApiKey getWithRelationships(@NonNull UUID id) {
141141
}
142142

143143
public String generateUserToken(IDToken idToken) {
144-
return generateUserToken(idToken, null);
144+
return generateUserToken(idToken, null, null);
145145
}
146146

147-
public String generateUserToken(IDToken idToken, String passportJwtToken) {
147+
public String generateUserToken(IDToken idToken, String passportJwtToken, String providerType) {
148148
val user = userService.getUserByToken(idToken);
149-
return generateUserToken(user, passportJwtToken);
149+
return generateUserToken(user, passportJwtToken, providerType);
150150
}
151151

152152
public String updateUserToken(String accessToken) {
@@ -175,12 +175,12 @@ public String generateUserToken(User u) {
175175
}
176176

177177
@SneakyThrows
178-
public String generateUserToken(User u, String passportJwtToken) {
178+
public String generateUserToken(User u, String passportJwtToken, String providerType) {
179179

180180
Set<String> scopes = extractExplicitScopes(u);
181181

182-
if (passportJwtToken != null){
183-
Set<String> scopesFromVisas = extractExplicitScopes(passportJwtToken);
182+
if (passportJwtToken != null && providerType != null){
183+
Set<String> scopesFromVisas = extractExplicitScopes(passportJwtToken, providerType);
184184
scopes = mergeScopes(scopes, scopesFromVisas);
185185
}
186186

@@ -601,8 +601,8 @@ private static Set<String> extractExplicitScopes(Application a) {
601601
}
602602

603603
@SneakyThrows
604-
private Set<String> extractExplicitScopes(String passportJwtToken){
605-
return mapToSet(explicitScopes(passportService.extractScopes(passportJwtToken)), Scope::toString);
604+
private Set<String> extractExplicitScopes(String passportJwtToken, String providerType){
605+
return mapToSet(explicitScopes(passportService.extractScopes(passportJwtToken, providerType)), Scope::toString);
606606
}
607607

608608
private Set<String> mergeScopes(Set<String> scopeSet, Set<String> scopeSetAdditional){

src/main/java/bio/overture/ego/service/VisaService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ public PassportVisa parseVisa(@NonNull String visaJwtToken) throws JsonProcessin
105105
}
106106

107107
// Checks if the visa is a valid visa
108-
public void isValidVisa(@NonNull String authToken) throws JwkException, JsonProcessingException {
108+
public void isValidVisa(@NonNull String authToken, @NonNull String providerType) throws JwkException, JsonProcessingException {
109109
DecodedJWT jwt = JWT.decode(authToken);
110-
Jwk jwk = cacheUtil.getPassportBrokerPublicKey().get(jwt.getKeyId());
110+
Jwk jwk = cacheUtil.getPassportBrokerPublicKey(providerType).get(jwt.getKeyId());
111111
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
112112
algorithm.verify(jwt);
113113
}

src/main/java/bio/overture/ego/utils/CacheUtil.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
import static org.springframework.http.HttpMethod.GET;
55

66
import com.auth0.jwk.Jwk;
7-
import com.fasterxml.jackson.core.JsonProcessingException;
87
import java.util.HashMap;
98
import java.util.List;
109
import java.util.Map;
1110
import lombok.extern.slf4j.Slf4j;
12-
import org.springframework.beans.factory.annotation.Value;
11+
import lombok.val;
12+
import org.springframework.beans.factory.annotation.Autowired;
1313
import org.springframework.cache.annotation.CacheEvict;
1414
import org.springframework.cache.annotation.Cacheable;
1515
import org.springframework.core.ParameterizedTypeReference;
1616
import org.springframework.http.ResponseEntity;
1717
import org.springframework.scheduling.annotation.Scheduled;
18+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
1819
import org.springframework.stereotype.Component;
1920
import org.springframework.web.client.HttpStatusCodeException;
2021
import org.springframework.web.client.RestTemplate;
@@ -23,20 +24,22 @@
2324
@Component
2425
public class CacheUtil {
2526

26-
@Value("${broker.publicKey.url}")
27-
private String brokerPublicKeyUrl;
27+
@Autowired
28+
private ClientRegistrationRepository clientRegistrationRepository;
2829

2930
private RestTemplate restTemplate;
3031

3132
@Cacheable("getPassportBrokerPublicKey")
32-
public Map<String, Jwk> getPassportBrokerPublicKey() throws JsonProcessingException {
33+
public Map<String, Jwk> getPassportBrokerPublicKey(String providerType) {
3334
ResponseEntity<Map<String, List>> response;
3435
Map<String, Jwk> jwkMap = new HashMap();
36+
37+
val clientRegistration = clientRegistrationRepository.findByRegistrationId(providerType);
3538
try {
3639
restTemplate = new RestTemplate();
3740
response =
3841
restTemplate.exchange(
39-
brokerPublicKeyUrl,
42+
clientRegistration.getProviderDetails().getJwkSetUri(),
4043
GET,
4144
null,
4245
new ParameterizedTypeReference<Map<String, List>>() {});

src/main/resources/application.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ spring:
7070
- openid
7171
- email
7272
- profile
73+
74+
# Passport brokers must have ga4gh_passport_v1 scope
7375
passport:
7476
clientId: ego-client
7577
clientSecret:
@@ -197,9 +199,6 @@ token:
197199
private-key: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDSU6oy48sJW6xzqzOSU1dAvUUeFKQSBHsCf7wGWUGpOxEczhtFiiyx4YUJtg+fyvwWxa4wO3GnQLBPIxBHY8JsnvjQN2lsTUoLqMB9nGpwF617uA/S2igm1u+cDpfi82kbi6SG1Sg30PM047R6oxTRGDLLkeMRF1gRaTBM0HfSL0j6ccU5KPgwYsFLE2We6jeR56iYJGC2KYLH4v8rcc2jRAdMbUntHMtUByF9BPSW7elQnyQH5Qzr/o0b59XLKwnJFn2Bp2yviC8cdyTDyhQGna0e+oESQR1j6u3Ux/mOmm3slRXscA8sH+pHmOEAtjYVf/ww36U8uZv+ctBCJyFVAgMBAAECggEBALrEeJqAFUfWFCkSmdUSFKT0bW/svFUTjXgGnZy1ncz9GpENpMH3lQDQVibteKpYwcom+Cr0XlQ66VUcudPrDjcOY7vhuMfnSh1YWLYyM4IeRHtcUxDVkFoM+vEFNHLf2zIOqqbgmboW3iDVIurT7iRO7KxAe/YtWJL9aVqMtBn7Lu7S7OvAU4ji5iLIBxjl82JYA+9lu/aQ6YGaoZuSO7bcU8Sivi+DKAahqN9XMKiB1XpC+PpaS/aec2S7xIlTdzoDGxEALRGlMe+xBEeQTBVJHBWrRIDPoHLTREeRC/9Pp+1Y4Dz8hd5Bi0n8/5r/q0liD+0vtmjsdU4E2QrktYECgYEA73qWvhCYHPMREAFtwz1mpp9ZhDCW6SF+njG7fBKcjz8OLcy15LXiTGc268ewtQqTMjPQlm1n2C6hGccGAIlMibQJo3KZHlTs125FUzDpTVgdlei6vU7M+gmfRSZed00J6jC04/qMR1tnV3HME3np7eRTKTA6Ts+zBwEvkbCetSkCgYEA4NY5iSBO1ybouIecDdD15uI2ItLPCBNMzu7IiK7IygIzuf+SyKyjhtFSR4vEi0gScOM7UMlwCMOVU10e4nMDknIWCDG9iFvmIEkGHGxgRrN5hX1Wrq74wF212lvvagH1IVWSHa8cVpMe+UwKu5Q1h4yzuYt6Q9wPQ7Qtn5emBE0CgYB2syispMUA9GnsqQii0Xhj9nAEWaEzhOqhtrzbTs5TIkoA4Yr3BkBY5oAOdjhcRBWZuJ0XMrtaKCKqCEAtW+CYEKkGXvMOWcHbNkkeZwv8zkQ73dNRqhFnjgVn3RDNyV20uteueK23YNLkQP+KV89fnuCpdcIw9joiqq/NYuIHoQKBgB5WaZ8KH/lCA8babYEjv/pubZWXUl4plISbja17wBYZ4/bl+F1hhhMr7Wk//743dF2NG7TT6W0VTvHXr9IoaMP65uQmKgfbNpsGn294ZClGEFClz+t0KpZyTpZvL0fjibr8u+GLfkxkP5qt2wjif7KRlrKjklTTva+KAVn2cW1FAoGBAMkX9ekIwhx/7uY6ndxKl8ZMDerjr6MhV0b08hHp3RxHbYVbcpN0UKspoYvZVgHwP18xlDij8yWRE2fapwgi4m82ZmYlg0qqJmyqIU9vBB3Jow903h1KPQrkmQEZxJ/4H8yrbgVf2HT+WUfjTFgaDZRl01bI3YkydCw91/Ub9HU6
198200
public-key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0lOqMuPLCVusc6szklNXQL1FHhSkEgR7An+8BllBqTsRHM4bRYosseGFCbYPn8r8FsWuMDtxp0CwTyMQR2PCbJ740DdpbE1KC6jAfZxqcBete7gP0tooJtbvnA6X4vNpG4ukhtUoN9DzNOO0eqMU0Rgyy5HjERdYEWkwTNB30i9I+nHFOSj4MGLBSxNlnuo3keeomCRgtimCx+L/K3HNo0QHTG1J7RzLVAchfQT0lu3pUJ8kB+UM6/6NG+fVyysJyRZ9gadsr4gvHHckw8oUBp2tHvqBEkEdY+rt1Mf5jppt7JUV7HAPLB/qR5jhALY2FX/8MN+lPLmb/nLQQichVQIDAQAB
199201

200-
broker:
201-
publicKey:
202-
url: https://login.elixir-czech.org/oidc/jwk
203202

204203
# Default values available for creation of entities
205204
default:

0 commit comments

Comments
 (0)