Software /
code /
prosody-modules
Comparison
mod_http_oauth2/mod_http_oauth2.lua @ 5254:b0ccdd12a70d
mod_http_oauth2: Prepare to handle multiple e.g. non-role scopes
This is to prepare to handle scopes like "openid" that don't map to
roles.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Thu, 16 Mar 2023 17:03:48 +0100 |
parent | 5252:85f0c6c1c24f |
child | 5255:001c8fdc91a4 |
comparison
equal
deleted
inserted
replaced
5253:d3b2d42daaee | 5254:b0ccdd12a70d |
---|---|
72 -- Tie it to the host if global | 72 -- Tie it to the host if global |
73 verification_key = hashes.hmac_sha256(registration_key, module.host); | 73 verification_key = hashes.hmac_sha256(registration_key, module.host); |
74 jwt_sign, jwt_verify = jwt.init(registration_algo, registration_key, registration_key, registration_options); | 74 jwt_sign, jwt_verify = jwt.init(registration_algo, registration_key, registration_key, registration_options); |
75 end | 75 end |
76 | 76 |
77 local function parse_scopes(scope_string) | |
78 return array(scope_string:gmatch("%S+")); | |
79 end | |
80 | |
77 local function filter_scopes(username, host, requested_scope_string) | 81 local function filter_scopes(username, host, requested_scope_string) |
78 if host ~= module.host then | 82 if host ~= module.host then |
79 return usermanager.get_jid_role(username.."@"..host, module.host).name; | 83 return usermanager.get_jid_role(username.."@"..host, module.host).name; |
80 end | 84 end |
81 | 85 |
82 if requested_scope_string then -- Specific role requested | 86 local selected_role, granted_scopes = nil, array(); |
83 -- TODO: The requested scope string is technically a space-delimited list | 87 |
84 -- of scopes, but for simplicity we're mapping this slot to role names. | 88 if requested_scope_string then -- Specific role(s) requested |
85 if usermanager.user_can_assume_role(username, module.host, requested_scope_string) then | 89 local requested_scopes = parse_scopes(requested_scope_string); |
86 return requested_scope_string; | 90 for _, scope in ipairs(requested_scopes) do |
87 end | 91 if selected_role == nil and usermanager.user_can_assume_role(username, module.host, scope) then |
88 end | 92 selected_role = scope; |
89 | 93 end |
90 return usermanager.get_user_role(username, module.host).name; | 94 end |
95 end | |
96 | |
97 if not selected_role then | |
98 -- By default use the users' default role | |
99 selected_role = usermanager.get_user_role(username, module.host).name; | |
100 end | |
101 granted_scopes:push(selected_role); | |
102 | |
103 return granted_scopes:concat(" "), selected_role; | |
91 end | 104 end |
92 | 105 |
93 local function code_expires_in(code) --> number, seconds until code expires | 106 local function code_expires_in(code) --> number, seconds until code expires |
94 return os.difftime(code.expires, os.time()); | 107 return os.difftime(code.expires, os.time()); |
95 end | 108 end |
138 -- client needs to be revoked | 151 -- client needs to be revoked |
139 local function client_subset(client) | 152 local function client_subset(client) |
140 return { name = client.client_name; uri = client.client_uri }; | 153 return { name = client.client_name; uri = client.client_uri }; |
141 end | 154 end |
142 | 155 |
143 local function new_access_token(token_jid, scope, ttl, client) | 156 local function new_access_token(token_jid, role, scope, ttl, client) |
144 local token_data; | 157 local token_data = {}; |
145 if client then | 158 if client then |
146 token_data = { oauth2_client = client_subset(client) }; | 159 token_data.oauth2_client = client_subset(client); |
147 end | 160 end |
148 local token = tokens.create_jid_token(token_jid, token_jid, scope, ttl, token_data, "oauth2"); | 161 if next(token_data) == nil then |
162 token_data = nil; | |
163 end | |
164 local token = tokens.create_jid_token(token_jid, token_jid, role, ttl, token_data, "oauth2"); | |
149 return { | 165 return { |
150 token_type = "bearer"; | 166 token_type = "bearer"; |
151 access_token = token; | 167 access_token = token; |
152 expires_in = ttl; | 168 expires_in = ttl; |
153 scope = scope; | 169 scope = scope; |
186 if not usermanager.test_password(request_username, request_host, request_password) then | 202 if not usermanager.test_password(request_username, request_host, request_password) then |
187 return oauth_error("invalid_grant", "incorrect credentials"); | 203 return oauth_error("invalid_grant", "incorrect credentials"); |
188 end | 204 end |
189 | 205 |
190 local granted_jid = jid.join(request_username, request_host, request_resource); | 206 local granted_jid = jid.join(request_username, request_host, request_resource); |
191 local granted_scopes = filter_scopes(request_username, request_host, params.scope); | 207 local granted_scopes, granted_role = filter_scopes(request_username, request_host, params.scope); |
192 return json.encode(new_access_token(granted_jid, granted_scopes, nil)); | 208 return json.encode(new_access_token(granted_jid, granted_role, granted_scopes, nil)); |
193 end | 209 end |
194 | 210 |
195 function response_type_handlers.code(client, params, granted_jid) | 211 function response_type_handlers.code(client, params, granted_jid) |
196 local request_username, request_host = jid.split(granted_jid); | 212 local request_username, request_host = jid.split(granted_jid); |
197 local granted_scopes = filter_scopes(request_username, request_host, params.scope); | 213 local granted_scopes, granted_role = filter_scopes(request_username, request_host, params.scope); |
198 | 214 |
199 local code = id.medium(); | 215 local code = id.medium(); |
200 local ok = codes:set(params.client_id .. "#" .. code, { | 216 local ok = codes:set(params.client_id .. "#" .. code, { |
201 expires = os.time() + 600; | 217 expires = os.time() + 600; |
202 granted_jid = granted_jid; | 218 granted_jid = granted_jid; |
203 granted_scopes = granted_scopes; | 219 granted_scopes = granted_scopes; |
220 granted_role = granted_role; | |
204 }); | 221 }); |
205 if not ok then | 222 if not ok then |
206 return {status_code = 429}; | 223 return {status_code = 429}; |
207 end | 224 end |
208 | 225 |
243 end | 260 end |
244 | 261 |
245 -- Implicit flow | 262 -- Implicit flow |
246 function response_type_handlers.token(client, params, granted_jid) | 263 function response_type_handlers.token(client, params, granted_jid) |
247 local request_username, request_host = jid.split(granted_jid); | 264 local request_username, request_host = jid.split(granted_jid); |
248 local granted_scopes = filter_scopes(request_username, request_host, params.scope); | 265 local granted_scopes, granted_role = filter_scopes(request_username, request_host, params.scope); |
249 local token_info = new_access_token(granted_jid, granted_scopes, nil, client); | 266 local token_info = new_access_token(granted_jid, granted_role, granted_scopes, nil, client); |
250 | 267 |
251 local redirect = url.parse(get_redirect_uri(client, params.redirect_uri)); | 268 local redirect = url.parse(get_redirect_uri(client, params.redirect_uri)); |
252 token_info.state = params.state; | 269 token_info.state = params.state; |
253 redirect.fragment = http.formencode(token_info); | 270 redirect.fragment = http.formencode(token_info); |
254 | 271 |
293 if not code or type(code) ~= "table" or code_expired(code) then | 310 if not code or type(code) ~= "table" or code_expired(code) then |
294 module:log("debug", "authorization_code invalid or expired: %q", code); | 311 module:log("debug", "authorization_code invalid or expired: %q", code); |
295 return oauth_error("invalid_client", "incorrect credentials"); | 312 return oauth_error("invalid_client", "incorrect credentials"); |
296 end | 313 end |
297 | 314 |
298 return json.encode(new_access_token(code.granted_jid, code.granted_scopes, nil, client)); | 315 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, nil, client)); |
299 end | 316 end |
300 | 317 |
301 -- Used to issue/verify short-lived tokens for the authorization process below | 318 -- Used to issue/verify short-lived tokens for the authorization process below |
302 local new_user_token, verify_user_token = jwt.init("HS256", random.bytes(32), nil, { default_ttl = 600 }); | 319 local new_user_token, verify_user_token = jwt.init("HS256", random.bytes(32), nil, { default_ttl = 600 }); |
303 | 320 |
412 if not request_host or request_host ~= module.host then | 429 if not request_host or request_host ~= module.host then |
413 return oauth_error("invalid_request", "invalid JID"); | 430 return oauth_error("invalid_request", "invalid JID"); |
414 end | 431 end |
415 if request_password == component_secret then | 432 if request_password == component_secret then |
416 local granted_jid = jid.join(request_username, request_host, request_resource); | 433 local granted_jid = jid.join(request_username, request_host, request_resource); |
417 return json.encode(new_access_token(granted_jid, nil, nil)); | 434 return json.encode(new_access_token(granted_jid, nil, nil, nil)); |
418 end | 435 end |
419 return oauth_error("invalid_grant", "incorrect credentials"); | 436 return oauth_error("invalid_grant", "incorrect credentials"); |
420 end | 437 end |
421 | 438 |
422 -- TODO How would this make sense with components? | 439 -- TODO How would this make sense with components? |