Mercurial > prosody-modules
comparison mod_http_oauth2/mod_http_oauth2.lua @ 6280:6ea80b73d8f2
mod_http_oauth2: Only require redirect URIs when using grant types that need it
In the Device flow, no redirect URI is used because the client instead
receives responses by polling. It is therefore unnecessary to enforce a
requirement that these include redirect URI(s).
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 03 Jul 2025 15:42:42 +0200 |
| parents | 5dc4ec836ce2 |
| children | 17d9533f7596 |
comparison
equal
deleted
inserted
replaced
| 6279:5dc4ec836ce2 | 6280:6ea80b73d8f2 |
|---|---|
| 373 end | 373 end |
| 374 -- else, not a valid loopback uri | 374 -- else, not a valid loopback uri |
| 375 end | 375 end |
| 376 | 376 |
| 377 local function get_redirect_uri(client, query_redirect_uri) -- record client, string : string | 377 local function get_redirect_uri(client, query_redirect_uri) -- record client, string : string |
| 378 if not query_redirect_uri then | 378 if query_redirect_uri == device_uri and client.grant_types then |
| 379 if array_contains(client.grant_types, device_uri) then | |
| 380 return query_redirect_uri; | |
| 381 end | |
| 382 -- Tried to use device authorization flow without registering it. | |
| 383 return; | |
| 384 elseif not query_redirect_uri and client.redirect_uris then | |
| 379 if #client.redirect_uris ~= 1 then | 385 if #client.redirect_uris ~= 1 then |
| 380 -- Client registered multiple URIs, it needs specify which one to use | 386 -- Client registered multiple URIs, it needs specify which one to use |
| 381 return; | 387 return; |
| 382 end | 388 end |
| 383 -- When only a single URI is registered, that's the default | 389 -- When only a single URI is registered, that's the default |
| 384 return client.redirect_uris[1]; | 390 return client.redirect_uris[1]; |
| 385 end | |
| 386 if query_redirect_uri == device_uri and client.grant_types then | |
| 387 for _, grant_type in ipairs(client.grant_types) do | |
| 388 if grant_type == device_uri then | |
| 389 return query_redirect_uri; | |
| 390 end | |
| 391 end | |
| 392 -- Tried to use device authorization flow without registering it. | |
| 393 return; | |
| 394 end | 391 end |
| 395 -- Verify the client-provided URI matches one previously registered | 392 -- Verify the client-provided URI matches one previously registered |
| 396 for _, redirect_uri in ipairs(client.redirect_uris) do | 393 for _, redirect_uri in ipairs(client.redirect_uris) do |
| 397 if query_redirect_uri == redirect_uri then | 394 if query_redirect_uri == redirect_uri then |
| 398 return redirect_uri | 395 return redirect_uri |
| 1286 type = "object"; | 1283 type = "object"; |
| 1287 required = { | 1284 required = { |
| 1288 -- These are shown to users in the template | 1285 -- These are shown to users in the template |
| 1289 "client_name"; | 1286 "client_name"; |
| 1290 "client_uri"; | 1287 "client_uri"; |
| 1291 -- We need at least one redirect URI for things to work | |
| 1292 "redirect_uris"; | |
| 1293 }; | 1288 }; |
| 1294 properties = { | 1289 properties = { |
| 1295 redirect_uris = { | 1290 redirect_uris = { |
| 1296 title = "List of Redirect URIs"; | 1291 title = "List of Redirect URIs"; |
| 1297 type = "array"; | 1292 type = "array"; |
| 1304 examples = { | 1299 examples = { |
| 1305 "https://app.example.com/redirect"; | 1300 "https://app.example.com/redirect"; |
| 1306 "http://localhost:8080/redirect"; | 1301 "http://localhost:8080/redirect"; |
| 1307 "com.example.app:/redirect"; | 1302 "com.example.app:/redirect"; |
| 1308 oob_uri; | 1303 oob_uri; |
| 1309 device_uri; | |
| 1310 }; | 1304 }; |
| 1305 ["not"] = { | |
| 1306 const = device_uri; | |
| 1307 } | |
| 1311 }; | 1308 }; |
| 1312 }; | 1309 }; |
| 1313 token_endpoint_auth_method = { | 1310 token_endpoint_auth_method = { |
| 1314 title = "Token Endpoint Authentication Method"; | 1311 title = "Token Endpoint Authentication Method"; |
| 1315 description = "Authentication method the client intends to use. Recommended is `client_secret_basic`. \z | 1312 description = "Authentication method the client intends to use. Recommended is `client_secret_basic`. \z |
| 1477 local client_uri = strict_url_parse(client_metadata.client_uri); | 1474 local client_uri = strict_url_parse(client_metadata.client_uri); |
| 1478 if not client_uri or client_uri.scheme ~= "https" or not client_uri.host or loopbacks:contains(client_uri.host) then | 1475 if not client_uri or client_uri.scheme ~= "https" or not client_uri.host or loopbacks:contains(client_uri.host) then |
| 1479 return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri"); | 1476 return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri"); |
| 1480 end | 1477 end |
| 1481 | 1478 |
| 1482 if not client_metadata.application_type and redirect_uri_allowed(client_metadata.redirect_uris[1], client_uri, "native") then | 1479 if not client_metadata.application_type then |
| 1483 client_metadata.application_type = "native"; | 1480 if client_metadata.redirect_uris and redirect_uri_allowed(client_metadata.redirect_uris[1], client_uri, "native") then |
| 1484 -- else defaults to "web" | 1481 client_metadata.application_type = "native"; |
| 1482 elseif array_contains(client_metadata.grant_types, device_uri) then | |
| 1483 client_metadata.application_type = "native"; | |
| 1484 end | |
| 1485 end | 1485 end |
| 1486 | 1486 |
| 1487 -- Fill in default values | 1487 -- Fill in default values |
| 1488 for propname, propspec in pairs(registration_schema.properties) do | 1488 for propname, propspec in pairs(registration_schema.properties) do |
| 1489 if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then | 1489 if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then |
| 1496 if not registration_schema.properties[propname] then | 1496 if not registration_schema.properties[propname] then |
| 1497 client_metadata[propname] = nil; | 1497 client_metadata[propname] = nil; |
| 1498 end | 1498 end |
| 1499 end | 1499 end |
| 1500 | 1500 |
| 1501 for _, redirect_uri in ipairs(client_metadata.redirect_uris) do | 1501 if client_metadata.redirect_uris then |
| 1502 if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then | 1502 for _, redirect_uri in ipairs(client_metadata.redirect_uris) do |
| 1503 return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI."); | 1503 if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then |
| 1504 return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI."); | |
| 1505 end | |
| 1504 end | 1506 end |
| 1505 end | 1507 end |
| 1506 | 1508 |
| 1507 for field, prop_schema in pairs(registration_schema.properties) do | 1509 for field, prop_schema in pairs(registration_schema.properties) do |
| 1508 if field ~= "client_uri" and prop_schema.format == "uri" and client_metadata[field] then | 1510 if field ~= "client_uri" and prop_schema.format == "uri" and client_metadata[field] then |
| 1517 | 1519 |
| 1518 if not (grant_types - allowed_grant_type_handlers):empty() then | 1520 if not (grant_types - allowed_grant_type_handlers):empty() then |
| 1519 return nil, oauth_error("invalid_client_metadata", "Disallowed 'grant_types' specified"); | 1521 return nil, oauth_error("invalid_client_metadata", "Disallowed 'grant_types' specified"); |
| 1520 elseif not (response_types - allowed_response_type_handlers):empty() then | 1522 elseif not (response_types - allowed_response_type_handlers):empty() then |
| 1521 return nil, oauth_error("invalid_client_metadata", "Disallowed 'response_types' specified"); | 1523 return nil, oauth_error("invalid_client_metadata", "Disallowed 'response_types' specified"); |
| 1524 end | |
| 1525 | |
| 1526 | |
| 1527 if not client_metadata.redirect_uris then | |
| 1528 if grant_types:contains("authorization_code") then | |
| 1529 return nil, oauth_error("invalid_client_metadata", "The 'authorization_code' grant requires 'redirect_uris' to be present."); | |
| 1530 elseif grant_types:contains("implicit") then | |
| 1531 return nil, oauth_error("invalid_client_metadata", "The 'implicit' grant requires 'redirect_uris' to be present."); | |
| 1532 end | |
| 1522 end | 1533 end |
| 1523 | 1534 |
| 1524 if grant_types:contains("authorization_code") and not response_types:contains("code") then | 1535 if grant_types:contains("authorization_code") and not response_types:contains("code") then |
| 1525 return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'"); | 1536 return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'"); |
| 1526 elseif grant_types:contains("implicit") and not response_types:contains("token") then | 1537 elseif grant_types:contains("implicit") and not response_types:contains("token") then |
