From 6c5d5fec129a29c6250b094f46881cfa5aaadcc7 Mon Sep 17 00:00:00 2001 From: rebecca Date: Thu, 17 Sep 2026 20:54:44 +0100 Subject: web: import from m1msq 15b348f --- web/session.lua | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 web/session.lua (limited to 'web/session.lua') diff --git a/web/session.lua b/web/session.lua new file mode 100644 index 0000000..3c6c297 --- /dev/null +++ b/web/session.lua @@ -0,0 +1,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} -- cgit v1.2.3