Mercurial > prosody-modules
comparison mod_http_oauth2/mod_http_oauth2.lua @ 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 | cd195283127f |
comparison
equal
deleted
inserted
replaced
5336:77ac04bd2f65 | 5337:8d8e85d6dc91 |
---|---|
79 | 79 |
80 local function parse_scopes(scope_string) | 80 local function parse_scopes(scope_string) |
81 return array(scope_string:gmatch("%S+")); | 81 return array(scope_string:gmatch("%S+")); |
82 end | 82 end |
83 | 83 |
84 local openid_claims = set.new({ "profile"; "email"; "address"; "phone" }); | |
85 | |
84 local function filter_scopes(username, requested_scope_string) | 86 local function filter_scopes(username, requested_scope_string) |
85 local selected_role, granted_scopes = nil, array(); | 87 local selected_role, granted_scopes = nil, array(); |
86 | 88 |
87 if requested_scope_string then -- Specific role(s) requested | 89 if requested_scope_string then -- Specific role(s) requested |
88 local requested_scopes = parse_scopes(requested_scope_string); | 90 local requested_scopes = parse_scopes(requested_scope_string); |
89 for _, scope in ipairs(requested_scopes) do | 91 for _, scope in ipairs(requested_scopes) do |
90 if scope == "openid" then | 92 if scope == "openid" or openid_claims:contains(scope) then |
91 granted_scopes:push(scope); | 93 granted_scopes:push(scope); |
92 end | 94 end |
93 if selected_role == nil and usermanager.user_can_assume_role(username, module.host, scope) then | 95 if selected_role == nil and usermanager.user_can_assume_role(username, module.host, scope) then |
94 selected_role = scope; | 96 selected_role = scope; |
95 end | 97 end |
756 local token_info,err = tokens.get_token_info(credentials.bearer_token); | 758 local token_info,err = tokens.get_token_info(credentials.bearer_token); |
757 if not token_info then | 759 if not token_info then |
758 module:log("debug", "UserInfo query failed token validation: %s", err) | 760 module:log("debug", "UserInfo query failed token validation: %s", err) |
759 return 403; | 761 return 403; |
760 end | 762 end |
761 -- TODO check that they actually have access to the userinfo endpoint, aka | 763 local scopes = set.new() |
762 -- the 'openid' scope. Tokens currently contain the JID in plain text so | 764 if type(token_info.grant.data) == "table" and type(token_info.grant.data.oauth2_scopes) == "string" then |
763 -- we're not really returning anything they did not know already. | 765 scopes:add_list(parse_scopes(token_info.grant.data.oauth2_scopes)); |
766 else | |
767 module:log("debug", "token_info = %q", token_info) | |
768 end | |
769 | |
770 if not scopes:contains("openid") then | |
771 module:log("debug", "Missing the 'openid' scope in %q", scopes) | |
772 -- The 'openid' scope is required for access to this endpoint. | |
773 return 403; | |
774 end | |
764 | 775 |
765 local user_info = { | 776 local user_info = { |
766 iss = get_issuer(); | 777 iss = get_issuer(); |
767 sub = url.build({ scheme = "xmpp"; path = token_info.jid }); | 778 sub = url.build({ scheme = "xmpp"; path = token_info.jid }); |
768 -- Additional UserInfo fields could be pulled from vcard4, depending on | |
769 -- permissions and scopes granted. | |
770 } | 779 } |
780 | |
781 local token_claims = set.intersection(openid_claims, scopes); | |
782 if not token_claims:empty() then | |
783 -- Another module can do that | |
784 module:fire_event("token/userinfo", { | |
785 token = token_info; | |
786 claims = token_claims; | |
787 username = jid.split(token_info.jid); | |
788 userinfo = user_info; | |
789 }); | |
790 end | |
791 | |
771 return { | 792 return { |
772 status_code = 200; | 793 status_code = 200; |
773 headers = { content_type = "application/json" }; | 794 headers = { content_type = "application/json" }; |
774 body = json.encode(user_info); | 795 body = json.encode(user_info); |
775 }; | 796 }; |