Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Vert.x Buffer for OIDC requests #16157

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,17 @@ public Uni<Tokens> refreshTokens(String refreshToken) {
return getJsonResponse(refreshGrantParams, true);
}

private Uni<Tokens> getJsonResponse(MultiMap reqBody, boolean refresh) {
private Uni<Tokens> 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<Uni<? extends Tokens>>() {
@Override
public Uni<Tokens> get() {
MultiMap body = reqBody;
MultiMap body = formBody;
HttpRequest<Buffer> 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) {
Expand All @@ -88,7 +90,7 @@ public Uni<Tokens> 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<HttpResponse<Buffer>> response = request.sendForm(body)
Uni<HttpResponse<Buffer>> response = request.sendBuffer(OidcCommonUtils.encodeForm(body))
.onFailure(ConnectException.class)
.retry()
.atMost(3);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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() {

}
Expand Down Expand Up @@ -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<String, String> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -204,20 +203,22 @@ public Uni<ChallengeData> 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<String> 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)
Expand All @@ -226,7 +227,8 @@ public Uni<ChallengeData> 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<String, String> 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()));
}
}

Expand All @@ -236,14 +238,6 @@ public Uni<ChallengeData> 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<SecurityIdentity> performCodeFlow(IdentityProviderManager identityProviderManager,
RoutingContext context, TenantConfigContext configContext, String code) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,22 @@ public Uni<AuthorizationCodeTokens> refreshAuthorizationCodeTokens(String refres
return getHttpResponse(metadata.getTokenUri(), refreshGrantParams).transform(resp -> getAuthorizationCodeTokens(resp));
}

private UniOnItem<HttpResponse<Buffer>> getHttpResponse(String uri, MultiMap reqBody) {
private UniOnItem<HttpResponse<Buffer>> getHttpResponse(String uri, MultiMap formBody) {
HttpRequest<Buffer> 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<HttpResponse<Buffer>> response = request.sendForm(reqBody)
Uni<HttpResponse<Buffer>> response = request.sendBuffer(OidcCommonUtils.encodeForm(formBody))
.onFailure(ConnectException.class)
.retry()
.atMost(3);
Expand Down