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");