Changeset

5623:59d5fc50f602

mod_http_oauth2: Implement refresh token rotation Makes refresh tokens one-time-use, handing out a new refresh token with each access token. Thus if a refresh token is stolen and used by an attacker, the next time the legitimate client tries to use the previous refresh token, it will not work and the attack will be noticed. If the attacker does not use the refresh token, it becomes invalid after the legitimate client uses it. This behavior is recommended by draft-ietf-oauth-security-topics
author Kim Alvefur <zash@zash.se>
date Sun, 23 Jul 2023 02:56:08 +0200
parents 5622:308b5b117379
children 5624:d8622797e315
files mod_http_oauth2/mod_http_oauth2.lua
diffstat 1 files changed, 11 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/mod_http_oauth2/mod_http_oauth2.lua	Fri Jul 21 00:38:04 2023 +0200
+++ b/mod_http_oauth2/mod_http_oauth2.lua	Sun Jul 23 02:56:08 2023 +0200
@@ -270,18 +270,23 @@
 		token_data = nil;
 	end
 
-	local refresh_token;
 	local grant = refresh_token_info and refresh_token_info.grant;
 	if not grant then
 		-- No existing grant, create one
 		grant = tokens.create_grant(token_jid, token_jid, default_refresh_ttl, token_data);
-		-- Create refresh token for the grant if desired
-		refresh_token = refresh_token_info ~= false and tokens.create_token(token_jid, grant, nil, nil, "oauth2-refresh");
-	else
-		-- Grant exists, reuse existing refresh token
-		refresh_token = refresh_token_info.token;
 	end
 
+	if refresh_token_info then
+		-- out with the old refresh tokens
+		local ok, err = tokens.revoke_token(refresh_token_info.token);
+		if not ok then
+			module:log("error", "Could not revoke refresh token: %s", err);
+			return 500;
+		end
+	end
+	-- in with the new refresh token
+	local refresh_token = refresh_token_info ~= false and tokens.create_token(token_jid, grant.id, nil, nil, "oauth2-refresh");
+
 	if role == "xmpp" then
 		-- Special scope meaning the users default role.
 		local user_default_role = usermanager.get_user_role(jid.node(token_jid), module.host);