Comparison

mod_http_oauth2/mod_http_oauth2.lua @ 5663:f6e8165a2ec2

Merge upstream
author Trần H. Trung <xmpp:trần.h.trung@trung.fun>
date Tue, 29 Aug 2023 22:46:27 +0700
parent 5651:dd2079b3dec6
child 5665:7c105277a9ca
comparison
equal deleted inserted replaced
5640:5e7e609fae0c 5663:f6e8165a2ec2
392 392
393 if pkce_required and not params.code_challenge then 393 if pkce_required and not params.code_challenge then
394 return oauth_error("invalid_request", "PKCE required"); 394 return oauth_error("invalid_request", "PKCE required");
395 end 395 end
396 396
397 local prefix = "authorization_code:";
397 local code = id.medium(); 398 local code = id.medium();
398 if params.redirect_uri == device_uri then 399 if params.redirect_uri == device_uri then
399 local is_device, device_state = verify_device_token(params.state); 400 local is_device, device_state = verify_device_token(params.state);
400 if is_device then 401 if is_device then
401 -- reconstruct the device_code 402 -- reconstruct the device_code
403 prefix = "device_code:";
402 code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code)); 404 code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
403 end 405 else
404 end 406 return oauth_error("invalid_request");
405 local ok = codes:set("authorization_code:" .. params.client_id .. "#" .. code, { 407 end
408 end
409 local ok = codes:set(prefix.. params.client_id .. "#" .. code, {
406 expires = os.time() + 600; 410 expires = os.time() + 600;
407 granted_jid = granted_jid; 411 granted_jid = granted_jid;
408 granted_scopes = granted_scopes; 412 granted_scopes = granted_scopes;
409 granted_role = granted_role; 413 granted_role = granted_role;
410 challenge = params.code_challenge; 414 challenge = params.code_challenge;
576 if not verify_client_secret(params.client_id, params.client_secret) then 580 if not verify_client_secret(params.client_id, params.client_secret) then
577 module:log("debug", "client_secret mismatch"); 581 module:log("debug", "client_secret mismatch");
578 return oauth_error("invalid_client", "incorrect credentials"); 582 return oauth_error("invalid_client", "incorrect credentials");
579 end 583 end
580 584
581 local code = codes:get("device_code:" .. params.device_code); 585 local code = codes:get("device_code:" .. params.client_id .. "#" .. params.device_code);
582 if type(code) ~= "table" or code_expired(code) then 586 if type(code) ~= "table" or code_expired(code) then
583 return oauth_error("expired_token"); 587 return oauth_error("expired_token");
584 elseif code.error then 588 elseif code.error then
585 return code.error; 589 return code.error;
586 elseif not code.granted_jid then 590 elseif not code.granted_jid then
587 return oauth_error("authorization_pending"); 591 return oauth_error("authorization_pending");
588 end 592 end
589 codes:set("device_code:" .. params.device_code, nil); 593 codes:set("device_code:" .. params.client_id .. "#" .. params.device_code, nil);
590 594
591 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token)); 595 return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token));
592 end 596 end
593 597
594 -- RFC 7636 Proof Key for Code Exchange by OAuth Public Clients 598 -- RFC 7636 Proof Key for Code Exchange by OAuth Public Clients
989 -- device code should be derivable after consent but not guessable by the user 993 -- device code should be derivable after consent but not guessable by the user
990 local device_code = b64url(hashes.hmac_sha256(verification_key, user_code)); 994 local device_code = b64url(hashes.hmac_sha256(verification_key, user_code));
991 local verification_uri = module:http_url() .. "/device"; 995 local verification_uri = module:http_url() .. "/device";
992 local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code }); 996 local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code });
993 997
994 local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = os.time() + 1200 }); 998 local expires = os.time() + 600;
999 local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = expires });
995 local uc_ok = codes:set("user_code:" .. user_code, 1000 local uc_ok = codes:set("user_code:" .. user_code,
996 { user_code = user_code; expires = os.time() + 600; client_id = params.client_id; 1001 { user_code = user_code; expires = expires; client_id = params.client_id;
997 scope = requested_scopes:concat(" ") }); 1002 scope = requested_scopes:concat(" ") });
998 if not dc_ok or not uc_ok then 1003 if not dc_ok or not uc_ok then
999 return oauth_error("temporarily_unavailable"); 1004 return oauth_error("temporarily_unavailable");
1000 end 1005 end
1001 1006
1039 }); 1044 });
1040 }; 1045 };
1041 } 1046 }
1042 end 1047 end
1043 1048
1049 local strict_auth_revoke = module:get_option_boolean("oauth2_require_auth_revoke", false);
1050
1044 local function handle_revocation_request(event) 1051 local function handle_revocation_request(event)
1045 local request, response = event.request, event.response; 1052 local request, response = event.request, event.response;
1046 response.headers.cache_control = "no-store"; 1053 response.headers.cache_control = "no-store";
1047 response.headers.pragma = "no-cache"; 1054 response.headers.pragma = "no-cache";
1048 if request.headers.authorization then 1055 if request.headers.authorization then
1053 end 1060 end
1054 -- OAuth "client" credentials 1061 -- OAuth "client" credentials
1055 if not verify_client_secret(credentials.username, credentials.password) then 1062 if not verify_client_secret(credentials.username, credentials.password) then
1056 return 401; 1063 return 401;
1057 end 1064 end
1065 -- TODO check that it's their token I guess?
1066 elseif strict_auth_revoke then
1067 -- Why require auth to revoke a leaked token?
1068 response.headers.www_authenticate = string.format("Basic realm=%q", module.host.."/"..module.name);
1069 return 401;
1058 end 1070 end
1059 1071
1060 local form_data = strict_formdecode(event.request.body); 1072 local form_data = strict_formdecode(event.request.body);
1061 if not form_data or not form_data.token then 1073 if not form_data or not form_data.token then
1062 response.headers.accept = "application/x-www-form-urlencoded"; 1074 response.headers.accept = "application/x-www-form-urlencoded";
1222 function create_client(client_metadata) 1234 function create_client(client_metadata)
1223 if not schema.validate(registration_schema, client_metadata) then 1235 if not schema.validate(registration_schema, client_metadata) then
1224 return nil, oauth_error("invalid_request", "Failed schema validation."); 1236 return nil, oauth_error("invalid_request", "Failed schema validation.");
1225 end 1237 end
1226 1238
1239 local client_uri = url.parse(client_metadata.client_uri);
1240 if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then
1241 return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri");
1242 end
1243
1244 if not client_metadata.application_type and redirect_uri_allowed(client_metadata.redirect_uris[1], client_uri, "native") then
1245 client_metadata.application_type = "native";
1246 -- else defaults to "web"
1247 end
1248
1227 -- Fill in default values 1249 -- Fill in default values
1228 for propname, propspec in pairs(registration_schema.properties) do 1250 for propname, propspec in pairs(registration_schema.properties) do
1229 if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then 1251 if client_metadata[propname] == nil and type(propspec) == "table" and propspec.default ~= nil then
1230 client_metadata[propname] = propspec.default; 1252 client_metadata[propname] = propspec.default;
1231 end 1253 end
1234 -- MUST ignore any metadata that it does not understand 1256 -- MUST ignore any metadata that it does not understand
1235 for propname in pairs(client_metadata) do 1257 for propname in pairs(client_metadata) do
1236 if not registration_schema.properties[propname] then 1258 if not registration_schema.properties[propname] then
1237 client_metadata[propname] = nil; 1259 client_metadata[propname] = nil;
1238 end 1260 end
1239 end
1240
1241 local client_uri = url.parse(client_metadata.client_uri);
1242 if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then
1243 return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri");
1244 end 1261 end
1245 1262
1246 for _, redirect_uri in ipairs(client_metadata.redirect_uris) do 1263 for _, redirect_uri in ipairs(client_metadata.redirect_uris) do
1247 if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then 1264 if not redirect_uri_allowed(redirect_uri, client_uri, client_metadata.application_type) then
1248 return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI."); 1265 return nil, oauth_error("invalid_redirect_uri", "Invalid, insecure or inappropriate redirect URI.");