Mercurial > prosody-modules
comparison mod_http_oauth2/mod_http_oauth2.lua @ 5477:5986e0edd7a3
mod_http_oauth2: Use validated redirect URI when returning errors to client
Parsing it from the query again without the validation done by
get_redirect_uri() may lead to open redirect issues.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 18 May 2023 14:17:58 +0200 |
| parents | 575f52b15f5a |
| children | af105c7a24b2 |
comparison
equal
deleted
inserted
replaced
| 5476:575f52b15f5a | 5477:5986e0edd7a3 |
|---|---|
| 604 -- OAuth errors should be returned to the client if possible, i.e. by | 604 -- OAuth errors should be returned to the client if possible, i.e. by |
| 605 -- appending the error information to the redirect_uri and sending the | 605 -- appending the error information to the redirect_uri and sending the |
| 606 -- redirect to the user-agent. In some cases we can't do this, e.g. if | 606 -- redirect to the user-agent. In some cases we can't do this, e.g. if |
| 607 -- the redirect_uri is missing or invalid. In those cases, we render an | 607 -- the redirect_uri is missing or invalid. In those cases, we render an |
| 608 -- error directly to the user-agent. | 608 -- error directly to the user-agent. |
| 609 local function error_response(request, err) | 609 local function error_response(request, redirect_uri, err) |
| 610 local q = request.url.query and http.formdecode(request.url.query); | |
| 611 local redirect_uri = q and q.redirect_uri; | |
| 612 if not redirect_uri or not is_secure_redirect(redirect_uri) then | 610 if not redirect_uri or not is_secure_redirect(redirect_uri) then |
| 613 module:log("warn", "Missing or invalid redirect_uri %q, rendering error to user-agent", redirect_uri); | 611 module:log("warn", "Missing or invalid redirect_uri %q, rendering error to user-agent", redirect_uri); |
| 614 return render_error(err); | 612 return render_error(err); |
| 615 end | 613 end |
| 614 local q = request.url.query and http.formdecode(request.url.query); | |
| 616 local redirect_query = url.parse(redirect_uri); | 615 local redirect_query = url.parse(redirect_uri); |
| 617 local sep = redirect_query.query and "&" or "?"; | 616 local sep = redirect_query.query and "&" or "?"; |
| 618 redirect_uri = redirect_uri | 617 redirect_uri = redirect_uri |
| 619 .. sep .. http.formencode(err.extra.oauth2_response) | 618 .. sep .. http.formencode(err.extra.oauth2_response) |
| 620 .. "&" .. http.formencode({ state = q.state, iss = get_issuer() }); | 619 .. "&" .. http.formencode({ state = q.state, iss = get_issuer() }); |
| 701 | 700 |
| 702 if not ok then | 701 if not ok then |
| 703 return render_error(oauth_error("invalid_request", "Invalid 'client_id' parameter")); | 702 return render_error(oauth_error("invalid_request", "Invalid 'client_id' parameter")); |
| 704 end | 703 end |
| 705 | 704 |
| 706 if not get_redirect_uri(client, params.redirect_uri) then | 705 local redirect_uri = get_redirect_uri(client, params.redirect_uri); |
| 706 if not redirect_uri then | |
| 707 return render_error(oauth_error("invalid_request", "Invalid 'redirect_uri' parameter")); | 707 return render_error(oauth_error("invalid_request", "Invalid 'redirect_uri' parameter")); |
| 708 end | 708 end |
| 709 -- From this point we know that redirect_uri is safe to use | 709 -- From this point we know that redirect_uri is safe to use |
| 710 | 710 |
| 711 local client_response_types = set.new(array(client.response_types or { "code" })); | 711 local client_response_types = set.new(array(client.response_types or { "code" })); |
| 712 client_response_types = set.intersection(client_response_types, allowed_response_type_handlers); | 712 client_response_types = set.intersection(client_response_types, allowed_response_type_handlers); |
| 713 if not client_response_types:contains(params.response_type) then | 713 if not client_response_types:contains(params.response_type) then |
| 714 return error_response(request, oauth_error("invalid_client", "'response_type' not allowed")); | 714 return error_response(request, redirect_uri, oauth_error("invalid_client", "'response_type' not allowed")); |
| 715 end | 715 end |
| 716 | 716 |
| 717 local requested_scopes = parse_scopes(params.scope or ""); | 717 local requested_scopes = parse_scopes(params.scope or ""); |
| 718 if client.scope then | 718 if client.scope then |
| 719 local client_scopes = set.new(parse_scopes(client.scope)); | 719 local client_scopes = set.new(parse_scopes(client.scope)); |
| 736 local scopes, roles = split_scopes(requested_scopes); | 736 local scopes, roles = split_scopes(requested_scopes); |
| 737 roles = user_assumable_roles(auth_state.user.username, roles); | 737 roles = user_assumable_roles(auth_state.user.username, roles); |
| 738 return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true); | 738 return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true); |
| 739 elseif not auth_state.consent then | 739 elseif not auth_state.consent then |
| 740 -- Notify client of rejection | 740 -- Notify client of rejection |
| 741 return error_response(request, oauth_error("access_denied")); | 741 return error_response(request, redirect_uri, oauth_error("access_denied")); |
| 742 end | 742 end |
| 743 -- else auth_state.consent == true | 743 -- else auth_state.consent == true |
| 744 | 744 |
| 745 local granted_scopes = auth_state.scopes | 745 local granted_scopes = auth_state.scopes |
| 746 if client.scope then | 746 if client.scope then |
| 762 nonce = params.nonce; | 762 nonce = params.nonce; |
| 763 }); | 763 }); |
| 764 local response_type = params.response_type; | 764 local response_type = params.response_type; |
| 765 local response_handler = response_type_handlers[response_type]; | 765 local response_handler = response_type_handlers[response_type]; |
| 766 if not response_handler then | 766 if not response_handler then |
| 767 return error_response(request, oauth_error("unsupported_response_type")); | 767 return error_response(request, redirect_uri, oauth_error("unsupported_response_type")); |
| 768 end | 768 end |
| 769 local ret = response_handler(client, params, user_jid, id_token); | 769 local ret = response_handler(client, params, user_jid, id_token); |
| 770 if errors.is_err(ret) then | 770 if errors.is_err(ret) then |
| 771 return error_response(request, ret); | 771 return error_response(request, redirect_uri, ret); |
| 772 end | 772 end |
| 773 return ret; | 773 return ret; |
| 774 end | 774 end |
| 775 | 775 |
| 776 local function handle_revocation_request(event) | 776 local function handle_revocation_request(event) |
