Software /
code /
prosody-modules
Comparison
mod_http_oauth2/mod_http_oauth2.lua @ 5663:f6e8165a2ec2
Merge upstream
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Tue, 29 Aug 2023 22:46:27 +0700 |
parent | 5651:dd2079b3dec6 |
child | 5665:7c105277a9ca |
comparison
equal
deleted
inserted
replaced
5640:5e7e609fae0c | 5663:f6e8165a2ec2 |
---|---|
392 | 392 |
393 if pkce_required and not params.code_challenge then | 393 if pkce_required and not params.code_challenge then |
394 return oauth_error("invalid_request", "PKCE required"); | 394 return oauth_error("invalid_request", "PKCE required"); |
395 end | 395 end |
396 | 396 |
397 local prefix = "authorization_code:"; | |
397 local code = id.medium(); | 398 local code = id.medium(); |
398 if params.redirect_uri == device_uri then | 399 if params.redirect_uri == device_uri then |
399 local is_device, device_state = verify_device_token(params.state); | 400 local is_device, device_state = verify_device_token(params.state); |
400 if is_device then | 401 if is_device then |
401 -- reconstruct the device_code | 402 -- reconstruct the device_code |
403 prefix = "device_code:"; | |
402 code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code)); | 404 code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code)); |
403 end | 405 else |
404 end | 406 return oauth_error("invalid_request"); |
405 local ok = codes:set("authorization_code:" .. params.client_id .. "#" .. code, { | 407 end |
408 end | |
409 local ok = codes:set(prefix.. params.client_id .. "#" .. code, { | |
406 expires = os.time() + 600; | 410 expires = os.time() + 600; |
407 granted_jid = granted_jid; | 411 granted_jid = granted_jid; |
408 granted_scopes = granted_scopes; | 412 granted_scopes = granted_scopes; |
409 granted_role = granted_role; | 413 granted_role = granted_role; |
410 challenge = params.code_challenge; | 414 challenge = params.code_challenge; |
576 if not verify_client_secret(params.client_id, params.client_secret) then | 580 if not verify_client_secret(params.client_id, params.client_secret) then |
577 module:log("debug", "client_secret mismatch"); | 581 module:log("debug", "client_secret mismatch"); |
578 return oauth_error("invalid_client", "incorrect credentials"); | 582 return oauth_error("invalid_client", "incorrect credentials"); |
579 end | 583 end |
580 | 584 |
581 local code = codes:get("device_code:" .. params.device_code); | 585 local code = codes:get("device_code:" .. params.client_id .. "#" .. params.device_code); |
582 if type(code) ~= "table" or code_expired(code) then | 586 if type(code) ~= "table" or code_expired(code) then |
583 return oauth_error("expired_token"); | 587 return oauth_error("expired_token"); |
584 elseif code.error then | 588 elseif code.error then |
585 return code.error; | 589 return code.error; |
586 elseif not code.granted_jid then | 590 elseif not code.granted_jid then |
587 return oauth_error("authorization_pending"); | 591 return oauth_error("authorization_pending"); |
588 end | 592 end |
589 codes:set("device_code:" .. params.device_code, nil); | 593 codes:set("device_code:" .. params.client_id .. "#" .. params.device_code, nil); |
590 | 594 |
591 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token)); | 595 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token)); |
592 end | 596 end |
593 | 597 |
594 -- RFC 7636 Proof Key for Code Exchange by OAuth Public Clients | 598 -- RFC 7636 Proof Key for Code Exchange by OAuth Public Clients |
989 -- device code should be derivable after consent but not guessable by the user | 993 -- device code should be derivable after consent but not guessable by the user |
990 local device_code = b64url(hashes.hmac_sha256(verification_key, user_code)); | 994 local device_code = b64url(hashes.hmac_sha256(verification_key, user_code)); |
991 local verification_uri = module:http_url() .. "/device"; | 995 local verification_uri = module:http_url() .. "/device"; |
992 local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code }); | 996 local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code }); |
993 | 997 |
994 local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = os.time() + 1200 }); | 998 local expires = os.time() + 600; |
999 local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = expires }); | |
995 local uc_ok = codes:set("user_code:" .. user_code, | 1000 local uc_ok = codes:set("user_code:" .. user_code, |
996 { user_code = user_code; expires = os.time() + 600; client_id = params.client_id; | 1001 { user_code = user_code; expires = expires; client_id = params.client_id; |
997 scope = requested_scopes:concat(" ") }); | 1002 scope = requested_scopes:concat(" ") }); |
998 if not dc_ok or not uc_ok then | 1003 if not dc_ok or not uc_ok then |
999 return oauth_error("temporarily_unavailable"); | 1004 return oauth_error("temporarily_unavailable"); |
1000 end | 1005 end |
1001 | 1006 |
1039 }); | 1044 }); |
1040 }; | 1045 }; |
1041 } | 1046 } |
1042 end | 1047 end |
1043 | 1048 |
1049 local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false); | |
1050 | |
1044 local function handle_revocation_request(event) | 1051 local function handle_revocation_request(event) |
1045 local request, response = event.request, event.response; | 1052 local request, response = event.request, event.response; |
1046 response.headers.cache_control = "no-store"; | 1053 response.headers.cache_control = "no-store"; |
1047 response.headers.pragma = "no-cache"; | 1054 response.headers.pragma = "no-cache"; |
1048 if request.headers.authorization then | 1055 if request.headers.authorization then |
1053 end | 1060 end |
1054 -- OAuth "client" credentials | 1061 -- OAuth "client" credentials |
1055 if not verify_client_secret(credentials.username, credentials.password) then | 1062 if not verify_client_secret(credentials.username, credentials.password) then |
1056 return 401; | 1063 return 401; |
1057 end | 1064 end |
1065 -- TODO check that it's their token I guess? | |
1066 elseif strict_auth_revoke then | |
1067 -- Why require auth to revoke a leaked token? | |
1068 response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name); | |
1069 return 401; | |
1058 end | 1070 end |
1059 | 1071 |
1060 local form_data = strict_formdecode(event.request.body); | 1072 local form_data = strict_formdecode(event.request.body); |
1061 if not form_data or not form_data.token then | 1073 if not form_data or not form_data.token then |
1062 response.headers.accept = "application/x-www-form-urlencoded"; | 1074 response.headers.accept = "application/x-www-form-urlencoded"; |
1222 function create_client(client_metadata) | 1234 function create_client(client_metadata) |
1223 if not schema.validate(registration_schema, client_metadata) then | 1235 if not schema.validate(registration_schema, client_metadata) then |
1224 return nil, oauth_error("invalid_request", "Failed schema validation."); | 1236 return nil, oauth_error("invalid_request", "Failed schema validation."); |
1225 end | 1237 end |
1226 | 1238 |
1239 local client_uri = url.parse(client_metadata.client_uri); | |
1240 if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then | |
1241 return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri"); | |
1242 end | |
1243 | |
1244 if not client_metadata.application_type and redirect_uri_allowed(client_metadata.redirect_uris[1], client_uri, "native") then | |
1245 client_metadata.application_type = "native"; | |
1246 -- else defaults to "web" | |
1247 end | |
1248 | |
1227 -- Fill in default values | 1249 -- Fill in default values |
1228 for propname, propspec in pairs(registration_schema.properties) do | 1250 for propname, propspec in pairs(registration_schema.properties) do |
1229 if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then | 1251 if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then |
1230 client_metadata[propname] = propspec.default; | 1252 client_metadata[propname] = propspec.default; |
1231 end | 1253 end |
1234 -- MUST ignore any metadata that it does not understand | 1256 -- MUST ignore any metadata that it does not understand |
1235 for propname in pairs(client_metadata) do | 1257 for propname in pairs(client_metadata) do |
1236 if not registration_schema.properties[propname] then | 1258 if not registration_schema.properties[propname] then |
1237 client_metadata[propname] = nil; | 1259 client_metadata[propname] = nil; |
1238 end | 1260 end |
1239 end | |
1240 | |
1241 local client_uri = url.parse(client_metadata.client_uri); | |
1242 if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then | |
1243 return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri"); | |
1244 end | 1261 end |
1245 | 1262 |
1246 for _, redirect_uri in ipairs(client_metadata.redirect_uris) do | 1263 for _, redirect_uri in ipairs(client_metadata.redirect_uris) do |
1247 if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then | 1264 if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then |
1248 return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI."); | 1265 return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI."); |