# HG changeset patch # User Kim Alvefur # Date 1681711269 -7200 # Node ID f8ec43db580bcf29095996290197bc9d5cbad0d6 # Parent ac9710126e1a6f28648a472d3c50f4032514afd5 mod_oidc_userinfo_vcard4: Provide profile details in mod_http_oauth2 diff -r ac9710126e1a -r f8ec43db580b mod_oidc_userinfo_vcard4/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_oidc_userinfo_vcard4/README.md Mon Apr 17 08:01:09 2023 +0200 @@ -0,0 +1,19 @@ +--- +summary: OIDC UserInfo profile details from vcard4 +labels: +- Stage-Alpha +rockspec: + dependencies: + - mod_http_oauth2 +--- + +This module extracts profile details from the user's [vcard4][XEP-0292] +and provides them in the [UserInfo] endpoint of [mod_http_oauth2] to +clients the user grants authorization. + +Whether this is really needed is unclear at this point. When logging in +with an XMPP client, it could fetch the actual vcard4 to retrieve these +details, so the UserInfo details would probably primarily be useful to +other OAuth 2 and OIDC clients. + +[UserInfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse diff -r ac9710126e1a -r f8ec43db580b mod_oidc_userinfo_vcard4/mod_oidc_userinfo_vcard4.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_oidc_userinfo_vcard4/mod_oidc_userinfo_vcard4.lua Mon Apr 17 08:01:09 2023 +0200 @@ -0,0 +1,80 @@ +-- Provide OpenID UserInfo data to mod_http_oauth2 +-- Alternatively, separate module for the whole HTTP endpoint? +-- +local nodeprep = require "util.encodings".stringprep.nodeprep; + +local mod_pep = module:depends "pep"; + +local gender_map = { M = "male"; F = "female"; O = "other"; N = "nnot applicable"; U = "unknown" } + +module:hook("token/userinfo", function(event) + local pep_service = mod_pep.get_pep_service(event.username); + + local vcard4 = select(3, pep_service:get_last_item("urn:xmpp:vcard4", true)); + + local userinfo = event.userinfo; + if vcard4 and event.claims:contains("profile") then + userinfo.name = vcard4:find("fn/text#"); + userinfo.family_name = vcard4:find("n/surname#"); + userinfo.given_name = vcard4:find("n/given#"); + userinfo.middle_name = vcard4:find("n/additional#"); + + userinfo.nickname = vcard4:find("nickname/text#"); + if not userinfo.nickname then + local ok, _, nick_item = pep_service:get_last_item("http://jabber.org/protocol/nick", true); + if ok and nick_item then + userinfo.nickname = nick_item:get_child_text("nick", "http://jabber.org/protocol/nick"); + end + end + + userinfo.preferred_username = event.username; + + -- profile -- page? not their website + -- picture -- mod_http_pep_avatar? + userinfo.website = vcard4:find("url/uri#"); + userinfo.birthdate = vcard4:find("bday/date#"); + userinfo.zoneinfo = vcard4:find("tz/text#"); + userinfo.locale = vcard4:find("lang/language-tag#"); + + userinfo.gender = gender_map[vcard4:find("gender/sex#")] or vcard4:find("gender/text#"); + + -- updated_at -- we don't keep a vcard change timestamp? + end + + if not userinfo.nickname and event.claims:contains("profile") then + local ok, _, nick_item = pep_service:get_last_item("http://jabber.org/protocol/nick", true); + if ok and nick_item then + userinfo.nickname = nick_item:get_child_text("nick", "http://jabber.org/protocol/nick"); + end + end + + if vcard4 and event.claims:contains("email") then + userinfo.email = vcard4:find("email/text#") + if userinfo.email then + userinfo.email_verified = false; + end + end + + if vcard4 and event.claims:contains("address") then + local adr = vcard4:get_child("adr"); + if adr then + userinfo.address = { + formatted = nil; + street_address = adr:get_child_text("street"); + locality = adr:get_child_text("locality"); + region = adr:get_child_text("region"); + postal_code = adr:get_child_text("code"); + country = adr:get_child_text("country"); + } + end + end + + if vcard4 and event.claims:contains("phone") then + userinfo.phone = vcard4:find("email/text#") + if userinfo.phone then + userinfo.phone_number_verified = false; + end + end + + +end, 10);