Software / code / prosody-modules
Comparison
mod_http_oauth2/mod_http_oauth2.lua @ 6309:342f88e8d522 draft
Merge update
| author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
|---|---|
| date | Sun, 15 Jun 2025 01:08:46 +0700 |
| parent | 6273:8ceedc336d0d |
| parent | 6308:e1c54de06905 |
| child | 6344:eb834f754f57 |
comparison
equal
deleted
inserted
replaced
| 6279:b92aea4e7ff4 | 6309:342f88e8d522 |
|---|---|
| 408 | 408 |
| 409 function grant_type_handlers.password(params, client) | 409 function grant_type_handlers.password(params, client) |
| 410 local request_username | 410 local request_username |
| 411 | 411 |
| 412 if expect_username_jid then | 412 if expect_username_jid then |
| 413 local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)")); | 413 local request_jid = params.username; |
| 414 if not request_jid then | |
| 415 return oauth_error("invalid_request", "missing 'username' (JID)"); | |
| 416 end | |
| 414 local _request_username, request_host = jid.prepped_split(request_jid); | 417 local _request_username, request_host = jid.prepped_split(request_jid); |
| 415 | 418 |
| 416 if not (_request_username and request_host) or request_host ~= module.host then | 419 if not (_request_username and request_host) or request_host ~= module.host then |
| 417 return oauth_error("invalid_request", "invalid JID"); | 420 return oauth_error("invalid_request", "invalid JID"); |
| 418 end | 421 end |
| 419 | 422 |
| 420 request_username = _request_username | 423 request_username = _request_username |
| 421 else | 424 else |
| 422 request_username = assert(params.username, oauth_error("invalid_request", "missing 'username'")); | 425 request_username = params.username; |
| 423 end | 426 if not request_username then |
| 424 | 427 return oauth_error("invalid_request", "missing 'username'"); |
| 425 local request_password = assert(params.password, oauth_error("invalid_request", "missing 'password'")); | 428 end |
| 429 end | |
| 430 | |
| 431 local request_password = params.password; | |
| 432 if not request_password then | |
| 433 return oauth_error("invalid_request", "missing 'password'"); | |
| 434 end | |
| 435 | |
| 436 local auth_event = { | |
| 437 session = { | |
| 438 type = "oauth2"; | |
| 439 ip = "::"; | |
| 440 username = request_username; | |
| 441 host = module.host; | |
| 442 log = module._log; | |
| 443 sasl_handler = { username = request_username; selected = "x-oauth2-password" }; | |
| 444 client_id = client.client_name; | |
| 445 }; | |
| 446 }; | |
| 426 | 447 |
| 427 if not usermanager.test_password(request_username, module.host, request_password) then | 448 if not usermanager.test_password(request_username, module.host, request_password) then |
| 449 module:fire_event("authentication-failure", auth_event); | |
| 428 return oauth_error("invalid_grant", "incorrect credentials"); | 450 return oauth_error("invalid_grant", "incorrect credentials"); |
| 429 end | 451 end |
| 452 | |
| 453 module:fire_event("authentication-success", auth_event); | |
| 430 | 454 |
| 431 local granted_jid = jid.join(request_username, module.host); | 455 local granted_jid = jid.join(request_username, module.host); |
| 432 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); | 456 local granted_scopes, granted_role = filter_scopes(request_username, params.scope); |
| 433 return json.encode(new_access_token(granted_jid, granted_role, granted_scopes, client)); | 457 return json.encode(new_access_token(granted_jid, granted_role, granted_scopes, client)); |
| 434 end | 458 end |
| 550 end | 574 end |
| 551 | 575 |
| 552 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token)); | 576 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token)); |
| 553 end | 577 end |
| 554 | 578 |
| 555 function grant_type_handlers.refresh_token(params) | 579 function grant_type_handlers.refresh_token(params, client) |
| 556 if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end | |
| 557 if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end | |
| 558 if not params.refresh_token then return oauth_error("invalid_request", "missing 'refresh_token'"); end | 580 if not params.refresh_token then return oauth_error("invalid_request", "missing 'refresh_token'"); end |
| 559 | |
| 560 local client = check_client(params.client_id); | |
| 561 if not client then | |
| 562 return oauth_error("invalid_client", "incorrect credentials"); | |
| 563 end | |
| 564 | |
| 565 if not verify_client_secret(params.client_id, params.client_secret) then | |
| 566 module:log("debug", "client_secret mismatch"); | |
| 567 return oauth_error("invalid_client", "incorrect credentials"); | |
| 568 end | |
| 569 | 581 |
| 570 local refresh_token_info = tokens.get_token_info(params.refresh_token); | 582 local refresh_token_info = tokens.get_token_info(params.refresh_token); |
| 571 if not refresh_token_info or refresh_token_info.purpose ~= "oauth2-refresh" then | 583 if not refresh_token_info or refresh_token_info.purpose ~= "oauth2-refresh" then |
| 572 return oauth_error("invalid_grant", "invalid refresh token"); | 584 return oauth_error("invalid_grant", "invalid refresh token"); |
| 573 end | 585 end |
| 596 refresh_token_info.token = params.refresh_token; | 608 refresh_token_info.token = params.refresh_token; |
| 597 | 609 |
| 598 return json.encode(new_access_token(refresh_token_info.jid, role, new_scopes, client, nil, refresh_token_info)); | 610 return json.encode(new_access_token(refresh_token_info.jid, role, new_scopes, client, nil, refresh_token_info)); |
| 599 end | 611 end |
| 600 | 612 |
| 601 grant_type_handlers[device_uri] = function(params) | 613 grant_type_handlers[device_uri] = function(params, client) |
| 602 if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end | |
| 603 if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end | |
| 604 if not params.device_code then return oauth_error("invalid_request", "missing 'device_code'"); end | 614 if not params.device_code then return oauth_error("invalid_request", "missing 'device_code'"); end |
| 605 | |
| 606 local client = check_client(params.client_id); | |
| 607 if not client then | |
| 608 return oauth_error("invalid_client", "incorrect credentials"); | |
| 609 end | |
| 610 | |
| 611 if not verify_client_secret(params.client_id, params.client_secret) then | |
| 612 module:log("debug", "client_secret mismatch"); | |
| 613 return oauth_error("invalid_client", "incorrect credentials"); | |
| 614 end | |
| 615 | 615 |
| 616 local code = codes:get("device_code:" .. params.client_id .. "#" .. params.device_code); | 616 local code = codes:get("device_code:" .. params.client_id .. "#" .. params.device_code); |
| 617 if type(code) ~= "table" or code_expired(code) then | 617 if type(code) ~= "table" or code_expired(code) then |
| 618 return oauth_error("expired_token"); | 618 return oauth_error("expired_token"); |
| 619 elseif code.error then | 619 elseif code.error then |
| 745 | 745 |
| 746 if module:get_host_type() == "component" then | 746 if module:get_host_type() == "component" then |
| 747 local component_secret = assert(module:get_option_string("component_secret"), "'component_secret' is a required setting when loaded on a Component"); | 747 local component_secret = assert(module:get_option_string("component_secret"), "'component_secret' is a required setting when loaded on a Component"); |
| 748 | 748 |
| 749 function grant_type_handlers.password(params) | 749 function grant_type_handlers.password(params) |
| 750 local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)")); | 750 local request_jid = params.username; |
| 751 local request_password = assert(params.password, oauth_error("invalid_request", "missing 'password'")); | 751 if not request_jid then |
| 752 return oauth_error("invalid_request", "missing 'username' (JID)"); | |
| 753 end | |
| 754 local request_password = params.password | |
| 755 if not request_password then | |
| 756 return oauth_error("invalid_request", "missing 'password'"); | |
| 757 end | |
| 752 local request_username, request_host, request_resource = jid.prepped_split(request_jid); | 758 local request_username, request_host, request_resource = jid.prepped_split(request_jid); |
| 753 if params.scope then | 759 if params.scope then |
| 754 -- TODO shouldn't we support scopes / roles here? | 760 -- TODO shouldn't we support scopes / roles here? |
| 755 return oauth_error("invalid_scope", "unknown scope requested"); | 761 return oauth_error("invalid_scope", "unknown scope requested"); |
| 756 end | 762 end |
| 779 -- appending the error information to the redirect_uri and sending the | 785 -- appending the error information to the redirect_uri and sending the |
| 780 -- redirect to the user-agent. In some cases we can't do this, e.g. if | 786 -- redirect to the user-agent. In some cases we can't do this, e.g. if |
| 781 -- the redirect_uri is missing or invalid. In those cases, we render an | 787 -- the redirect_uri is missing or invalid. In those cases, we render an |
| 782 -- error directly to the user-agent. | 788 -- error directly to the user-agent. |
| 783 local function error_response(request, redirect_uri, err) | 789 local function error_response(request, redirect_uri, err) |
| 784 if not redirect_uri or redirect_uri == oob_uri or redirect_uri == device_uri then | 790 if not redirect_uri or redirect_uri == oob_uri then |
| 785 return render_error(err); | 791 return render_error(err); |
| 786 end | 792 end |
| 787 local q = strict_formdecode(request.url.query); | 793 local params = strict_formdecode(request.url.query); |
| 794 if redirect_uri == device_uri then | |
| 795 local is_device, device_state = verify_device_token(params.state); | |
| 796 if is_device then | |
| 797 local device_code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code)); | |
| 798 local code = codes:get("device_code:" .. params.client_id .. "#" .. device_code); | |
| 799 if type(code) == "table" then | |
| 800 code.error = err; | |
| 801 code.expires = os.time() + 60; | |
| 802 codes:set("device_code:" .. params.client_id .. "#" .. device_code, code); | |
| 803 end | |
| 804 end | |
| 805 return render_error(err); | |
| 806 end | |
| 788 local redirect_query = url.parse(redirect_uri); | 807 local redirect_query = url.parse(redirect_uri); |
| 789 local sep = redirect_query.query and "&" or "?"; | 808 local sep = redirect_query.query and "&" or "?"; |
| 790 redirect_uri = redirect_uri | 809 redirect_uri = redirect_uri |
| 791 .. sep .. http.formencode(err.extra.oauth2_response) | 810 .. sep .. http.formencode(err.extra.oauth2_response) |
| 792 .. "&" .. http.formencode({ state = q.state, iss = get_issuer() }); | 811 .. "&" .. http.formencode({ state = params.state, iss = get_issuer() }); |
| 793 module:log("debug", "Sending error response to client via redirect to %s", redirect_uri); | 812 module:log("debug", "Sending error response to client via redirect to %s", redirect_uri); |
| 794 return { | 813 return { |
| 795 status_code = 303; | 814 status_code = 303; |
| 796 headers = { | 815 headers = { |
| 797 cache_control = "no-store"; | 816 cache_control = "no-store"; |
| 840 else | 859 else |
| 841 module:log("debug", "Challenge method %q enabled", handler_type); | 860 module:log("debug", "Challenge method %q enabled", handler_type); |
| 842 end | 861 end |
| 843 end | 862 end |
| 844 | 863 |
| 864 local function array_contains(haystack, needle) | |
| 865 for _, item in ipairs(haystack) do | |
| 866 if item == needle then | |
| 867 return true | |
| 868 end | |
| 869 end | |
| 870 return false | |
| 871 end | |
| 872 | |
| 845 function handle_token_grant(event) | 873 function handle_token_grant(event) |
| 846 local credentials = get_request_credentials(event.request); | 874 local credentials = get_request_credentials(event.request); |
| 847 | 875 |
| 848 event.response.headers.content_type = "application/json"; | 876 event.response.headers.content_type = "application/json"; |
| 849 event.response.headers.cache_control = "no-store"; | 877 event.response.headers.cache_control = "no-store"; |
| 872 return oauth_error("invalid_client", "incorrect credentials"); | 900 return oauth_error("invalid_client", "incorrect credentials"); |
| 873 end | 901 end |
| 874 | 902 |
| 875 | 903 |
| 876 local grant_type = params.grant_type | 904 local grant_type = params.grant_type |
| 905 if not array_contains(client.grant_types or { "authorization_code" }, grant_type) then | |
| 906 return oauth_error("invalid_request", "'grant_type' not registered"); | |
| 907 end | |
| 908 | |
| 877 local grant_handler = grant_type_handlers[grant_type]; | 909 local grant_handler = grant_type_handlers[grant_type]; |
| 878 if not grant_handler then | 910 if not grant_handler then |
| 879 return oauth_error("invalid_request", "No such grant type."); | 911 return oauth_error("invalid_request", "'grant_type' not available"); |
| 880 end | 912 end |
| 913 | |
| 881 return grant_handler(params, client); | 914 return grant_handler(params, client); |
| 882 end | 915 end |
| 883 | 916 |
| 884 local function handle_authorization_request(event) | 917 local function handle_authorization_request(event) |
| 885 local request = event.request; | 918 local request = event.request; |
| 907 if not redirect_uri then | 940 if not redirect_uri then |
| 908 return render_error(oauth_error("invalid_request", "Invalid 'redirect_uri' parameter")); | 941 return render_error(oauth_error("invalid_request", "Invalid 'redirect_uri' parameter")); |
| 909 end | 942 end |
| 910 -- From this point we know that redirect_uri is safe to use | 943 -- From this point we know that redirect_uri is safe to use |
| 911 | 944 |
| 912 local client_response_types = set.new(array(client.response_types or { "code" })); | 945 local response_type = params.response_type; |
| 913 client_response_types = set.intersection(client_response_types, allowed_response_type_handlers); | 946 if not array_contains(client.response_types or { "code" }, response_type) then |
| 914 if not client_response_types:contains(params.response_type) then | 947 return error_response(request, redirect_uri, oauth_error("invalid_client", "'response_type' not registered")); |
| 915 return error_response(request, redirect_uri, oauth_error("invalid_client", "'response_type' not allowed")); | 948 end |
| 949 if not allowed_response_type_handlers:contains(response_type) then | |
| 950 return error_response(request, redirect_uri, oauth_error("unsupported_response_type", "'response_type' not allowed")); | |
| 951 end | |
| 952 local response_handler = response_type_handlers[response_type]; | |
| 953 if not response_handler then | |
| 954 return error_response(request, redirect_uri, oauth_error("unsupported_response_type")); | |
| 916 end | 955 end |
| 917 | 956 |
| 918 local requested_scopes = parse_scopes(params.scope or ""); | 957 local requested_scopes = parse_scopes(params.scope or ""); |
| 919 if client.scope then | 958 if client.scope then |
| 920 local client_scopes = set.new(parse_scopes(client.scope)); | 959 local client_scopes = set.new(parse_scopes(client.scope)); |
| 974 -- Render consent page | 1013 -- Render consent page |
| 975 return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true); | 1014 return render_page(templates.consent, { state = auth_state; client = client; scopes = scopes+roles }, true); |
| 976 end | 1015 end |
| 977 elseif not auth_state.consent then | 1016 elseif not auth_state.consent then |
| 978 -- Notify client of rejection | 1017 -- Notify client of rejection |
| 979 if redirect_uri == device_uri then | |
| 980 local is_device, device_state = verify_device_token(params.state); | |
| 981 if is_device then | |
| 982 local device_code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code)); | |
| 983 local code = codes:get("device_code:" .. params.client_id .. "#" .. device_code); | |
| 984 code.error = oauth_error("access_denied"); | |
| 985 code.expires = os.time() + 60; | |
| 986 codes:set("device_code:" .. params.client_id .. "#" .. device_code, code); | |
| 987 end | |
| 988 end | |
| 989 return error_response(request, redirect_uri, oauth_error("access_denied")); | 1018 return error_response(request, redirect_uri, oauth_error("access_denied")); |
| 990 end | 1019 end |
| 991 -- else auth_state.consent == true | 1020 -- else auth_state.consent == true |
| 992 | 1021 |
| 993 local granted_scopes = auth_state.scopes | 1022 local granted_scopes = auth_state.scopes |
| 1007 iss = get_issuer(); | 1036 iss = get_issuer(); |
| 1008 sub = url.build({ scheme = "xmpp"; path = user_jid }); | 1037 sub = url.build({ scheme = "xmpp"; path = user_jid }); |
| 1009 aud = params.client_id; | 1038 aud = params.client_id; |
| 1010 auth_time = auth_state.user.iat; | 1039 auth_time = auth_state.user.iat; |
| 1011 nonce = params.nonce; | 1040 nonce = params.nonce; |
| 1012 amr = auth_state.user.amr; | 1041 amr = auth_state.user.amr; -- RFC 8176: Authentication Method Reference Values |
| 1013 }); | 1042 }); |
| 1014 local response_type = params.response_type; | |
| 1015 local response_handler = response_type_handlers[response_type]; | |
| 1016 if not response_handler then | |
| 1017 return error_response(request, redirect_uri, oauth_error("unsupported_response_type")); | |
| 1018 end | |
| 1019 local ret = response_handler(client, params, user_jid, id_token); | 1043 local ret = response_handler(client, params, user_jid, id_token); |
| 1020 if errors.is_err(ret) then | 1044 if errors.is_err(ret) then |
| 1021 return error_response(request, redirect_uri, ret); | 1045 return error_response(request, redirect_uri, ret); |
| 1022 end | 1046 end |
| 1023 return ret; | 1047 return ret; |
| 1305 default = "web"; | 1329 default = "web"; |
| 1306 }; | 1330 }; |
| 1307 response_types = { | 1331 response_types = { |
| 1308 title = "Response Types"; | 1332 title = "Response Types"; |
| 1309 type = "array"; | 1333 type = "array"; |
| 1310 minItems = 1; | |
| 1311 uniqueItems = true; | 1334 uniqueItems = true; |
| 1312 items = { type = "string"; enum = { "code"; "token" } }; | 1335 items = { type = "string"; enum = { "code"; "token" } }; |
| 1313 default = { "code" }; | 1336 default = { "code" }; |
| 1314 }; | 1337 }; |
| 1315 client_name = { | 1338 client_name = { |
| 1469 end | 1492 end |
| 1470 | 1493 |
| 1471 local grant_types = set.new(client_metadata.grant_types); | 1494 local grant_types = set.new(client_metadata.grant_types); |
| 1472 local response_types = set.new(client_metadata.response_types); | 1495 local response_types = set.new(client_metadata.response_types); |
| 1473 | 1496 |
| 1497 if not (grant_types - allowed_grant_type_handlers):empty() then | |
| 1498 return nil, oauth_error("invalid_client_metadata", "Disallowed 'grant_types' specified"); | |
| 1499 elseif not (response_types - allowed_response_type_handlers):empty() then | |
| 1500 return nil, oauth_error("invalid_client_metadata", "Disallowed 'response_types' specified"); | |
| 1501 end | |
| 1502 | |
| 1474 if grant_types:contains("authorization_code") and not response_types:contains("code") then | 1503 if grant_types:contains("authorization_code") and not response_types:contains("code") then |
| 1475 return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'"); | 1504 return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'"); |
| 1476 elseif grant_types:contains("implicit") and not response_types:contains("token") then | 1505 elseif grant_types:contains("implicit") and not response_types:contains("token") then |
| 1477 return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'"); | 1506 return nil, oauth_error("invalid_client_metadata", "Inconsistency between 'grant_types' and 'response_types'"); |
| 1478 end | |
| 1479 | |
| 1480 if set.intersection(grant_types, allowed_grant_type_handlers):empty() then | |
| 1481 return nil, oauth_error("invalid_client_metadata", "No allowed 'grant_types' specified"); | |
| 1482 elseif set.intersection(response_types, allowed_response_type_handlers):empty() then | |
| 1483 return nil, oauth_error("invalid_client_metadata", "No allowed 'response_types' specified"); | |
| 1484 end | 1507 end |
| 1485 | 1508 |
| 1486 if client_metadata.token_endpoint_auth_method ~= "none" then | 1509 if client_metadata.token_endpoint_auth_method ~= "none" then |
| 1487 -- Ensure that each client_id JWT with a client_secret is unique. | 1510 -- Ensure that each client_id JWT with a client_secret is unique. |
| 1488 -- A short ID along with the issued at timestamp should be sufficient to | 1511 -- A short ID along with the issued at timestamp should be sufficient to |
| 1667 authorization_server_metadata = { | 1690 authorization_server_metadata = { |
| 1668 -- RFC 8414: OAuth 2.0 Authorization Server Metadata | 1691 -- RFC 8414: OAuth 2.0 Authorization Server Metadata |
| 1669 issuer = get_issuer(); | 1692 issuer = get_issuer(); |
| 1670 authorization_endpoint = handle_authorization_request and module:http_url() .. "/authorize" or nil; | 1693 authorization_endpoint = handle_authorization_request and module:http_url() .. "/authorize" or nil; |
| 1671 token_endpoint = handle_token_grant and module:http_url() .. "/token" or nil; | 1694 token_endpoint = handle_token_grant and module:http_url() .. "/token" or nil; |
| 1695 jwks_uri = nil; -- REQUIRED in OpenID Discovery but not in OAuth 2.0 Metadata | |
| 1672 registration_endpoint = handle_register_request and module:http_url() .. "/register" or nil; | 1696 registration_endpoint = handle_register_request and module:http_url() .. "/register" or nil; |
| 1673 scopes_supported = usermanager.get_all_roles | 1697 scopes_supported = usermanager.get_all_roles |
| 1674 and array(it.keys(usermanager.get_all_roles(module.host))):push("xmpp"):append(array(openid_claims:items())); | 1698 and array(it.keys(usermanager.get_all_roles(module.host))):push("xmpp"):append(array(openid_claims:items())); |
| 1675 response_types_supported = array(it.keys(response_type_handlers)); | 1699 response_types_supported = array(it.keys(response_type_handlers)); |
| 1676 token_endpoint_auth_methods_supported = array({ "client_secret_post"; "client_secret_basic" }); | 1700 response_modes_supported = array(it.keys(response_type_handlers)):map(tmap { token = "fragment"; code = "query" }); |
| 1701 grant_types_supported = array(it.keys(grant_type_handlers)); | |
| 1702 token_endpoint_auth_methods_supported = array({ "client_secret_basic"; "client_secret_post"; "none" }); | |
| 1703 token_endpoint_auth_signing_alg_values_supported = nil; | |
| 1704 service_documentation = module:get_option_string("oauth2_service_documentation", "https://modules.prosody.im/mod_http_oauth2.html"); | |
| 1705 ui_locales_supported = allowed_locales[1] and allowed_locales; | |
| 1677 op_policy_uri = module:get_option_string("oauth2_policy_url", nil); | 1706 op_policy_uri = module:get_option_string("oauth2_policy_url", nil); |
| 1678 op_tos_uri = module:get_option_string("oauth2_terms_url", nil); | 1707 op_tos_uri = module:get_option_string("oauth2_terms_url", nil); |
| 1679 revocation_endpoint = handle_revocation_request and module:http_url() .. "/revoke" or nil; | 1708 revocation_endpoint = handle_revocation_request and module:http_url() .. "/revoke" or nil; |
| 1680 revocation_endpoint_auth_methods_supported = array({ "client_secret_basic" }); | 1709 revocation_endpoint_auth_methods_supported = array({ "client_secret_basic"; "client_secret_post"; "none" }); |
| 1681 device_authorization_endpoint = handle_device_authorization_request and module:http_url() .. "/device"; | 1710 revocation_endpoint_auth_signing_alg_values_supported = nil; |
| 1682 introspection_endpoint = handle_introspection_request and module:http_url() .. "/introspect"; | 1711 introspection_endpoint = handle_introspection_request and module:http_url() .. "/introspect"; |
| 1683 introspection_endpoint_auth_methods_supported = nil; | 1712 introspection_endpoint_auth_methods_supported = nil; |
| 1713 introspection_endpoint_auth_signing_alg_values_supported = nil; | |
| 1684 code_challenge_methods_supported = array(it.keys(verifier_transforms)); | 1714 code_challenge_methods_supported = array(it.keys(verifier_transforms)); |
| 1685 grant_types_supported = array(it.keys(grant_type_handlers)); | 1715 |
| 1686 response_modes_supported = array(it.keys(response_type_handlers)):map(tmap { token = "fragment"; code = "query" }); | 1716 -- RFC 8628: OAuth 2.0 Device Authorization Grant |
| 1717 device_authorization_endpoint = handle_device_authorization_request and module:http_url() .. "/device"; | |
| 1718 | |
| 1719 -- RFC 9207: OAuth 2.0 Authorization Server Issuer Identification | |
| 1687 authorization_response_iss_parameter_supported = true; | 1720 authorization_response_iss_parameter_supported = true; |
| 1688 service_documentation = module:get_option_string("oauth2_service_documentation", "https://modules.prosody.im/mod_http_oauth2.html"); | 1721 |
| 1689 ui_locales_supported = allowed_locales[1] and allowed_locales; | 1722 -- OpenID Connect Discovery 1.0 |
| 1690 | |
| 1691 -- OpenID | |
| 1692 userinfo_endpoint = handle_userinfo_request and module:http_url() .. "/userinfo" or nil; | 1723 userinfo_endpoint = handle_userinfo_request and module:http_url() .. "/userinfo" or nil; |
| 1693 jwks_uri = nil; -- REQUIRED in OpenID Discovery but not in OAuth 2.0 Metadata | |
| 1694 id_token_signing_alg_values_supported = { "HS256" }; -- The algorithm RS256 MUST be included, but we use HS256 and client_secret as shared key. | 1724 id_token_signing_alg_values_supported = { "HS256" }; -- The algorithm RS256 MUST be included, but we use HS256 and client_secret as shared key. |
| 1695 } | 1725 } |
| 1696 return authorization_server_metadata; | 1726 return authorization_server_metadata; |
| 1697 end | 1727 end |
| 1698 | 1728 |