├── .gitignore ├── LICENSE ├── README.md ├── jwt-0.5-2.rockspec ├── spec └── jwt_spec.lua └── src ├── jwt.lua └── jwt ├── jwe.lua ├── jws.lua ├── plain.lua └── utils.lua /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Olivine-Labs/lua-jwt/cd66386f0eab90333ae905234363c4591e9f035e/.gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Olivine Labs 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lua-jwt 2 | ======= 3 | 4 | JSON Web Token library 5 | 6 | http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-15 7 | -------------------------------------------------------------------------------- /jwt-0.5-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "jwt" 2 | version = "0.5-2" 3 | source = { 4 | url = "https://github.com/Olivine-Labs/lua-jwt/archive/v0.5.tar.gz", 5 | dir = "lua-jwt-0.5" 6 | } 7 | description = { 8 | summary = "A library for encoding and decoding json web tokens.", 9 | detailed = [[ 10 | ]], 11 | homepage = "", 12 | license = "MIT " 13 | } 14 | dependencies = { 15 | "lua >= 5.1", 16 | "busted >= 1.7-1", 17 | "luaossl >= 20151221-0", 18 | "lua-cjson-ol >= 1.0-1", 19 | "basexx >= 0.2.0-0" 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | ["jwt"] = "src/jwt.lua", 25 | ["jwt.jws"] = "src/jwt/jws.lua", 26 | ["jwt.jwe"] = "src/jwt/jwe.lua", 27 | ["jwt.plain"] = "src/jwt/plain.lua", 28 | ["jwt.utils"] = "src/jwt/utils.lua", 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spec/jwt_spec.lua: -------------------------------------------------------------------------------- 1 | describe("JWT spec", function() 2 | 3 | local jwt = require 'jwt' 4 | local crypto = pcall (require, 'crypto') and require 'crypto' 5 | local pkey = require 'jwt.utils'.pkey 6 | local plainJwt = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ" 7 | 8 | it("can decode a plain text token", function() 9 | local token, msg = jwt.decode(plainJwt) 10 | assert(token or error(msg)) 11 | assert(token.iss == "joe") 12 | assert(token.exp == 1300819380) 13 | assert(token["http://example.com/is_root"] == true) 14 | end) 15 | 16 | it("can encode a plain text token", function() 17 | local claim = { 18 | iss = "joe", 19 | exp = 1300819380, 20 | ["http://example.com/is_root"] = true 21 | } 22 | local token = jwt.encode(claim) 23 | assert.are.same(jwt.decode(token), claim) 24 | assert.are.same(jwt.decode(plainJwt), claim) 25 | end) 26 | 27 | it("it can encode/decode a signed plain text token with alg=HS256", function() 28 | local claims = { 29 | test = "test", 30 | } 31 | local token = jwt.encode(claims, {alg = "HS256", keys = {private = "key"}}) 32 | local decodedClaims = jwt.decode(token, {keys = {public = "key"}}) 33 | assert.are.same(claims, decodedClaims) 34 | end) 35 | 36 | it("it cannot encode/decode a signed plain text token with alg=HS256 and an incorrect key", function() 37 | local claims = { 38 | test = "test", 39 | } 40 | local token = jwt.encode(claims, {alg = "HS256", keys = {private = "key"}}) 41 | local decodedClaims = jwt.decode(token, {keys = {public = "notthekey"}}) 42 | assert.has_error(function() assert.are.same(claims, decodedClaims) end) 43 | end) 44 | 45 | it("it can encode/decode a signed plain text token with alg=RS256", function() 46 | local claims = { 47 | test = "test", 48 | empty={}, 49 | } 50 | local keys = { 51 | private = 52 | [[-----BEGIN RSA PRIVATE KEY----- 53 | MIIBOwIBAAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/D5z2A7KPYXUgUP0jd5yL 54 | Z7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQJBAJYXWNmw7Cgbkk1+v3C0 55 | dyeqHYF0UD5vtHLxs/BWLPI2lZO0e6ixFNI4uIuatBox1Zbzt1TSy8T09Slt4tNL 56 | CAECIQD6PHDGtKXcI2wUSvh4y8y7XfvwlwRPU2AzWZ1zvOCbbQIhANzgMpUNOZL2 57 | vakju4sal1yZeXUWO8FurmsAyotAx9tBAiB2oQKh4PAkXZKWSDhlI9CqHtMaaq17 58 | Yb5geaKARNGCPQIgILYrh5ufzT4xtJ0QJ3fWtuYb8NVMIEeuGTbSyHDdqIECIQDZ 59 | 3LNCyR2ykwetc6KqbQh3W4VkuatAQgMv5pNdFLrfmg== 60 | -----END RSA PRIVATE KEY-----]], 61 | public = 62 | [[-----BEGIN PUBLIC KEY----- 63 | MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/ 64 | D5z2A7KPYXUgUP0jd5yLZ7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQ== 65 | -----END PUBLIC KEY-----]], 66 | } 67 | local token, _ = jwt.encode(claims, {alg = "RS256", keys = { private = keys.private }}) 68 | local decodedClaims, err = jwt.decode(token, {keys = { public = keys.public }}) 69 | assert.table(decodedClaims, err) 70 | assert.are.same(claims, decodedClaims) 71 | end) 72 | 73 | if crypto then 74 | it("it cannot encode/decode a signed plain text token with alg=RS256 (using luacrypto pkey)", function() 75 | local claims = { 76 | test = "test", 77 | empty={}, 78 | } 79 | local key = crypto.pkey.generate("rsa", 512) 80 | local private_key = 81 | [[-----BEGIN RSA PRIVATE KEY----- 82 | MIIBOwIBAAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/D5z2A7KPYXUgUP0jd5yL 83 | Z7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQJBAJYXWNmw7Cgbkk1+v3C0 84 | dyeqHYF0UD5vtHLxs/BWLPI2lZO0e6ixFNI4uIuatBox1Zbzt1TSy8T09Slt4tNL 85 | CAECIQD6PHDGtKXcI2wUSvh4y8y7XfvwlwRPU2AzWZ1zvOCbbQIhANzgMpUNOZL2 86 | vakju4sal1yZeXUWO8FurmsAyotAx9tBAiB2oQKh4PAkXZKWSDhlI9CqHtMaaq17 87 | Yb5geaKARNGCPQIgILYrh5ufzT4xtJ0QJ3fWtuYb8NVMIEeuGTbSyHDdqIECIQDZ 88 | 3LNCyR2ykwetc6KqbQh3W4VkuatAQgMv5pNdFLrfmg== 89 | -----END RSA PRIVATE KEY-----]] 90 | local token, _ = jwt.encode(claims, {alg = "RS256", keys = { private = private_key }}) 91 | assert.has.error (function () 92 | jwt.encode(claims, {alg = "RS256", keys = {private = key}}) 93 | end) 94 | assert.has.error (function () 95 | jwt.decode(token, {keys = {public = key}}) 96 | end) 97 | end) 98 | end 99 | 100 | it("it cannot encode/decode a signed plain text token with alg=RS256 (using luaossl pkey)", function() 101 | local claims = { 102 | test = "test", 103 | empty={}, 104 | } 105 | local key = pkey.new{type = "rsa", bits=512} 106 | local private_key = 107 | [[-----BEGIN RSA PRIVATE KEY----- 108 | MIIBOwIBAAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/D5z2A7KPYXUgUP0jd5yL 109 | Z7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQJBAJYXWNmw7Cgbkk1+v3C0 110 | dyeqHYF0UD5vtHLxs/BWLPI2lZO0e6ixFNI4uIuatBox1Zbzt1TSy8T09Slt4tNL 111 | CAECIQD6PHDGtKXcI2wUSvh4y8y7XfvwlwRPU2AzWZ1zvOCbbQIhANzgMpUNOZL2 112 | vakju4sal1yZeXUWO8FurmsAyotAx9tBAiB2oQKh4PAkXZKWSDhlI9CqHtMaaq17 113 | Yb5geaKARNGCPQIgILYrh5ufzT4xtJ0QJ3fWtuYb8NVMIEeuGTbSyHDdqIECIQDZ 114 | 3LNCyR2ykwetc6KqbQh3W4VkuatAQgMv5pNdFLrfmg== 115 | -----END RSA PRIVATE KEY-----]] 116 | local token, _ = jwt.encode(claims, {alg = "RS256", keys = { private = private_key }}) 117 | assert.has.error (function () 118 | jwt.encode(claims, {alg = "RS256", keys = {private = key}}) 119 | end) 120 | assert.has.error (function () 121 | jwt.decode(token, {keys = {public = key}}) 122 | end) 123 | end) 124 | 125 | it("it cannot encode/decode a signed plain text token with alg=RS256 and an incorrect key", function() 126 | local claims = { 127 | test = "test", 128 | } 129 | local keys = { 130 | private = 131 | [[-----BEGIN RSA PRIVATE KEY----- 132 | MIIBOwIBAAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/D5z2A7KPYXUgUP0jd5yL 133 | Z7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQJBAJYXWNmw7Cgbkk1+v3C0 134 | dyeqHYF0UD5vtHLxs/BWLPI2lZO0e6ixFNI4uIuatBox1Zbzt1TSy8T09Slt4tNL 135 | CAECIQD6PHDGtKXcI2wUSvh4y8y7XfvwlwRPU2AzWZ1zvOCbbQIhANzgMpUNOZL2 136 | vakju4sal1yZeXUWO8FurmsAyotAx9tBAiB2oQKh4PAkXZKWSDhlI9CqHtMaaq17 137 | Yb5geaKARNGCPQIgILYrh5ufzT4xtJ0QJ3fWtuYb8NVMIEeuGTbSyHDdqIECIQDZ 138 | 3LNCyR2ykwetc6KqbQh3W4VkuatAQgMv5pNdFLrfmg== 139 | -----END RSA PRIVATE KEY-----]], 140 | public = 141 | [[-----BEGIN PUBLIC KEY----- 142 | MQwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/ 143 | D5z2A7KPYXUgUP0jd5yLZ7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQ== 144 | -----END PUBLIC KEY-----]], 145 | } 146 | local token = jwt.encode(claims, {alg = "RS256", keys = { private = keys.private }}) 147 | local decodedClaims = jwt.decode(token, {keys = { public = keys.public }}) 148 | assert.are_not.same(claims, decodedClaims) 149 | end) 150 | 151 | it("can verify a signature", function() 152 | local token = "eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJhOGZjZTFkZi1iNGFlLTRjNDEtYmFjNi1iNWJiZjI4MGMyOWMiLCJzdWIiOiI3YmZjNThlYy03N2RkLTQ4NzQtYmViZC1iYTg0MTAzMDEyNzkiLCJzY29wZSI6WyJvYXV0aC5hcHByb3ZhbHMiLCJvcGVuaWQiXSwiY2xpZW50X2lkIjoibG9naW4iLCJjaWQiOiJsb2dpbiIsImdyYW50X3R5cGUiOiJwYXNzd29yZCIsInVzZXJfaWQiOiI3YmZjNThlYy03N2RkLTQ4NzQtYmViZC1iYTg0MTAzMDEyNzkiLCJ1c2VyX25hbWUiOiJhZG1pbkBmb3Jpby5jb20iLCJlbWFpbCI6ImFkbWluQGZvcmlvLmNvbSIsImlhdCI6MTM5ODkwNjcyNywiZXhwIjoxMzk4OTQ5OTI3LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0Ojk3NjMvdWFhL29hdXRoL3Rva2VuIiwiYXVkIjpbIm9hdXRoIiwib3BlbmlkIl19.xOa5ZpXksgoaA_XJ3yHMjlLcbSoM6XJy-e60zfyP7bRmu0EKEGZdZrl2iJVh6OTIn8z6UuvcY282C1A5LtRgpir4wqhIrphd-Mi9gfxra0pJvtydd4XqVpuNdW7GDaC43VXpvUtetmfn-YAo2jkD9G22mUuT2sFdt5NqFL7Rk4tVRILes73OWxfQpuoReWvRBik-sJXxC9ADmTuzR36OvomIrso42R8aufU2ku_zPve8IhYLvn3vHmYCt0zNZkX-jSV8YtGodr9V-dKs9na41YvGp2UxkBcV7LKoGSRELSSNJ8JLF-bjO3zYSSbT42-yeHeKfoWAeP6R7S_0c_AYRA" 153 | local key = [[-----BEGIN PUBLIC KEY----- 154 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3h3hbKXM40yH18djU0eM 155 | asMIJ2jEtRn4DzJEcPvRDu+zFUzzNqSUFbD6pYIv/S+C31edIvyfi9kxMdZOKEIm 156 | AHasLJ6PTBej+ruzIWHNf2Yse7+egXEit5bcKb3J9FOpCDHE+YjM4S9QaQT2hr30 157 | Y7iIVcNURJn0k2T6HL+AVt0oUbupUdJjS9S5GUSQ0F74t74J9g7X4sOSTjl3RBxB 158 | mUzfYor3w1HVwP+R0awAzSlNYZdWWJJM6aZXH76nqfv6blKTW0on12b71YWRWKYP 159 | GxG1KwES6v5+PeLzlJDIDRcI8pl49fJYoXyasF8pskS63o9q8ibQspk+nzL9lD4E 160 | EQIDAQAB 161 | -----END PUBLIC KEY-----]] 162 | 163 | local claims, err = jwt.decode(token, {keys={public=key}}) 164 | assert(claims, err) 165 | end) 166 | 167 | it("Is not fooled by modified tokens that claim to be unsigned", function() 168 | local encoded = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTQ0NTQ3MDA3MywiZXhwIjoxNDQ1NDczNjczLCJpYXQiOjE0NDU0NzAwNzMsImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.NEbef_xsCzaLMU0Oh-Q_XLQyvF25vSIGlCcupi5-us05HFNhbTG7_2ElRmn0ew4DCBssT14GEU_8TjtcdmBkta7gCqKLF7X07UASVkL7lS_6VAu8lHGD4U4N-35AByu5gO2RQf5V3tt5WSpv2qABF4R_msF_qjZ8Ii8o_Fth6YxH6eDFgNAOCWwWwB3hvK2mJ6te9ZK04C00qc1U4xOFdO8geXaW7ohoXBCv1h8VT7sbsmyZ14ce6ASliHVCGjoXyXRGfFMQPKdJ5t4x_pSdH85MQn08nsYXIPCzMo3Fl2lFJyKbXLl3pMF1pwKpKpSxoCjbsWcjot1RmzpLbTcvfg' 169 | local token, err = jwt.decode(encoded) 170 | assert(not token) 171 | assert(err ~= nil) 172 | end) 173 | 174 | it("can encode and decode rs256", function() 175 | local keys = { 176 | private = 177 | [[-----BEGIN RSA PRIVATE KEY----- 178 | MIIBOwIBAAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/D5z2A7KPYXUgUP0jd5yL 179 | Z7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQJBAJYXWNmw7Cgbkk1+v3C0 180 | dyeqHYF0UD5vtHLxs/BWLPI2lZO0e6ixFNI4uIuatBox1Zbzt1TSy8T09Slt4tNL 181 | CAECIQD6PHDGtKXcI2wUSvh4y8y7XfvwlwRPU2AzWZ1zvOCbbQIhANzgMpUNOZL2 182 | vakju4sal1yZeXUWO8FurmsAyotAx9tBAiB2oQKh4PAkXZKWSDhlI9CqHtMaaq17 183 | Yb5geaKARNGCPQIgILYrh5ufzT4xtJ0QJ3fWtuYb8NVMIEeuGTbSyHDdqIECIQDZ 184 | 3LNCyR2ykwetc6KqbQh3W4VkuatAQgMv5pNdFLrfmg== 185 | -----END RSA PRIVATE KEY-----]], 186 | public = 187 | [[-----BEGIN PUBLIC KEY----- 188 | MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/ 189 | D5z2A7KPYXUgUP0jd5yLZ7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQ== 190 | -----END PUBLIC KEY-----]], 191 | } 192 | local claims = { 193 | test = "test", 194 | longClaim = "iubvn1oubv91henvicuqnw93bn19u ndij npkhabsdvlb23iou4bijbandlivubhql3ubvliuqwdbnvliuqwhv9ulqbhiulbiluabsdvuhbq9urbv9ubqubxuvbu9qbdshvuhqniuhv9uhbfq9uhr89hqu9ebnv9uqhu9rbvp9843$#BVCo²¸´no414i" 195 | } 196 | local token = jwt.encode(claims, {alg = "RS256", keys = keys}) 197 | local decodedClaims, err = jwt.decode(token, {keys = keys}) 198 | if not decodedClaims then error(err) end 199 | assert.are.same(claims, decodedClaims) 200 | end) 201 | 202 | it("does not throw an error when key is invalid in rs256", function() 203 | local keys = { 204 | private = 205 | [[-----BEGIN RSA PRIVATE KEY----- 206 | MIIBOwIBAAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/D5z2A7KPYXUgUP0jd5yL 207 | Z7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQJBAJYXWNmw7Cgbkk1+v3C0 208 | dyeqHYF0UD5vtHLxs/BWLPI2lZO0e6ixFNI4uIuatBox1Zbzt1TSy8T09Slt4tNL 209 | CAECIQD6PHDGtKXcI2wUSvh4y8y7XfvwlwRPU2AzWZ1zvOCbbQIhANzgMpUNOZL2 210 | vakju4sal1yZeXUWO8FurmsAyotAx9tBAiB2oQKh4PAkXZKWSDhlI9CqHtMaaq17 211 | Yb5geaKARNGCPQIgILYrh5ufzT4xtJ0QJ3fWtuYb8NVMIEeuGTbSyHDdqIECIQDZ 212 | 3LNCyR2ykwetc6KqbQh3W4VkuatAQgMv5pNdFLrfmg== 213 | -----END RSA PRIVATE KEY-----]], 214 | public = 215 | [[-----BEGIN PUBLIC KEY----- 216 | MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANfnFz7xPmYVdJxZE7sQ5quh/XUzB5y/ 217 | D5z2A7KPYXUgUP0jd5yLZ7+pVBcFSUm5AZXJLXH4jPVOXztcmiu4ta0CAwEAAQ== 218 | -----END PUBLIC KEY-----]], 219 | } 220 | local invalid_key = "a really invalid key" 221 | local data = { 222 | test = "test", 223 | } 224 | local token = jwt.encode(data, {alg = "RS256", keys = keys}) 225 | assert.has.no.error(function () 226 | local result, err = jwt.decode(token, {keys = {public = invalid_key }}) 227 | assert.is_nil(result) 228 | assert.is_not_nil(err) 229 | end) 230 | end) 231 | 232 | it("throws an error when failing to generate a signature", function() 233 | local invalid_key = "a really invalid key" 234 | local data = { 235 | test = "test", 236 | } 237 | assert.has_error(function () 238 | local token, err = jwt.encode(data, {alg = "RS256", keys = { private = invalid_key }}) 239 | assert.is_nil(token) 240 | assert.is_not_nil(err) 241 | end) 242 | end) 243 | end) 244 | -------------------------------------------------------------------------------- /src/jwt.lua: -------------------------------------------------------------------------------- 1 | local meta = {} 2 | local data = {} 3 | 4 | local json = require 'jwt.utils'.json 5 | local basexx = require 'basexx' 6 | local jws = require 'jwt.jws' 7 | local jwe = require 'jwt.jwe' 8 | local plain = require 'jwt.plain' 9 | 10 | local function getJwt(options) 11 | if options and options.alg and options.alg ~= "none" then 12 | if options.enc then 13 | return jwe 14 | else 15 | return jws 16 | end 17 | else 18 | return plain 19 | end 20 | end 21 | 22 | function meta:__metatable() 23 | return false 24 | end 25 | 26 | function meta:__index(key) 27 | return data[key] 28 | end 29 | 30 | local function header(options) 31 | if options then 32 | return { 33 | alg = options.alg, 34 | enc = options.enc, 35 | iss = options.iss, 36 | aud = options.aud, 37 | sub = options.sub, 38 | } 39 | end 40 | return { 41 | alg = "none", 42 | } 43 | end 44 | 45 | function data.encode(claims, options) 46 | local jwt = getJwt(options) 47 | local header = header(options) 48 | local token, err = jwt:encode(header, claims, options) 49 | if not token then return nil, err end 50 | return token 51 | end 52 | 53 | function data.decode(str, options) 54 | if not str then return nil, "Parameter 1 cannot be nil" end 55 | local dotFirst = str:find(".", nil, true) 56 | if not dotFirst then return nil, "Invalid token" end 57 | local header = json.decode((basexx.from_url64(str:sub(1,dotFirst-1)))) 58 | 59 | return getJwt(header):decode(header, str, options) 60 | end 61 | 62 | function meta:__newindex(key) 63 | error('Read Only') 64 | end 65 | 66 | return setmetatable({}, meta) 67 | -------------------------------------------------------------------------------- /src/jwt/jwe.lua: -------------------------------------------------------------------------------- 1 | local data = {} 2 | 3 | function data:encode(claims, options) 4 | return nil, "Not Implemented" 5 | end 6 | 7 | function data:decode(header, str, options) 8 | return nil, "Not Implemented" 9 | end 10 | 11 | return data 12 | -------------------------------------------------------------------------------- /src/jwt/jws.lua: -------------------------------------------------------------------------------- 1 | local basexx = require 'basexx' 2 | local utils = require 'jwt.utils' 3 | 4 | local pkey = utils.pkey 5 | local json = utils.json 6 | local HMAC = utils.HMAC 7 | local DIGEST = utils.DIGEST 8 | 9 | local data = {} 10 | 11 | local function tohex(s) 12 | local result = "" 13 | for i = 1, #s do 14 | result = result..string.format("%.2x", string.byte(s, i)) 15 | end 16 | return result 17 | end 18 | 19 | data.sign = { 20 | ['HS256'] = function(data, key) return HMAC.SHA256(key, data, true) end, 21 | ['HS384'] = function(data, key) return HMAC.SHA384(key, data, true) end, 22 | ['HS512'] = function(data, key) return HMAC.SHA512(key, data, true) end, 23 | ['RS256'] = function(data, key) 24 | assert(type(key) == "string") 25 | local ok, result = pcall(function() 26 | return pkey.sign('SHA256', key, data) 27 | end) 28 | if not ok then return nil, result end 29 | return result 30 | end, 31 | } 32 | 33 | data.verify = { 34 | ['HS256'] = function(data, signature, key) return signature == HMAC.SHA256(key, data, true) end, 35 | ['HS384'] = function(data, signature, key) return signature == HMAC.SHA384(key, data, true) end, 36 | ['HS512'] = function(data, signature, key) return signature == HMAC.SHA512(key, data, true) end, 37 | ['RS256'] = function(data, signature, key) 38 | assert(type(key) == "string") 39 | local ok, result = pcall(function() 40 | return pkey.verify('SHA256', key, signature, data) 41 | end) 42 | if not ok then return nil, result end 43 | return result 44 | end, 45 | } 46 | 47 | function data:encode(header, claims, options) 48 | if not options then error("options are required") end 49 | if not options.keys then error("keys are required") end 50 | local envelope = basexx.to_url64(json.encode(header)).."."..basexx.to_url64(json.encode(claims)) 51 | local signature, err = self.sign[header.alg](envelope, options.keys.private) 52 | if not signature then error('failed to generate signature') end 53 | return envelope ..'.'..basexx.to_url64(signature) 54 | end 55 | 56 | function data:decode(header, str, options) 57 | local dotFirst = str:find(".", nil, true) 58 | local signature 59 | local bodyStr 60 | local rawHeader = str:sub(1, dotFirst-1) 61 | local str = str:sub(dotFirst+1) 62 | local dotSecond = str:find(".", nil, true) 63 | if dotSecond then 64 | bodyStr = str:sub(1, dotSecond-1) 65 | if options and options.keys and options.keys.public then 66 | signature = basexx.from_url64(str:sub(dotSecond+1)) 67 | end 68 | else 69 | if options and options.keys and options.keys.public then 70 | return nil, "Invalid token" 71 | end 72 | bodyStr = str 73 | end 74 | 75 | local message = basexx.from_url64(bodyStr) 76 | if signature then 77 | if not self.verify[header.alg](rawHeader.."."..bodyStr, signature, options.keys.public) then 78 | return nil, "Invalid token" 79 | end 80 | end 81 | 82 | return json.decode(message) 83 | end 84 | 85 | return data 86 | -------------------------------------------------------------------------------- /src/jwt/plain.lua: -------------------------------------------------------------------------------- 1 | local basexx = require 'basexx' 2 | local json = require 'jwt.utils'.json 3 | 4 | local data = {} 5 | 6 | function data:encode(header, claims) 7 | return basexx.to_url64(json.encode(header)) .. "." .. basexx.to_url64(json.encode(claims)) 8 | end 9 | 10 | function data:decode(header, str, options) 11 | if options and options.keys then return nil, 'Token is alg=none but keys specified in decode' end 12 | local dotFirst = str:find("%.") 13 | str = str:sub(dotFirst+1) 14 | local dotSecond = str:find("%.") 15 | if dotSecond then return nil, 'Invalid Token, alg=none but has signature' end 16 | return json.decode(basexx.from_url64(str)) 17 | end 18 | 19 | return data 20 | -------------------------------------------------------------------------------- /src/jwt/utils.lua: -------------------------------------------------------------------------------- 1 | local function prequire(mod) 2 | local ok, m = pcall(require, mod) 3 | if not ok then return nil, m end 4 | return m 5 | end 6 | 7 | local json = prequire "cjson" 8 | if json and json.encode_empty_table then 9 | json.encode_empty_table('array') 10 | else 11 | json = require "dkjson" 12 | end 13 | 14 | local HMAC, DIGEST 15 | local pkey_new, pkey_sign, pkey_verify 16 | 17 | local function tohex(s) 18 | local result = "" 19 | for i = 1, #s do 20 | result = result..string.format("%.2x", string.byte(s, i)) 21 | end 22 | return result 23 | end 24 | 25 | if prequire "openssl.digest" then -- luaossl 26 | local digest = require 'openssl.digest' 27 | local hmac = require 'openssl.hmac' 28 | local pkey = require 'openssl.pkey' 29 | 30 | HMAC = { 31 | SHA256 = function(data, key, hex) 32 | local s = hmac.new(key, 'sha256'):final (data) 33 | if hex then s = tohex(s) end 34 | return s 35 | end; 36 | SHA384 = function(data, key, hex) 37 | local s = hmac.new(key, 'sha384'):final (data) 38 | if hex then s = tohex(s) end 39 | return s 40 | end; 41 | SHA512 = function(data, key, hex) 42 | local s = hmac.new(key, 'sha512'):final (data) 43 | if hex then s = tohex(s) end 44 | return s 45 | end; 46 | } 47 | 48 | DIGEST = { 49 | SHA256 = function(data, hex) 50 | local s = digest.new(key, 'sha256'):update (data) 51 | if hex then s = tohex(s) end 52 | return s 53 | end; 54 | SHA384 = function(data, hex) 55 | local s = digest.new(key, 'sha384'):update (data) 56 | if hex then s = tohex(s) end 57 | return s 58 | end; 59 | SHA512 = function(data, hex) 60 | local s = digest.new(key, 'sha512'):update (data) 61 | if hex then s = tohex(s) end 62 | return s 63 | end; 64 | } 65 | 66 | pkey_sign = function(digest, key, data) 67 | data = DIGEST[digest](data, false) 68 | return pkey.new(key):sign(data) 69 | end 70 | 71 | pkey_verify = function(digest, key, signature, data) 72 | data = DIGEST[digest](data, false) 73 | return pkey.new(key):verify(signature, data) 74 | end 75 | 76 | pkey_new = function(t) 77 | return pkey.new(t) 78 | end 79 | else -- lua-openssl 80 | local openssl = require "openssl" 81 | local digest = assert(openssl.digest) 82 | local hmac = assert(openssl.hmac) 83 | local pkey = assert(openssl.pkey) 84 | 85 | HMAC = { 86 | SHA256 = function(data, key, hex) return hmac.digest('sha256', key, data, not hex) end; 87 | SHA384 = function(data, key, hex) return hmac.digest('sha384', key, data, not hex) end; 88 | SHA512 = function(data, key, hex) return hmac.digest('sha512', key, data, not hex) end; 89 | } 90 | 91 | DIGEST = { 92 | SHA256 = function(data, hex) return digest.digest('sha256', data, not hex) end; 93 | SHA384 = function(data, hex) return digest.digest('sha384', data, not hex) end; 94 | SHA512 = function(data, hex) return digest.digest('sha512', data, not hex) end; 95 | } 96 | 97 | pkey_sign = function(digest, key, data) 98 | key = assert(pkey.read(key, true)) 99 | return pkey.sign(key, data, digest) 100 | end 101 | 102 | pkey_verify = function(digest, key, signature, data) 103 | key = assert(pkey.read(key, false)) 104 | return pkey.verify(key, data, signature, digest) 105 | end 106 | 107 | pkey_new = function(t) 108 | return pkey.new(t.type, t.bits) 109 | end 110 | end 111 | 112 | return { 113 | HMAC = HMAC; 114 | DIGEST = DIGEST; 115 | json = json; 116 | pkey = { 117 | new = pkey_new; 118 | verify = pkey_verify; 119 | sign = pkey_sign 120 | }; 121 | } 122 | --------------------------------------------------------------------------------