Software /
code /
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 |
parent | 5476:575f52b15f5a |
child | 5478: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) |