comparison mod_http_oauth2/mod_http_oauth2.lua @ 5447:aa4828f040c5

mod_http_oauth2: Enforce client scope restrictions in authorization When registering a client, a scope field can be included as a promise to only ever use those. Here we enforce that promise, if given, ensuring a client can't request or be granted a scope it didn't provide in its registration. While currently there is no restrictions at registration time, this could be changed in the future in various ways.
author Kim Alvefur <zash@zash.se>
date Thu, 11 May 2023 19:33:44 +0200
parents dd7bddc87f98
children 9d542e86e19a
comparison
equal deleted inserted replaced
5446:dd7bddc87f98 5447:aa4828f040c5
475 return { 475 return {
476 error = user == "token-expired" and "Session expired - try again" or nil; 476 error = user == "token-expired" and "Session expired - try again" or nil;
477 }; 477 };
478 end 478 end
479 479
480 local scope = array():append(form):filter(function(field) 480 local scopes = array():append(form):filter(function(field)
481 return field.name == "scope"; 481 return field.name == "scope";
482 end):pluck("value"):concat(" "); 482 end):pluck("value");
483 483
484 user.token = form.user_token; 484 user.token = form.user_token;
485 return { 485 return {
486 user = user; 486 user = user;
487 scope = scope; 487 scopes = scopes;
488 consent = form.consent == "granted"; 488 consent = form.consent == "granted";
489 }; 489 };
490 end 490 end
491 491
492 return {}; 492 return {};
647 client_response_types = set.intersection(client_response_types, allowed_response_type_handlers); 647 client_response_types = set.intersection(client_response_types, allowed_response_type_handlers);
648 if not client_response_types:contains(params.response_type) then 648 if not client_response_types:contains(params.response_type) then
649 return oauth_error("invalid_client", "response_type not allowed"); 649 return oauth_error("invalid_client", "response_type not allowed");
650 end 650 end
651 651
652 local requested_scopes = parse_scopes(params.scope or "");
653 if client.scope then
654 local client_scopes = set.new(parse_scopes(client.scope));
655 requested_scopes:filter(function(scope)
656 return client_scopes:contains(scope);
657 end);
658 end
659
652 local auth_state = get_auth_state(request); 660 local auth_state = get_auth_state(request);
653 if not auth_state.user then 661 if not auth_state.user then
654 -- Render login page 662 -- Render login page
655 return render_page(templates.login, { state = auth_state, client = client }); 663 return render_page(templates.login, { state = auth_state, client = client });
656 elseif auth_state.consent == nil then 664 elseif auth_state.consent == nil then
657 -- Render consent page 665 -- Render consent page
658 local scopes, roles = split_scopes(parse_scopes(params.scope or "")); 666 local scopes, roles = split_scopes(requested_scopes);
659 return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true); 667 return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true);
660 elseif not auth_state.consent then 668 elseif not auth_state.consent then
661 -- Notify client of rejection 669 -- Notify client of rejection
662 return error_response(request, oauth_error("access_denied")); 670 return error_response(request, oauth_error("access_denied"));
663 end 671 end
664 -- else auth_state.consent == true 672 -- else auth_state.consent == true
665 673
666 params.scope = auth_state.scope; 674 local granted_scopes = auth_state.scopes
675 if client.scope then
676 local client_scopes = set.new(parse_scopes(client.scope));
677 granted_scopes:filter(function(scope)
678 return client_scopes:contains(scope);
679 end);
680 end
681
682 params.scope = granted_scopes:concat(" ");
667 683
668 local user_jid = jid.join(auth_state.user.username, module.host); 684 local user_jid = jid.join(auth_state.user.username, module.host);
669 local client_secret = make_client_secret(params.client_id); 685 local client_secret = make_client_secret(params.client_id);
670 local id_token_signer = jwt.new_signer("HS256", client_secret); 686 local id_token_signer = jwt.new_signer("HS256", client_secret);
671 local id_token = id_token_signer({ 687 local id_token = id_token_signer({