This is an automated email from the ASF dual-hosted git repository.

bbende pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 335f08e5a4 NIFI-5302 Add Support for Client Credentials Flow with OIDC 
(#8532)
335f08e5a4 is described below

commit 335f08e5a474d3b78abf687c801d1e4e179549ec
Author: David Handermann <[email protected]>
AuthorDate: Thu Apr 25 11:33:32 2024 -0500

    NIFI-5302 Add Support for Client Credentials Flow with OIDC (#8532)
    
    * NIFI-5302 Added Support for Client Credentials Flow with OIDC
    - Added JwtDecoder implementation supporting delegation based on Issuer
    * NIFI-5302 Added Client Credentials Grant Type section to OIDC docs
    * NIFI-5302 Replaced deprecated OkHttp3ClientHttpRequestFactory
---
 .../src/main/asciidoc/administration-guide.adoc    |  13 ++
 .../AuthenticationSecurityConfiguration.java       |   2 +
 .../ClientRegistrationConfiguration.java           | 153 +++++++++++++++++
 .../JwtAuthenticationSecurityConfiguration.java    | 176 +++++++++----------
 .../configuration/JwtDecoderConfiguration.java     | 186 +++++++++++++++++++++
 .../configuration/OidcSecurityConfiguration.java   | 161 +++---------------
 .../jwt/converter/StandardIssuerJwtDecoder.java    | 139 +++++++++++++++
 .../authentication/AccessTokenDecoderFactory.java  |  50 ++++++
 .../StandardOidcIdTokenDecoderFactory.java         |  26 +--
 .../converter/StandardIssuerJwtDecoderTest.java    | 142 ++++++++++++++++
 10 files changed, 805 insertions(+), 243 deletions(-)

diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc 
b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index b2fbeb50c4..92a3119ea5 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -549,6 +549,19 @@ Access Token has expired, indicating that the Resource 
Owner has terminated the
 termination occurs when the user closes the browser without initiating the 
logout process. The scheduled process avoids
 extended storage of Refresh Tokens for users who are no longer interacting 
with the application.
 
+The OpenID Connect implementation also supports the OAuth 2 Client Credentials 
Grant Type as described in
+link:https://www.rfc-editor.org/rfc/rfc6749#section-4.4[RFC 6749 Section 
4.4^]. With OpenID Connect integration enabled,
+NiFi evaluates the JSON Web Token Issuer Claim named `iss` and delegates to 
either the configured Authorization Server
+or internal processing for signature verification. When the `iss` claim value 
matches the `issuer` from the OpenID
+Connect Discovery Configuration, NiFi uses the JSON Web Keys from the 
Authorization Server for signature verification.
+In all other cases, NiFi verifies JSON Web Token signatures using an internal 
public key.
+
+The Client Credentials Grant Type enables machine-to-machine authentication 
and requires token request processing outside
+of NiFi itself to obtain an Access Token. NiFi must also be configured to 
authorize requests based on the identity
+defined in a signed Access Token. Access Tokens obtained using the Client 
Credentials Grant Type do not include the
+standard `email`, which requires configuring a fallback claim to identify the 
machine user. The most common claim for
+identification is the Subject Claim named `sub`, which contains the Client ID.
+
 OpenID Connect integration supports the following settings in 
_nifi.properties_.
 
 [options="header"]
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/AuthenticationSecurityConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/AuthenticationSecurityConfiguration.java
index 33d4d74717..da18a01ec1 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/AuthenticationSecurityConfiguration.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/AuthenticationSecurityConfiguration.java
@@ -34,7 +34,9 @@ import 
org.springframework.security.authentication.AuthenticationManager;
  */
 @Configuration
 @Import({
+        ClientRegistrationConfiguration.class,
         JwtAuthenticationSecurityConfiguration.class,
+        JwtDecoderConfiguration.class,
         KerberosAuthenticationSecurityConfiguration.class,
         KnoxAuthenticationSecurityConfiguration.class,
         OidcSecurityConfiguration.class,
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/ClientRegistrationConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/ClientRegistrationConfiguration.java
new file mode 100644
index 0000000000..6205f32931
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/ClientRegistrationConfiguration.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.configuration;
+
+import org.apache.nifi.security.util.SslContextFactory;
+import org.apache.nifi.security.util.StandardTlsConfiguration;
+import org.apache.nifi.security.util.TlsConfiguration;
+import org.apache.nifi.security.util.TlsException;
+import org.apache.nifi.util.FormatUtils;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.oidc.OidcConfigurationException;
+import 
org.apache.nifi.web.security.oidc.registration.ClientRegistrationProvider;
+import 
org.apache.nifi.web.security.oidc.registration.DisabledClientRegistrationRepository;
+import 
org.apache.nifi.web.security.oidc.registration.StandardClientRegistrationProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.JdkClientHttpRequestFactory;
+import org.springframework.http.converter.FormHttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import 
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import 
org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistration;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import 
org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
+import 
org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+
+import javax.net.ssl.SSLContext;
+import java.net.http.HttpClient;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * OpenID Connect Client Registration configuration with supporting components
+ */
+@Configuration
+public class ClientRegistrationConfiguration {
+
+    private static final Duration DEFAULT_SOCKET_TIMEOUT = 
Duration.ofSeconds(5);
+
+    private static final String NIFI_TRUSTSTORE_STRATEGY = "NIFI";
+
+    private final NiFiProperties properties;
+
+    @Autowired
+    public ClientRegistrationConfiguration(final NiFiProperties properties) {
+        this.properties = Objects.requireNonNull(properties, "Application 
properties required");
+    }
+
+    /**
+     * Client Registration Repository for OpenID Connect Discovery
+     *
+     * @return Client Registration Repository
+     */
+    @Bean
+    public ClientRegistrationRepository clientRegistrationRepository() {
+        final ClientRegistrationRepository clientRegistrationRepository;
+        if (properties.isOidcEnabled()) {
+            final ClientRegistrationProvider clientRegistrationProvider = new 
StandardClientRegistrationProvider(properties, oidcRestOperations());
+            final ClientRegistration clientRegistration = 
clientRegistrationProvider.getClientRegistration();
+            clientRegistrationRepository = new 
InMemoryClientRegistrationRepository(clientRegistration);
+        } else {
+            clientRegistrationRepository = new 
DisabledClientRegistrationRepository();
+        }
+        return clientRegistrationRepository;
+    }
+
+
+    /**
+     * OpenID Connect REST Operations for communication with Authorization 
Servers
+     *
+     * @return REST Operations
+     */
+    @Bean
+    public RestOperations oidcRestOperations() {
+        final RestTemplate restTemplate = new 
RestTemplate(oidcClientHttpRequestFactory());
+        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
+        restTemplate.setMessageConverters(
+                Arrays.asList(
+                        new FormHttpMessageConverter(),
+                        new OAuth2AccessTokenResponseHttpMessageConverter(),
+                        new StringHttpMessageConverter(),
+                        new MappingJackson2HttpMessageConverter()
+                )
+        );
+        return restTemplate;
+    }
+
+    /**
+     * OpenID Connect Client HTTP Request Factory for communication with 
Authorization Servers
+     *
+     * @return Client HTTP Request Factory
+     */
+    @Bean
+    public ClientHttpRequestFactory oidcClientHttpRequestFactory() {
+        final HttpClient httpClient = getHttpClient();
+        final JdkClientHttpRequestFactory clientHttpRequestFactory = new 
JdkClientHttpRequestFactory(httpClient);
+        final Duration readTimeout = 
getTimeout(properties.getOidcReadTimeout());
+        clientHttpRequestFactory.setReadTimeout(readTimeout);
+        return clientHttpRequestFactory;
+    }
+
+    private HttpClient getHttpClient() {
+        final Duration connectTimeout = 
getTimeout(properties.getOidcConnectTimeout());
+        final HttpClient.Builder builder = 
HttpClient.newBuilder().connectTimeout(connectTimeout);
+
+        if 
(NIFI_TRUSTSTORE_STRATEGY.equals(properties.getOidcClientTruststoreStrategy())) 
{
+            setSslSocketFactory(builder);
+        }
+
+        return builder.build();
+    }
+
+    private Duration getTimeout(final String timeoutExpression) {
+        try {
+            final double duration = 
FormatUtils.getPreciseTimeDuration(timeoutExpression, TimeUnit.MILLISECONDS);
+            final long rounded = Math.round(duration);
+            return Duration.ofMillis(rounded);
+        } catch (final RuntimeException e) {
+            return DEFAULT_SOCKET_TIMEOUT;
+        }
+    }
+
+    private void setSslSocketFactory(final HttpClient.Builder builder) {
+        final TlsConfiguration tlsConfiguration = 
StandardTlsConfiguration.fromNiFiProperties(properties);
+
+        try {
+            final SSLContext sslContext = 
Objects.requireNonNull(SslContextFactory.createSslContext(tlsConfiguration), 
"SSLContext required");
+            builder.sslContext(sslContext);
+        } catch (final TlsException e) {
+            throw new OidcConfigurationException("OpenID Connect HTTP TLS 
configuration failed", e);
+        }
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/JwtAuthenticationSecurityConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/JwtAuthenticationSecurityConfiguration.java
index 100e500cad..1057c50a1e 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/JwtAuthenticationSecurityConfiguration.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/JwtAuthenticationSecurityConfiguration.java
@@ -16,79 +16,54 @@
  */
 package org.apache.nifi.web.security.configuration;
 
-import com.nimbusds.jose.proc.JWSKeySelector;
-import com.nimbusds.jose.proc.SecurityContext;
-import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier;
-import com.nimbusds.jwt.proc.DefaultJWTProcessor;
-import com.nimbusds.jwt.proc.JWTClaimsSetVerifier;
-import com.nimbusds.jwt.proc.JWTProcessor;
 import org.apache.nifi.authorization.Authorizer;
-import org.apache.nifi.components.state.StateManager;
-import org.apache.nifi.components.state.StateManagerProvider;
 import org.apache.nifi.util.NiFiProperties;
 import 
org.apache.nifi.web.security.jwt.converter.StandardJwtAuthenticationConverter;
 import org.apache.nifi.web.security.StandardAuthenticationEntryPoint;
-import org.apache.nifi.web.security.jwt.jws.StandardJWSKeySelector;
 import org.apache.nifi.web.security.jwt.jws.StandardJwsSignerProvider;
 import org.apache.nifi.web.security.jwt.key.command.KeyExpirationCommand;
 import org.apache.nifi.web.security.jwt.key.command.KeyGenerationCommand;
 import org.apache.nifi.web.security.jwt.key.StandardVerificationKeySelector;
-import 
org.apache.nifi.web.security.jwt.key.service.StandardVerificationKeyService;
 import org.apache.nifi.web.security.jwt.key.service.VerificationKeyService;
-import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
 import org.apache.nifi.web.security.jwt.provider.IssuerProvider;
+import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
 import org.apache.nifi.web.security.jwt.provider.StandardBearerTokenProvider;
 import org.apache.nifi.web.security.jwt.provider.StandardIssuerProvider;
-import org.apache.nifi.web.security.jwt.provider.SupportedClaim;
 import org.apache.nifi.web.security.jwt.resolver.StandardBearerTokenResolver;
 import org.apache.nifi.web.security.jwt.revocation.JwtLogoutListener;
 import org.apache.nifi.web.security.jwt.revocation.JwtRevocationService;
-import org.apache.nifi.web.security.jwt.revocation.JwtRevocationValidator;
 import org.apache.nifi.web.security.jwt.revocation.StandardJwtLogoutListener;
-import 
org.apache.nifi.web.security.jwt.revocation.StandardJwtRevocationService;
 import 
org.apache.nifi.web.security.jwt.revocation.command.RevocationExpirationCommand;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
-import org.springframework.security.oauth2.core.OAuth2TokenValidator;
-import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.security.oauth2.jwt.JwtValidators;
-import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import 
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
 import 
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
 import 
org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
 import 
org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
 
 import java.time.Duration;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * JSON Web Token Configuration for Authentication Security
  */
 @Configuration
 public class JwtAuthenticationSecurityConfiguration {
-    private static final Set<String> REQUIRED_CLAIMS = new 
HashSet<>(Arrays.asList(
-            SupportedClaim.ISSUER.getClaim(),
-            SupportedClaim.SUBJECT.getClaim(),
-            SupportedClaim.AUDIENCE.getClaim(),
-            SupportedClaim.EXPIRATION.getClaim(),
-            SupportedClaim.NOT_BEFORE.getClaim(),
-            SupportedClaim.ISSUED_AT.getClaim(),
-            SupportedClaim.JWT_ID.getClaim(),
-            SupportedClaim.GROUPS.getClaim()
-    ));
 
     private final NiFiProperties niFiProperties;
 
     private final Authorizer authorizer;
 
-    private final StateManagerProvider stateManagerProvider;
+    private final JwtDecoder jwtDecoder;
+
+    private final JwtRevocationService jwtRevocationService;
+
+    private final StandardVerificationKeySelector verificationKeySelector;
+
+    private final VerificationKeyService verificationKeyService;
 
     private final Duration keyRotationPeriod;
 
@@ -96,15 +71,26 @@ public class JwtAuthenticationSecurityConfiguration {
     public JwtAuthenticationSecurityConfiguration(
             final NiFiProperties niFiProperties,
             final Authorizer authorizer,
-            final StateManagerProvider stateManagerProvider
+            final JwtDecoder jwtDecoder,
+            final JwtRevocationService jwtRevocationService,
+            final StandardVerificationKeySelector 
standardVerificationKeySelector,
+            final VerificationKeyService verificationKeyService
     ) {
         this.niFiProperties = niFiProperties;
         this.authorizer = authorizer;
-        this.stateManagerProvider = stateManagerProvider;
+        this.jwtDecoder = jwtDecoder;
+        this.jwtRevocationService = jwtRevocationService;
+        this.verificationKeySelector = standardVerificationKeySelector;
+        this.verificationKeyService = verificationKeyService;
         this.keyRotationPeriod = 
niFiProperties.getSecurityUserJwsKeyRotationPeriod();
     }
 
-
+    /**
+     * Bearer Token Authentication Filter responsible for reading and 
authenticating Bearer JSON Web Tokens from HTTP Requests
+     *
+     * @param authenticationManager Authentication Manager configured with JWT 
Authentication Provider
+     * @return Bearer Token Authentication Filter
+     */
     @Bean
     public BearerTokenAuthenticationFilter 
bearerTokenAuthenticationFilter(final AuthenticationManager 
authenticationManager) {
         final BearerTokenAuthenticationFilter bearerTokenAuthenticationFilter 
= new BearerTokenAuthenticationFilter(authenticationManager);
@@ -113,74 +99,64 @@ public class JwtAuthenticationSecurityConfiguration {
         return bearerTokenAuthenticationFilter;
     }
 
+    /**
+     * Bearer Token Resolver responsible for reading Bearer JSON Web Tokens 
from HTTP headers or cookies
+     *
+     * @return Standard implementation of Bearer Token Resolver
+     */
     @Bean
     public BearerTokenResolver bearerTokenResolver() {
         return new StandardBearerTokenResolver();
     }
 
+    /**
+     * Authentication Entry Point delegating to Bearer Token Entry Point for 
returning headers on authentication failures
+     *
+     * @return Authentication Entry Point
+     */
     @Bean
     public StandardAuthenticationEntryPoint authenticationEntryPoint() {
         final BearerTokenAuthenticationEntryPoint 
bearerTokenAuthenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();
         return new 
StandardAuthenticationEntryPoint(bearerTokenAuthenticationEntryPoint);
     }
 
+    /**
+     * JSON Web Token Authentication Provider responsible for decoding and 
verifying Bearer Tokens from HTTP Requests
+     *
+     * @return JSON Web Token Authentication Provider
+     */
     @Bean
     public JwtAuthenticationProvider jwtAuthenticationProvider() {
-        final JwtAuthenticationProvider jwtAuthenticationProvider = new 
JwtAuthenticationProvider(jwtDecoder());
+        final JwtAuthenticationProvider jwtAuthenticationProvider = new 
JwtAuthenticationProvider(jwtDecoder);
         
jwtAuthenticationProvider.setJwtAuthenticationConverter(jwtAuthenticationConverter());
         return jwtAuthenticationProvider;
     }
 
-    @Bean
-    public JwtDecoder jwtDecoder() {
-        final NimbusJwtDecoder jwtDecoder = new 
NimbusJwtDecoder(jwtProcessor());
-        final OAuth2TokenValidator<Jwt> jwtValidator = new 
DelegatingOAuth2TokenValidator<>(
-                JwtValidators.createDefault(),
-                jwtRevocationValidator()
-        );
-        jwtDecoder.setJwtValidator(jwtValidator);
-        return jwtDecoder;
-    }
-
-    @Bean
-    public OAuth2TokenValidator<Jwt> jwtRevocationValidator() {
-        return new JwtRevocationValidator(jwtRevocationService());
-    }
-
-    @Bean
-    public JwtRevocationService jwtRevocationService() {
-        final StateManager stateManager = 
stateManagerProvider.getStateManager(StandardJwtRevocationService.class.getName());
-        return new StandardJwtRevocationService(stateManager);
-    }
-
+    /**
+     * JSON Web Token Logout Listener responsible for revoking application 
Bearer Tokens after logout completion
+     *
+     * @return JSON Web Token Logout Listener using Revocation Service for 
tracking
+     */
     @Bean
     public JwtLogoutListener jwtLogoutListener() {
-        return new StandardJwtLogoutListener(jwtDecoder(), 
jwtRevocationService());
-    }
-
-    @Bean
-    public JWTProcessor<SecurityContext> jwtProcessor() {
-        final DefaultJWTProcessor<SecurityContext> jwtProcessor = new 
DefaultJWTProcessor<>();
-        jwtProcessor.setJWSKeySelector(jwsKeySelector());
-        jwtProcessor.setJWTClaimsSetVerifier(claimsSetVerifier());
-        return jwtProcessor;
-    }
-
-    @Bean
-    public JWSKeySelector<SecurityContext> jwsKeySelector() {
-        return new StandardJWSKeySelector<>(verificationKeySelector());
-    }
-
-    @Bean
-    public JWTClaimsSetVerifier<SecurityContext> claimsSetVerifier() {
-        return new DefaultJWTClaimsVerifier<>(null, REQUIRED_CLAIMS);
+        return new StandardJwtLogoutListener(jwtDecoder, jwtRevocationService);
     }
 
+    /**
+     * JSON Web Token Authentication Converter provides application User 
objects
+     *
+     * @return Authentication Converter from JSON Web Tokens to User objects
+     */
     @Bean
     public StandardJwtAuthenticationConverter jwtAuthenticationConverter() {
         return new StandardJwtAuthenticationConverter(authorizer, 
niFiProperties);
     }
 
+    /**
+     * Application Bearer Token Provider responsible for signing and encoding 
new JSON Web Tokens
+     *
+     * @return Application Bearer Token Provider
+     */
     @Bean
     public BearerTokenProvider bearerTokenProvider() {
         return new StandardBearerTokenProvider(jwsSignerProvider(), 
issuerProvider());
@@ -191,43 +167,57 @@ public class JwtAuthenticationSecurityConfiguration {
         return new 
StandardIssuerProvider(niFiProperties.getProperty(NiFiProperties.WEB_HTTPS_HOST),
 niFiProperties.getConfiguredHttpOrHttpsPort());
     }
 
+    /**
+     * JSON Web Signature Signer Provider responsible for managing Bearer 
Token signing key pairs
+     *
+     * @return JSON Web Signature Signer Provider
+     */
     @Bean
     public StandardJwsSignerProvider jwsSignerProvider() {
-        return new StandardJwsSignerProvider(verificationKeySelector());
-    }
-
-    @Bean
-    public StandardVerificationKeySelector verificationKeySelector() {
-        return new StandardVerificationKeySelector(verificationKeyService(), 
keyRotationPeriod);
-    }
-
-    @Bean
-    public VerificationKeyService verificationKeyService() {
-        final StateManager stateManager = 
stateManagerProvider.getStateManager(StandardVerificationKeyService.class.getName());
-        return new StandardVerificationKeyService(stateManager);
+        return new StandardJwsSignerProvider(verificationKeySelector);
     }
 
+    /**
+     * Key Generation Command responsible for rotating JSON Web Signature key 
pairs based on configuration
+     *
+     * @return Key Generation Command scheduled according to application 
properties
+     */
     @Bean
     public KeyGenerationCommand keyGenerationCommand() {
-        final KeyGenerationCommand command = new 
KeyGenerationCommand(jwsSignerProvider(), verificationKeySelector());
+        final KeyGenerationCommand command = new 
KeyGenerationCommand(jwsSignerProvider(), verificationKeySelector);
         commandScheduler().scheduleAtFixedRate(command, keyRotationPeriod);
         return command;
     }
 
+    /**
+     * Key Expiration Command responsible for removing expired signing key 
pairs
+     *
+     * @return Key Expiration Command scheduled according to application 
properties
+     */
     @Bean
     public KeyExpirationCommand keyExpirationCommand() {
-        final KeyExpirationCommand command = new 
KeyExpirationCommand(verificationKeyService());
+        final KeyExpirationCommand command = new 
KeyExpirationCommand(verificationKeyService);
         commandScheduler().scheduleAtFixedRate(command, keyRotationPeriod);
         return command;
     }
 
+    /**
+     * Revocation Expiration Command responsible for removing expired 
application Bearer Token revocation records
+     *
+     * @return Revocation Expiration Command scheduled according to 
application properties
+     */
     @Bean
     public RevocationExpirationCommand revocationExpirationCommand() {
-        final RevocationExpirationCommand command = new 
RevocationExpirationCommand(jwtRevocationService());
+        final RevocationExpirationCommand command = new 
RevocationExpirationCommand(jwtRevocationService);
         commandScheduler().scheduleAtFixedRate(command, keyRotationPeriod);
         return command;
     }
 
+    /**
+     * Command Scheduler responsible for running commands in background thread
+     *
+     * @return Thread Pool Task Scheduler with named threads
+     */
     @Bean
     public ThreadPoolTaskScheduler commandScheduler() {
         final ThreadPoolTaskScheduler scheduler = new 
ThreadPoolTaskScheduler();
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/JwtDecoderConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/JwtDecoderConfiguration.java
new file mode 100644
index 0000000000..8da3e70f7b
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/JwtDecoderConfiguration.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.configuration;
+
+import com.nimbusds.jose.proc.JWSKeySelector;
+import com.nimbusds.jose.proc.SecurityContext;
+import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier;
+import com.nimbusds.jwt.proc.DefaultJWTProcessor;
+import com.nimbusds.jwt.proc.JWTClaimsSetVerifier;
+import com.nimbusds.jwt.proc.JWTProcessor;
+import org.apache.nifi.components.state.StateManager;
+import org.apache.nifi.components.state.StateManagerProvider;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.jwt.converter.StandardIssuerJwtDecoder;
+import org.apache.nifi.web.security.jwt.jws.StandardJWSKeySelector;
+import org.apache.nifi.web.security.jwt.key.StandardVerificationKeySelector;
+import 
org.apache.nifi.web.security.jwt.key.service.StandardVerificationKeyService;
+import org.apache.nifi.web.security.jwt.key.service.VerificationKeyService;
+import org.apache.nifi.web.security.jwt.provider.SupportedClaim;
+import org.apache.nifi.web.security.jwt.revocation.JwtRevocationService;
+import org.apache.nifi.web.security.jwt.revocation.JwtRevocationValidator;
+import 
org.apache.nifi.web.security.jwt.revocation.StandardJwtRevocationService;
+import 
org.apache.nifi.web.security.oidc.authentication.AccessTokenDecoderFactory;
+import 
org.apache.nifi.web.security.oidc.authentication.StandardOidcIdTokenDecoderFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistration;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
+import org.springframework.security.oauth2.jwt.JwtValidators;
+import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
+import org.springframework.web.client.RestOperations;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * JSON Web Token Decoder Configuration with component supporting Bearer Token 
parsing and verification
+ */
+@Configuration
+public class JwtDecoderConfiguration {
+    private static final Set<String> REQUIRED_CLAIMS = new 
HashSet<>(Arrays.asList(
+            SupportedClaim.ISSUER.getClaim(),
+            SupportedClaim.SUBJECT.getClaim(),
+            SupportedClaim.AUDIENCE.getClaim(),
+            SupportedClaim.EXPIRATION.getClaim(),
+            SupportedClaim.NOT_BEFORE.getClaim(),
+            SupportedClaim.ISSUED_AT.getClaim(),
+            SupportedClaim.JWT_ID.getClaim(),
+            SupportedClaim.GROUPS.getClaim()
+    ));
+
+    private final NiFiProperties properties;
+
+    private final ClientRegistrationRepository clientRegistrationRepository;
+
+    private final RestOperations oidcRestOperations;
+
+    private final StateManagerProvider stateManagerProvider;
+
+    private final Duration keyRotationPeriod;
+
+    @Autowired
+    public JwtDecoderConfiguration(
+            final NiFiProperties properties,
+            final ClientRegistrationRepository clientRegistrationRepository,
+            @Qualifier("oidcRestOperations")
+            final RestOperations oidcRestOperations,
+            final StateManagerProvider stateManagerProvider
+    ) {
+        this.properties = Objects.requireNonNull(properties, "Application 
properties required");
+        this.clientRegistrationRepository = 
Objects.requireNonNull(clientRegistrationRepository, "Client Registration 
Repository required");
+        this.oidcRestOperations = Objects.requireNonNull(oidcRestOperations, 
"OIDC REST Operations required");
+        this.stateManagerProvider = 
Objects.requireNonNull(stateManagerProvider, "State Manager Provider required");
+        this.keyRotationPeriod = 
properties.getSecurityUserJwsKeyRotationPeriod();
+    }
+
+    /**
+     * JWT Decoder responsible for parsing and verifying Bearer Tokens from 
application or OIDC Identity Provider
+     *
+     * @return JWT Decoder delegating to OpenID Connect JWT Decoder on 
matching Issuer claims
+     */
+    @Bean
+    public JwtDecoder jwtDecoder() {
+        final NimbusJwtDecoder applicationJwtDecoder = new 
NimbusJwtDecoder(jwtProcessor());
+        applicationJwtDecoder.setJwtValidator(jwtTokenValidator());
+        final AccessTokenDecoderFactory accessTokenDecoderFactory = new 
AccessTokenDecoderFactory(properties.getOidcPreferredJwsAlgorithm(), 
oidcRestOperations);
+        return new StandardIssuerJwtDecoder(applicationJwtDecoder, 
accessTokenDecoderFactory, clientRegistrationRepository);
+    }
+
+    /**
+     * JSON Web Token Processor supporting application Bearer Token Decoder 
with configured Signing Key Selector
+     *
+     * @return Application JSON Web Token Processor for verification
+     */
+    @Bean
+    public JWTProcessor<SecurityContext> jwtProcessor() {
+        final DefaultJWTProcessor<SecurityContext> jwtProcessor = new 
DefaultJWTProcessor<>();
+        final JWSKeySelector<SecurityContext> jwsKeySelector = new 
StandardJWSKeySelector<>(verificationKeySelector());
+        jwtProcessor.setJWSKeySelector(jwsKeySelector);
+
+        final JWTClaimsSetVerifier<SecurityContext> claimsSetVerifier = new 
DefaultJWTClaimsVerifier<>(null, REQUIRED_CLAIMS);
+        jwtProcessor.setJWTClaimsSetVerifier(claimsSetVerifier);
+        return jwtProcessor;
+    }
+
+    /**
+     * Token Validator responsible for validating JWT claims after parsing and 
verification based on matching Issuer
+     *
+     * @return Token Validator supporting application Bearer Tokens
+     */
+    @Bean
+    public OAuth2TokenValidator<Jwt> jwtTokenValidator() {
+        final OAuth2TokenValidator<Jwt> jwtRevocationValidator = new 
JwtRevocationValidator(jwtRevocationService());
+        return new DelegatingOAuth2TokenValidator<>(
+                JwtValidators.createDefault(),
+                jwtRevocationValidator
+        );
+    }
+
+    /**
+     * OpenID Connect Identifier Token Decoder with configured JWS Algorithm 
for verification
+     *
+     * @return OpenID Connect Identifier Token Decoder
+     */
+    @Bean
+    public JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
+        final String preferredJwdAlgorithm = 
properties.getOidcPreferredJwsAlgorithm();
+        return new StandardOidcIdTokenDecoderFactory(preferredJwdAlgorithm, 
oidcRestOperations);
+    }
+
+    /**
+     * JWT Revocation Service with backing local State Manager for tracking 
revoked application Bearer Tokens
+     *
+     * @return JWT Revocation Service using local State Manager
+     */
+    @Bean
+    public JwtRevocationService jwtRevocationService() {
+        final StateManager stateManager = 
stateManagerProvider.getStateManager(StandardJwtRevocationService.class.getName());
+        return new StandardJwtRevocationService(stateManager);
+    }
+
+    /**
+     * Verification Key Selector with configured key rotation period
+     *
+     * @return Verification Key Selector supporting JSON Web Token signature 
verification
+     */
+    @Bean
+    public StandardVerificationKeySelector verificationKeySelector() {
+        return new StandardVerificationKeySelector(verificationKeyService(), 
keyRotationPeriod);
+    }
+
+    /**
+     * Verification Key Service using local State Manager for storing public 
keys
+     *
+     * @return Standard Verification Key Service with local State Manager
+     */
+    @Bean
+    public VerificationKeyService verificationKeyService() {
+        final StateManager stateManager = 
stateManagerProvider.getStateManager(StandardVerificationKeyService.class.getName());
+        return new StandardVerificationKeyService(stateManager);
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/OidcSecurityConfiguration.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/OidcSecurityConfiguration.java
index fcc3c87f29..b4f13ec91c 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/OidcSecurityConfiguration.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/configuration/OidcSecurityConfiguration.java
@@ -18,23 +18,16 @@ package org.apache.nifi.web.security.configuration;
 
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
-import okhttp3.OkHttpClient;
 import org.apache.nifi.authorization.util.IdentityMappingUtil;
 import org.apache.nifi.components.state.StateManager;
 import org.apache.nifi.components.state.StateManagerProvider;
 import org.apache.nifi.encrypt.PropertyEncryptor;
-import org.apache.nifi.security.util.SslContextFactory;
-import org.apache.nifi.security.util.StandardTlsConfiguration;
-import org.apache.nifi.security.util.TlsConfiguration;
-import org.apache.nifi.security.util.TlsException;
 import org.apache.nifi.util.FormatUtils;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.security.StandardAuthenticationEntryPoint;
 import org.apache.nifi.web.security.jwt.provider.BearerTokenProvider;
 import org.apache.nifi.web.security.logout.LogoutRequestManager;
-import org.apache.nifi.web.security.oidc.OidcConfigurationException;
 import org.apache.nifi.web.security.oidc.OidcUrlPath;
-import 
org.apache.nifi.web.security.oidc.authentication.StandardOidcIdTokenDecoderFactory;
 import 
org.apache.nifi.web.security.oidc.client.web.AuthorizedClientExpirationCommand;
 import 
org.apache.nifi.web.security.oidc.client.web.OidcBearerTokenRefreshFilter;
 import 
org.apache.nifi.web.security.oidc.client.web.StandardOAuth2AuthorizationRequestResolver;
@@ -45,41 +38,31 @@ import 
org.apache.nifi.web.security.oidc.client.web.converter.StandardAuthorized
 import 
org.apache.nifi.web.security.oidc.client.web.StandardOidcAuthorizedClientRepository;
 import org.apache.nifi.web.security.oidc.logout.OidcLogoutFilter;
 import org.apache.nifi.web.security.oidc.logout.OidcLogoutSuccessHandler;
-import 
org.apache.nifi.web.security.oidc.registration.ClientRegistrationProvider;
-import 
org.apache.nifi.web.security.oidc.registration.DisabledClientRegistrationRepository;
-import 
org.apache.nifi.web.security.oidc.registration.StandardClientRegistrationProvider;
 import 
org.apache.nifi.web.security.oidc.revocation.StandardTokenRevocationResponseClient;
 import 
org.apache.nifi.web.security.oidc.revocation.TokenRevocationResponseClient;
 import org.apache.nifi.web.security.oidc.userinfo.StandardOidcUserService;
 import 
org.apache.nifi.web.security.oidc.web.authentication.OidcAuthenticationSuccessHandler;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.cache.caffeine.CaffeineCache;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.ClientHttpRequestFactory;
-import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
-import org.springframework.http.converter.FormHttpMessageConverter;
-import org.springframework.http.converter.StringHttpMessageConverter;
-import 
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 import org.springframework.security.authentication.AuthenticationManager;
 import 
org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
 import 
org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
 import 
org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
 import 
org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
-import 
org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
 import 
org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider;
 import 
org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
 import 
org.springframework.security.oauth2.client.registration.ClientRegistration;
 import 
org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
-import 
org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
 import 
org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
 import 
org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
 import 
org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter;
 import 
org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
 import 
org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
 import 
org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
-import 
org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
 import 
org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
@@ -89,15 +72,9 @@ import 
org.springframework.security.web.authentication.session.NullAuthenticated
 import org.springframework.security.web.savedrequest.NullRequestCache;
 import org.springframework.security.web.savedrequest.RequestCache;
 import org.springframework.web.client.RestOperations;
-import org.springframework.web.client.RestTemplate;
 
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
 import java.time.Duration;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
@@ -111,10 +88,6 @@ public class OidcSecurityConfiguration {
 
     private static final long AUTHORIZATION_REQUEST_CACHE_SIZE = 1000;
 
-    private static final Duration DEFAULT_SOCKET_TIMEOUT = 
Duration.ofSeconds(5);
-
-    private static final String NIFI_TRUSTSTORE_STRATEGY = "NIFI";
-
     private static final RequestCache nullRequestCache = new 
NullRequestCache();
 
     private final Duration keyRotationPeriod;
@@ -129,8 +102,14 @@ public class OidcSecurityConfiguration {
 
     private final BearerTokenResolver bearerTokenResolver;
 
+    private final ClientRegistrationRepository clientRegistrationRepository;
+
     private final JwtDecoder jwtDecoder;
 
+    private final JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory;
+
+    private final RestOperations oidcRestOperations;
+
     private final LogoutRequestManager logoutRequestManager;
 
     @Autowired
@@ -140,7 +119,11 @@ public class OidcSecurityConfiguration {
             final PropertyEncryptor propertyEncryptor,
             final BearerTokenProvider bearerTokenProvider,
             final BearerTokenResolver bearerTokenResolver,
+            final ClientRegistrationRepository clientRegistrationRepository,
             final JwtDecoder jwtDecoder,
+            final JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory,
+            @Qualifier("oidcRestOperations")
+            final RestOperations oidcRestOperations,
             final LogoutRequestManager logoutRequestManager
     ) {
         this.properties = Objects.requireNonNull(properties, "Properties 
required");
@@ -148,7 +131,10 @@ public class OidcSecurityConfiguration {
         this.propertyEncryptor = Objects.requireNonNull(propertyEncryptor, 
"Property Encryptor required");
         this.bearerTokenProvider = Objects.requireNonNull(bearerTokenProvider, 
"Bearer Token Provider required");
         this.bearerTokenResolver = Objects.requireNonNull(bearerTokenResolver, 
"Bearer Token Resolver required");
+        this.clientRegistrationRepository = 
Objects.requireNonNull(clientRegistrationRepository, "Registration Repository 
required");
         this.jwtDecoder = Objects.requireNonNull(jwtDecoder, "JWT Decoder 
required");
+        this.idTokenDecoderFactory = 
Objects.requireNonNull(idTokenDecoderFactory, "ID Token Decoder Factory 
required");
+        this.oidcRestOperations = Objects.requireNonNull(oidcRestOperations, 
"OIDC REST Operations required");
         this.logoutRequestManager = 
Objects.requireNonNull(logoutRequestManager, "Logout Request Manager required");
         this.keyRotationPeriod = 
properties.getSecurityUserJwsKeyRotationPeriod();
     }
@@ -163,7 +149,7 @@ public class OidcSecurityConfiguration {
     @Bean
     public OAuth2AuthorizationCodeGrantFilter 
oAuth2AuthorizationCodeGrantFilter(final AuthenticationManager 
authenticationManager) {
         final OAuth2AuthorizationCodeGrantFilter filter = new 
OAuth2AuthorizationCodeGrantFilter(
-                clientRegistrationRepository(),
+                clientRegistrationRepository,
                 authorizedClientRepository(),
                 authenticationManager
         );
@@ -180,7 +166,7 @@ public class OidcSecurityConfiguration {
      */
     @Bean
     public OAuth2AuthorizationRequestRedirectFilter 
oAuth2AuthorizationRequestRedirectFilter() {
-        final StandardOAuth2AuthorizationRequestResolver 
authorizationRequestResolver = new 
StandardOAuth2AuthorizationRequestResolver(clientRegistrationRepository());
+        final StandardOAuth2AuthorizationRequestResolver 
authorizationRequestResolver = new 
StandardOAuth2AuthorizationRequestResolver(clientRegistrationRepository);
         final OAuth2AuthorizationRequestRedirectFilter filter = new 
OAuth2AuthorizationRequestRedirectFilter(authorizationRequestResolver);
         
filter.setAuthorizationRequestRepository(authorizationRequestRepository());
         filter.setRequestCache(nullRequestCache);
@@ -197,7 +183,7 @@ public class OidcSecurityConfiguration {
     @Bean
     public OAuth2LoginAuthenticationFilter 
oAuth2LoginAuthenticationFilter(final AuthenticationManager 
authenticationManager, final StandardAuthenticationEntryPoint 
authenticationEntryPoint) {
         final OAuth2LoginAuthenticationFilter filter = new 
OAuth2LoginAuthenticationFilter(
-                clientRegistrationRepository(),
+                clientRegistrationRepository,
                 authorizedClientRepository(),
                 OidcUrlPath.CALLBACK.getPath()
         );
@@ -222,7 +208,7 @@ public class OidcSecurityConfiguration {
     @Bean
     public OidcBearerTokenRefreshFilter oidcBearerTokenRefreshFilter() {
         final DefaultRefreshTokenTokenResponseClient 
refreshTokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
-        refreshTokenResponseClient.setRestOperations(oidcRestOperations());
+        refreshTokenResponseClient.setRestOperations(oidcRestOperations);
 
         final String refreshWindowProperty = 
properties.getOidcTokenRefreshWindow();
         final double refreshWindowSeconds = 
FormatUtils.getPreciseTimeDuration(refreshWindowProperty, TimeUnit.SECONDS);
@@ -257,7 +243,7 @@ public class OidcSecurityConfiguration {
     public LogoutSuccessHandler oidcLogoutSuccessHandler() {
         return new OidcLogoutSuccessHandler(
                 logoutRequestManager,
-                clientRegistrationRepository(),
+                clientRegistrationRepository,
                 authorizedClientRepository(),
                 tokenRevocationResponseClient()
         );
@@ -274,7 +260,7 @@ public class OidcSecurityConfiguration {
                 accessTokenResponseClient(),
                 oidcUserService()
         );
-        provider.setJwtDecoderFactory(idTokenDecoderFactory());
+        provider.setJwtDecoderFactory(idTokenDecoderFactory);
         return provider;
     }
 
@@ -286,7 +272,7 @@ public class OidcSecurityConfiguration {
     @Bean
     public 
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> 
accessTokenResponseClient() {
         final DefaultAuthorizationCodeTokenResponseClient 
accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
-        accessTokenResponseClient.setRestOperations(oidcRestOperations());
+        accessTokenResponseClient.setRestOperations(oidcRestOperations);
         return accessTokenResponseClient;
     }
 
@@ -302,7 +288,7 @@ public class OidcSecurityConfiguration {
                 IdentityMappingUtil.getIdentityMappings(properties)
         );
         final DefaultOAuth2UserService userService = new 
DefaultOAuth2UserService();
-        userService.setRestOperations(oidcRestOperations());
+        userService.setRestOperations(oidcRestOperations);
         oidcUserService.setOauth2UserService(userService);
         return oidcUserService;
     }
@@ -344,7 +330,7 @@ public class OidcSecurityConfiguration {
      */
     @Bean
     public AuthorizedClientConverter authorizedClientConverter() {
-        return new StandardAuthorizedClientConverter(propertyEncryptor, 
clientRegistrationRepository());
+        return new StandardAuthorizedClientConverter(propertyEncryptor, 
clientRegistrationRepository);
     }
 
     /**
@@ -362,17 +348,6 @@ public class OidcSecurityConfiguration {
         return new StandardAuthorizationRequestRepository(cache);
     }
 
-    /**
-     * OpenID Connect Identifier Token Decoder with configured JWS Algorithm 
for verification
-     *
-     * @return OpenID Connect Identifier Token Decoder
-     */
-    @Bean
-    public JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
-        final String preferredJwdAlgorithm = 
properties.getOidcPreferredJwsAlgorithm();
-        return new StandardOidcIdTokenDecoderFactory(preferredJwdAlgorithm, 
oidcRestOperations());
-    }
-
     /**
      * Token Revocation Response Client responsible for transmitting Refresh 
Token revocation requests to the Provider
      *
@@ -380,95 +355,7 @@ public class OidcSecurityConfiguration {
      */
     @Bean
     public TokenRevocationResponseClient tokenRevocationResponseClient() {
-        return new StandardTokenRevocationResponseClient(oidcRestOperations(), 
clientRegistrationRepository());
-    }
-
-    /**
-     * Client Registration Repository for OpenID Connect Discovery
-     *
-     * @return Client Registration Repository
-     */
-    @Bean
-    public ClientRegistrationRepository clientRegistrationRepository() {
-        final ClientRegistrationRepository clientRegistrationRepository;
-        if (properties.isOidcEnabled()) {
-            final ClientRegistrationProvider clientRegistrationProvider = new 
StandardClientRegistrationProvider(properties, oidcRestOperations());
-            final ClientRegistration clientRegistration = 
clientRegistrationProvider.getClientRegistration();
-            clientRegistrationRepository = new 
InMemoryClientRegistrationRepository(clientRegistration);
-        } else {
-            clientRegistrationRepository = new 
DisabledClientRegistrationRepository();
-        }
-        return clientRegistrationRepository;
-    }
-
-    /**
-     * OpenID Connect REST Operations for communication with Authorization 
Servers
-     *
-     * @return REST Operations
-     */
-    @Bean
-    public RestOperations oidcRestOperations() {
-        final RestTemplate restTemplate = new 
RestTemplate(oidcClientHttpRequestFactory());
-        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
-        restTemplate.setMessageConverters(
-                Arrays.asList(
-                        new FormHttpMessageConverter(),
-                        new OAuth2AccessTokenResponseHttpMessageConverter(),
-                        new StringHttpMessageConverter(),
-                        new MappingJackson2HttpMessageConverter()
-                )
-        );
-        return restTemplate;
-    }
-
-    /**
-     * OpenID Connect Client HTTP Request Factory for communication with 
Authorization Servers
-     *
-     * @return Client HTTP Request Factory
-     */
-    @Bean
-    public ClientHttpRequestFactory oidcClientHttpRequestFactory() {
-        final OkHttpClient httpClient = getHttpClient();
-        return new OkHttp3ClientHttpRequestFactory(httpClient);
-    }
-
-    private OkHttpClient getHttpClient() {
-        final Duration connectTimeout = 
getTimeout(properties.getOidcConnectTimeout());
-        final Duration readTimeout = 
getTimeout(properties.getOidcReadTimeout());
-
-        final OkHttpClient.Builder builder = new OkHttpClient.Builder()
-                .connectTimeout(connectTimeout)
-                .readTimeout(readTimeout);
-
-        if 
(NIFI_TRUSTSTORE_STRATEGY.equals(properties.getOidcClientTruststoreStrategy())) 
{
-            setSslSocketFactory(builder);
-        }
-
-        return builder.build();
-    }
-
-    private Duration getTimeout(final String timeoutExpression) {
-        try {
-            final double duration = 
FormatUtils.getPreciseTimeDuration(timeoutExpression, TimeUnit.MILLISECONDS);
-            final long rounded = Math.round(duration);
-            return Duration.ofMillis(rounded);
-        } catch (final RuntimeException e) {
-            return DEFAULT_SOCKET_TIMEOUT;
-        }
-    }
-
-    private void setSslSocketFactory(final OkHttpClient.Builder builder) {
-        final TlsConfiguration tlsConfiguration = 
StandardTlsConfiguration.fromNiFiProperties(properties);
-
-        try {
-            final X509TrustManager trustManager = 
Objects.requireNonNull(SslContextFactory.getX509TrustManager(tlsConfiguration), 
"TrustManager required");
-            final TrustManager[] trustManagers = new TrustManager[] { 
trustManager };
-            final SSLContext sslContext = 
Objects.requireNonNull(SslContextFactory.createSslContext(tlsConfiguration, 
trustManagers), "SSLContext required");
-            final SSLSocketFactory sslSocketFactory = 
sslContext.getSocketFactory();
-            builder.sslSocketFactory(sslSocketFactory, trustManager);
-        } catch (final TlsException e) {
-            throw new OidcConfigurationException("OpenID Connect HTTP TLS 
configuration failed", e);
-        }
+        return new StandardTokenRevocationResponseClient(oidcRestOperations, 
clientRegistrationRepository);
     }
 
     private OidcAuthenticationSuccessHandler getAuthenticationSuccessHandler() 
{
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/converter/StandardIssuerJwtDecoder.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/converter/StandardIssuerJwtDecoder.java
new file mode 100644
index 0000000000..288a2e2199
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/converter/StandardIssuerJwtDecoder.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.jwt.converter;
+
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.JWTParser;
+import com.nimbusds.jwt.PlainJWT;
+import org.apache.nifi.web.security.oidc.client.web.OidcRegistrationProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistration;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.jwt.BadJwtException;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
+import org.springframework.security.oauth2.jwt.JwtException;
+
+import java.util.Objects;
+
+/**
+ * JSON Web Token Decoder capable of delegating to specific Decoder based on 
Issuer claims for OpenID Connect Clients
+ */
+public class StandardIssuerJwtDecoder implements JwtDecoder {
+    private static final Logger logger = 
LoggerFactory.getLogger(StandardIssuerJwtDecoder.class);
+
+    private final JwtDecoder applicationJwtDecoder;
+
+    private final ClientRegistration clientRegistration;
+
+    private final JwtDecoder clientRegistrationJwtDecoder;
+
+    /**
+     * Standard constructor with default application JWT Decoder and factory 
providing OpenID Connect JWT Decoder
+     * @param applicationJwtDecoder Default JSON Web Token Decoder used when 
matching Issuer claim not found
+     * @param jwtDecoderFactory OpenID Connect JWT Decoder Factory
+     * @param clientRegistrationRepository OpenID Connect Client Registration 
Repository
+     */
+    public StandardIssuerJwtDecoder(
+            final JwtDecoder applicationJwtDecoder,
+            final JwtDecoderFactory<ClientRegistration> jwtDecoderFactory,
+            final ClientRegistrationRepository clientRegistrationRepository
+    ) {
+        this.applicationJwtDecoder = 
Objects.requireNonNull(applicationJwtDecoder, "Application JWT Decoder 
required");
+        this.clientRegistration = 
clientRegistrationRepository.findByRegistrationId(OidcRegistrationProperty.REGISTRATION_ID.getProperty());
+        if (clientRegistration == null) {
+            logger.debug("OIDC Client Registration not configured for JWT 
Decoder");
+            this.clientRegistrationJwtDecoder = null;
+        } else {
+            Objects.requireNonNull(jwtDecoderFactory, "JWT Decoder Factory 
required");
+            this.clientRegistrationJwtDecoder = 
jwtDecoderFactory.createDecoder(clientRegistration);
+        }
+    }
+
+    /**
+     * Decode JSON Web Token using OpenID Connect Decoder for matched Issuer 
or default application JWT Decoder
+     *
+     * @param token JSON Web Token serialized and encoded
+     * @return Decoded and validated JSON Web Token
+     * @throws JwtException Thrown on malformed tokens or decoding failures
+     */
+    @Override
+    public Jwt decode(final String token) throws JwtException {
+        final Jwt decoded;
+
+        if (clientRegistration == null) {
+            decoded = applicationJwtDecoder.decode(token);
+        } else {
+            final JWT parsed = parse(token);
+            final String tokenIssuer = getTokenIssuer(parsed);
+
+            if (isIssuerRegistered(tokenIssuer)) {
+                decoded = clientRegistrationJwtDecoder.decode(token);
+            } else {
+                decoded = applicationJwtDecoder.decode(token);
+            }
+        }
+
+        return decoded;
+    }
+
+    private boolean isIssuerRegistered(final String tokenIssuer) {
+        final boolean registered;
+
+        if (clientRegistration == null) {
+            registered = false;
+        } else {
+            final ClientRegistration.ProviderDetails providerDetails = 
clientRegistration.getProviderDetails();
+            final String issuerUri = providerDetails.getIssuerUri();
+            registered = issuerUri.equals(tokenIssuer);
+        }
+
+        return registered;
+    }
+
+    private String getTokenIssuer(final JWT parsed) {
+        try {
+            final JWTClaimsSet claimsSet = parsed.getJWTClaimsSet();
+            final String issuer = claimsSet.getIssuer();
+            if (issuer == null || issuer.isEmpty()) {
+                throw new BadJwtException("Token Issuer claim not found");
+            }
+            return issuer;
+        } catch (final Exception e) {
+            throw new BadJwtException("Token Issuer parsing failed", e);
+        }
+    }
+
+    private JWT parse(final String token) {
+        if (token == null || token.isEmpty()) {
+            throw new BadJwtException("Token not found");
+        }
+
+        try {
+            final JWT parsed = JWTParser.parse(token);
+            if (parsed instanceof PlainJWT) {
+                throw new BadJwtException("Unsigned Token not supported");
+            }
+            return parsed;
+        } catch (final Exception e) {
+            throw new BadJwtException("Token parsing failed", e);
+        }
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/authentication/AccessTokenDecoderFactory.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/authentication/AccessTokenDecoderFactory.java
new file mode 100644
index 0000000000..9de1f24ed6
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/authentication/AccessTokenDecoderFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.oidc.authentication;
+
+import 
org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtValidators;
+import org.springframework.web.client.RestOperations;
+
+/**
+ * Access Token Decoder Factory provides Token Validation based on expected 
Access Token claims instead of ID Token Claims
+ */
+public class AccessTokenDecoderFactory extends 
StandardOidcIdTokenDecoderFactory {
+    /**
+     * Standard constructor with optional JWS Algorithm and required REST 
Operations for retrieving JSON Web Keys
+     *
+     * @param preferredJwsAlgorithm Preferred JSON Web Signature Algorithm 
default to RS256 when not provided
+     * @param restOperations REST Operations required for retrieving JSON Web 
Key Set with Signature Algorithms
+     */
+    public AccessTokenDecoderFactory(final String preferredJwsAlgorithm, final 
RestOperations restOperations) {
+        super(preferredJwsAlgorithm, restOperations);
+    }
+
+    /**
+     * Get OAuth2 Token Validator with Timestamp and Issuer Validators
+     *
+     * @param clientRegistration Client Registration
+     * @return OAuth2 Token Validator with Timestamp and Issuer Validators
+     */
+    @Override
+    protected OAuth2TokenValidator<Jwt> getTokenValidator(final 
ClientRegistration clientRegistration) {
+        final String issuerUri = 
clientRegistration.getProviderDetails().getIssuerUri();
+        return JwtValidators.createDefaultWithIssuer(issuerUri);
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/authentication/StandardOidcIdTokenDecoderFactory.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/authentication/StandardOidcIdTokenDecoderFactory.java
index ed8ff9ee70..22389f468f 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/authentication/StandardOidcIdTokenDecoderFactory.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/oidc/authentication/StandardOidcIdTokenDecoderFactory.java
@@ -104,6 +104,19 @@ public class StandardOidcIdTokenDecoderFactory implements 
JwtDecoderFactory<Clie
         });
     }
 
+    /**
+     * Get OAuth2 Token Validator based on Spring Security 
DefaultOidcIdTokenValidatorFactory
+     *
+     * @param clientRegistration Client Registration
+     * @return OAuth2 Token Validator with Timestamp and OpenID Connect ID 
Token Validators
+     */
+    protected OAuth2TokenValidator<Jwt> getTokenValidator(final 
ClientRegistration clientRegistration) {
+        return new DelegatingOAuth2TokenValidator<>(
+                new JwtTimestampValidator(),
+                new OidcIdTokenValidator(clientRegistration)
+        );
+    }
+
     private NimbusJwtDecoder buildDecoder(final ClientRegistration 
clientRegistration) {
         final NimbusJwtDecoder decoder;
 
@@ -143,19 +156,6 @@ public class StandardOidcIdTokenDecoderFactory implements 
JwtDecoderFactory<Clie
         return decoder;
     }
 
-    /**
-     * Get OAuth2 Token Validator based on Spring Security 
DefaultOidcIdTokenValidatorFactory
-     *
-     * @param clientRegistration Client Registration
-     * @return OAuth2 Token Validator with Timestamp and OpenID Connect ID 
Token Validators
-     */
-    private OAuth2TokenValidator<Jwt> getTokenValidator(final 
ClientRegistration clientRegistration) {
-        return new DelegatingOAuth2TokenValidator<>(
-                new JwtTimestampValidator(),
-                new OidcIdTokenValidator(clientRegistration)
-        );
-    }
-
     private JwsAlgorithm getJwsAlgorithm(final String preferredJwsAlgorithm) {
         final JwsAlgorithm jwsAlgorithm;
 
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/converter/StandardIssuerJwtDecoderTest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/converter/StandardIssuerJwtDecoderTest.java
new file mode 100644
index 0000000000..b49a8f1ea9
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/jwt/converter/StandardIssuerJwtDecoderTest.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.security.jwt.converter;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistration;
+import 
org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.jwt.BadJwtException;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
+
+import java.time.Duration;
+import java.time.Instant;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class StandardIssuerJwtDecoderTest {
+    private static final String HEADER_PAYLOAD = 
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJuaWZpIiwiaXNzIjoiaHR0cHM6Ly9uaWZpLmFwYWNoZS5vcmcifQ";
+
+    private static final String TOKEN_VALUE = 
String.format("%s.cqEFAyICNyF5kDbYtsSgA73auanainaO44_q1GEDXeQ", HEADER_PAYLOAD);
+
+    private static final String ISSUER = "https://nifi.apache.org";;
+
+    private static final String LOCALHOST_ISSUER = "https://localhost";;
+
+    private static final String TYPE_FIELD = "typ";
+
+    private static final String JWT_TYPE = "JWT";
+
+    @Mock
+    private JwtDecoder applicationJwtDecoder;
+
+    @Mock
+    private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory;
+
+    @Mock
+    private ClientRegistrationRepository clientRegistrationRepository;
+
+    @Mock
+    private ClientRegistration clientRegistration;
+
+    @Mock
+    private ClientRegistration.ProviderDetails providerDetails;
+
+    @Mock
+    private JwtDecoder clientRegistrationDecoder;
+
+    @Test
+    void testClientRegistrationNotConfigured() {
+        
when(clientRegistrationRepository.findByRegistrationId(anyString())).thenReturn(null);
+        final StandardIssuerJwtDecoder decoder = new 
StandardIssuerJwtDecoder(applicationJwtDecoder, jwtDecoderFactory, 
clientRegistrationRepository);
+
+        final Jwt jwt = getJwt();
+        when(applicationJwtDecoder.decode(eq(TOKEN_VALUE))).thenReturn(jwt);
+
+        final Jwt decoded = decoder.decode(TOKEN_VALUE);
+
+        assertEquals(jwt, decoded);
+    }
+
+    @Test
+    void testClientRegistrationConfiguredIssuerFound() {
+        setClientRegistration();
+        final StandardIssuerJwtDecoder decoder = new 
StandardIssuerJwtDecoder(applicationJwtDecoder, jwtDecoderFactory, 
clientRegistrationRepository);
+
+        
when(clientRegistration.getProviderDetails()).thenReturn(providerDetails);
+        when(providerDetails.getIssuerUri()).thenReturn(ISSUER);
+        final Jwt jwt = getJwt();
+        
when(clientRegistrationDecoder.decode(eq(TOKEN_VALUE))).thenReturn(jwt);
+
+        final Jwt decoded = decoder.decode(TOKEN_VALUE);
+
+        assertEquals(jwt, decoded);
+    }
+
+    @Test
+    void testClientRegistrationConfiguredIssuerNotFound() {
+        setClientRegistration();
+        final StandardIssuerJwtDecoder decoder = new 
StandardIssuerJwtDecoder(applicationJwtDecoder, jwtDecoderFactory, 
clientRegistrationRepository);
+
+        
when(clientRegistration.getProviderDetails()).thenReturn(providerDetails);
+        when(providerDetails.getIssuerUri()).thenReturn(LOCALHOST_ISSUER);
+        final Jwt jwt = getJwt();
+        when(applicationJwtDecoder.decode(eq(TOKEN_VALUE))).thenReturn(jwt);
+
+        final Jwt decoded = decoder.decode(TOKEN_VALUE);
+
+        assertEquals(jwt, decoded);
+    }
+
+    @Test
+    void testClientRegistrationConfiguredTokenNotFound() {
+        setClientRegistration();
+        final StandardIssuerJwtDecoder decoder = new 
StandardIssuerJwtDecoder(applicationJwtDecoder, jwtDecoderFactory, 
clientRegistrationRepository);
+
+        assertThrows(BadJwtException.class, () -> decoder.decode(null));
+    }
+
+    @Test
+    void testClientRegistrationConfiguredTokenNotValid() {
+        setClientRegistration();
+        final StandardIssuerJwtDecoder decoder = new 
StandardIssuerJwtDecoder(applicationJwtDecoder, jwtDecoderFactory, 
clientRegistrationRepository);
+
+        assertThrows(BadJwtException.class, () -> 
decoder.decode(String.class.getSimpleName()));
+    }
+
+    private void setClientRegistration() {
+        
when(clientRegistrationRepository.findByRegistrationId(anyString())).thenReturn(clientRegistration);
+        
when(jwtDecoderFactory.createDecoder(eq(clientRegistration))).thenReturn(clientRegistrationDecoder);
+    }
+
+    private Jwt getJwt() {
+        return Jwt.withTokenValue(TOKEN_VALUE)
+                .header(TYPE_FIELD, JWT_TYPE)
+                .issuedAt(Instant.now())
+                .expiresAt(Instant.now().plus(Duration.ofHours(1)))
+                .build();
+    }
+}

Reply via email to