1
1
2
2
-- import requirements
3
- local cjson = require " cjson"
3
+
4
+ -- allow either cjson, or th-LuaJSON
5
+ local has_cjson , jsonmod = pcall (require , " cjson" )
6
+ if not has_cjson then
7
+ jsonmod = require " json"
8
+ end
4
9
5
10
-- Ubuntu broke the install. Puts the source in /usr/share/lua/5.1/https.lua,
6
11
-- but since the source defines itself as the module "ssl.https", after we
7
- -- load the source, we need to grab the actual thing. Building from source
8
- -- wasn't practical.
9
- -- TODO: make this more generic but still work with Ubuntu
10
- require " https" --
12
+ -- load the source, we need to grab the actual thing.
13
+ pcall (require ," https" )
11
14
local https = require " ssl.https" -- /usr/share/lua/5.1/https.lua
12
15
local ltn12 = require (" ltn12" )
13
16
17
+ local uri = ngx .var .uri
18
+ local uri_args = ngx .req .get_uri_args ()
19
+ local scheme = ngx .var .scheme
20
+ local server_name = ngx .var .server_name
21
+
14
22
-- setup some app-level vars
15
23
local client_id = ngx .var .ngo_client_id
16
24
local client_secret = ngx .var .ngo_client_secret
17
25
local domain = ngx .var .ngo_domain
18
- local cb_scheme = ngx .var .ngo_callback_scheme or ngx . var . scheme
19
- local cb_server_name = ngx .var .ngo_callback_host or ngx . var . server_name
26
+ local cb_scheme = ngx .var .ngo_callback_scheme or scheme
27
+ local cb_server_name = ngx .var .ngo_callback_host or server_name
20
28
local cb_uri = ngx .var .ngo_callback_uri or " /_oauth"
21
29
local cb_url = cb_scheme .. " ://" .. cb_server_name .. cb_uri
30
+ local redir_url = cb_scheme .. " ://" .. cb_server_name .. uri
22
31
local signout_uri = ngx .var .ngo_signout_uri or " /_signout"
23
32
local debug = ngx .var .ngo_debug
24
33
local whitelist = ngx .var .ngo_whitelist
25
34
local blacklist = ngx .var .ngo_blacklist
26
35
local secure_cookies = ngx .var .ngo_secure_cookies
27
-
28
- local uri_args = ngx .req .get_uri_args ()
36
+ local token_secret = ngx .var .ngo_token_secret or " UNSET"
37
+ local set_user = ngx .var .ngo_user
38
+ local email_as_user = ngx .var .ngo_email_as_user
39
+
40
+ -- Force the user to set a token secret
41
+ if token_secret == " UNSET" then
42
+ ngx .log (ngx .ERR , " $ngo_token_secret must be set in Nginx config!" )
43
+ return ngx .exit (ngx .HTTP_UNAUTHORIZED )
44
+ end
29
45
30
46
-- See https://developers.google.com/accounts/docs/OAuth2WebServer
31
- if ngx . var . uri == signout_uri then
47
+ if uri == signout_uri then
32
48
ngx .header [" Set-Cookie" ] = " AccessToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"
33
- return ngx .redirect (ngx . var . scheme .. " ://" .. ngx . var .server_name )
49
+ return ngx .redirect (cb_scheme .. " ://" .. server_name )
34
50
end
35
51
36
- if not ngx .var .cookie_AccessToken then
52
+ -- Enforce token security and expiration
53
+ local oauth_expires = tonumber (ngx .var .cookie_OauthExpires ) or 0
54
+ local oauth_email = ngx .unescape_uri (ngx .var .cookie_OauthEmail or " " )
55
+ local oauth_access_token = ngx .unescape_uri (ngx .var .cookie_OauthAccessToken or " " )
56
+ local expected_token = ngx .encode_base64 (ngx .hmac_sha1 (token_secret , cb_server_name .. oauth_email .. oauth_expires ))
57
+
58
+ if oauth_access_token == expected_token and oauth_expires and oauth_expires > ngx .time () then
59
+ -- Populate the nginx 'ngo_user' variable with our Oauth username, if requested
60
+ if set_user then
61
+ local oauth_user , oauth_domain = oauth_email :match (" ([^@]+)@(.+)" )
62
+ if email_as_user then
63
+ ngx .var .ngo_user = email
64
+ else
65
+ ngx .var .ngo_user = oauth_user
66
+ end
67
+ end
68
+ return
69
+ else
37
70
-- If no access token and this isn't the callback URI, redirect to oauth
38
- if ngx . var . uri ~= cb_uri then
71
+ if uri ~= cb_uri then
39
72
-- Redirect to the /oauth endpoint, request access to ALL scopes
40
- return ngx .redirect (" https://accounts.google.com/o/oauth2/auth?client_id=" .. client_id .. " &scope=email&response_type=code&redirect_uri=" .. ngx .escape_uri (cb_url ).. " &state=" .. ngx .escape_uri (ngx . var . uri ).. " &login_hint=" .. ngx .escape_uri (domain ))
73
+ return ngx .redirect (" https://accounts.google.com/o/oauth2/auth?client_id=" .. client_id .. " &scope=email&response_type=code&redirect_uri=" .. ngx .escape_uri (cb_url ).. " &state=" .. ngx .escape_uri (redir_url ).. " &login_hint=" .. ngx .escape_uri (domain ))
41
74
end
42
75
43
76
-- Fetch teh authorization code from the parameters
@@ -72,8 +105,9 @@ if not ngx.var.cookie_AccessToken then
72
105
end
73
106
74
107
-- use version 1 cookies so we don't have to encode. MSIE-old beware
75
- local json = cjson .decode ( res )
108
+ local json = jsonmod .decode ( res )
76
109
local access_token = json [" access_token" ]
110
+ local expires = ngx .time () + json [" expires_in" ]
77
111
local cookie_tail = " ;version=1;path=/;Max-Age=" .. json [" expires_in" ]
78
112
if secure_cookies then
79
113
cookie_tail = cookie_tail .. " ;secure"
@@ -100,15 +134,18 @@ if not ngx.var.cookie_AccessToken then
100
134
ngx .log (ngx .ERR , " DEBUG: userinfo response " .. res2 .. code2 .. status2 .. table.concat (result_table ))
101
135
end
102
136
103
- json = cjson .decode ( table.concat (result_table ) )
137
+ json = jsonmod .decode ( table.concat (result_table ) )
104
138
105
139
local name = json [" name" ]
106
140
local email = json [" email" ]
107
141
local picture = json [" picture" ]
142
+ local token = ngx .encode_base64 (ngx .hmac_sha1 (token_secret , cb_server_name .. email .. expires ))
143
+
144
+ local oauth_user , oauth_domain = email :match (" ([^@]+)@(.+)" )
108
145
109
146
-- If no whitelist or blacklist, match on domain
110
- if not whitelist and not blacklist then
111
- if not string.find ( email , " @ " .. domain ) then
147
+ if not whitelist and not blacklist and domain then
148
+ if oauth_domain ~= domain then
112
149
if debug then
113
150
ngx .log (ngx .ERR , " DEBUG: " .. email .. " not in " .. domain )
114
151
end
@@ -117,7 +154,7 @@ if not ngx.var.cookie_AccessToken then
117
154
end
118
155
119
156
if whitelist then
120
- if not string.find (whitelist , email ) then
157
+ if not string.find (" " .. whitelist .. " " , " " .. email .. " " ) then
121
158
if debug then
122
159
ngx .log (ngx .ERR , " DEBUG: " .. email .. " not in whitelist" )
123
160
end
@@ -126,7 +163,7 @@ if not ngx.var.cookie_AccessToken then
126
163
end
127
164
128
165
if blacklist then
129
- if string.find (blacklist , email ) then
166
+ if string.find (" " .. blacklist .. " " , " " .. email .. " " ) then
130
167
if debug then
131
168
ngx .log (ngx .ERR , " DEBUG: " .. email .. " in blacklist" )
132
169
end
@@ -135,12 +172,22 @@ if not ngx.var.cookie_AccessToken then
135
172
end
136
173
137
174
ngx .header [" Set-Cookie" ] = {
138
- " AccessToken=" .. access_token .. cookie_tail ,
139
- " Name=" .. ngx .escape_uri (name ).. cookie_tail ,
140
- " Email=" .. ngx .escape_uri (email ).. cookie_tail ,
141
- " Picture=" .. ngx .escape_uri (picture ).. cookie_tail
175
+ " OauthAccessToken=" .. ngx .escape_uri (token ).. cookie_tail ,
176
+ " OauthExpires=" .. expires .. cookie_tail ,
177
+ " OauthName=" .. ngx .escape_uri (name ).. cookie_tail ,
178
+ " OauthEmail=" .. ngx .escape_uri (email ).. cookie_tail ,
179
+ " OauthPicture=" .. ngx .escape_uri (picture ).. cookie_tail
142
180
}
143
181
182
+ -- Poplate our ngo_user variable
183
+ if set_user then
184
+ if email_as_user then
185
+ ngx .var .ngo_user = email
186
+ else
187
+ ngx .var .ngo_user = oauth_user
188
+ end
189
+ end
190
+
144
191
-- Redirect
145
192
if debug then
146
193
ngx .log (ngx .ERR , " DEBUG: authorized " .. json [" email" ].. " , redirecting to " .. uri_args [" state" ])
0 commit comments