@@ -67,19 +67,21 @@ String operation() {
6767 private final MultiMap tokenGrantParams ;
6868 private final MultiMap commonRefreshGrantParams ;
6969 private final String grantType ;
70- private final String clientSecretBasicAuthScheme ;
7170 private final Key clientJwtKey ;
7271 private final boolean jwtBearerAuthentication ;
7372 private final OidcClientConfig oidcConfig ;
7473 private final Map <OidcEndpoint .Type , List <OidcRequestFilter >> requestFilters ;
7574 private final Map <OidcEndpoint .Type , List <OidcResponseFilter >> responseFilters ;
7675 private final ClientAssertionProvider clientAssertionProvider ;
7776 private volatile boolean closed ;
77+ private volatile String clientSecret ;
78+ private volatile String clientSecretBasicAuthScheme ;
7879
79- OidcClientImpl (WebClient client , String tokenRequestUri , String tokenRevokeUri , String grantType ,
80+ private OidcClientImpl (WebClient client , String tokenRequestUri , String tokenRevokeUri , String grantType ,
8081 MultiMap tokenGrantParams , MultiMap commonRefreshGrantParams , OidcClientConfig oidcClientConfig ,
8182 Map <OidcEndpoint .Type , List <OidcRequestFilter >> requestFilters ,
82- Map <OidcEndpoint .Type , List <OidcResponseFilter >> responseFilters , Vertx vertx ) {
83+ Map <OidcEndpoint .Type , List <OidcResponseFilter >> responseFilters , Vertx vertx ,
84+ ClientCredentials clientCredentials ) {
8385 this .client = client ;
8486 this .tokenRequestUri = tokenRequestUri ;
8587 this .tokenRevokeUri = tokenRevokeUri ;
@@ -89,9 +91,10 @@ String operation() {
8991 this .oidcConfig = oidcClientConfig ;
9092 this .requestFilters = requestFilters ;
9193 this .responseFilters = responseFilters ;
92- this .clientSecretBasicAuthScheme = OidcCommonUtils . initClientSecretBasicAuth ( oidcClientConfig ) ;
94+ this .clientSecretBasicAuthScheme = clientCredentials . clientSecretBasicAuthScheme ;
9395 this .jwtBearerAuthentication = oidcClientConfig .credentials ().jwt ().source () == Source .BEARER ;
94- this .clientJwtKey = jwtBearerAuthentication ? null : OidcCommonUtils .initClientJwtKey (oidcClientConfig , false );
96+ this .clientJwtKey = jwtBearerAuthentication ? null : clientCredentials .clientJwtKey ;
97+ this .clientSecret = clientCredentials .clientSecret ;
9598 if (jwtBearerAuthentication && oidcClientConfig .credentials ().jwt ().tokenPath ().isPresent ()) {
9699 this .clientAssertionProvider = new ClientAssertionProvider (vertx ,
97100 oidcClientConfig .credentials ().jwt ().tokenPath ().get ());
@@ -187,12 +190,20 @@ public Uni<Tokens> get() {
187190 });
188191 }
189192
190- private UniOnItem <HttpResponse <Buffer >> postRequest (
193+ private record PreparedPostRequest (Uni <HttpResponse <Buffer >> postRequest , CredentialsToRetry credentialsToRetry ) {
194+ enum CredentialsToRetry {
195+ CLIENT_SECRET ,
196+ CLIENT_SECRET_BASIC_AUTH_SCHEME
197+ }
198+ }
199+
200+ private PreparedPostRequest preparePostRequest (
191201 OidcRequestContextProperties requestProps ,
192202 OidcEndpoint .Type endpointType , HttpRequest <Buffer > request ,
193203 MultiMap formBody ,
194204 Map <String , String > additionalGrantParameters ,
195205 Operation op ) {
206+ PreparedPostRequest .CredentialsToRetry credentialsToRetry = null ;
196207 MultiMap body = formBody ;
197208 request .putHeader (HttpHeaders .CONTENT_TYPE .toString (),
198209 HttpHeaders .APPLICATION_X_WWW_FORM_URLENCODED .toString ());
@@ -204,6 +215,9 @@ private UniOnItem<HttpResponse<Buffer>> postRequest(
204215
205216 if (clientSecretBasicAuthScheme != null ) {
206217 request .putHeader (AUTHORIZATION_HEADER , clientSecretBasicAuthScheme );
218+ if (hasClientSecretProvider ()) {
219+ credentialsToRetry = PreparedPostRequest .CredentialsToRetry .CLIENT_SECRET_BASIC_AUTH_SCHEME ;
220+ }
207221 } else if (jwtBearerAuthentication ) {
208222 String clientAssertion = additionalGrantParameters .get (OidcConstants .CLIENT_ASSERTION );
209223 if (clientAssertion == null && clientAssertionProvider != null ) {
@@ -245,7 +259,10 @@ private UniOnItem<HttpResponse<Buffer>> postRequest(
245259 } else if (OidcCommonUtils .isClientSecretPostAuthRequired (oidcConfig .credentials ())) {
246260 body = !isRefresh (op ) ? copyMultiMap (body ) : body ;
247261 body .set (OidcConstants .CLIENT_ID , oidcConfig .clientId ().get ());
248- body .set (OidcConstants .CLIENT_SECRET , OidcCommonUtils .clientSecret (oidcConfig .credentials ()));
262+ body .set (OidcConstants .CLIENT_SECRET , clientSecret );
263+ if (hasClientSecretProvider ()) {
264+ credentialsToRetry = PreparedPostRequest .CredentialsToRetry .CLIENT_SECRET ;
265+ }
249266 } else {
250267 body = !isRefresh (op ) ? copyMultiMap (body ) : body ;
251268 body = copyMultiMap (body ).set (OidcConstants .CLIENT_ID , oidcConfig .clientId ().get ());
@@ -272,7 +289,72 @@ private UniOnItem<HttpResponse<Buffer>> postRequest(
272289 // don't wrap it to avoid information leak
273290 return new OidcClientException ("OIDC Server is not available" );
274291 });
275- return response .onItem ();
292+ return new PreparedPostRequest (response , credentialsToRetry );
293+ }
294+
295+ private boolean hasClientSecretProvider () {
296+ return oidcConfig .credentials ().clientSecret ().provider ().key ().isPresent ();
297+ }
298+
299+ private UniOnItem <HttpResponse <Buffer >> postRequest (
300+ OidcRequestContextProperties requestProps ,
301+ OidcEndpoint .Type endpointType , HttpRequest <Buffer > request ,
302+ MultiMap formBody ,
303+ Map <String , String > additionalGrantParameters ,
304+ Operation op ) {
305+
306+ final MultiMap newFormBody ;
307+ boolean hasClientSecretProvider = hasClientSecretProvider ();
308+ if (hasClientSecretProvider ) {
309+ newFormBody = copyMultiMap (formBody );
310+ } else {
311+ newFormBody = formBody ;
312+ }
313+
314+ var preparedRequest = preparePostRequest (requestProps , endpointType , request , newFormBody , additionalGrantParameters ,
315+ op );
316+ if (hasClientSecretProvider && preparedRequest .credentialsToRetry != null ) {
317+ return preparedRequest .postRequest .flatMap (httpResponse -> {
318+ if (httpResponse .statusCode () == 401 ) {
319+ // here we need to deal with error responses (like unauthorized_client) possibly caused by
320+ // invalid credentialsToRetry; if credentialsToRetry provider updated credentialsToRetry, we should retry
321+ var credentialsRefresh = switch (preparedRequest .credentialsToRetry ) {
322+ case CLIENT_SECRET -> OidcCommonUtils .clientSecret (oidcConfig .credentials ())
323+ .map (newClientSecret -> {
324+ if (newClientSecret != null && !newClientSecret .equals (clientSecret )) {
325+ this .clientSecret = newClientSecret ;
326+ return true ;
327+ }
328+ return false ;
329+ });
330+ case CLIENT_SECRET_BASIC_AUTH_SCHEME -> OidcCommonUtils .clientSecret (oidcConfig .credentials ())
331+ .map (newClientSecret -> {
332+ var newClientSecretBasicAuthScheme = OidcCommonUtils .initClientSecretBasicAuth (oidcConfig ,
333+ newClientSecret );
334+ if (newClientSecretBasicAuthScheme != null
335+ && !newClientSecretBasicAuthScheme .equals (clientSecretBasicAuthScheme )) {
336+ this .clientSecret = newClientSecret ;
337+ this .clientSecretBasicAuthScheme = newClientSecretBasicAuthScheme ;
338+ return true ;
339+ }
340+ return false ;
341+ });
342+ };
343+
344+ return credentialsRefresh .flatMap (credentialsRefreshed -> {
345+ if (Boolean .TRUE .equals (credentialsRefreshed )) {
346+ LOG .debug ("HTTP request failed with response status code 401 and the CredentialsProvider"
347+ + " provided new credentials, retrying the request with new credentials" );
348+ return preparePostRequest (requestProps , endpointType , request , formBody ,
349+ additionalGrantParameters , op ).postRequest ;
350+ }
351+ return Uni .createFrom ().item (httpResponse );
352+ });
353+ }
354+ return Uni .createFrom ().item (httpResponse );
355+ }).onItem ();
356+ }
357+ return preparedRequest .postRequest .onItem ();
276358 }
277359
278360 private Tokens emitGrantTokens (OidcRequestContextProperties requestProps , HttpResponse <Buffer > resp , Operation op ) {
@@ -393,4 +475,31 @@ OidcClientConfig getConfig() {
393475 static boolean isRefresh (Operation op ) {
394476 return op == Operation .REFRESH ;
395477 }
478+
479+ static Uni <OidcClient > of (WebClient client , String tokenRequestUri , String tokenRevokeUri , String grantType ,
480+ MultiMap tokenGrantParams , MultiMap commonRefreshGrantParams , OidcClientConfig oidcClientConfig ,
481+ Map <OidcEndpoint .Type , List <OidcRequestFilter >> requestFilters ,
482+ Map <OidcEndpoint .Type , List <OidcResponseFilter >> responseFilters , Vertx vertx ) {
483+ return OidcCommonUtils .clientSecret (oidcClientConfig .credentials ())
484+ .onItem ().ifNotNull ()
485+ .transform (clientSecret -> new ClientCredentials (clientSecret ,
486+ OidcCommonUtils .initClientSecretBasicAuth (oidcClientConfig , clientSecret )))
487+ .onItem ().ifNull ()
488+ .switchTo (() -> OidcCommonUtils .initClientJwtKey (oidcClientConfig , false ).map (ClientCredentials ::new ))
489+ .onFailure ().invoke (t -> LOG .error ("Failed to create OidcClientImpl" , t ))
490+ .map (clientCredentials -> new OidcClientImpl (client , tokenRequestUri , tokenRevokeUri , grantType ,
491+ tokenGrantParams ,
492+ commonRefreshGrantParams , oidcClientConfig , requestFilters , responseFilters , vertx , clientCredentials ));
493+ }
494+
495+ private record ClientCredentials (Key clientJwtKey , String clientSecret , String clientSecretBasicAuthScheme ) {
496+
497+ private ClientCredentials (Key clientJwtKey ) {
498+ this (clientJwtKey , null , null );
499+ }
500+
501+ private ClientCredentials (String clientSecret , String clientSecretBasicAuthScheme ) {
502+ this (null , clientSecret , clientSecretBasicAuthScheme );
503+ }
504+ }
396505}
0 commit comments