changeset 5682:527c747711f3

mod_http_oauth2: Limit revocation to clients own tokens in strict mode RFC 7009 section 2.1 states: > The authorization server first validates the client credentials (in > case of a confidential client) and then verifies whether the token was > issued to the client making the revocation request. If this > validation fails, the request is refused and the client is informed of > the error by the authorization server as described below. The first part was already covered (in strict mode). This adds the later part using the hash of client_id recorded in 0860497152af It still seems weird to me that revoking a leaked token should not be allowed whoever might have discovered it, as that seems the responsible thing to do.
author Kim Alvefur <zash@zash.se>
date Sun, 29 Oct 2023 11:30:49 +0100
parents 8cb3da7df521
children c69320fc438b
files mod_http_oauth2/mod_http_oauth2.lua
diffstat 1 files changed, 21 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/mod_http_oauth2/mod_http_oauth2.lua	Sun Oct 29 11:20:15 2023 +0100
+++ b/mod_http_oauth2/mod_http_oauth2.lua	Sun Oct 29 11:30:49 2023 +0100
@@ -1099,15 +1099,17 @@
 	};
 end
 
+-- RFC 7009 says that the authorization server should validate that only the client that a token was issued to should be able to revoke it. However
+-- this would prevent someone who comes across a leaked token from doing the responsible thing and revoking it, so this is not enforced by default.
 local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false);
 
 local function handle_revocation_request(event)
 	local request, response = event.request, event.response;
 	response.headers.cache_control = "no-store";
 	response.headers.pragma = "no-cache";
-	if request.headers.authorization then
-		local credentials = get_request_credentials(request);
-		if not credentials or credentials.type ~= "basic" then
+	local credentials = get_request_credentials(request);
+	if credentials then
+		if credentials.type ~= "basic" then
 			response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
 			return 401;
 		end
@@ -1127,6 +1129,22 @@
 		response.headers.accept = "application/x-www-form-urlencoded";
 		return 415;
 	end
+
+	if credentials then
+		local client = check_client(credentials.username);
+		if not client then
+			return 401;
+		end
+		local token_info = tokens.get_token_info(form_data.token);
+		if not token_info then
+			return 404;
+		end
+		local token_client = token_info.grant.data.oauth2_client;
+		if not token_client or token_client.hash ~= client.client_hash then
+			return 403;
+		end
+	end
+
 	local ok, err = tokens.revoke_token(form_data.token);
 	if not ok then
 		module:log("warn", "Unable to revoke token: %s", tostring(err));