1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
local base64 = require'base64'
local hmac = require'openssl.hmac'
local pprint=require'pprint'
local json = require'dkjson'
local http_cookie = require'http.cookie'
local unpack=unpack or table.unpack
local t_enc = base64.makeencoder('-','_')
local t_dec = base64.makedecoder('-','_')
local function enc(s) return base64.encode(s,t_enc):gsub('=','') end
local function dec(s) return base64.decode(s..("="):rep((4-#s)%4),t_dec) end
local function encode(app, fields)
local header = enc(json.encode{alg="HS256",typ="JWT"})
fields.exp = os.time()+app.session_ttl
local payload = enc(json.encode(fields))
local front = header..'.'..payload
local h = hmac.new(app.secret_key,'sha256')
local sig = enc(h:final(front))
return front..'.'..sig
end
local function _decode(app, token)
local parts = {}
for s in token:gmatch("[^%.]+") do table.insert(parts,s) end
assert(#parts==3) local front=parts[1]..'.'..parts[2]
local dparts = {} for i,v in ipairs(parts) do dparts[i]=dec(v) end
local header,payload,sig = unpack(dparts)
header=assert(json.decode(header)) payload=assert(json.decode(payload))
assert(header.typ=='JWT' and header.alg=='HS256','bad header')
assert(type(payload.exp)=='number' and payload.exp > os.time(),'expired')
local nsig = hmac.new(app.secret_key,'sha256'):final(front)
assert(nsig==sig,'bad signature')
return payload
end
local function decode(...) return pcall(_decode,...) end
local function pre_req(req)
if req.cookies.SESSION then
local ok, val = decode(req.app,req.cookies.SESSION)
if ok then req.session = val return
else print('\trejected session: '..val) end
end
req.session = setmetatable({},{__jsontype='object'})
end
local function post_req(req, body, headers, status)
if req.session then headers:append('set-cookie',
http_cookie.bake('SESSION',encode(req.app,req.session),
os.time()+req.app.session_ttl, nil, '/'))
end end
local function install(app)
assert(app.session_ttl, 'missing app.session_ttl')
assert(app.secret_key, 'missing app.secret_key')
app:add_hook('pre_req',pre_req)
app:add_hook('post_req',post_req)
end
return {install=install}
|