You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by pr...@apache.org on 2022/04/29 12:37:09 UTC

[ranger] 02/03: RANGER-3727: Create common module for handling authentication

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

pradeep pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git

commit fb53a3dc0022020e68e4ae9edb316a58f7d0a800
Author: Kishor Gollapalliwar <ki...@gmail.com>
AuthorDate: Thu Apr 28 12:32:51 2022 +0530

    RANGER-3727: Create common module for handling authentication
    
    Signed-off-by: pradeep <pr...@apache.org>
---
 pom.xml                                            |   2 +
 ranger-authn/.gitignore                            |   1 +
 ranger-authn/pom.xml                               |  67 +++++
 .../apache/ranger/authz/handler/RangerAuth.java    |  67 +++++
 .../ranger/authz/handler/RangerAuthHandler.java    |  28 ++
 .../handler/jwt/RangerDefaultJwtAuthHandler.java   |  95 +++++++
 .../authz/handler/jwt/RangerJwtAuthHandler.java    | 313 +++++++++++++++++++++
 7 files changed, 573 insertions(+)

diff --git a/pom.xml b/pom.xml
index ec8c6751d..56c82dc82 100644
--- a/pom.xml
+++ b/pom.xml
@@ -310,6 +310,7 @@
                 <module>ranger-kylin-plugin-shim</module>
                 <module>plugin-elasticsearch</module>
                 <module>ranger-elasticsearch-plugin-shim</module>
+                <module>ranger-authn</module>
                 <!--
                    'distro' should be the last module. If a module gets inserted after
                    ranger-elasticsearch-plugin-shim, make sure to update dependency in distro/pom.xml
@@ -607,6 +608,7 @@
                 <module>ranger-presto-plugin-shim</module>
                 <module>plugin-elasticsearch</module>
                 <module>ranger-elasticsearch-plugin-shim</module>
+                <module>ranger-authn</module>
                 <!--
                    'distro' should be the last module. If a module gets inserted after
                    ranger-elasticsearch-plugin-shim, make sure to update dependency in distro/pom.xml
diff --git a/ranger-authn/.gitignore b/ranger-authn/.gitignore
new file mode 100644
index 000000000..b83d22266
--- /dev/null
+++ b/ranger-authn/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/ranger-authn/pom.xml b/ranger-authn/pom.xml
new file mode 100644
index 000000000..827fd0746
--- /dev/null
+++ b/ranger-authn/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>ranger-authn</artifactId>
+    <name>ranger-authn</name>
+    <description>Ranger Authentication module</description>
+    <packaging>jar</packaging>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+    <parent>
+        <groupId>org.apache.ranger</groupId>
+        <artifactId>ranger</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-common</artifactId>
+            <version>${hadoop.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.netty</groupId>
+                    <artifactId>netty-all</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>${commons.lang.version}</version>
+        </dependency>
+
+    <!-- JWT -->
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+            <version>${nimbus-jose-jwt.version}</version>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuth.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuth.java
new file mode 100644
index 000000000..b0757ae7d
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuth.java
@@ -0,0 +1,67 @@
+/*
+ * 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.ranger.authz.handler;
+
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+
+public class RangerAuth {
+    public static enum AUTH_TYPE {
+        JWT_JWKS("JWT-JWKS");
+
+        private final String authType;
+
+        private AUTH_TYPE(String authType) {
+            this.authType = authType;
+        }
+    }
+
+    private String    userName;
+    private AUTH_TYPE type;
+    private boolean   isAuthenticated;
+
+    public RangerAuth(final AuthenticationToken authenticationToken, AUTH_TYPE type) {
+        this.userName        = authenticationToken.getName();
+        this.isAuthenticated = true;
+        this.type            = type;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public AUTH_TYPE getType() {
+        return type;
+    }
+
+    public void setType(AUTH_TYPE type) {
+        this.type = type;
+    }
+
+    public boolean isAuthenticated() {
+        return isAuthenticated;
+    }
+
+    public void setAuthenticated(boolean isAuthenticated) {
+        this.isAuthenticated = isAuthenticated;
+    }
+}
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuthHandler.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuthHandler.java
new file mode 100644
index 000000000..4dcc37cc7
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/RangerAuthHandler.java
@@ -0,0 +1,28 @@
+/*
+ * 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.ranger.authz.handler;
+
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface RangerAuthHandler {
+    void initialize(final Properties config) throws Exception;
+    RangerAuth authenticate(final HttpServletRequest request);
+}
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerDefaultJwtAuthHandler.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerDefaultJwtAuthHandler.java
new file mode 100644
index 000000000..c0d5b8d9a
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerDefaultJwtAuthHandler.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ranger.authz.handler.jwt;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.ranger.authz.handler.RangerAuth;
+
+import com.nimbusds.jose.proc.JWSKeySelector;
+import com.nimbusds.jose.proc.SecurityContext;
+import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
+import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier;
+import com.nimbusds.jwt.proc.DefaultJWTProcessor;
+import com.nimbusds.jwt.proc.JWTClaimsSetVerifier;
+
+/**
+ * Default implementation of Ranger JWT authentication
+ *
+ */
+public class RangerDefaultJwtAuthHandler extends RangerJwtAuthHandler {
+
+    protected static final String AUTHORIZATION_HEADER = "Authorization";
+
+    @Override
+    public ConfigurableJWTProcessor<SecurityContext> getJwtProcessor(JWSKeySelector<SecurityContext> keySelector) {
+        ConfigurableJWTProcessor<SecurityContext> jwtProcessor   = new DefaultJWTProcessor<>();
+        JWTClaimsSetVerifier<SecurityContext>     claimsVerifier = new DefaultJWTClaimsVerifier<>();
+
+        jwtProcessor.setJWSKeySelector(keySelector);
+        jwtProcessor.setJWTClaimsSetVerifier(claimsVerifier);
+
+        return jwtProcessor;
+    }
+
+    @Override
+    public RangerAuth authenticate(HttpServletRequest httpServletRequest) {
+        RangerAuth rangerAuth       = null;
+        String     jwtAuthHeaderStr = getJwtAuthHeader(httpServletRequest);
+        String     jwtCookieStr     = StringUtils.isBlank(jwtAuthHeaderStr) ? getJwtCookie(httpServletRequest) : null;
+
+        AuthenticationToken authenticationToken = authenticate(jwtAuthHeaderStr, jwtCookieStr);
+
+        if (authenticationToken != null) {
+            rangerAuth = new RangerAuth(authenticationToken, RangerAuth.AUTH_TYPE.JWT_JWKS);
+        }
+
+        return rangerAuth;
+    }
+
+    public static boolean canAuthenticateRequest(final ServletRequest request) {
+        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        String     jwtAuthHeaderStr = getJwtAuthHeader(httpServletRequest);
+        String     jwtCookieStr     = StringUtils.isBlank(jwtAuthHeaderStr) ? getJwtCookie(httpServletRequest) : null;
+
+        return shouldProceedAuth(jwtAuthHeaderStr, jwtCookieStr);
+    }
+
+    public static String getJwtAuthHeader(final HttpServletRequest httpServletRequest) {
+        return httpServletRequest.getHeader(AUTHORIZATION_HEADER);
+    }
+
+    public static String getJwtCookie(final HttpServletRequest httpServletRequest) {
+        String jwtCookieStr = null;
+        Cookie[] cookies = httpServletRequest.getCookies();
+        if (cookies != null) {
+            for (Cookie cookie : cookies) {
+                if (cookieName.equals(cookie.getName())) {
+                    jwtCookieStr = cookie.getName() + "=" + cookie.getValue();
+                    break;
+                }
+            }
+        }
+        return jwtCookieStr;
+    }
+}
diff --git a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
new file mode 100644
index 000000000..0973b42de
--- /dev/null
+++ b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
@@ -0,0 +1,313 @@
+/*
+ * 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.ranger.authz.handler.jwt;
+
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.authentication.util.CertificateUtil;
+import org.apache.ranger.authz.handler.RangerAuthHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSObject;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.jwk.source.RemoteJWKSet;
+import com.nimbusds.jose.proc.BadJOSEException;
+import com.nimbusds.jose.proc.JWSKeySelector;
+import com.nimbusds.jose.proc.JWSVerificationKeySelector;
+import com.nimbusds.jose.proc.SecurityContext;
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
+
+public abstract class RangerJwtAuthHandler implements RangerAuthHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(RangerJwtAuthHandler.class);
+
+    private JWSVerifier        verifier            = null;
+    private String             jwksProviderUrl     = null;
+    public static final String TYPE                = "ranger-jwt";        // Constant that identifies the authentication mechanism.
+    public static final String KEY_PROVIDER_URL    = "jwks.provider-url"; // JWKS provider URL
+    public static final String KEY_JWT_PUBLIC_KEY  = "jwt.public-key";    // JWT token provider public key
+    public static final String KEY_JWT_COOKIE_NAME = "jwt.cookie-name";   // JWT cookie name
+    public static final String KEY_JWT_AUDIENCES   = "jwt.audiences";
+    public static final String JWT_AUTHZ_PREFIX    = "Bearer ";
+
+    protected List<String>               audiences = null;
+    protected JWKSource<SecurityContext> keySource = null;
+
+    protected static String cookieName = "hadoop-jwt";
+
+    @Override
+    public void initialize(final Properties config) throws Exception {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("===>>> RangerJwtAuthHandler.initialize()");
+        }
+
+        // mandatory configurations
+        jwksProviderUrl = config.getProperty(KEY_PROVIDER_URL);
+        if (!StringUtils.isBlank(jwksProviderUrl)) {
+	    keySource = new RemoteJWKSet<>(new URL(jwksProviderUrl));
+        }
+
+        // optional configurations
+        String pemPublicKey = config.getProperty(KEY_JWT_PUBLIC_KEY);
+
+        // setup JWT provider public key if configured
+        if (StringUtils.isNotBlank(pemPublicKey)) {
+            verifier = new RSASSAVerifier(CertificateUtil.parseRSAPublicKey(pemPublicKey));
+        } else if (StringUtils.isBlank(jwksProviderUrl)) {
+	    throw new Exception("RangerJwtAuthHandler: Mandatory configs ('jwks.provider-url' & 'jwt.public-key') are missing, must provide atleast one.");
+	}
+
+        // setup custom cookie name if configured
+        String customCookieName = config.getProperty(KEY_JWT_COOKIE_NAME);
+        if (customCookieName != null) {
+            cookieName = customCookieName;
+        }
+
+        // setup audiences if configured
+        String audiencesStr = config.getProperty(KEY_JWT_AUDIENCES);
+        if (StringUtils.isNotBlank(audiencesStr)) {
+            audiences = Arrays.asList(audiencesStr.split(","));
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<<<=== RangerJwtAuthHandler.initialize()");
+        }
+    }
+
+    protected AuthenticationToken authenticate(final String jwtAuthHeader, final String jwtCookie) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("===>>> RangerJwtAuthHandler.authenticate()");
+        }
+
+        AuthenticationToken token = null;
+        if (shouldProceedAuth(jwtAuthHeader, jwtCookie)) {
+            String serializedJWT = getJWT(jwtAuthHeader, jwtCookie);
+
+            if (StringUtils.isNotBlank(serializedJWT)) {
+                try {
+                    final SignedJWT jwtToken = SignedJWT.parse(serializedJWT);
+                    boolean         valid    = validateToken(jwtToken);
+                    if (valid) {
+                        final String userName = jwtToken.getJWTClaimsSet().getSubject();
+                        LOG.info("Issuing AuthenticationToken for user: [{}]", userName);
+                        token = new AuthenticationToken(userName, userName, TYPE);
+                    } else {
+                        LOG.warn("Validation failed for JWT token: [{}] ", jwtToken.serialize());
+                    }
+                } catch (ParseException pe) {
+                    LOG.warn("Unable to parse the JWT token", pe);
+                }
+            } else {
+                LOG.warn("JWT token not found.");
+            }
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<<<=== RangerJwtAuthHandler.authenticate()");
+        }
+
+        return token;
+    }
+
+    protected String getJWT(final String jwtAuthHeader, final String jwtCookie) {
+        String serializedJWT = null;
+
+        // try to fetch from AUTH header
+        if (StringUtils.isNotBlank(jwtAuthHeader) && jwtAuthHeader.startsWith(JWT_AUTHZ_PREFIX)) {
+            serializedJWT = jwtAuthHeader.substring(JWT_AUTHZ_PREFIX.length());
+        }
+
+        // if not found in AUTH header, try to fetch from cookie
+        if (StringUtils.isBlank(serializedJWT) && StringUtils.isNotBlank(jwtCookie)) {
+            String[] cookie = jwtCookie.split("=");
+            if (cookieName.equals(cookie[0])) {
+                serializedJWT = cookie[1];
+            }
+        }
+
+        return serializedJWT;
+    }
+
+    /**
+     * This method provides a single method for validating the JWT for use in
+     * request processing. It provides for the override of specific aspects of this
+     * implementation through submethods used within but also allows for the
+     * override of the entire token validation algorithm.
+     *
+     * @param jwtToken the token to validate
+     * @return true if valid
+     */
+    protected boolean validateToken(final SignedJWT jwtToken) {
+        boolean expValid = validateExpiration(jwtToken);
+        boolean sigValid = false;
+        boolean audValid = false;
+
+        if (expValid) {
+            sigValid = validateSignature(jwtToken);
+
+            if (sigValid) {
+                audValid = validateAudiences(jwtToken);
+            }
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("expValid={}, sigValid={}, audValid={}", expValid, sigValid, audValid);
+        }
+
+        return sigValid && audValid && expValid;
+    }
+
+    /**
+     * Verify the signature of the JWT token in this method. This method depends on
+     * the public key that was established during init based upon the provisioned
+     * public key. Override this method in subclasses in order to customize the
+     * signature verification behavior.
+     *
+     * @param jwtToken the token that contains the signature to be validated
+     * @return valid true if signature verifies successfully; false otherwise
+     */
+    protected boolean validateSignature(final SignedJWT jwtToken) {
+        boolean valid = false;
+
+        if (JWSObject.State.SIGNED == jwtToken.getState()) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("JWT token is in a SIGNED state");
+            }
+
+            if (jwtToken.getSignature() != null) {
+                try {
+                    if (StringUtils.isNotBlank(jwksProviderUrl)) {
+                        JWSKeySelector<SecurityContext> keySelector = new JWSVerificationKeySelector<>(jwtToken.getHeader().getAlgorithm(), keySource);
+
+                        // Create a JWT processor for the access tokens
+                        ConfigurableJWTProcessor<SecurityContext> jwtProcessor = getJwtProcessor(keySelector);
+
+                        // Process the token
+                        jwtProcessor.process(jwtToken, null);
+                        valid = true;
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("JWT token has been successfully verified.");
+                        }
+                    } else if (verifier != null) {
+                        if (jwtToken.verify(verifier)) {
+                            valid = true;
+                            if (LOG.isDebugEnabled()) {
+                                LOG.debug("JWT token has been successfully verified.");
+                            }
+                        } else {
+                            LOG.warn("JWT signature verification failed.");
+                        }
+                    } else {
+                        LOG.warn("Cannot authenticate JWT token as neither JWKS provider URL nor public key provided.");
+                    }
+                } catch (JOSEException | BadJOSEException e) {
+                    LOG.error("Error while validating signature.", e);
+                }
+            }
+        }
+
+        if (!valid) {
+            LOG.warn("Signature could not be verified.");
+        }
+
+        return valid;
+    }
+
+    public abstract ConfigurableJWTProcessor<SecurityContext> getJwtProcessor(final JWSKeySelector<SecurityContext> keySelector);
+
+    /**
+     * Validate whether any of the accepted audience claims is present in the issued
+     * token claims list for audience. Override this method in subclasses in order
+     * to customize the audience validation behavior.
+     *
+     * @param jwtToken the JWT token where the allowed audiences will be found
+     * @return true if an expected audience is present, otherwise false
+     */
+    protected boolean validateAudiences(final SignedJWT jwtToken) {
+        boolean valid = false;
+        try {
+            List<String> tokenAudienceList = jwtToken.getJWTClaimsSet().getAudience();
+            // if there were no expected audiences configured then just
+            // consider any audience acceptable
+            if (audiences == null) {
+                valid = true;
+            } else {
+                // if any of the configured audiences is found then consider it
+                // acceptable
+                for (String aud : tokenAudienceList) {
+                    if (audiences.contains(aud)) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("JWT token audience has been successfully validated.");
+                        }
+                        valid = true;
+                        break;
+                    }
+                }
+                if (!valid) {
+                    LOG.warn("JWT audience validation failed.");
+                }
+            }
+        } catch (ParseException pe) {
+            LOG.warn("Unable to parse the JWT token.", pe);
+        }
+        return valid;
+    }
+
+    /**
+     * Validate that the expiration time of the JWT token has not been violated. If
+     * it has then throw an AuthenticationException. Override this method in
+     * subclasses in order to customize the expiration validation behavior.
+     *
+     * @param jwtToken the token that contains the expiration date to validate
+     * @return valid true if the token has not expired; false otherwise
+     */
+    protected boolean validateExpiration(final SignedJWT jwtToken) {
+        boolean valid = false;
+        try {
+            Date expires = jwtToken.getJWTClaimsSet().getExpirationTime();
+            if (expires == null || new Date().before(expires)) {
+                valid = true;
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("JWT token expiration date has been successfully validated.");
+                }
+            } else {
+                LOG.warn("JWT token provided is expired.");
+            }
+        } catch (ParseException pe) {
+            LOG.warn("Failed to validate JWT expiry.", pe);
+        }
+
+        return valid;
+    }
+
+    public static boolean shouldProceedAuth(final String authHeader, final String jwtCookie) {
+        return (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(JWT_AUTHZ_PREFIX)) || (StringUtils.isNotBlank(jwtCookie) && jwtCookie.startsWith(cookieName));
+    }
+}