# HG changeset patch # User Kim Alvefur # Date 1752589720 -7200 # Node ID 6e80b2cb5fe68399f449f076fc8fb03ac812d404 # Parent 9102d75131e405a7b3a88e70eab2e2b1cac8e659 mod_http_oauth2: Show scope descriptions This should help the user understand and provide _informed_ consent, although the texts can certainly be improved. Explaining these scopes is non-trivial. diff -r 9102d75131e4 -r 6e80b2cb5fe6 mod_http_oauth2/html/consent.html --- a/mod_http_oauth2/html/consent.html Tue Jul 15 12:21:23 2025 +0200 +++ b/mod_http_oauth2/html/consent.html Tue Jul 15 16:28:40 2025 +0200 @@ -35,8 +35,12 @@
View policy
}
Requested permissions
-
{scopes# - } +
+
{scopes# +
+
+ {item.description&
{item.description}
}} +
diff -r 9102d75131e4 -r 6e80b2cb5fe6 mod_http_oauth2/mod_http_oauth2.lua --- a/mod_http_oauth2/mod_http_oauth2.lua Tue Jul 15 12:21:23 2025 +0200 +++ b/mod_http_oauth2/mod_http_oauth2.lua Tue Jul 15 16:28:40 2025 +0200 @@ -188,18 +188,22 @@ local openid_claims = set.new(); -module:add_item("openid-claim", "openid"); +module:add_item("openid-claim", { claim = "openid"; title = "OpenID"; + description = "Tells the application your JID and when you authenticated."; }); -- https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess -- The "offline_access" scope grants access to refresh tokens -module:add_item("openid-claim", "offline_access"); +module:add_item("openid-claim", { claim = "offline_access"; title = "Offline Access"; + description = "Application may renew access without interaction."; }); module:handle_items("openid-claim", function(event) authorization_server_metadata = nil; - openid_claims:add(event.item); + openid_claims:add(event.item.claim or event.item); end, function() authorization_server_metadata = nil; - openid_claims = set.new(module:get_host_items("openid-claim")); + openid_claims = set.new(array.new(module:get_host_items("openid-claim")):map(function(item) + return item.claim or item; + end)); end, true); -- array -> array, array, array @@ -1031,7 +1035,31 @@ else -- Render consent page - return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true); + module:log("debug", "scopes=%q", scopes); + local scope_choices = array.new(module:get_host_items("openid-claim")):map(function(item) + if type(item) == "string" then + return { claim = item }; + elseif type(item) == "table" and type(item.claim) == "string" then + return item; + end + end):filter(function (item) + if array_contains(scopes, item.claim) then + module:log("debug", "scopes contains %q", item); + return true; + else + module:log("debug", "scopes contains NO %q", item); + return false; + end + end); + for _, role in ipairs(roles) do + if role == "xmpp" then + scope_choices:push({ scope = role; title = "XMPP"; + description = "Unlimited access to your account, including sending and receiving messages."; }); + else + scope_choices:push({ scope = role; title = role, description = "Prosody Role" }); + end + end + return render_page(templates.consent, { state = auth_state; client = client; scopes = scope_choices }, true); end elseif not auth_state.consent then -- Notify client of rejection @@ -1056,10 +1084,12 @@ local client_secret = make_client_secret(params.client_id); local id_token_signer = jwt.new_signer("HS256", client_secret); id_token = id_token_signer({ - iss = get_issuer(); - sub = url.build({ scheme = "xmpp"; path = user_jid }); - aud = params.client_id; - auth_time = auth_state.user.iat; + iss = get_issuer(); -- REQUIRED + sub = url.build({ scheme = "xmpp"; path = user_jid }); -- REQUIRED + aud = params.client_id; -- REQUIRED + -- exp REQUIRED, set by util.jwt + -- iat REQUIRED, set by util.jwt + auth_time = auth_state.user.iat; -- REQUIRED when Essential Claim, otherwise OPTIONAL nonce = params.nonce; amr = auth_state.user.amr; -- RFC 8176: Authentication Method Reference Values });