forked from devMls/nginx-jwt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnginx-jwt.lua
144 lines (115 loc) · 4.15 KB
/
nginx-jwt.lua
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
local jwt = require "resty.jwt"
local cjson = require "cjson"
local basexx = require "basexx"
local secret = os.getenv("JWT_SECRET")
assert(secret ~= nil, "Environment variable JWT_SECRET not set")
if os.getenv("JWT_SECRET_IS_BASE64_ENCODED") == 'true' then
-- convert from URL-safe Base64 to Base64
local r = #secret % 4
if r == 2 then
secret = secret .. "=="
elseif r == 3 then
secret = secret .. "="
end
secret = string.gsub(secret, "-", "+")
secret = string.gsub(secret, "_", "/")
-- convert from Base64 to UTF-8 string
secret = basexx.from_base64(secret)
end
local M = {}
function M.auth(claim_specs)
-- require Authorization request header
local auth_header = ngx.var.http_Authorization
local token = nil
token_site = os.getenv("NGINX_JWT_TOKEN_SITE")
if token_site == nil then
ngx.log(ngx.WARN, "No token site found, use default: HEADER")
token_site = "HEADER"
end
if token_site == "HEADER" then
if auth_header == nil then
ngx.log(ngx.WARN, "No Authorization header")
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
ngx.log(ngx.INFO, "Authorization: " .. auth_header)
-- require Bearer token
_, _, token = string.find(auth_header, "Bearer%s+(.+)")
end
if token_site == "COOKIE" then
token = ngx.var.cookie_bearer
end
if token_site == "REQUEST" then
token = ngx.var.arg_bearer
end
if token == nil then
ngx.log(ngx.WARN, "Missing token")
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
ngx.log(ngx.INFO, "Token: " .. token)
-- require valid JWT
local jwt_obj = jwt:verify(secret, token, 0)
if jwt_obj.verified == false then
ngx.log(ngx.WARN, "Invalid token: ".. jwt_obj.reason)
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
ngx.log(ngx.INFO, "JWT: " .. cjson.encode(jwt_obj))
-- optionally require specific claims
if claim_specs ~= nil then
--TODO: test
-- make sure they passed a Table
if type(claim_specs) ~= 'table' then
ngx.log(ngx.STDERR, "Configuration error: claim_specs arg must be a table")
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- process each claim
local blocking_claim = ""
for claim, spec in pairs(claim_specs) do
-- make sure token actually contains the claim
local claim_value = jwt_obj.payload[claim]
if claim_value == nil then
blocking_claim = claim .. " (missing)"
break
end
local spec_actions = {
-- claim spec is a string (pattern)
["string"] = function (pattern, val)
return string.match(val, pattern) ~= nil
end,
-- claim spec is a predicate function
["function"] = function (func, val)
-- convert truthy to true/false
if func(val) then
return true
else
return false
end
end
}
local spec_action = spec_actions[type(spec)]
-- make sure claim spec is a supported type
-- TODO: test
if spec_action == nil then
ngx.log(ngx.STDERR, "Configuration error: claim_specs arg claim '" .. claim .. "' must be a string or a table")
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- make sure token claim value satisfies the claim spec
if not spec_action(spec, claim_value) then
blocking_claim = claim
break
end
end
if blocking_claim ~= "" then
ngx.log(ngx.WARN, "User did not satisfy claim: ".. blocking_claim)
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
end
-- write the X-Auth-UserId header
ngx.header["X-Auth-UserId"] = jwt_obj.payload.sub
end
function M.table_contains(table, item)
for _, value in pairs(table) do
if value == item then return true end
end
return false
end
return M