From 9e66209b51251b1d571bbcadd1109cc943b79798 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 31 Mar 2021 17:57:58 +0100 Subject: [PATCH] Use Vert.x Buffer for OIDC requests --- .../oidc/client/runtime/OidcClientImpl.java | 8 ++++-- .../oidc/common/runtime/OidcCommonUtils.java | 28 +++++++++++++++++++ .../runtime/CodeAuthenticationMechanism.java | 22 ++++++--------- .../oidc/runtime/OidcProviderClient.java | 15 +++++----- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java index e8b87012149b7..5f883af8300cc 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java @@ -70,15 +70,17 @@ public Uni refreshTokens(String refreshToken) { return getJsonResponse(refreshGrantParams, true); } - private Uni getJsonResponse(MultiMap reqBody, boolean refresh) { + private Uni getJsonResponse(MultiMap formBody, boolean refresh) { //Uni needs to be lazy by default, we don't send the request unless //something has subscribed to it. This is important for the CAS state //management in TokensHelper return Uni.createFrom().deferred(new Supplier>() { @Override public Uni get() { - MultiMap body = reqBody; + MultiMap body = formBody; HttpRequest request = client.postAbs(tokenRequestUri); + request.putHeader(HttpHeaders.CONTENT_TYPE.toString(), + HttpHeaders.APPLICATION_X_WWW_FORM_URLENCODED.toString()); if (clientSecretBasicAuthScheme != null) { request.putHeader(AUTHORIZATION_HEADER, clientSecretBasicAuthScheme); } else if (clientJwtKey != null) { @@ -88,7 +90,7 @@ public Uni get() { body.add(OidcConstants.CLIENT_ASSERTION, OidcCommonUtils.signJwtWithKey(oidcConfig, clientJwtKey)); } // Retry up to three times with a one second delay between the retries if the connection is closed - Uni> response = request.sendForm(body) + Uni> response = request.sendBuffer(OidcCommonUtils.encodeForm(body)) .onFailure(ConnectException.class) .retry() .atMost(3); diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java index b037aeb527f9b..e9cb3de936361 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java @@ -1,11 +1,13 @@ package io.quarkus.oidc.common.runtime; import java.io.InputStream; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyStore; import java.security.PrivateKey; import java.util.Base64; +import java.util.Map; import java.util.Optional; import javax.crypto.SecretKey; @@ -22,8 +24,13 @@ import io.vertx.core.http.HttpClientOptions; import io.vertx.core.json.JsonObject; import io.vertx.core.net.ProxyOptions; +import io.vertx.mutiny.core.MultiMap; +import io.vertx.mutiny.core.buffer.Buffer; public class OidcCommonUtils { + static final byte AMP = '&'; + static final byte EQ = '='; + private OidcCommonUtils() { } @@ -55,6 +62,27 @@ public static String prependSlash(String path) { return !path.startsWith("/") ? "/" + path : path; } + public static Buffer encodeForm(MultiMap form) { + Buffer buffer = Buffer.buffer(); + for (Map.Entry entry : form) { + if (buffer.length() != 0) { + buffer.appendByte(AMP); + } + buffer.appendString(entry.getKey()); + buffer.appendByte(EQ); + buffer.appendString(urlEncode(entry.getValue())); + } + return buffer; + } + + public static String urlEncode(String value) { + try { + return URLEncoder.encode(value, StandardCharsets.UTF_8.name()); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + public static void setHttpClientOptions(OidcCommonConfig oidcConfig, TlsConfig tlsConfig, HttpClientOptions options) { boolean trustAll = oidcConfig.tls.verification.isPresent() ? oidcConfig.tls.verification.get() == Verification.NONE : tlsConfig.trustAll; diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java index fa5aeba7b3e38..dc927bf217780 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java @@ -4,8 +4,6 @@ import static io.quarkus.oidc.runtime.OidcIdentityProvider.REFRESH_TOKEN_GRANT_RESPONSE; import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.security.Permission; import java.util.ArrayList; import java.util.List; @@ -28,6 +26,7 @@ import io.quarkus.oidc.OidcTenantConfig.Authentication; import io.quarkus.oidc.RefreshToken; import io.quarkus.oidc.SecurityEvent; +import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; import io.quarkus.runtime.BlockingOperationControl; import io.quarkus.security.AuthenticationCompletionException; @@ -204,20 +203,22 @@ public Uni getChallengeInternal(RoutingContext context, TenantCon // client_id codeFlowParams.append(AMP).append(OidcConstants.CLIENT_ID).append(EQ) - .append(urlEncode(configContext.oidcConfig.clientId.get())); + .append(OidcCommonUtils.urlEncode(configContext.oidcConfig.clientId.get())); // scope List scopes = new ArrayList<>(); scopes.add("openid"); configContext.oidcConfig.getAuthentication().scopes.ifPresent(scopes::addAll); - codeFlowParams.append(AMP).append(OidcConstants.TOKEN_SCOPE).append(EQ).append(urlEncode(String.join(" ", scopes))); + codeFlowParams.append(AMP).append(OidcConstants.TOKEN_SCOPE).append(EQ) + .append(OidcCommonUtils.urlEncode(String.join(" ", scopes))); // redirect_uri String redirectPath = getRedirectPath(configContext, context); String redirectUriParam = buildUri(context, isForceHttps(configContext), redirectPath); LOG.debugf("Authentication request redirect_uri parameter: %s", redirectUriParam); - codeFlowParams.append(AMP).append(OidcConstants.CODE_FLOW_REDIRECT_URI).append(EQ).append(urlEncode(redirectUriParam)); + codeFlowParams.append(AMP).append(OidcConstants.CODE_FLOW_REDIRECT_URI).append(EQ) + .append(OidcCommonUtils.urlEncode(redirectUriParam)); // state codeFlowParams.append(AMP).append(OidcConstants.CODE_FLOW_STATE).append(EQ) @@ -226,7 +227,8 @@ public Uni getChallengeInternal(RoutingContext context, TenantCon // extra redirect parameters, see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequests if (configContext.oidcConfig.authentication.getExtraParams() != null) { for (Map.Entry entry : configContext.oidcConfig.authentication.getExtraParams().entrySet()) { - codeFlowParams.append(AMP).append(entry.getKey()).append(EQ).append(urlEncode(entry.getValue())); + codeFlowParams.append(AMP).append(entry.getKey()).append(EQ) + .append(OidcCommonUtils.urlEncode(entry.getValue())); } } @@ -236,14 +238,6 @@ public Uni getChallengeInternal(RoutingContext context, TenantCon authorizationURL)); } - private static String urlEncode(String value) { - try { - return URLEncoder.encode(value, StandardCharsets.UTF_8.name()); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - private Uni performCodeFlow(IdentityProviderManager identityProviderManager, RoutingContext context, TenantConfigContext configContext, String code) { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java index 8e3eec0b7e5e0..4e12948ccd069 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java @@ -95,21 +95,22 @@ public Uni refreshAuthorizationCodeTokens(String refres return getHttpResponse(metadata.getTokenUri(), refreshGrantParams).transform(resp -> getAuthorizationCodeTokens(resp)); } - private UniOnItem> getHttpResponse(String uri, MultiMap reqBody) { + private UniOnItem> getHttpResponse(String uri, MultiMap formBody) { HttpRequest request = client.postAbs(uri); + request.putHeader(HttpHeaders.CONTENT_TYPE.toString(), HttpHeaders.APPLICATION_X_WWW_FORM_URLENCODED.toString()); if (clientSecretBasicAuthScheme != null) { request.putHeader(AUTHORIZATION_HEADER, clientSecretBasicAuthScheme); } else if (clientJwtKey != null) { - reqBody.add(OidcConstants.CLIENT_ASSERTION_TYPE, OidcConstants.JWT_BEARER_CLIENT_ASSERTION_TYPE); - reqBody.add(OidcConstants.CLIENT_ASSERTION, OidcCommonUtils.signJwtWithKey(oidcConfig, clientJwtKey)); + formBody.add(OidcConstants.CLIENT_ASSERTION_TYPE, OidcConstants.JWT_BEARER_CLIENT_ASSERTION_TYPE); + formBody.add(OidcConstants.CLIENT_ASSERTION, OidcCommonUtils.signJwtWithKey(oidcConfig, clientJwtKey)); } else if (OidcCommonUtils.isClientSecretPostAuthRequired(oidcConfig.credentials)) { - reqBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); - reqBody.add(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials)); + formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); + formBody.add(OidcConstants.CLIENT_SECRET, OidcCommonUtils.clientSecret(oidcConfig.credentials)); } else { - reqBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); + formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); } // Retry up to three times with a one second delay between the retries if the connection is closed. - Uni> response = request.sendForm(reqBody) + Uni> response = request.sendBuffer(OidcCommonUtils.encodeForm(formBody)) .onFailure(ConnectException.class) .retry() .atMost(3);