Initial commit - realms platform

This commit is contained in:
doomtube 2026-01-05 22:54:27 -05:00
parent c590ab6d18
commit c717c3751c
234 changed files with 74103 additions and 15231 deletions

167
openresty/lua/jwt.lua Normal file
View file

@ -0,0 +1,167 @@
-- JWT validation module for OpenResty edge authentication
-- Validates JWT tokens before requests hit the backend
local jwt = require "resty.jwt"
local _M = {}
-- Get JWT secret (cached after first call)
local jwt_secret_cache = nil
local function get_jwt_secret()
if jwt_secret_cache then
return jwt_secret_cache
end
-- os.getenv works in OpenResty when 'env JWT_SECRET;' is in nginx.conf
local secret = os.getenv("JWT_SECRET")
if secret and secret ~= "" then
jwt_secret_cache = secret
ngx.log(ngx.INFO, "JWT_SECRET loaded successfully (", string.len(secret), " chars)")
return secret
end
ngx.log(ngx.ERR, "JWT_SECRET environment variable not set or empty")
return nil
end
-- Validate JWT and inject user headers for backend
-- Call this in access_by_lua_block for authenticated endpoints
function _M.validate_and_inject()
-- Get token from httpOnly cookie (name: auth_token)
local token = ngx.var.cookie_auth_token
if not token or token == "" then
ngx.log(ngx.DEBUG, "No auth_token cookie found")
ngx.status = 401
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"No authentication token"}')
return ngx.exit(401)
end
-- Get JWT secret
local secret = get_jwt_secret()
if not secret then
-- SECURITY FIX: Fail closed - do NOT allow requests through if secret is missing
-- This prevents authentication bypass if env var is misconfigured
ngx.log(ngx.ERR, "JWT_SECRET not configured - blocking request")
ngx.status = 500
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"Server configuration error"}')
return ngx.exit(500)
end
-- Verify JWT signature and decode
local jwt_obj = jwt:verify(secret, token)
if not jwt_obj then
ngx.log(ngx.ERR, "JWT verification returned nil")
ngx.status = 401
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"Invalid token"}')
return ngx.exit(401)
end
if not jwt_obj.verified then
local reason = jwt_obj.reason or "unknown"
ngx.log(ngx.WARN, "JWT verification failed: ", reason)
ngx.status = 401
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"Invalid token: ' .. reason .. '"}')
return ngx.exit(401)
end
-- Payload should exist if verified
local payload = jwt_obj.payload
if not payload then
ngx.log(ngx.ERR, "JWT verified but no payload")
ngx.status = 401
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"Invalid token structure"}')
return ngx.exit(401)
end
-- Check expiry (jwt library should handle this, but be safe)
local exp = payload.exp
if exp then
local now = ngx.time()
if exp < now then
ngx.log(ngx.DEBUG, "JWT expired: exp=", exp, " now=", now)
ngx.status = 401
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"Token expired"}')
return ngx.exit(401)
end
end
-- Check issuer
local iss = payload.iss
if iss ~= "streaming-app" then
ngx.log(ngx.WARN, "Invalid JWT issuer: ", tostring(iss))
ngx.status = 401
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"Invalid token issuer"}')
return ngx.exit(401)
end
-- SECURITY: Check if user account is disabled
local is_disabled = payload.is_disabled
if is_disabled == "1" or is_disabled == true then
ngx.log(ngx.WARN, "Disabled user attempted access: ", tostring(payload.username))
ngx.status = 403
ngx.header["Content-Type"] = "application/json"
ngx.say('{"error":"Account disabled"}')
return ngx.exit(403)
end
-- Inject user info headers for backend (allows backend to skip re-validation)
ngx.req.set_header("X-User-ID", tostring(payload.user_id or ""))
ngx.req.set_header("X-Username", tostring(payload.username or ""))
ngx.req.set_header("X-Is-Admin", tostring(payload.is_admin or "0"))
ngx.req.set_header("X-Is-Moderator", tostring(payload.is_moderator or "0"))
ngx.req.set_header("X-Is-Streamer", tostring(payload.is_streamer or "0"))
ngx.req.set_header("X-Is-Restreamer", tostring(payload.is_restreamer or "0"))
ngx.req.set_header("X-Token-Version", tostring(payload.token_version or "1"))
ngx.req.set_header("X-User-Color", tostring(payload.color_code or "#561D5E"))
ngx.req.set_header("X-Avatar-URL", tostring(payload.avatar_url or ""))
ngx.req.set_header("X-JWT-Validated", "true")
ngx.log(ngx.DEBUG, "JWT validated for user: ", tostring(payload.username))
end
-- Optional: Get user info without blocking (for endpoints that work with or without auth)
-- Returns user payload if authenticated, nil otherwise
function _M.get_user_if_authenticated()
local token = ngx.var.cookie_auth_token
if not token or token == "" then
return nil
end
local secret = get_jwt_secret()
if not secret then
return nil
end
local jwt_obj = jwt:verify(secret, token)
if not jwt_obj or not jwt_obj.verified then
return nil
end
local payload = jwt_obj.payload
if not payload then
return nil
end
-- Check expiry
if payload.exp and payload.exp < ngx.time() then
return nil
end
-- Check disabled
local is_disabled = payload.is_disabled
if is_disabled == "1" or is_disabled == true then
return nil
end
return payload
end
return _M