The wso2-mi-jwt-validator is a custom handler and mediator for the WSO2 Micro Integrator. This class validates JWT tokens against one or more JWKS endpoints and can be used as either a custom handler or a custom mediator.
Latest version: 1.4.0 on Maven Central Repository
- wso2-mi-jwt-validator
Add the following dependencies to your pom.xml file:
<dependency>
<groupId>io.integon.wso2mi.jwt</groupId>
<artifactId>wso2-mi-jwt-validator</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>10.3</version>
</dependency>The wso2-mi-jwt-validator relies on the nimbus-jose-jwt library for JWT validation.
Add these JAR files to the MI directory "/home/wso2carbon/wso2mi-{version}/lib":
- wso2-mi-jwt-validator-1.4.0.jar (or latest version)
- nimbus-jose-jwt-10.3.jar (or latest version)
Download links:
| Parameter Name | Description | Examples |
|---|---|---|
| jwtHeader | Header name containing the JWT Token. | <property name="jwtHeader" value="Authorization"/> <property name="jwtHeader" value="env:JWT_HEADER"/> |
| iatClaim | Maximum token age in seconds. | <property name="iatClaim" value="1800"/> <property name="iatClaim" value="env:IAT_CLAIM"/> <property name="iatClaim" value=""/> |
| issClaim | Regex for issuer claim. Multiple values: ^(myiss1|myiss2|myiss3)$ |
<property name="issClaim" value="issuer"/> <property name="issClaim" value="env:ISS_CLAIM"/> <property name="issClaim" value=""/> |
| subClaim | Expected subject claim. | <property name="subClaim" value="subject"/> <property name="subClaim" value="env:SUB_CLAIM"/> <property name="subClaim" value=""/> |
| audClaim | Expected audience claim. | <property name="audClaim" value="audience"/> <property name="audClaim" value="env:AUD_CLAIM"/> <property name="audClaim" value=""/> |
| jtiClaim | Set to "enabled" to verify token uniqueness using cache. | <property name="jtiClaim" value="enabled"/> <property name="jtiClaim" value="env:JTI_CLAIM"/> |
| customClaims | Validate additional custom claims using a comma-separated list of key:regex pairs. Each claim must match its corresponding regular expression. Format: claimName:regex,claimName2:regex2 Example for multiple allowed roles and locations: role:admin|manager,location:Zurich|Geneva|Bern |
<property name="customClaims" value="role:admin|manager,location:Zurich|Geneva|Bern"/> <property name="customClaims" value="env:CUSTOM_CLAIMS"/> |
| jwksEndpoint | JWKS endpoint URL(s) or environment variable. Multiple endpoints use comma separation. | <property name="jwksEndpoint" value="https://example.com/oauth2/jwks"/> <property name="jwksEndpoint" value="env:JWKS_ENDPOINT"/> |
| jwksTimeout | Timeout in seconds for JWKS endpoint caching. | <property name="jwksTimeout" value="30"/> <property name="jwksTimeout" value="env:JWKS_TIMEOUT"/> |
| jwksRefreshTime | Time in seconds after which the JWKS endpoint is refreshed. | <property name="jwksRefreshTime" value="15"/> <property name="jwksRefreshTime" value="env:JWKS_REFRESH_TIME"/> |
| forwardToken | If 'true', decoded JWT payload is set as 'X-JWT' property. | <property name="forwardToken" value="true"/> <property name="forwardToken" value="env:FORWARD_TOKEN"/> |
Note: All properties can be specified directly or via environment variables using the env: prefix.
Optional parameters:
- All claim checks (iatClaim, issClaim, subClaim, audClaim, jtiClaim) - claims not specified will not be checked
- jwksTimeout (default: 6000)
- jwksRefreshTime (default: 3000)
- forwardToken (default: false)
| Parameter Name | Description | Examples |
|---|---|---|
| jwtToken | JWT token to validate. | <property name="jwtToken" expression="$trp:Authorization"/> <property name="jwtToken" expression="$ctx:jwt"/> |
| iatClaim | Maximum token age in seconds. | <property name="iatClaim" value="1800"/> <property name="iatClaim" value="env:IAT_CLAIM"/> <property name="iatClaim" value=""/> |
| issClaim | Regex for issuer claim. Multiple values: ^(myiss1|myiss2|myiss3)$ |
<property name="issClaim" value="issuer"/> <property name="issClaim" value="env:ISS_CLAIM"/> <property name="issClaim" value=""/> |
| subClaim | Expected subject claim. | <property name="subClaim" value="subject"/> <property name="subClaim" value="env:SUB_CLAIM"/> <property name="subClaim" value=""/> |
| audClaim | Expected audience claim. | <property name="audClaim" value="audience"/> <property name="audClaim" value="env:AUD_CLAIM"/> <property name="audClaim" value=""/> |
| jtiClaim | Set to "enabled" to verify token uniqueness using cache. | <property name="jtiClaim" value="enabled"/> <property name="jtiClaim" value="env:JTI_CLAIM"/> |
| customClaims | Validate additional custom claims using a comma-separated list of key:regex pairs. Each claim must match its corresponding regular expression. Format: claimName:regex,claimName2:regex2 Example for multiple allowed roles and locations: role:admin|manager,location:Zurich|Geneva|Bern |
<property name="customClaims" value="role:admin|manager,location:Zurich|Geneva|Bern"/> <property name="customClaims" value="env:CUSTOM_CLAIMS"/> |
| jwksEndpoint | JWKS endpoint URL(s) or environment variable. Multiple endpoints use comma separation. | <property name="jwksEndpoint" value="https://example.com/oauth2/jwks"/> <property name="jwksEndpoint" value="env:JWKS_ENDPOINT"/> |
| jwksTimeout | Timeout in seconds for JWKS endpoint caching. | <property name="jwksTimeout" value="30"/> <property name="jwksTimeout" value="env:JWKS_TIMEOUT"/> |
| jwksRefreshTime | Time in seconds after which the JWKS endpoint is refreshed. | <property name="jwksRefreshTime" value="15"/> <property name="jwksRefreshTime" value="env:JWKS_REFRESH_TIME"/> |
| forwardToken | If 'true', decoded JWT payload is set as 'X-JWT' property. | <property name="forwardToken" value="true"/> <property name="forwardToken" value="env:FORWARD_TOKEN"/> |
| respond | If 'true', mediator responds without triggering faultSequence. | <property name="respond" value="true"/> <property name="respond" value="env:RESPOND"/> |
Note: All properties can be specified directly or via environment variables using the env: prefix.
Optional parameters:
- All claim checks (iatClaim, issClaim, subClaim, audClaim, jtiClaim) - claims not specified will not be checked
- jwksTimeout (default: 6000)
- jwksRefreshTime (default: 3000)
- forwardToken (default: false)
- respond (default: false)
<api context="/jwtHealth" name="jwt-health-api" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/">
...
</resource>
<handlers>
<handler class="io.integon.JwtAuthHandler">
<property name="jwtHeader" value="Authorization"/>
<property name="jwksEndpoint" value="https://apim.ch/oauth2/jwks"/>
</handler>
</handlers>
</api><api context="/jwtHealth" name="jwt-health-api" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/">
...
</resource>
<handlers>
<handler class="io.integon.JwtAuthHandler">
<property name="jwtHeader" value="env:JWT_HEADER"/>
<property name="jwksEndpoint" value="env:JWKS_ENDPOINT"/>
</handler>
</handlers>
</api><api context="/jwtHealth" name="jwt-health-api" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/">
...
</resource>
<handlers>
<handler class="io.integon.JwtAuthHandler">
<property name="jwtHeader" value="Authorization"/>
<property name="jwksEndpoint" value="https://apim.ch/oauth2/jwks"/>
<property name="jwksTimeout" value="30"/>
<property name="jwksRefreshTime" value="15"/>
</handler>
</handlers>
</api><api context="/jwtHealth" name="jwt-health-api" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/">
...
</resource>
<handlers>
<handler class="io.integon.JwtAuthHandler">
<property name="jwtHeader" value="Authorization"/>
<property name="jwksEndpoint" value="https://apim.ch/oauth2/jwks"/>
<property name="iatClaim" value="1800"/>
<property name="issClaim" value="issuer"/>
<property name="subClaim" value="subject"/>
<property name="audClaim" value="audience"/>
<property name="jtiClaim" value="enabled"/>
</handler>
</handlers>
</api><api context="/jwtHealth" name="jwt-health-api" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/">
...
</resource>
<handlers>
<handler class="io.integon.JwtAuthHandler">
<property name="jwtHeader" value="Authorization"/>
<property name="jwksEndpoint" value="https://apim.ch/oauth2/jwks"/>
<property name="iatClaim" value="env:IAT_MAX_AGE"/>
<property name="issClaim" value="env:EXPECTED_ISSUER"/>
<property name="subClaim" value="env:EXPECTED_SUBJECT"/>
<property name="audClaim" value="env:EXPECTED_AUDIENCE"/>
<property name="jtiClaim" value="env:JTI_CHECK"/>
</handler>
</handlers>
</api><api context="/jwtHealth" name="jwt-health-api" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/">
...
</resource>
<handlers>
<handler class="io.integon.JwtAuthHandler">
<property name="jwtHeader" value="Authorization"/>
<property name="jwksEndpoint" value="https://apim.ch/oauth2/jwks,https://apim-test.ch/oauth2/jwks"/>
</handler>
</handlers>
</api><proxy xmlns="http://ws.apache.org/ns/synapse" name="jwt-auth-mi" transports="http https" startOnLoad="true">
<description>JWT Mediator Test Proxy</description>
<target>
<inSequence>
<propertyGroup name="jwt-auth-mi">
<property name="jwtToken" expression="$trp:Authorization"/>
<property name="jwksEndpoint" value="https://apim-dev.ch/oauth2/jwks"/>
</propertyGroup>
<class name="io.integon.JwtAuthMediator"/>
<!-- Your sequence continues here -->
</inSequence>
<faultSequence>
<log level="custom" category="ERROR">
<property name="jwt-auth-mi" value="faultSequence" />
<property name="ERROR_CODE" expression="$ctx:ERROR_CODE"/>
<property name="ERROR_MESSAGE" expression="$ctx:ERROR_MESSAGE"/>
</log>
<property name="HTTP_SC" expression="$ctx:ERROR_CODE" scope="axis2"/>
<respond/>
</faultSequence>
</target>
</proxy><proxy xmlns="http://ws.apache.org/ns/synapse" name="jwt-auth-mi" transports="http https" startOnLoad="true">
<description>JWT Mediator Test Proxy</description>
<target>
<inSequence>
<propertyGroup name="jwt-auth-mi">
<property name="jwtToken" expression="$trp:Authorization"/>
<property name="jwksEndpoint" value="env:JWKS_ENDPOINT"/>
<property name="iatClaim" value="env:IAT_MAX_AGE" />
<property name="issClaim" value="env:EXPECTED_ISSUER" />
<property name="subClaim" value="env:EXPECTED_SUBJECT" />
<property name="audClaim" value="env:EXPECTED_AUDIENCE" />
<property name="jtiClaim" value="env:JTI_CHECK" />
</propertyGroup>
<class name="io.integon.JwtAuthMediator"/>
<!-- Your sequence continues here -->
</inSequence>
<faultSequence>
<!-- Error handling -->
</faultSequence>
</target>
</proxy><proxy xmlns="http://ws.apache.org/ns/synapse" name="jwt-auth-mi" transports="http https" startOnLoad="true">
<description>JWT Mediator Test Proxy</description>
<target>
<inSequence>
<propertyGroup name="jwt-auth-mi">
<property name="jwtToken" expression="$trp:Authorization"/>
<property name="jwksEndpoint" value="env:JWKS_ENDPOINT"/>
<property name="jwksTimeout" value="3000"/>
<property name="jwksRefreshTime" value="1000"/>
</propertyGroup>
<class name="io.integon.JwtAuthMediator"/>
<!-- Your sequence continues here -->
</inSequence>
<faultSequence>
<!-- Error handling -->
</faultSequence>
</target>
</proxy><proxy xmlns="http://ws.apache.org/ns/synapse" name="jwt-auth-mi" transports="http https" startOnLoad="true">
<description>JWT Mediator Test Proxy</description>
<target>
<inSequence>
<propertyGroup name="jwt-auth-mi">
<property name="jwtToken" expression="$trp:Authorization"/>
<property name="iatClaim" expression="$trp:test" />
<property name="issClaim" value="https://apim-dev.integon.ch:443/oauth2/token" />
<property name="subClaim" value="admin" />
<property name="audClaim" value="Y3wBS2AsdgHW6z2GfEfUpairc_Ma" />
<property name="jtiClaim" value="enabled" />
<property name="jwksEndpoint" value="env:JWKS_ENDPOINT"/>
</propertyGroup>
<class name="io.integon.JwtAuthMediator"/>
<!-- Your sequence continues here -->
</inSequence>
<faultSequence>
<!-- Error handling -->
</faultSequence>
</target>
</proxy><proxy xmlns="http://ws.apache.org/ns/synapse" name="jwt-auth-mi" transports="http https" startOnLoad="true">
<description>JWT Mediator Test Proxy</description>
<target>
<inSequence>
<propertyGroup name="jwt-auth-mi">
<property name="jwtToken" expression="$trp:Authorization"/>
<property name="jwksEndpoint" value="https://apim-dev.ch/oauth2/jwks,https://apim-test.ch/oauth2/jwks"/>
</propertyGroup>
<class name="io.integon.JwtAuthMediator"/>
<!-- Your sequence continues here -->
</inSequence>
<faultSequence>
<!-- Error handling -->
</faultSequence>
</target>
</proxy>To enable debugging for the JWT validator, add the following to "../mi-home/conf/log4j2.properties":
logger.JwtAuthMediator.name = io.integon.JwtAuthMediator
logger.JwtAuthMediator.level = DEBUG
logger.JwtAuthHandler.name = io.integon.JwtAuthHandler
logger.JwtAuthHandler.level = DEBUGThen add these loggers to the list of loggers:
loggers = ..., ..., ..., JwtAuthMediator, JwtAuthHandler