diff --git a/openapi-security/pom.xml b/openapi-security/pom.xml
index 2f5d38a6..64eb57b2 100644
--- a/openapi-security/pom.xml
+++ b/openapi-security/pom.xml
@@ -54,6 +54,18 @@
com.networknt
status
+
+ com.networknt
+ basic-auth
+
+
+ com.networknt
+ api-key
+
+
+ com.networknt
+ server
+
com.networknt
json-overlay
diff --git a/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java b/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java
index 6b4cbe18..b65a947f 100644
--- a/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java
+++ b/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java
@@ -105,11 +105,19 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
logger.debug("JwtVerifyHandler.handleRequest ends.");
return;
}
+ // only UnifiedSecurityHandler will have the jwkServiceIds as the third parameter.
+ if(handleJwt(exchange, null, reqPath, null)) {
+ if(logger.isDebugEnabled()) logger.debug("JwtVerifyHandler.handleRequest ends.");
+ Handler.next(exchange, next);
+ }
+ }
+
+ public boolean handleJwt(HttpServerExchange exchange, String pathPrefix, String reqPath, List jwkServiceIds) throws Exception {
Map auditInfo = null;
HeaderMap headerMap = exchange.getRequestHeaders();
String authorization = headerMap.getFirst(Headers.AUTHORIZATION);
- if (logger.isTraceEnabled() && authorization != null)
+ if (logger.isTraceEnabled() && authorization != null && authorization.length() > 10)
logger.trace("Authorization header = " + authorization.substring(0, 10));
authorization = this.getScopeToken(authorization, headerMap);
@@ -125,7 +133,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
try {
- JwtClaims claims = jwtVerifier.verifyJwt(jwt, ignoreExpiry, true, reqPath);
+ JwtClaims claims = jwtVerifier.verifyJwt(jwt, ignoreExpiry, true, pathPrefix, reqPath, jwkServiceIds);
if (logger.isTraceEnabled())
logger.trace("claims = " + claims.toJson());
@@ -156,7 +164,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
if (!config.isEnableH2c() && this.checkForH2CRequest(headerMap)) {
setExchangeStatus(exchange, STATUS_METHOD_NOT_ALLOWED);
if (logger.isDebugEnabled()) logger.debug("JwtVerifyHandler.handleRequest ends with an error.");
- return;
+ return false;
}
String callerId = headerMap.getFirst(HttpStringConstants.CALLER_ID);
@@ -178,7 +186,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
} else {
// this will return an error message to the client.
}
- return;
+ return false;
}
/* validate scope from operation */
@@ -186,11 +194,11 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
String scopeJwt = JwtVerifier.getJwtFromAuthorization(scopeHeader);
List secondaryScopes = new ArrayList<>();
- if(!this.hasValidSecondaryScopes(exchange, scopeJwt, secondaryScopes, ignoreExpiry, reqPath, auditInfo)) {
- return;
+ if(!this.hasValidSecondaryScopes(exchange, scopeJwt, secondaryScopes, ignoreExpiry, pathPrefix, reqPath, jwkServiceIds, auditInfo)) {
+ return false;
}
if(!this.hasValidScope(exchange, scopeHeader, secondaryScopes, claims, operation)) {
- return;
+ return false;
}
}
if (logger.isTraceEnabled())
@@ -199,7 +207,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
if (logger.isDebugEnabled())
logger.debug("JwtVerifyHandler.handleRequest ends.");
- Handler.next(exchange, next);
+ return true;
} catch (InvalidJwtException e) {
// only log it and unauthorized is returned.
@@ -227,8 +235,8 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception {
setExchangeStatus(exchange, STATUS_MISSING_AUTH_TOKEN);
exchange.endExchange();
}
+ return true;
}
-
/**
* Get authToken from X-Scope-Token header.
* This covers situations where there is a secondary auth token.
@@ -319,13 +327,13 @@ protected Operation getOperation(HttpServerExchange exchange, OpenApiOperation o
* @param reqPath - the request path as string
* @return - return true if the secondary scopes are valid or if there are no secondary scopes.
*/
- protected boolean hasValidSecondaryScopes(HttpServerExchange exchange, String scopeJwt, List secondaryScopes, boolean ignoreExpiry, String reqPath, Map auditInfo) {
+ protected boolean hasValidSecondaryScopes(HttpServerExchange exchange, String scopeJwt, List secondaryScopes, boolean ignoreExpiry, String pathPrefix, String reqPath, List jwkServiceIds, Map auditInfo) {
if (scopeJwt != null) {
if (logger.isTraceEnabled())
logger.trace("start verifying scope token = " + scopeJwt.substring(0, 10));
try {
- JwtClaims scopeClaims = jwtVerifier.verifyJwt(scopeJwt, ignoreExpiry, true, reqPath);
+ JwtClaims scopeClaims = jwtVerifier.verifyJwt(scopeJwt, ignoreExpiry, true, pathPrefix, reqPath, jwkServiceIds);
Object scopeClaim = scopeClaims.getClaimValue(Constants.SCOPE_STRING);
if (scopeClaim instanceof String) {
diff --git a/openapi-security/src/main/java/com/networknt/openapi/UnifiedPathPrefixAuth.java b/openapi-security/src/main/java/com/networknt/openapi/UnifiedPathPrefixAuth.java
new file mode 100644
index 00000000..1308ab83
--- /dev/null
+++ b/openapi-security/src/main/java/com/networknt/openapi/UnifiedPathPrefixAuth.java
@@ -0,0 +1,51 @@
+package com.networknt.openapi;
+
+import java.util.List;
+
+public class UnifiedPathPrefixAuth {
+ String pathPrefix;
+ boolean basic;
+ boolean jwt;
+ boolean apikey;
+ List jwkServiceIds;
+
+ public String getPathPrefix() {
+ return pathPrefix;
+ }
+
+ public void setPathPrefix(String pathPrefix) {
+ this.pathPrefix = pathPrefix;
+ }
+
+ public boolean isBasic() {
+ return basic;
+ }
+
+ public void setBasic(boolean basic) {
+ this.basic = basic;
+ }
+
+ public boolean isJwt() {
+ return jwt;
+ }
+
+ public void setJwt(boolean jwt) {
+ this.jwt = jwt;
+ }
+
+ public boolean isApikey() {
+ return apikey;
+ }
+
+ public void setApikey(boolean apikey) {
+ this.apikey = apikey;
+ }
+
+ public List getJwkServiceIds() {
+ return jwkServiceIds;
+ }
+
+ public void setJwkServiceIds(List jwkServiceIds) {
+ this.jwkServiceIds = jwkServiceIds;
+ }
+}
diff --git a/openapi-security/src/main/java/com/networknt/openapi/UnifiedSecurityConfig.java b/openapi-security/src/main/java/com/networknt/openapi/UnifiedSecurityConfig.java
new file mode 100644
index 00000000..a7a66024
--- /dev/null
+++ b/openapi-security/src/main/java/com/networknt/openapi/UnifiedSecurityConfig.java
@@ -0,0 +1,177 @@
+package com.networknt.openapi;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.networknt.config.Config;
+import com.networknt.config.ConfigException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class UnifiedSecurityConfig {
+ private static final Logger logger = LoggerFactory.getLogger(UnifiedSecurityConfig.class);
+ public static final String CONFIG_NAME = "unified-security";
+ public static final String ENABLED = "enabled";
+ public static final String ANONYMOUS_PREFIXES = "anonymousPrefixes";
+ public static final String PATH_PREFIX_AUTHS = "pathPrefixAuths";
+ public static final String PREFIX = "prefix";
+ public static final String BASIC = "basic";
+ public static final String JWT = "jwt";
+ public static final String APIKEY = "apikey";
+ public static final String JWK_SERVICE_IDS = "jwkServiceIds";
+
+ boolean enabled;
+ List anonymousPrefixes;
+ List pathPrefixAuths;
+
+ private Config config;
+ private Map mappedConfig;
+
+ private UnifiedSecurityConfig() {
+ this(CONFIG_NAME);
+ }
+
+ /**
+ * Please note that this constructor is only for testing to load different config files
+ * to test different configurations.
+ * @param configName String
+ */
+ private UnifiedSecurityConfig(String configName) {
+ config = Config.getInstance();
+ mappedConfig = config.getJsonMapConfigNoCache(configName);
+ setConfigData();
+ setConfigList();
+ }
+ public static UnifiedSecurityConfig load() {
+ return new UnifiedSecurityConfig();
+ }
+
+ public static UnifiedSecurityConfig load(String configName) {
+ return new UnifiedSecurityConfig(configName);
+ }
+
+ void reload() {
+ mappedConfig = config.getJsonMapConfigNoCache(CONFIG_NAME);
+ setConfigData();
+ setConfigList();
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public List getAnonymousPrefixes() {
+ return anonymousPrefixes;
+ }
+
+ public void setAnonymousPrefixes(List anonymousPrefixes) {
+ this.anonymousPrefixes = anonymousPrefixes;
+ }
+
+ public List getPathPrefixAuths() {
+ return pathPrefixAuths;
+ }
+
+ public void setPathPrefixAuths(List pathPrefixAuths) {
+ this.pathPrefixAuths = pathPrefixAuths;
+ }
+
+ private void setConfigData() {
+ Object object = mappedConfig.get(ENABLED);
+ if(object != null && (Boolean) object) {
+ setEnabled(true);
+ }
+ }
+
+ private void setConfigList() {
+ // anonymous prefixes
+ if (mappedConfig != null && mappedConfig.get(ANONYMOUS_PREFIXES) != null) {
+ Object object = mappedConfig.get(ANONYMOUS_PREFIXES);
+ anonymousPrefixes = new ArrayList<>();
+ if(object instanceof String) {
+ String s = (String)object;
+ s = s.trim();
+ if(logger.isTraceEnabled()) logger.trace("s = " + s);
+ if(s.startsWith("[")) {
+ // json format
+ try {
+ anonymousPrefixes = Config.getInstance().getMapper().readValue(s, new TypeReference>() {});
+ } catch (Exception e) {
+ throw new ConfigException("could not parse the anonymousPrefixes json with a list of strings.");
+ }
+ } else {
+ // comma separated
+ anonymousPrefixes = Arrays.asList(s.split("\\s*,\\s*"));
+ }
+ } else if (object instanceof List) {
+ List prefixes = (List)object;
+ prefixes.forEach(item -> {
+ anonymousPrefixes.add((String)item);
+ });
+ } else {
+ throw new ConfigException("anonymousPrefixes must be a string or a list of strings.");
+ }
+ }
+
+ // path prefix auth mapping
+ if (mappedConfig.get(PATH_PREFIX_AUTHS) != null) {
+ Object object = mappedConfig.get(PATH_PREFIX_AUTHS);
+ pathPrefixAuths = new ArrayList<>();
+ if(object instanceof String) {
+ String s = (String)object;
+ s = s.trim();
+ if(logger.isTraceEnabled()) logger.trace("pathPrefixAuth s = " + s);
+ if(s.startsWith("[")) {
+ // json format
+ try {
+ pathPrefixAuths = Config.getInstance().getMapper().readValue(s, new TypeReference>() {});
+ } catch (Exception e) {
+ throw new ConfigException("could not parse the pathPrefixAuths json with a list of string and object.");
+ }
+ } else {
+ throw new ConfigException("pathPrefixAuths must be a list of string object map.");
+ }
+ } else if (object instanceof List) {
+ // the object is a list of map, we need convert it to PathPrefixAuth object.
+ List
+
+ com.networknt
+ basic-auth
+ ${version.light-4j}
+
+
+ com.networknt
+ api-key
+ ${version.light-4j}
+
com.networknt
client