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