JWT validation
The following examples show how to use the JwtValidationFilter to validate signed and encrypted JWT.
The JwtValidationFilter can access JWTs in the request, provided in a
header, query parameter, form parameter, cookie, or other way. If an upstream
filter makes the JWT available in the request’s attributes context, the
JwtValidationFilter can access the JWT through the context, for example, at
${attributes.jwtToValidate}
.
For convenience, the JWT in this example is provided by the JwtBuilderFilter, and passed to the JwtValidationFilter in a cookie.
The following figure shows the flow of information in the example:
-
Create a signed then encrypted JWT as described in Pass runtime data in a JWT signed with PEM then encrypted with a symmetric key.
-
Add the following route to IG, replacing value of the property
secretsDir
with the directory for the PEM files:$HOME/.openig/config/routes/jwt-validate.json
appdata\OpenIG\config\routes\jwt-validate.json
{ "name": "jwt-validate", "condition": "${find(request.uri.path, '^/jwt-validate')}", "properties": { "secretsDir": "path/to/secrets" }, "capture": "all", "heap": [ { "name": "SystemAndEnvSecretStore", "type": "SystemAndEnvSecretStore", "config": { "mappings": [{ "secretId": "id.decrypted.key.for.signing.jwt", "format": "BASE64" }] } }, { "name": "pemPropertyFormat", "type": "PemPropertyFormat", "config": { "decryptionSecretId": "id.decrypted.key.for.signing.jwt", "secretsProvider": "SystemAndEnvSecretStore" } }, { "name": "FileSystemSecretStore-1", "type": "FileSystemSecretStore", "config": { "format": "PLAIN", "directory": "&{secretsDir}", "mappings": [{ "secretId": "id.encrypted.key.for.signing.jwt.pem", "format": "pemPropertyFormat" }, { "secretId": "symmetric.key.for.encrypting.jwt", "format": { "type": "SecretKeyPropertyFormat", "config": { "format": "BASE64", "algorithm": "AES" } } }] } } ], "handler": { "type": "Chain", "config": { "filters": [{ "type": "JwtValidationFilter", "config": { "jwt": "${request.cookies['my-jwt'][0].value}", "secretsProvider": "FileSystemSecretStore-1", "decryptionSecretId": "symmetric.key.for.encrypting.jwt", "customizer": { "type": "ScriptableJwtValidatorCustomizer", "config": { "type": "application/x-groovy", "source": [ "builder.claim('name', JsonValue::asString, isEqualTo('demo'))", "builder.claim('email', JsonValue::asString, isEqualTo('demo@example.com'));" ] } }, "failureHandler": { "type": "ScriptableHandler", "config": { "type": "application/x-groovy", "source": [ "def response = new Response(Status.FORBIDDEN)", "response.headers['Content-Type'] = 'text/html; charset=utf-8'", "def errors = contexts.jwtValidationError.violations.collect{it.description}", "def display = \"<html>Can't validate JWT:<br> ${contexts.jwtValidationError.jwt} \"", "display <<=\"<br><br>For the following errors:<br> ${errors.join(\"<br>\")}</html>\"", "response.entity=display as String", "return response" ] } } } }], "handler": { "type": "StaticResponseHandler", "config": { "status": 200, "headers": { "Content-Type": [ "text/html; charset=UTF-8" ] }, "entity": "<html><h2>Validated JWT:</h2><p>${contexts.jwtValidation.value}</p><h2>JWT payload:</h2><p>${contexts.jwtValidation.info}</p></html>" } } } } }
Notice the following features of the route:
-
The route matches requests to
/jwt-validate
. -
The JwtValidationFilter takes the value of the JWT from
my-jwt
. -
The SystemAndEnvSecretStore, PemPropertyFormat, and FileSystemSecretStore objects in the heap are the same as those in the route to create the JWT. The JwtValidationFilter uses the same objects to validate the JWT.
-
The JwtBuilderFilter
customizer
requires that the JWT claims matchname:demo
andemail:demo@example.com
. -
If the JWT is validated, the StaticResponseHandler displays the validated value. Otherwise, the FailureHandler displays the reason for the failed validation.
-
-
Test the setup:
-
If you are logged in to AM, log out and clear any cookies.
-
Go to http://ig.example.com:8080/jwtbuilder-sign-then-encrypt to build a JWT, and log in to AM as user
demo
, passwordCh4ng31t
. The sample app displays the signed JWT along with its header and payload. -
Go to http://ig.example.com:8080/jwt-validate to validate the JWT. The validated JWT and its payload are displayed.
-
Test the setup again, but log in to AM as a different user, or change the email address of the demo user in AM. The JWT is not validated, and an error is displayed.
-