changeset 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
files mod_http_oauth2/mod_http_oauth2.lua
diffstat 1 files changed, 21 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- 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);