# HG changeset patch # User Trần H. Trung # Date 1693323987 -25200 # Node ID f6e8165a2ec2f1977d0748ed2acb61979fc4c150 # Parent 5e7e609fae0c3f6caa09e0cce26003c7f37cc706# Parent 9ce8ee5304380308aec9ec703c1a6abe1401b8ef Merge upstream diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_bidi/README.markdown --- a/mod_bidi/README.markdown Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_bidi/README.markdown Tue Aug 29 22:46:27 2023 +0700 @@ -1,11 +1,15 @@ --- labels: -- 'Stage-Stable' -summary: 'XEP-0288: Bidirectional Server-to-Server Connections' -... +- Stage-Stable +summary: "XEP-0288: Bidirectional Server-to-Server Connections" +--- -Introduction -============ +::: {.alert .alert-warning} +This module is unreliable when used with Prosody 0.12, switch to +[mod_s2s_bidi][doc:modules:mod_s2s_bidi] +::: + +# Introduction This module implements [XEP-0288: Bidirectional Server-to-Server Connections](http://xmpp.org/extensions/xep-0288.html). It allows @@ -14,13 +18,9 @@ Install and enable it like any other module. It has no configuration. -Compatibility -============= +# Compatibility - ------- -------------------------- - trunk Bidi available natively with [mod_s2s_bidi][doc:modules:mod_s2s_bidi] - 0.11 Works - 0.10 Works - 0.9 Works - 0.8 Works (use the 0.8 repo) - ------- -------------------------- + ------ ------------------------------------------- + 0.12 Bidi available natively with [mod_s2s_bidi][doc:modules:mod_s2s_bidi] + 0.11 Works + ------ ------------------------------------------- diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_client_management/mod_client_management.lua --- a/mod_client_management/mod_client_management.lua Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_client_management/mod_client_management.lua Tue Aug 29 22:46:27 2023 +0700 @@ -437,6 +437,10 @@ return true, "No clients associated with this account"; end + local function date_or_time(last_seen) + return last_seen and os.date(os.difftime(os.time(), last_seen) >= 86400 and "%Y-%m-%d" or "%H:%M:%S", last_seen); + end + local colspec = { { title = "ID"; key = "id"; width = "1p" }; { @@ -446,13 +450,18 @@ mapper = user_agent_tostring; }; { + title = "First seen"; + key = "first_seen"; + width = math.max(#os.date("%Y-%m-%d"), #os.date("%H:%M:%S")); + align = "right"; + mapper = date_or_time; + }; + { title = "Last seen"; key = "last_seen"; width = math.max(#os.date("%Y-%m-%d"), #os.date("%H:%M:%S")); align = "right"; - mapper = function(last_seen) - return last_seen and os.date(os.difftime(os.time(), last_seen) >= 86400 and "%Y-%m-%d" or "%H:%M:%S", last_seen); - end; + mapper = date_or_time; }; { title = "Authentication"; diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/README.markdown --- a/mod_http_oauth2/README.markdown Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/README.markdown Tue Aug 29 22:46:27 2023 +0700 @@ -55,7 +55,7 @@ - [RFC 9207: OAuth 2.0 Authorization Server Issuer Identification](https://www.rfc-editor.org/rfc/rfc9207.html) - [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) - [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) (_partial, e.g. missing JWKS_) -- [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html) +- [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html) ## Configuration @@ -146,9 +146,6 @@ `client_uri`. `native` - - `native` - : For native e.g. desktop clients etc. `redirect_uris` **MUST** match one of: diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/html/consent.html --- a/mod_http_oauth2/html/consent.html Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/html/consent.html Tue Aug 29 22:46:27 2023 +0700 @@ -1,22 +1,25 @@ - + - + {site_name} - Authorize {client.client_name} - + -
- {state.error&
+{state.error& +

{state.error}

-
} - +
+ } +

{site_name}

+
+
-
Authorize new application

A new application wants to connect to your account.

+
Name
{client.client_name}
@@ -33,18 +36,18 @@
Requested permissions
{scopes# - } + }

To allow {client.client_name} to access your account - {state.user.username}@{state.user.host} and associated data, - select 'Allow'. Otherwise, select 'Deny'. + {state.user.username}@{state.user.host} and associated data, + select 'Allow'. Otherwise, select 'Deny'.

- - - + + +
diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/html/device.html --- a/mod_http_oauth2/html/device.html Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/html/device.html Tue Aug 29 22:46:27 2023 +0700 @@ -1,30 +1,33 @@ - + - + {site_name} - Authorize{client&d} Device - + +{error& + +

{error.text}

+
+
} +
+

{site_name}

+
-

{site_name}

Device Authorization - {error&
-

{error.text}

-
} {client&

Authorization completed. You can go back to {client.client_name}.

} {client~

Enter the code to continue.

- - + +
}
- diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/html/error.html --- a/mod_http_oauth2/html/error.html Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/html/error.html Tue Aug 29 22:46:27 2023 +0700 @@ -1,14 +1,16 @@ - + - + {site_name} - Error - + +
+

{site_name}

+
-

{site_name}

Authentication error

There was a problem with the authentication request. If you were trying to sign in to a third-party application, you may want to report this issue to the developers.

diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/html/login.html --- a/mod_http_oauth2/html/login.html Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/html/login.html Tue Aug 29 22:46:27 2023 +0700 @@ -1,24 +1,30 @@ - + - + {site_name} - Sign in - + +{state.error& + +

{state.error}

+
+
} +
+

{site_name}

+
-

{site_name}

Sign in

Sign in to your account to continue.

- {state.error&
-

{state.error}

-
}
-
-
- +
+
+
diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/html/oob.html --- a/mod_http_oauth2/html/oob.html Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/html/oob.html Tue Aug 29 22:46:27 2023 +0700 @@ -1,18 +1,20 @@ - + - + {site_name} - Authorization Code - + +
+

{site_name}

+
-

{site_name}

Your Authorization Code

Here’s your authorization code, copy and paste it into {client.client_name}

-

+

diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/html/style.css --- a/mod_http_oauth2/html/style.css Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/html/style.css Tue Aug 29 22:46:27 2023 +0700 @@ -1,6 +1,5 @@ body { - margin-top:14%; text-align:center; background-color:#f8f8f8; font-family:sans-serif @@ -21,7 +20,7 @@ .error { - margin: 0.75em; + margin: 0.75em auto; background-color: #f8d7da; color: #842029; border: solid 1px #f5c2c7; @@ -53,7 +52,7 @@ text-align: left; } -main { +header, main, footer { max-width: 600px; padding: 0 1.5em 1.5em 1.5em; } @@ -106,7 +105,10 @@ @media(min-width: 768px) { - main + body { + margin-top:14vh; + } + header, main, footer { margin-left: auto; margin-right: auto; diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_http_oauth2/mod_http_oauth2.lua --- a/mod_http_oauth2/mod_http_oauth2.lua Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_http_oauth2/mod_http_oauth2.lua Tue Aug 29 22:46:27 2023 +0700 @@ -394,15 +394,19 @@ return oauth_error("invalid_request", "PKCE required"); end + local prefix = "authorization_code:"; local code = id.medium(); if params.redirect_uri == device_uri then local is_device, device_state = verify_device_token(params.state); if is_device then -- reconstruct the device_code + prefix = "device_code:"; code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code)); + else + return oauth_error("invalid_request"); end end - local ok = codes:set("authorization_code:" .. params.client_id .. "#" .. code, { + local ok = codes:set(prefix.. params.client_id .. "#" .. code, { expires = os.time() + 600; granted_jid = granted_jid; granted_scopes = granted_scopes; @@ -578,7 +582,7 @@ return oauth_error("invalid_client", "incorrect credentials"); end - local code = codes:get("device_code:" .. params.device_code); + local code = codes:get("device_code:" .. params.client_id .. "#" .. params.device_code); if type(code) ~= "table" or code_expired(code) then return oauth_error("expired_token"); elseif code.error then @@ -586,7 +590,7 @@ elseif not code.granted_jid then return oauth_error("authorization_pending"); end - codes:set("device_code:" .. params.device_code, nil); + codes:set("device_code:" .. params.client_id .. "#" .. params.device_code, nil); return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token)); end @@ -991,9 +995,10 @@ local verification_uri = module:http_url() .. "/device"; local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code }); - local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = os.time() + 1200 }); + local expires = os.time() + 600; + local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = expires }); local uc_ok = codes:set("user_code:" .. user_code, - { user_code = user_code; expires = os.time() + 600; client_id = params.client_id; + { user_code = user_code; expires = expires; client_id = params.client_id; scope = requested_scopes:concat(" ") }); if not dc_ok or not uc_ok then return oauth_error("temporarily_unavailable"); @@ -1041,6 +1046,8 @@ } end +local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false); + local function handle_revocation_request(event) local request, response = event.request, event.response; response.headers.cache_control = "no-store"; @@ -1055,6 +1062,11 @@ if not verify_client_secret(credentials.username, credentials.password) then return 401; end + -- TODO check that it's their token I guess? + elseif strict_auth_revoke then + -- Why require auth to revoke a leaked token? + response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name); + return 401; end local form_data = strict_formdecode(event.request.body); @@ -1224,6 +1236,16 @@ return nil, oauth_error("invalid_request", "Failed schema validation."); end + local client_uri = url.parse(client_metadata.client_uri); + if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then + return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri"); + end + + if not client_metadata.application_type and redirect_uri_allowed(client_metadata.redirect_uris[1], client_uri, "native") then + client_metadata.application_type = "native"; + -- else defaults to "web" + end + -- Fill in default values for propname, propspec in pairs(registration_schema.properties) do if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then @@ -1238,11 +1260,6 @@ end end - local client_uri = url.parse(client_metadata.client_uri); - if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then - return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri"); - end - for _, redirect_uri in ipairs(client_metadata.redirect_uris) do if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI."); diff -r 5e7e609fae0c -r f6e8165a2ec2 mod_rest/example/rest.sh --- a/mod_rest/example/rest.sh Tue Aug 01 01:27:45 2023 +0700 +++ b/mod_rest/example/rest.sh Tue Aug 29 22:46:27 2023 +0700 @@ -5,23 +5,25 @@ # Dependencies: # - https://httpie.io/ -# - https://github.com/stedolan/jq -# - some sort of XDG 'open' command +# - https://hg.sr.ht/~zash/httpie-oauth2 + +# shellcheck disable=SC1091 # Settings HOST="" DOMAIN="" -AUTH_METHOD="session-read-only" -AUTH_ID="rest" - if [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/restrc" ]; then # Config file can contain the above settings source "${XDG_CONFIG_HOME:-$HOME/.config}/restrc" + + if [ -z "${SCOPE:-}" ]; then + SCOPE="openid xmpp" + fi fi if [[ $# == 0 ]]; then - echo "${0##*/} [-h HOST] [-u USER|--login] [/path] kind=(message|presence|iq) ...." + echo "${0##*/} [-h HOST] [/path] kind=(message|presence|iq) ...." # Last arguments are handed to HTTPie, so refer to its docs for further details exit 0 fi @@ -45,96 +47,6 @@ fi fi -if [[ "$1" == "-u" ]]; then - # -u username - AUTH_METHOD="auth" - AUTH_ID="$2" - shift 2 -elif [[ "$1" == "-rw" ]]; then - # To e.g. save Accept headers to the session - AUTH_METHOD="session" - shift 1 -fi - -if [[ "$1" == "--login" ]]; then - shift 1 - - # Check cache for OAuth client - if [ -f "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST" ]; then - source "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST" - fi - - OAUTH_META="$(http --check-status --json "https://$HOST/.well-known/oauth-authorization-server" Accept:application/json)" - AUTHORIZATION_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.authorization_endpoint')" - TOKEN_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.token_endpoint')" - - if [ -z "${OAUTH_CLIENT_INFO:-}" ]; then - # Register a new OAuth client - REGISTRATION_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.registration_endpoint')" - OAUTH_CLIENT_INFO="$(http --check-status "$REGISTRATION_ENDPOINT" Content-Type:application/json Accept:application/json client_name=rest.sh client_uri="https://modules.prosody.im/mod_rest" application_type=native software_id=0bdb0eb9-18e8-43af-a7f6-bd26613374c0 redirect_uris:='["urn:ietf:wg:oauth:2.0:oob"]')" - mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/rest/" - typeset -p OAUTH_CLIENT_INFO >> "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST" - fi - - CLIENT_ID="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_id')" - CLIENT_SECRET="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_secret')" - - if [ -n "${REFRESH_TOKEN:-}" ]; then - TOKEN_RESPONSE="$(http --check-status --form "$TOKEN_ENDPOINT" 'grant_type=refresh_token' "client_id=$CLIENT_ID" "client_secret=$CLIENT_SECRET" "refresh_token=$REFRESH_TOKEN")" - ACCESS_TOKEN="$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')" - if [ "$ACCESS_TOKEN" == "null" ]; then - ACCESS_TOKEN="" - fi - fi - - if [ -z "${ACCESS_TOKEN:-}" ]; then - CODE_CHALLENGE="$(head -c 33 /dev/urandom | base64 | tr /+ _-)" - open "$AUTHORIZATION_ENDPOINT?response_type=code&client_id=$CLIENT_ID&code_challenge=$CODE_CHALLENGE&scope=${SCOPE:-openid+prosody:user}" - read -p "Paste authorization code: " -s -r AUTHORIZATION_CODE - - TOKEN_RESPONSE="$(http --check-status --form "$TOKEN_ENDPOINT" 'grant_type=authorization_code' "client_id=$CLIENT_ID" "client_secret=$CLIENT_SECRET" "code=$AUTHORIZATION_CODE" code_verifier="$CODE_CHALLENGE")" - ACCESS_TOKEN="$(echo "$TOKEN_RESPONSE" | jq -e -r '.access_token')" - REFRESH_TOKEN="$(echo "$TOKEN_RESPONSE" | jq -r '.refresh_token')" - - if [ "$REFRESH_TOKEN" != "null" ]; then - # FIXME Better type check would be nice, but nobody should ever have the - # string "null" as a legitimate refresh token... - typeset -p REFRESH_TOKEN >> "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST" - fi - - if [ -n "${COLORTERM:-}" ]; then - echo -ne '\e[1K\e[G' - else - echo - fi - fi - - USERINFO_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.userinfo_endpoint')" - http --check-status -b --session rest "$USERINFO_ENDPOINT" "Authorization:Bearer $ACCESS_TOKEN" Accept:application/json >&2 - AUTH_METHOD="session-read-only" - AUTH_ID="rest" - -elif [[ "$1" == "--logout" ]]; then - # Revoke token - source "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST" - - OAUTH_META="$(http --check-status --json "https://$HOST/.well-known/oauth-authorization-server" Accept:application/json)" - REVOCATION_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.revocation_endpoint')" - - CLIENT_ID="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_id')" - CLIENT_SECRET="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_secret')" - - http -h --check-status --auth "$CLIENT_ID:$CLIENT_SECRET" --form "$REVOCATION_ENDPOINT" token="$REFRESH_TOKEN" - - # Overwrite the token - typeset -p OAUTH_CLIENT_INFO > "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST" - exit 0 -fi - -if [[ $# == 0 ]]; then - # Just login? - exit 0 -fi # For e.g /disco/example.com and such GET queries GET_PATH="" @@ -143,4 +55,4 @@ shift 1 fi -http --check-status -p b "--$AUTH_METHOD" "$AUTH_ID" "https://$HOST/rest$GET_PATH" "$@" +https --check-status -p b --session rest -A oauth2 -a "$HOST" --oauth2-scope "$SCOPE" "$HOST/rest$GET_PATH" "$@"