Skip to content

Commit

Permalink
Use Vert.x Buffer for OIDC requests
Browse files Browse the repository at this point in the history
  • Loading branch information
sberyozkin committed Mar 31, 2021
1 parent 488d009 commit 9e66209
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 24 deletions.
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

0 comments on commit 9e66209

Please sign in to comment.