changeset 5337:8d8e85d6dc91

mod_http_oauth2: Support OpenID UserInfo claims Actually filling in those details is left to another module because I don't really wanna mix in a dependency on PEP or mod_vcard here, those implementation details can be in a second module. Some might want to fill this from LDAP or something as well.
author Kim Alvefur <zash@zash.se>
date Mon, 10 Apr 2023 10:49:02 +0200
parents 77ac04bd2f65
children 03044a6f5f4c
files mod_http_oauth2/mod_http_oauth2.lua
diffstat 1 files changed, 27 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/mod_http_oauth2/mod_http_oauth2.lua	Mon Apr 10 10:47:51 2023 +0200
+++ b/mod_http_oauth2/mod_http_oauth2.lua	Mon Apr 10 10:49:02 2023 +0200
@@ -81,13 +81,15 @@
 	return array(scope_string:gmatch("%S+"));
 end
 
+local openid_claims = set.new({ "profile"; "email"; "address"; "phone" });
+
 local function filter_scopes(username, requested_scope_string)
 	local selected_role, granted_scopes = nil, array();
 
 	if requested_scope_string then -- Specific role(s) requested
 		local requested_scopes = parse_scopes(requested_scope_string);
 		for _, scope in ipairs(requested_scopes) do
-			if scope == "openid" then
+			if scope == "openid" or openid_claims:contains(scope) then
 				granted_scopes:push(scope);
 			end
 			if selected_role == nil and usermanager.user_can_assume_role(username, module.host, scope) then
@@ -758,16 +760,35 @@
 		module:log("debug", "UserInfo query failed token validation: %s", err)
 		return 403;
 	end
-	-- TODO check that they actually have access to the userinfo endpoint, aka
-	-- the 'openid' scope. Tokens currently contain the JID in plain text so
-	-- we're not really returning anything they did not know already.
+	local scopes = set.new()
+	if type(token_info.grant.data) == "table" and type(token_info.grant.data.oauth2_scopes) == "string" then
+		scopes:add_list(parse_scopes(token_info.grant.data.oauth2_scopes));
+	else
+		module:log("debug", "token_info = %q", token_info)
+	end
+
+	if not scopes:contains("openid") then
+		module:log("debug", "Missing the 'openid' scope in %q", scopes)
+		-- The 'openid' scope is required for access to this endpoint.
+		return 403;
+	end
 
 	local user_info = {
 		iss = get_issuer();
 		sub = url.build({ scheme = "xmpp"; path = token_info.jid });
-		-- Additional UserInfo fields could be pulled from vcard4, depending on
-		-- permissions and scopes granted.
 	}
+
+	local token_claims = set.intersection(openid_claims, scopes);
+	if not token_claims:empty() then
+		-- Another module can do that
+		module:fire_event("token/userinfo", {
+			token = token_info;
+			claims = token_claims;
+			username = jid.split(token_info.jid);
+			userinfo = user_info;
+		});
+	end
+
 	return {
 		status_code = 200;
 		headers = { content_type = "application/json" };