Mercurial > prosody-modules
comparison mod_http_oauth2/mod_http_oauth2.lua @ 5680:b43c989fb69c
mod_http_oauth2: Implement introspection endpoint
"Tell me about this token"
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 25 May 2023 09:31:21 +0200 |
| parents | bbde136a4c29 |
| children | 8cb3da7df521 |
comparison
equal
deleted
inserted
replaced
| 5679:e274431bf4ce | 5680:b43c989fb69c |
|---|---|
| 136 | 136 |
| 137 client.client_hash = b64url(hashes.sha256(client_id)); | 137 client.client_hash = b64url(hashes.sha256(client_id)); |
| 138 return client; | 138 return client; |
| 139 end | 139 end |
| 140 | 140 |
| 141 local purpose_map = { ["oauth2-refresh"] = "refresh_token"; ["oauth"] = "access_token" }; | |
| 142 | |
| 141 -- scope : string | array | set | 143 -- scope : string | array | set |
| 142 -- | 144 -- |
| 143 -- at each step, allow the same or a subset of scopes | 145 -- at each step, allow the same or a subset of scopes |
| 144 -- (all ( client ( grant ( token ) ) )) | 146 -- (all ( client ( grant ( token ) ) )) |
| 145 -- preserve order since it determines role if more than one granted | 147 -- preserve order since it determines role if more than one granted |
| 1043 scope = device_info.scope; | 1045 scope = device_info.scope; |
| 1044 state = new_device_token({ user_code = params.user_code }); | 1046 state = new_device_token({ user_code = params.user_code }); |
| 1045 }); | 1047 }); |
| 1046 }; | 1048 }; |
| 1047 } | 1049 } |
| 1050 end | |
| 1051 | |
| 1052 local function handle_introspection_request(event) | |
| 1053 local request = event.request; | |
| 1054 local credentials = get_request_credentials(request); | |
| 1055 if not credentials or credentials.type ~= "basic" then | |
| 1056 event.response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name); | |
| 1057 return 401; | |
| 1058 end | |
| 1059 -- OAuth "client" credentials | |
| 1060 if not verify_client_secret(credentials.username, credentials.password) then | |
| 1061 return 401; | |
| 1062 end | |
| 1063 | |
| 1064 local form_data = http.formdecode(request.body or "="); | |
| 1065 local token = form_data.token; | |
| 1066 if not token then | |
| 1067 return 400; | |
| 1068 end | |
| 1069 | |
| 1070 local token_info = tokens.get_token_info(form_data.token); | |
| 1071 if not token_info then | |
| 1072 return { headers = { content_type = "application/json" }; body = json.encode { active = false } }; | |
| 1073 end | |
| 1074 | |
| 1075 return { | |
| 1076 headers = { content_type = "application/json" }; | |
| 1077 body = json.encode { | |
| 1078 active = true; | |
| 1079 client_id = credentials.username; -- We don't really know for sure | |
| 1080 username = jid.node(token_info.jid); | |
| 1081 scope = token_info.grant.data.oauth2_scopes; | |
| 1082 token_type = purpose_map[token_info.purpose]; | |
| 1083 exp = token.expires; | |
| 1084 iat = token.created; | |
| 1085 sub = url.build({ scheme = "xmpp"; path = token_info.jid }); | |
| 1086 aud = nil; | |
| 1087 iss = get_issuer(); | |
| 1088 jti = token_info.id; | |
| 1089 }; | |
| 1090 }; | |
| 1048 end | 1091 end |
| 1049 | 1092 |
| 1050 local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false); | 1093 local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false); |
| 1051 | 1094 |
| 1052 local function handle_revocation_request(event) | 1095 local function handle_revocation_request(event) |
| 1423 -- Step 4 is later repeated using the refresh token to get new access tokens. | 1466 -- Step 4 is later repeated using the refresh token to get new access tokens. |
| 1424 | 1467 |
| 1425 -- Step 5. Revoke token (access or refresh) | 1468 -- Step 5. Revoke token (access or refresh) |
| 1426 ["POST /revoke"] = handle_revocation_request; | 1469 ["POST /revoke"] = handle_revocation_request; |
| 1427 | 1470 |
| 1471 -- Get info about a token | |
| 1472 ["POST /introspect"] = handle_introspection_request; | |
| 1473 | |
| 1428 -- OpenID | 1474 -- OpenID |
| 1429 ["GET /userinfo"] = handle_userinfo_request; | 1475 ["GET /userinfo"] = handle_userinfo_request; |
| 1430 | 1476 |
| 1431 -- Optional static content for templates | 1477 -- Optional static content for templates |
| 1432 ["GET /style.css"] = templates.css and { | 1478 ["GET /style.css"] = templates.css and { |
| 1444 | 1490 |
| 1445 -- Some convenient fallback handlers | 1491 -- Some convenient fallback handlers |
| 1446 ["GET /register"] = { headers = { content_type = "application/schema+json" }; body = json.encode(registration_schema) }; | 1492 ["GET /register"] = { headers = { content_type = "application/schema+json" }; body = json.encode(registration_schema) }; |
| 1447 ["GET /token"] = function() return 405; end; | 1493 ["GET /token"] = function() return 405; end; |
| 1448 ["GET /revoke"] = function() return 405; end; | 1494 ["GET /revoke"] = function() return 405; end; |
| 1495 ["GET /introspect"] = function() return 405; end; | |
| 1449 }; | 1496 }; |
| 1450 }); | 1497 }); |
| 1451 | 1498 |
| 1452 local http_server = require "net.http.server"; | 1499 local http_server = require "net.http.server"; |
| 1453 | 1500 |
| 1480 op_policy_uri = module:get_option_string("oauth2_policy_url", nil); | 1527 op_policy_uri = module:get_option_string("oauth2_policy_url", nil); |
| 1481 op_tos_uri = module:get_option_string("oauth2_terms_url", nil); | 1528 op_tos_uri = module:get_option_string("oauth2_terms_url", nil); |
| 1482 revocation_endpoint = handle_revocation_request and module:http_url() .. "/revoke" or nil; | 1529 revocation_endpoint = handle_revocation_request and module:http_url() .. "/revoke" or nil; |
| 1483 revocation_endpoint_auth_methods_supported = array({ "client_secret_basic" }); | 1530 revocation_endpoint_auth_methods_supported = array({ "client_secret_basic" }); |
| 1484 device_authorization_endpoint = handle_device_authorization_request and module:http_url() .. "/device"; | 1531 device_authorization_endpoint = handle_device_authorization_request and module:http_url() .. "/device"; |
| 1532 introspection_endpoint = handle_introspection_request and module:http_url() .. "/introspect"; | |
| 1533 introspection_endpoint_auth_methods_supported = nil; | |
| 1485 code_challenge_methods_supported = array(it.keys(verifier_transforms)); | 1534 code_challenge_methods_supported = array(it.keys(verifier_transforms)); |
| 1486 grant_types_supported = array(it.keys(grant_type_handlers)); | 1535 grant_types_supported = array(it.keys(grant_type_handlers)); |
| 1487 response_modes_supported = array(it.keys(response_type_handlers)):map(tmap { token = "fragment"; code = "query" }); | 1536 response_modes_supported = array(it.keys(response_type_handlers)):map(tmap { token = "fragment"; code = "query" }); |
| 1488 authorization_response_iss_parameter_supported = true; | 1537 authorization_response_iss_parameter_supported = true; |
| 1489 service_documentation = module:get_option_string("oauth2_service_documentation", "https://modules.prosody.im/mod_http_oauth2.html"); | 1538 service_documentation = module:get_option_string("oauth2_service_documentation", "https://modules.prosody.im/mod_http_oauth2.html"); |
