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.
--- a/mod_http_oauth2/mod_http_oauth2.lua Thu May 11 21:37:35 2023 +0200
+++ b/mod_http_oauth2/mod_http_oauth2.lua Thu May 11 19:33:44 2023 +0200
@@ -477,14 +477,14 @@
};
end
- local scope = array():append(form):filter(function(field)
+ local scopes = array():append(form):filter(function(field)
return field.name == "scope";
- end):pluck("value"):concat(" ");
+ end):pluck("value");
user.token = form.user_token;
return {
user = user;
- scope = scope;
+ scopes = scopes;
consent = form.consent == "granted";
};
end
@@ -649,13 +649,21 @@
return oauth_error("invalid_client", "response_type not allowed");
end
+ local requested_scopes = parse_scopes(params.scope or "");
+ if client.scope then
+ local client_scopes = set.new(parse_scopes(client.scope));
+ requested_scopes:filter(function(scope)
+ return client_scopes:contains(scope);
+ end);
+ end
+
local auth_state = get_auth_state(request);
if not auth_state.user then
-- Render login page
return render_page(templates.login, { state = auth_state, client = client });
elseif auth_state.consent == nil then
-- Render consent page
- local scopes, roles = split_scopes(parse_scopes(params.scope or ""));
+ local scopes, roles = split_scopes(requested_scopes);
return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true);
elseif not auth_state.consent then
-- Notify client of rejection
@@ -663,7 +671,15 @@
end
-- else auth_state.consent == true
- params.scope = auth_state.scope;
+ local granted_scopes = auth_state.scopes
+ if client.scope then
+ local client_scopes = set.new(parse_scopes(client.scope));
+ granted_scopes:filter(function(scope)
+ return client_scopes:contains(scope);
+ end);
+ end
+
+ params.scope = granted_scopes:concat(" ");
local user_jid = jid.join(auth_state.user.username, module.host);
local client_secret = make_client_secret(params.client_id);