├── .gitignore ├── LICENSE ├── README.md ├── handler.lua ├── kong-plugin-jwt-claims-validate-1.0-1.rockspec └── schema.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kong-plugin-jwt-claims-validate 2 | 3 | Validates custom JWT claims with specific values 4 | 5 | ## How it works 6 | 7 | When enabled, this plugin will verify that specified claims are present in a 8 | JSON Web Token [JWT](http://www.jwt.io) and that the claim value has the 9 | specified claim value. If these conditions are met, then the request is sent to 10 | the upstream URL. 11 | 12 | The plugin will look for the JWT in the `Authorization: Bearer ` header, 13 | or it will look in the query string parameters specified in the plugin 14 | configuration. 15 | 16 | 17 | > This plugin does not verify the signature of the JWT so you should use this 18 | > alongside the built-in [Kong JWT plugin](http://getkong.org/plugins/jwt). The 19 | > Kong JWT plugin will execute first to authenticate the request, then this 20 | > plugin will execute sometime aftewards. 21 | 22 | This plugin is useful to generate scoped JSON Web Tokens that allow a consumer 23 | to access multiple Kong APIs with a single JWT credential. 24 | 25 | The plugin will return a `HTTP 401` status with a message about the invalid 26 | claim property that failed. For example 27 | 28 | ```bash 29 | < HTTP/1.1 401 Unauthorized 30 | < 31 | {"message":"JSON Web Token has invalid claim value for 'username'"} 32 | ``` 33 | ## Configuration 34 | 35 | The **kong-plugin-jwt-claims-validate** plugin can only be applied to an API, 36 | and not a consumer. It can be enabled with the following request 37 | 38 | ```bash 39 | curl -X POST http://localhost:8001/apis/29414666-6b91-430a-9ff0-50d691b03a45/plugins \ 40 | -d '{ 41 | "name": "jwt-claims-validate", 42 | "config": { 43 | "uri_param_names": "jwt", 44 | "claims": { 45 | "username": "wshirey", 46 | "id": 19829132, 47 | "is_admin": true 48 | } 49 | } 50 | }' 51 | ``` 52 | 53 | This will check the JWT payload body to at least include the following JSON 54 | 55 | ```JSON 56 | { 57 | "username": "wshirey", 58 | "id": 19829132, 59 | "is_admin": true 60 | } 61 | ``` 62 | 63 | > The plugin only supports scalar values, so if you have a nested JSON array or 64 | > object in the payload body, it will not iterate over the values to check if it 65 | > has your specified value. 66 | 67 | An example JWT can be found at the [jwt.io playground](https://jwt.io/#debugger?&id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IndzaGlyZXkiLCJpZCI6MTk4MjkxMzIsImlzX2FkbWluIjp0cnVlfQ.DTUXDMnSKJxSv9yo9Gih1sWMyeQ_X436wYqU2-Np1ss) 68 | 69 | ## Schema 70 | 71 | form parameter|required|description 72 | ---|---|--- 73 | `name`|*required*|The name of the plugin to use, in this case: `jwt-claims-validate` 74 | `uri_param_names`|*optional*|A list of querystring parameters that Kong will inspect to retrieve JWTs. Defaults to `jwt`. 75 | `claims`|*required*|A table of claims that Kong will validate in the JWT payload body. Lua types supported are *[boolean, string, number]*. 76 | 77 | ## See also 78 | 79 | - [Kong JWT Plugin](http://getkong.org/plugins/jwt) 80 | - [Learn about JSON Web Tokens at jwt.io](http://jwt.io) -------------------------------------------------------------------------------- /handler.lua: -------------------------------------------------------------------------------- 1 | local BasePlugin = require "kong.plugins.base_plugin" 2 | local responses = require "kong.tools.responses" 3 | local jwt_decoder = require "kong.plugins.jwt.jwt_parser" 4 | local req_set_header = ngx.req.set_header 5 | local ngx_re_gmatch = ngx.re.gmatch 6 | 7 | local JwtClaimsValidateHandler = BasePlugin:extend() 8 | 9 | local function retrieve_token(request, conf) 10 | local uri_parameters = request.get_uri_args() 11 | 12 | for _, v in ipairs(conf.uri_param_names) do 13 | if uri_parameters[v] then 14 | return uri_parameters[v] 15 | end 16 | end 17 | 18 | local authorization_header = request.get_headers()["authorization"] 19 | if authorization_header then 20 | local iterator, iter_err = ngx_re_gmatch(authorization_header, "\\s*[Bb]earer\\s+(.+)") 21 | if not iterator then 22 | return nil, iter_err 23 | end 24 | 25 | local m, err = iterator() 26 | if err then 27 | return nil, err 28 | end 29 | 30 | if m and #m > 0 then 31 | return m[1] 32 | end 33 | end 34 | end 35 | 36 | function JwtClaimsValidateHandler:new() 37 | JwtClaimsValidateHandler.super.new(self, "jwt-claims-headers") 38 | end 39 | 40 | function JwtClaimsValidateHandler:access(conf) 41 | JwtClaimsValidateHandler.super.access(self) 42 | local continue_on_error = conf.continue_on_error 43 | 44 | local token, err = retrieve_token(ngx.req, conf) 45 | if err and not continue_on_error then 46 | return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) 47 | end 48 | 49 | if not token and not continue_on_error then 50 | return responses.send_HTTP_UNAUTHORIZED() 51 | end 52 | 53 | local jwt, err = jwt_decoder:new(token) 54 | if err and not continue_on_error then 55 | return responses.send_HTTP_INTERNAL_SERVER_ERROR() 56 | end 57 | 58 | local claims = jwt.claims 59 | for claim_key,claim_value in pairs(conf.claims) do 60 | if claims[claim_key] == nil or claims[claim_key] ~= claim_value then 61 | return responses.send_HTTP_UNAUTHORIZED("JSON Web Token has invalid claim value for '"..claim_key.."'") 62 | end 63 | end 64 | end 65 | 66 | return JwtClaimsValidateHandler -------------------------------------------------------------------------------- /kong-plugin-jwt-claims-validate-1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "kong-plugin-jwt-claims-validate" 2 | version = "1.0-1" 3 | source = { 4 | url = "git+https://github.com/wshirey/kong-plugin-jwt-claims-validate.git", 5 | tag = "v1.0" 6 | } 7 | description = { 8 | summary = "A Kong plugin to check JWT claim values", 9 | homepage = "https://github.com/wshirey/kong-plugin-jwt-claims-validate", 10 | license = "MIT" 11 | } 12 | dependencies = { 13 | "lua ~> 5.1" 14 | } 15 | build = { 16 | type = "builtin", 17 | modules = { 18 | ["kong.plugins.jwt-claims-validate.handler"] = "handler.lua", 19 | ["kong.plugins.jwt-claims-validate.schema"] = "schema.lua" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /schema.lua: -------------------------------------------------------------------------------- 1 | local cjson = require "cjson" 2 | 3 | local function claim_check(value, conf) 4 | local valid_types = { 5 | ["string"] = true, 6 | ["boolean"] = true, 7 | ["number"] = true 8 | } 9 | 10 | for k,v in pairs(value) do 11 | local type = type(v) 12 | if not valid_types[type] or type == cjson.null then 13 | return false, "'claims."..k.."' is not one following types: [boolean, string, number]" 14 | end 15 | 16 | return true, nil 17 | end 18 | end 19 | 20 | return { 21 | no_consumer = true, 22 | fields = { 23 | uri_param_names = {type = "array", default = {"jwt"}}, 24 | claims = { type = "table", func = claim_check } 25 | } 26 | } --------------------------------------------------------------------------------