Mercurial > prosody-modules
comparison mod_http_oauth2/mod_http_oauth2.lua @ 5513:0005d4201030
mod_http_oauth2: Reject duplicate form-urlencoded parameters
Per RFC 6749 section 3.1
> Request and response parameters MUST NOT be included more than once.
Thanks to OAuch for pointing out
Also cleans up some of the icky behavior of formdecode(), like returning
a string if no '=' is included.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Fri, 02 Jun 2023 11:03:57 +0200 |
| parents | 1fbc8718bed6 |
| children | 61b8d3eb91a4 |
comparison
equal
deleted
inserted
replaced
| 5512:1fbc8718bed6 | 5513:0005d4201030 |
|---|---|
| 26 return function(k) | 26 return function(k) |
| 27 return t[k]; | 27 return t[k]; |
| 28 end | 28 end |
| 29 end | 29 end |
| 30 | 30 |
| 31 local function strict_formdecode(query) | |
| 32 if not query then | |
| 33 return nil; | |
| 34 end | |
| 35 local params = http.formdecode(query); | |
| 36 if type(params) ~= "table" then | |
| 37 return nil, "no-pairs"; | |
| 38 end | |
| 39 local dups = {}; | |
| 40 for _, pair in ipairs(params) do | |
| 41 if dups[pair.name] then | |
| 42 return nil, "duplicate"; | |
| 43 end | |
| 44 dups[pair.name] = true; | |
| 45 end | |
| 46 return params; | |
| 47 end | |
| 48 | |
| 31 local function read_file(base_path, fn, required) | 49 local function read_file(base_path, fn, required) |
| 32 local f, err = io.open(base_path .. "/" .. fn); | 50 local f, err = io.open(base_path .. "/" .. fn); |
| 33 if not f then | 51 if not f then |
| 34 module:log(required and "error" or "debug", "Unable to load template file: %s", err); | 52 module:log(required and "error" or "debug", "Unable to load template file: %s", err); |
| 35 if required then | 53 if required then |
| 368 return oauth_error("invalid_redirect_uri"); | 386 return oauth_error("invalid_redirect_uri"); |
| 369 end | 387 end |
| 370 | 388 |
| 371 local redirect = url.parse(redirect_uri); | 389 local redirect = url.parse(redirect_uri); |
| 372 | 390 |
| 373 local query = http.formdecode(redirect.query or ""); | 391 local query = strict_formdecode(redirect.query); |
| 374 if type(query) ~= "table" then query = {}; end | 392 if type(query) ~= "table" then query = {}; end |
| 375 table.insert(query, { name = "code", value = code }); | 393 table.insert(query, { name = "code", value = code }); |
| 376 table.insert(query, { name = "iss", value = get_issuer() }); | 394 table.insert(query, { name = "iss", value = get_issuer() }); |
| 377 if params.state then | 395 if params.state then |
| 378 table.insert(query, { name = "state", value = params.state }); | 396 table.insert(query, { name = "state", value = params.state }); |
| 531 local function get_auth_state(request) | 549 local function get_auth_state(request) |
| 532 local form = request.method == "POST" | 550 local form = request.method == "POST" |
| 533 and request.body | 551 and request.body |
| 534 and request.body ~= "" | 552 and request.body ~= "" |
| 535 and request.headers.content_type == "application/x-www-form-urlencoded" | 553 and request.headers.content_type == "application/x-www-form-urlencoded" |
| 536 and http.formdecode(request.body); | 554 and strict_formdecode(request.body); |
| 537 | 555 |
| 538 if type(form) ~= "table" then return {}; end | 556 if type(form) ~= "table" then return {}; end |
| 539 | 557 |
| 540 if not form.user_token then | 558 if not form.user_token then |
| 541 -- First step: login | 559 -- First step: login |
| 641 -- error directly to the user-agent. | 659 -- error directly to the user-agent. |
| 642 local function error_response(request, redirect_uri, err) | 660 local function error_response(request, redirect_uri, err) |
| 643 if not redirect_uri or redirect_uri == oob_uri then | 661 if not redirect_uri or redirect_uri == oob_uri then |
| 644 return render_error(err); | 662 return render_error(err); |
| 645 end | 663 end |
| 646 local q = request.url.query and http.formdecode(request.url.query); | 664 local q = strict_formdecode(request.url.query); |
| 647 local redirect_query = url.parse(redirect_uri); | 665 local redirect_query = url.parse(redirect_uri); |
| 648 local sep = redirect_query.query and "&" or "?"; | 666 local sep = redirect_query.query and "&" or "?"; |
| 649 redirect_uri = redirect_uri | 667 redirect_uri = redirect_uri |
| 650 .. sep .. http.formencode(err.extra.oauth2_response) | 668 .. sep .. http.formencode(err.extra.oauth2_response) |
| 651 .. "&" .. http.formencode({ state = q.state, iss = get_issuer() }); | 669 .. "&" .. http.formencode({ state = q.state, iss = get_issuer() }); |
| 695 local credentials = get_request_credentials(event.request); | 713 local credentials = get_request_credentials(event.request); |
| 696 | 714 |
| 697 event.response.headers.content_type = "application/json"; | 715 event.response.headers.content_type = "application/json"; |
| 698 event.response.headers.cache_control = "no-store"; | 716 event.response.headers.cache_control = "no-store"; |
| 699 event.response.headers.pragma = "no-cache"; | 717 event.response.headers.pragma = "no-cache"; |
| 700 local params = http.formdecode(event.request.body); | 718 local params = strict_formdecode(event.request.body); |
| 701 if not params then | 719 if not params then |
| 702 return oauth_error("invalid_request"); | 720 return oauth_error("invalid_request"); |
| 703 end | 721 end |
| 704 | 722 |
| 705 if credentials and credentials.type == "basic" then | 723 if credentials and credentials.type == "basic" then |
| 721 | 739 |
| 722 -- Directly returning errors to the user before we have a validated client object | 740 -- Directly returning errors to the user before we have a validated client object |
| 723 if not request.url.query then | 741 if not request.url.query then |
| 724 return render_error(oauth_error("invalid_request", "Missing query parameters")); | 742 return render_error(oauth_error("invalid_request", "Missing query parameters")); |
| 725 end | 743 end |
| 726 local params = http.formdecode(request.url.query); | 744 local params = strict_formdecode(request.url.query); |
| 727 if not params then | 745 if not params then |
| 728 return render_error(oauth_error("invalid_request", "Invalid query parameters")); | 746 return render_error(oauth_error("invalid_request", "Invalid query parameters")); |
| 729 end | 747 end |
| 730 | 748 |
| 731 if not params.client_id then | 749 if not params.client_id then |
| 823 if not verify_client_secret(credentials.username, credentials.password) then | 841 if not verify_client_secret(credentials.username, credentials.password) then |
| 824 return 401; | 842 return 401; |
| 825 end | 843 end |
| 826 end | 844 end |
| 827 | 845 |
| 828 local form_data = http.formdecode(event.request.body or ""); | 846 local form_data = strict_formdecode(event.request.body); |
| 829 if not form_data or not form_data.token then | 847 if not form_data or not form_data.token then |
| 830 response.headers.accept = "application/x-www-form-urlencoded"; | 848 response.headers.accept = "application/x-www-form-urlencoded"; |
| 831 return 415; | 849 return 415; |
| 832 end | 850 end |
| 833 local ok, err = tokens.revoke_token(form_data.token); | 851 local ok, err = tokens.revoke_token(form_data.token); |
