Skip to content

Commit 506f664

Browse files
author
joel.tazzari
committed
Cache idToken
1 parent c7c7297 commit 506f664

1 file changed

Lines changed: 45 additions & 24 deletions

File tree

plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/keycloak/KeycloakOAuth2Provider.java

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151

5252
public class KeycloakOAuth2Provider extends AdapterBase implements UserOAuth2Authenticator {
5353

54+
protected String idToken = null;
55+
5456
@Inject
5557
OauthProviderDao oauthProviderDao;
5658

@@ -89,6 +91,7 @@ public boolean verifyUser(String email, String secretCode) {
8991
if (StringUtils.isBlank(verifiedEmail) || !email.equals(verifiedEmail)) {
9092
throw new CloudRuntimeException("Unable to verify the email address with the provided secret");
9193
}
94+
clearIdToken();
9295

9396
return true;
9497
}
@@ -97,45 +100,59 @@ public boolean verifyUser(String email, String secretCode) {
97100
public String verifyCodeAndFetchEmail(String secretCode) {
98101
OauthProviderVO provider = oauthProviderDao.findByProvider(getName());
99102

100-
String auth = provider.getClientId() + ":" + provider.getSecretKey();
101-
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
103+
if (StringUtils.isBlank(idToken)) {
104+
String auth = provider.getClientId() + ":" + provider.getSecretKey();
105+
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
102106

103-
List<NameValuePair> params = new ArrayList<>();
104-
params.add(new BasicNameValuePair("grant_type", "authorization_code"));
105-
params.add(new BasicNameValuePair("code", secretCode));
106-
params.add(new BasicNameValuePair("redirect_uri", provider.getRedirectUri()));
107+
List<NameValuePair> params = new ArrayList<>();
108+
params.add(new BasicNameValuePair("grant_type", "authorization_code"));
109+
params.add(new BasicNameValuePair("code", secretCode));
110+
params.add(new BasicNameValuePair("redirect_uri", provider.getRedirectUri()));
107111

108-
HttpPost post = new HttpPost(provider.getTokenUrl());
109-
post.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodedAuth);
112+
HttpPost post = new HttpPost(provider.getTokenUrl());
113+
post.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodedAuth);
110114

111-
try {
112-
post.setEntity(new UrlEncodedFormEntity(params));
113-
} catch (UnsupportedEncodingException e) {
114-
throw new CloudRuntimeException("Unable to generating URL parameters: " + e.getMessage());
115-
}
115+
try {
116+
post.setEntity(new UrlEncodedFormEntity(params));
117+
} catch (UnsupportedEncodingException e) {
118+
throw new CloudRuntimeException("Unable to generating URL parameters: " + e.getMessage());
119+
}
116120

117-
try (CloseableHttpResponse response = httpClient.execute(post)) {
118-
String body = EntityUtils.toString(response.getEntity());
121+
try (CloseableHttpResponse response = httpClient.execute(post)) {
122+
String body = EntityUtils.toString(response.getEntity());
119123

120-
if (response.getStatusLine().getStatusCode() != 200) {
121-
throw new CloudRuntimeException("Keycloak error during token generation: " + body);
122-
}
124+
if (response.getStatusLine().getStatusCode() != 200) {
125+
throw new CloudRuntimeException("Keycloak error during token generation: " + body);
126+
}
123127

124-
JsonObject json = JsonParser.parseString(body).getAsJsonObject();
125-
String idToken = json.get("id_token").getAsString();
128+
JsonObject json = JsonParser.parseString(body).getAsJsonObject();
129+
String idToken = json.get("id_token").getAsString();
130+
validateIdToken(idToken, provider);
126131

127-
return validateIdTokenAndGetEmail(idToken, provider);
128-
} catch (IOException e) {
129-
throw new CloudRuntimeException("Unable to connect to Keycloak server", e);
132+
this.idToken = idToken;
133+
} catch (IOException e) {
134+
throw new CloudRuntimeException("Unable to connect to Keycloak server", e);
135+
}
130136
}
137+
138+
return obtainEmail(idToken, provider);
131139
}
132140

133141
@Override
134142
public String getUserEmailAddress() throws CloudRuntimeException {
135143
return null;
136144
}
137145

138-
private String validateIdTokenAndGetEmail(String idTokenStr, OauthProviderVO provider) {
146+
private void validateIdToken(String idTokenStr, OauthProviderVO provider) {
147+
JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idTokenStr);
148+
JwtClaims claims = jwtConsumer.getJwtToken().getClaims();
149+
150+
if (!claims.getAudiences().contains(provider.getClientId())) {
151+
throw new CloudAuthenticationException("Audience mismatch");
152+
}
153+
}
154+
155+
private String obtainEmail(String idTokenStr, OauthProviderVO provider) {
139156
JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idTokenStr);
140157
JwtClaims claims = jwtConsumer.getJwtToken().getClaims();
141158

@@ -146,6 +163,10 @@ private String validateIdTokenAndGetEmail(String idTokenStr, OauthProviderVO pro
146163
return (String) claims.getClaim("email");
147164
}
148165

166+
protected void clearIdToken() {
167+
idToken = null;
168+
}
169+
149170
public void setHttpClient(CloseableHttpClient httpClient) {
150171
this.httpClient = httpClient;
151172
}

0 commit comments

Comments
 (0)