# HG changeset patch # User Kim Alvefur # Date 1683242593 -7200 # Node ID f2c7bb3af6002be67b816148ef4e252b071af5bd # Parent 3902082c42c4521b7a5291f6ca61a80fe395a19f mod_http_oauth2: Add role selector to consent page List includes all roles available to the user, if more than one. Defaults to either the first role in the scope string or the users primary role. Earlier draft listed all roles, but having options that can't be selected is bad UX and the entire list of all roles on the server could be long, and perhaps even sensitive. Allows e.g. picking a role with fewer permissions than what might otherwise have been selected. UX wise, doing this with more checkboxes or possibly radio buttons would have been confusion and/or looked messier. Fixes the previous situation where unselecting a role would default to the primary role, which could be more permissions than requested. diff -r 3902082c42c4 -r f2c7bb3af600 mod_http_oauth2/html/consent.html --- a/mod_http_oauth2/html/consent.html Fri May 05 00:57:20 2023 +0200 +++ b/mod_http_oauth2/html/consent.html Fri May 05 01:23:13 2023 +0200 @@ -38,7 +38,10 @@
Requested permissions{scopes# - } + }{roles& + }
diff -r 3902082c42c4 -r f2c7bb3af600 mod_http_oauth2/mod_http_oauth2.lua --- a/mod_http_oauth2/mod_http_oauth2.lua Fri May 05 00:57:20 2023 +0200 +++ b/mod_http_oauth2/mod_http_oauth2.lua Fri May 05 01:23:13 2023 +0200 @@ -477,7 +477,7 @@ end local scope = array():append(form):filter(function(field) - return field.name == "scope"; + return field.name == "scope" or field.name == "role"; end):pluck("value"):concat(" "); user.token = form.user_token; @@ -654,7 +654,20 @@ return render_page(templates.login, { state = auth_state, client = client }); elseif auth_state.consent == nil then -- Render consent page - return render_page(templates.consent, { state = auth_state; client = client; scopes = parse_scopes(params.scope or "") }, true); + local scopes, requested_roles = split_scopes(parse_scopes(params.scope or "")); + local default_role = select_role(auth_state.user.username, requested_roles); + local roles = array(it.values(usermanager.get_all_roles(module.host))):filter(function(role) + return can_assume_role(auth_state.user.username, role.name); + end):sort(function(a, b) + return (a.priority or 0) < (b.priority or 0) + end):map(function(role) + return { name = role.name; selected = role.name == default_role }; + end); + if not roles[2] then + -- Only one role to choose from, might as well skip the selector + roles = nil; + end + return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes; roles = roles }, true); elseif not auth_state.consent then -- Notify client of rejection return error_response(request, oauth_error("access_denied"));