Skip to content

Commit

Permalink
fixes #283 add SwtVerifyHandler and test case to support token info i…
Browse files Browse the repository at this point in the history
…ntrospection (#284)
  • Loading branch information
stevehu authored Feb 20, 2023
1 parent 068456e commit 7e894f2
Show file tree
Hide file tree
Showing 13 changed files with 811 additions and 125 deletions.
9 changes: 4 additions & 5 deletions openapi-security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
<groupId>com.networknt</groupId>
<artifactId>config</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>client</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>utility</artifactId>
Expand Down Expand Up @@ -99,11 +103,6 @@
<artifactId>jose4j</artifactId>
</dependency>

<dependency>
<groupId>com.networknt</groupId>
<artifactId>client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public boolean handleJwt(HttpServerExchange exchange, String pathPrefix, String

boolean ignoreExpiry = config.isIgnoreJwtExpiry();

String jwt = JwtVerifier.getJwtFromAuthorization(authorization);
String jwt = JwtVerifier.getTokenFromAuthorization(authorization);

if (jwt != null) {

Expand Down Expand Up @@ -172,7 +172,7 @@ public boolean handleJwt(HttpServerExchange exchange, String pathPrefix, String
auditInfo.put(Constants.SUBJECT_CLAIMS, claims);
auditInfo.put(Constants.CLIENT_ID_STRING, clientId);

if (!config.isEnableH2c() && this.checkForH2CRequest(headerMap)) {
if (!config.isEnableH2c() && jwtVerifier.checkForH2CRequest(headerMap)) {
setExchangeStatus(exchange, STATUS_METHOD_NOT_ALLOWED);
if (logger.isDebugEnabled()) logger.debug("JwtVerifyHandler.handleRequest ends with an error.");
return false;
Expand Down Expand Up @@ -202,7 +202,7 @@ public boolean handleJwt(HttpServerExchange exchange, String pathPrefix, String

/* validate scope from operation */
String scopeHeader = headerMap.getFirst(HttpStringConstants.SCOPE_TOKEN);
String scopeJwt = JwtVerifier.getJwtFromAuthorization(scopeHeader);
String scopeJwt = JwtVerifier.getTokenFromAuthorization(scopeHeader);
List<String> secondaryScopes = new ArrayList<>();

if(!this.hasValidSecondaryScopes(exchange, scopeJwt, secondaryScopes, ignoreExpiry, pathPrefix, reqPath, jwkServiceIds, auditInfo)) {
Expand Down Expand Up @@ -272,20 +272,6 @@ protected String getScopeToken(String authorization, HeaderMap headerMap) {
return returnToken;
}

/**
* Checks to see if the current exchange type is Upgrade.
* Two conditions required for a valid upgrade request.
* - 'Connection' header is set to 'upgrade'.
* - 'Upgrade' is present.
*
* @param headerMap - map containing all exchange headers
* @return - returns true if the request is an Upgrade request.
*/
protected boolean checkForH2CRequest(HeaderMap headerMap) {
return headerMap.getFirst(Headers.UPGRADE) != null
&& headerMap.getFirst(Headers.CONNECTION) != null
&& headerMap.getFirst(Headers.CONNECTION).equalsIgnoreCase("upgrade");
}

/**
* Gets the operation from the spec. If not defined or defined incorrectly, return null.
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public class UnifiedPathPrefixAuth {
String pathPrefix;
boolean basic;
boolean jwt;
boolean swt;
boolean apikey;
List<String> jwkServiceIds;

Expand Down Expand Up @@ -33,6 +34,14 @@ public void setJwt(boolean jwt) {
this.jwt = jwt;
}

public boolean isSwt() {
return swt;
}

public void setSwt(boolean swt) {
this.swt = swt;
}

public boolean isApikey() {
return apikey;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class UnifiedSecurityConfig {
public static final String PREFIX = "prefix";
public static final String BASIC = "basic";
public static final String JWT = "jwt";
public static final String SWT = "swt";
public static final String APIKEY = "apikey";
public static final String JWK_SERVICE_IDS = "jwkServiceIds";

Expand Down Expand Up @@ -147,6 +148,7 @@ private void setConfigList() {
unifiedPathPrefixAuth.setPathPrefix((String)value.get(PREFIX));
unifiedPathPrefixAuth.setBasic(value.get(BASIC) == null ? false : (Boolean)value.get(BASIC));
unifiedPathPrefixAuth.setJwt(value.get(JWT) == null ? false : (Boolean)value.get(JWT));
unifiedPathPrefixAuth.setSwt(value.get(SWT) == null ? false : (Boolean)value.get(SWT));
unifiedPathPrefixAuth.setApikey(value.get(APIKEY) == null ? false : (Boolean)value.get(APIKEY));
Object ids = value.get(JWK_SERVICE_IDS);
if(ids instanceof String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class UnifiedSecurityHandler implements MiddlewareHandler {
static final String BASIC_PREFIX = "BASIC";
static final String API_KEY = "apikey";
static final String JWT = "jwt";
static final String SWT = "swt";
static final String MISSING_AUTH_TOKEN = "ERR10002";
static final String INVALID_AUTHORIZATION_HEADER = "ERR12003";
static final String HANDLER_NOT_FOUND = "ERR11200";
Expand Down Expand Up @@ -63,10 +64,10 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
found = true;
if(logger.isTraceEnabled()) logger.trace("Found with requestPath = " + reqPath + " prefix = " + pathPrefixAuth.getPathPrefix());
// check jwt and basic first with authorization header, then check the apikey if it is enabled.
if(pathPrefixAuth.isBasic() || pathPrefixAuth.isJwt()) {
if(pathPrefixAuth.isBasic() || pathPrefixAuth.isJwt() || pathPrefixAuth.isSwt()) {
String authorization = exchange.getRequestHeaders().getFirst(Headers.AUTHORIZATION);
if(authorization == null) {
logger.error("Basic or JWT is enabled and authorization header is missing.");
logger.error("Basic or JWT or SWT is enabled and authorization header is missing.");
setExchangeStatus(exchange, MISSING_AUTH_TOKEN);
if(logger.isDebugEnabled())
logger.debug("UnifiedSecurityHandler.handleRequest ends with an error.");
Expand Down Expand Up @@ -99,23 +100,42 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
}
} else if (BEARER_PREFIX.equalsIgnoreCase(authorization.substring(0, 6))) {
Map<String, HttpHandler> handlers = Handler.getHandlers();
JwtVerifyHandler handler = (JwtVerifyHandler) handlers.get(JWT);
if(handler == null) {
logger.error("Cannot find JwtVerifyHandler with alias name jwt.");
setExchangeStatus(exchange, HANDLER_NOT_FOUND, "com.networknt.openapi.JwtVerifyHandler@jwt");
exchange.endExchange();
return;
} else {
// get the jwkServiceIds list.
if(handler.handleJwt(exchange, pathPrefixAuth.getPathPrefix(), reqPath, pathPrefixAuth.getJwkServiceIds())) {
// verification is passed, go to the next handler in the chain.
break;
if(pathPrefixAuth.isJwt()) {
JwtVerifyHandler handler = (JwtVerifyHandler) handlers.get(JWT);
if (handler == null) {
logger.error("Cannot find JwtVerifyHandler with alias name jwt.");
setExchangeStatus(exchange, HANDLER_NOT_FOUND, "com.networknt.openapi.JwtVerifyHandler@jwt");
exchange.endExchange();
return;
} else {
// verification is not passed and an error is returned. Don't call the next handler.
// get the jwkServiceIds list.
if (handler.handleJwt(exchange, pathPrefixAuth.getPathPrefix(), reqPath, pathPrefixAuth.getJwkServiceIds())) {
// verification is passed, go to the next handler in the chain.
break;
} else {
// verification is not passed and an error is returned. Don't call the next handler.
return;
}
}
} else {
// this must be swt token
SwtVerifyHandler handler = (SwtVerifyHandler) handlers.get(SWT);
if (handler == null) {
logger.error("Cannot find SwtVerifyHandler with alias name swt.");
setExchangeStatus(exchange, HANDLER_NOT_FOUND, "com.networknt.openapi.SwtVerifyHandler@swt");
exchange.endExchange();
return;
} else {
// get the jwkServiceIds list.
if (handler.handleSwt(exchange, reqPath, pathPrefixAuth.getJwkServiceIds())) {
// verification is passed, go to the next handler in the chain.
break;
} else {
// verification is not passed and an error is returned. Don't call the next handler.
return;
}
}
}

} else {
String s = authorization.length() > 10 ? authorization.substring(0, 10) : authorization;
logger.error("Invalid/Unsupported authorization header {}", s);
Expand Down
10 changes: 6 additions & 4 deletions openapi-security/src/main/resources/config/openapi-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
# request path prefix in skipPathPrefixes.
enableVerifyJwt: ${openapi-security.enableVerifyJwt:true}

# Enable the SWT verification flag. The SwtVerifierHandler will skip the SWT token verification
# if this flag is false. It should only be set to false on the dev environment for testing
# purposes. If you have some endpoints that want to skip the SWT verification, you can put the
# request path prefix in skipPathPrefixes.
enableVerifySwt: ${openapi-security.enableVerifySwt:true}

# Extract JWT scope token from the X-Scope-Token header and validate the JWT token
enableExtractScopeToken: ${openapi-security.enableExtractScopeToken:true}

Expand All @@ -27,10 +33,6 @@ enableVerifyScope: ${openapi-security.enableVerifyScope:true}
# doesn't have a specification to retrieve the defined scopes, the handler will skip the scope verification.
skipVerifyScopeWithoutSpec: ${openapi-security.skipVerifyScopeWithoutSpec:false}

# Enable JWT scope verification.
# Only valid when (enableVerifyJwt is true) AND (enableVerifyScope is true)
enableVerifyJwtScopeToken: ${openapi-security.enableVerifyJwtScopeToken:true}

# If set true, the JWT verifier handler will pass if the JWT token is expired already. Unless
# you have a strong reason, please use it only on the dev environment if your OAuth 2 provider
# doesn't support long-lived token for dev environment or test automation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public class SecurityConfigTest {
public void testLoadConfig() {
SecurityConfig config = SecurityConfig.load("openapi-security");
Assert.assertTrue(config.isEnableVerifyJwt());
Assert.assertFalse(config.isEnableVerifyJwtScopeToken());
Assert.assertEquals(2, config.getCertificate().size());

}
Expand Down
Loading

0 comments on commit 7e894f2

Please sign in to comment.