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.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/jwt-lib/basexx.lua Mon Jun 22 00:42:40 2015 -0400 1.3 @@ -0,0 +1,167 @@ 1.4 +------------------------------------------------------------------------------------------------------------------------ 1.5 +-- util functions 1.6 +------------------------------------------------------------------------------------------------------------------------ 1.7 + 1.8 +function divide_string( str, max, fillChar ) 1.9 + fillChar = fillChar or "" 1.10 + local result = {} 1.11 + 1.12 + local start = 1 1.13 + for i = 1, #str do 1.14 + if i % max == 0 then 1.15 + table.insert( result, str:sub( start, i ) ) 1.16 + start = i + 1 1.17 + elseif i == #str then 1.18 + table.insert( result, str:sub( start, i ) ) 1.19 + end 1.20 + end 1.21 + 1.22 + return result 1.23 +end 1.24 + 1.25 +function number_to_bit( num, length ) 1.26 + local bits = {} 1.27 + 1.28 + while num > 0 do 1.29 + local rest = math.fmod( num, 2 ) 1.30 + table.insert( bits, rest ) 1.31 + num = ( num - rest ) / 2 1.32 + end 1.33 + 1.34 + while #bits < length do 1.35 + table.insert( bits, "0" ) 1.36 + end 1.37 + 1.38 + return string.reverse( table.concat( bits ) ) 1.39 +end 1.40 + 1.41 +------------------------------------------------------------------------------------------------------------------------ 1.42 + 1.43 +local basexx = {} 1.44 + 1.45 +------------------------------------------------------------------------------------------------------------------------ 1.46 +-- base2(bitfield) decode and encode function 1.47 +------------------------------------------------------------------------------------------------------------------------ 1.48 + 1.49 +local bitMap = { o = "0", i = "1", l = "1" } 1.50 + 1.51 +function basexx.from_bit( str ) 1.52 + str = string.lower( str ) 1.53 + str = str:gsub( '[ilo]', function( c ) return bitMap[ c ] end ) 1.54 + return ( str:gsub( '........', function ( cc ) 1.55 + return string.char( tonumber( cc, 2 ) ) 1.56 + end ) ) 1.57 +end 1.58 + 1.59 +function basexx.to_bit( str ) 1.60 + return ( str:gsub( '.', function ( c ) 1.61 + local byte = string.byte( c ) 1.62 + local bits = {} 1.63 + for i = 1,8 do 1.64 + table.insert( bits, byte % 2 ) 1.65 + byte = math.floor( byte / 2 ) 1.66 + end 1.67 + return table.concat( bits ):reverse() 1.68 + end ) ) 1.69 +end 1.70 + 1.71 +------------------------------------------------------------------------------------------------------------------------ 1.72 +-- base16(hex) decode and encode function 1.73 +------------------------------------------------------------------------------------------------------------------------ 1.74 + 1.75 +function basexx.from_hex( str ) 1.76 + return ( str:gsub( '..', function ( cc ) 1.77 + return string.char( tonumber( cc, 16 ) ) 1.78 + end ) ) 1.79 +end 1.80 + 1.81 +function basexx.to_hex( str ) 1.82 + return ( str:gsub( '.', function ( c ) 1.83 + return string.format('%02X', string.byte( c ) ) 1.84 + end ) ) 1.85 +end 1.86 + 1.87 +------------------------------------------------------------------------------------------------------------------------ 1.88 +-- generic function to decode and encode base32/base64 1.89 +------------------------------------------------------------------------------------------------------------------------ 1.90 + 1.91 +local function from_basexx( str, alphabet, bits ) 1.92 + local result = {} 1.93 + for i = 1, #str do 1.94 + local c = string.sub( str, i, i ) 1.95 + if c ~= '=' then 1.96 + local index = string.find( alphabet, c ) 1.97 + table.insert( result, number_to_bit( index - 1, bits ) ) 1.98 + end 1.99 + end 1.100 + 1.101 + local value = table.concat( result ) 1.102 + local pad = #value % 8 1.103 + return basexx.from_bit( string.sub( value, 1, #value - pad ) ) 1.104 +end 1.105 + 1.106 +local function to_basexx( str, alphabet, bits, pad ) 1.107 + local bitString = basexx.to_bit( str ) 1.108 + 1.109 + local chunks = divide_string( bitString, bits ) 1.110 + local result = {} 1.111 + for key,value in ipairs( chunks ) do 1.112 + if ( #value < bits ) then 1.113 + value = value .. string.rep( '0', bits - #value ) 1.114 + end 1.115 + local pos = tonumber( value, 2 ) + 1 1.116 + table.insert( result, alphabet:sub( pos, pos ) ) 1.117 + end 1.118 + 1.119 + table.insert( result, pad ) 1.120 + return table.concat( result ) 1.121 +end 1.122 + 1.123 +------------------------------------------------------------------------------------------------------------------------ 1.124 +-- rfc 3548: http://www.rfc-editor.org/rfc/rfc3548.txt 1.125 +------------------------------------------------------------------------------------------------------------------------ 1.126 + 1.127 +local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" 1.128 + 1.129 +function basexx.from_base32( str ) 1.130 + return from_basexx( string.upper( str ), base32Alphabet, 5 ) 1.131 +end 1.132 + 1.133 +function basexx.to_base32( str ) 1.134 + return to_basexx( str, base32Alphabet, 5, ({ '', '======', '====', '===', '=' })[ #str % 5 + 1 ] ) 1.135 +end 1.136 + 1.137 +------------------------------------------------------------------------------------------------------------------------ 1.138 +-- crockford: http://www.crockford.com/wrmg/base32.html 1.139 +------------------------------------------------------------------------------------------------------------------------ 1.140 + 1.141 +local crockfordAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" 1.142 +local crockfordMap = { O = "0", I = "1", L = "1", U = "V" } 1.143 + 1.144 +function basexx.from_crockford( str ) 1.145 + str = string.upper( str ) 1.146 + str = str:gsub( '[ILOU]', function( c ) return crockfordMap[ c ] end ) 1.147 + return from_basexx( str, crockfordAlphabet, 5 ) 1.148 +end 1.149 + 1.150 +function basexx.to_crockford( str ) 1.151 + return to_basexx( str, crockfordAlphabet, 5, "" ) 1.152 +end 1.153 + 1.154 +------------------------------------------------------------------------------------------------------------------------ 1.155 +-- base64 decode and encode function 1.156 +------------------------------------------------------------------------------------------------------------------------ 1.157 + 1.158 +local base64Alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 1.159 + 1.160 +function basexx.from_base64( str ) 1.161 + return from_basexx( str, base64Alphabet, 6 ) 1.162 +end 1.163 + 1.164 +function basexx.to_base64( str ) 1.165 + return to_basexx( str, base64Alphabet, 6, ({ '', '==', '=' })[ #str % 3 + 1 ] ) 1.166 +end 1.167 + 1.168 +------------------------------------------------------------------------------------------------------------------------ 1.169 + 1.170 +return basexx