Software /
code /
prosody-modules
Comparison
mod_http_oauth2/mod_http_oauth2.lua @ 5703: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 |
parent | 5688:bbde136a4c29 |
child | 5704:8cb3da7df521 |
comparison
equal
deleted
inserted
replaced
5702:e274431bf4ce | 5703: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"); |