nginx
nginx/jwt-lib/basexx.lua
First basic pass at JWT auth. Mostly just a fork of https://github.com/ficusio/openresty, with a few twists: * We've narrowed down some of the configuration options, and we're passing more headers (essentially exposing all the data in the JWT as headers). * We no longer automatically return a 401 unauthorized if the JWT verification fails; we just don't assign it the headers. The consuming service can decide whether or not they want to accept the request. * We automatically fail the verification of a JWT if the token has expired in the last minute (or shouldn't be used for the next minute). If the token has expired, we return a 401 that our clients can catch and use a refresh token automatically from. If the token can't be used for another minute, we quietly just refuse to add auth headers to the request.
| paddy@0 | 1 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 2 -- util functions |
| paddy@0 | 3 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 4 |
| paddy@0 | 5 function divide_string( str, max, fillChar ) |
| paddy@0 | 6 fillChar = fillChar or "" |
| paddy@0 | 7 local result = {} |
| paddy@0 | 8 |
| paddy@0 | 9 local start = 1 |
| paddy@0 | 10 for i = 1, #str do |
| paddy@0 | 11 if i % max == 0 then |
| paddy@0 | 12 table.insert( result, str:sub( start, i ) ) |
| paddy@0 | 13 start = i + 1 |
| paddy@0 | 14 elseif i == #str then |
| paddy@0 | 15 table.insert( result, str:sub( start, i ) ) |
| paddy@0 | 16 end |
| paddy@0 | 17 end |
| paddy@0 | 18 |
| paddy@0 | 19 return result |
| paddy@0 | 20 end |
| paddy@0 | 21 |
| paddy@0 | 22 function number_to_bit( num, length ) |
| paddy@0 | 23 local bits = {} |
| paddy@0 | 24 |
| paddy@0 | 25 while num > 0 do |
| paddy@0 | 26 local rest = math.fmod( num, 2 ) |
| paddy@0 | 27 table.insert( bits, rest ) |
| paddy@0 | 28 num = ( num - rest ) / 2 |
| paddy@0 | 29 end |
| paddy@0 | 30 |
| paddy@0 | 31 while #bits < length do |
| paddy@0 | 32 table.insert( bits, "0" ) |
| paddy@0 | 33 end |
| paddy@0 | 34 |
| paddy@0 | 35 return string.reverse( table.concat( bits ) ) |
| paddy@0 | 36 end |
| paddy@0 | 37 |
| paddy@0 | 38 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 39 |
| paddy@0 | 40 local basexx = {} |
| paddy@0 | 41 |
| paddy@0 | 42 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 43 -- base2(bitfield) decode and encode function |
| paddy@0 | 44 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 45 |
| paddy@0 | 46 local bitMap = { o = "0", i = "1", l = "1" } |
| paddy@0 | 47 |
| paddy@0 | 48 function basexx.from_bit( str ) |
| paddy@0 | 49 str = string.lower( str ) |
| paddy@0 | 50 str = str:gsub( '[ilo]', function( c ) return bitMap[ c ] end ) |
| paddy@0 | 51 return ( str:gsub( '........', function ( cc ) |
| paddy@0 | 52 return string.char( tonumber( cc, 2 ) ) |
| paddy@0 | 53 end ) ) |
| paddy@0 | 54 end |
| paddy@0 | 55 |
| paddy@0 | 56 function basexx.to_bit( str ) |
| paddy@0 | 57 return ( str:gsub( '.', function ( c ) |
| paddy@0 | 58 local byte = string.byte( c ) |
| paddy@0 | 59 local bits = {} |
| paddy@0 | 60 for i = 1,8 do |
| paddy@0 | 61 table.insert( bits, byte % 2 ) |
| paddy@0 | 62 byte = math.floor( byte / 2 ) |
| paddy@0 | 63 end |
| paddy@0 | 64 return table.concat( bits ):reverse() |
| paddy@0 | 65 end ) ) |
| paddy@0 | 66 end |
| paddy@0 | 67 |
| paddy@0 | 68 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 69 -- base16(hex) decode and encode function |
| paddy@0 | 70 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 71 |
| paddy@0 | 72 function basexx.from_hex( str ) |
| paddy@0 | 73 return ( str:gsub( '..', function ( cc ) |
| paddy@0 | 74 return string.char( tonumber( cc, 16 ) ) |
| paddy@0 | 75 end ) ) |
| paddy@0 | 76 end |
| paddy@0 | 77 |
| paddy@0 | 78 function basexx.to_hex( str ) |
| paddy@0 | 79 return ( str:gsub( '.', function ( c ) |
| paddy@0 | 80 return string.format('%02X', string.byte( c ) ) |
| paddy@0 | 81 end ) ) |
| paddy@0 | 82 end |
| paddy@0 | 83 |
| paddy@0 | 84 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 85 -- generic function to decode and encode base32/base64 |
| paddy@0 | 86 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 87 |
| paddy@0 | 88 local function from_basexx( str, alphabet, bits ) |
| paddy@0 | 89 local result = {} |
| paddy@0 | 90 for i = 1, #str do |
| paddy@0 | 91 local c = string.sub( str, i, i ) |
| paddy@0 | 92 if c ~= '=' then |
| paddy@0 | 93 local index = string.find( alphabet, c ) |
| paddy@0 | 94 table.insert( result, number_to_bit( index - 1, bits ) ) |
| paddy@0 | 95 end |
| paddy@0 | 96 end |
| paddy@0 | 97 |
| paddy@0 | 98 local value = table.concat( result ) |
| paddy@0 | 99 local pad = #value % 8 |
| paddy@0 | 100 return basexx.from_bit( string.sub( value, 1, #value - pad ) ) |
| paddy@0 | 101 end |
| paddy@0 | 102 |
| paddy@0 | 103 local function to_basexx( str, alphabet, bits, pad ) |
| paddy@0 | 104 local bitString = basexx.to_bit( str ) |
| paddy@0 | 105 |
| paddy@0 | 106 local chunks = divide_string( bitString, bits ) |
| paddy@0 | 107 local result = {} |
| paddy@0 | 108 for key,value in ipairs( chunks ) do |
| paddy@0 | 109 if ( #value < bits ) then |
| paddy@0 | 110 value = value .. string.rep( '0', bits - #value ) |
| paddy@0 | 111 end |
| paddy@0 | 112 local pos = tonumber( value, 2 ) + 1 |
| paddy@0 | 113 table.insert( result, alphabet:sub( pos, pos ) ) |
| paddy@0 | 114 end |
| paddy@0 | 115 |
| paddy@0 | 116 table.insert( result, pad ) |
| paddy@0 | 117 return table.concat( result ) |
| paddy@0 | 118 end |
| paddy@0 | 119 |
| paddy@0 | 120 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 121 -- rfc 3548: http://www.rfc-editor.org/rfc/rfc3548.txt |
| paddy@0 | 122 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 123 |
| paddy@0 | 124 local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" |
| paddy@0 | 125 |
| paddy@0 | 126 function basexx.from_base32( str ) |
| paddy@0 | 127 return from_basexx( string.upper( str ), base32Alphabet, 5 ) |
| paddy@0 | 128 end |
| paddy@0 | 129 |
| paddy@0 | 130 function basexx.to_base32( str ) |
| paddy@0 | 131 return to_basexx( str, base32Alphabet, 5, ({ '', '======', '====', '===', '=' })[ #str % 5 + 1 ] ) |
| paddy@0 | 132 end |
| paddy@0 | 133 |
| paddy@0 | 134 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 135 -- crockford: http://www.crockford.com/wrmg/base32.html |
| paddy@0 | 136 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 137 |
| paddy@0 | 138 local crockfordAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" |
| paddy@0 | 139 local crockfordMap = { O = "0", I = "1", L = "1", U = "V" } |
| paddy@0 | 140 |
| paddy@0 | 141 function basexx.from_crockford( str ) |
| paddy@0 | 142 str = string.upper( str ) |
| paddy@0 | 143 str = str:gsub( '[ILOU]', function( c ) return crockfordMap[ c ] end ) |
| paddy@0 | 144 return from_basexx( str, crockfordAlphabet, 5 ) |
| paddy@0 | 145 end |
| paddy@0 | 146 |
| paddy@0 | 147 function basexx.to_crockford( str ) |
| paddy@0 | 148 return to_basexx( str, crockfordAlphabet, 5, "" ) |
| paddy@0 | 149 end |
| paddy@0 | 150 |
| paddy@0 | 151 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 152 -- base64 decode and encode function |
| paddy@0 | 153 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 154 |
| paddy@0 | 155 local base64Alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' |
| paddy@0 | 156 |
| paddy@0 | 157 function basexx.from_base64( str ) |
| paddy@0 | 158 return from_basexx( str, base64Alphabet, 6 ) |
| paddy@0 | 159 end |
| paddy@0 | 160 |
| paddy@0 | 161 function basexx.to_base64( str ) |
| paddy@0 | 162 return to_basexx( str, base64Alphabet, 6, ({ '', '==', '=' })[ #str % 3 + 1 ] ) |
| paddy@0 | 163 end |
| paddy@0 | 164 |
| paddy@0 | 165 ------------------------------------------------------------------------------------------------------------------------ |
| paddy@0 | 166 |
| paddy@0 | 167 return basexx |