mod_http_oauth2/mod_http_oauth2.lua
changeset 5611 ad9b8f659c96
parent 5609 17aa3bac7f3a
child 5612 1893ae742f66
equal deleted inserted replaced
5610:39bb7232326d 5611:ad9b8f659c96
   396 		if is_device then
   396 		if is_device then
   397 			-- reconstruct the device_code
   397 			-- reconstruct the device_code
   398 			code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
   398 			code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
   399 		end
   399 		end
   400 	end
   400 	end
   401 	local ok = codes:set(params.client_id .. "#" .. code, {
   401 	local ok = codes:set("authorization_code:" .. params.client_id .. "#" .. code, {
   402 		expires = os.time() + 600;
   402 		expires = os.time() + 600;
   403 		granted_jid = granted_jid;
   403 		granted_jid = granted_jid;
   404 		granted_scopes = granted_scopes;
   404 		granted_scopes = granted_scopes;
   405 		granted_role = granted_role;
   405 		granted_role = granted_role;
   406 		challenge = params.code_challenge;
   406 		challenge = params.code_challenge;
   489 
   489 
   490 	if not verify_client_secret(params.client_id, params.client_secret) then
   490 	if not verify_client_secret(params.client_id, params.client_secret) then
   491 		module:log("debug", "client_secret mismatch");
   491 		module:log("debug", "client_secret mismatch");
   492 		return oauth_error("invalid_client", "incorrect credentials");
   492 		return oauth_error("invalid_client", "incorrect credentials");
   493 	end
   493 	end
   494 	local code, err = codes:get(params.client_id .. "#" .. params.code);
   494 	local code, err = codes:get("authorization_code:" .. params.client_id .. "#" .. params.code);
   495 	if err then error(err); end
   495 	if err then error(err); end
   496 	-- MUST NOT use the authorization code more than once, so remove it to
   496 	-- MUST NOT use the authorization code more than once, so remove it to
   497 	-- prevent a second attempted use
   497 	-- prevent a second attempted use
   498 	-- TODO if a second attempt *is* made, revoke any tokens issued
   498 	-- TODO if a second attempt *is* made, revoke any tokens issued
   499 	codes:set(params.client_id .. "#" .. params.code, nil);
   499 	codes:set("authorization_code:" .. params.client_id .. "#" .. params.code, nil);
   500 	if not code or type(code) ~= "table" or code_expired(code) then
   500 	if not code or type(code) ~= "table" or code_expired(code) then
   501 		module:log("debug", "authorization_code invalid or expired: %q", code);
   501 		module:log("debug", "authorization_code invalid or expired: %q", code);
   502 		return oauth_error("invalid_client", "incorrect credentials");
   502 		return oauth_error("invalid_client", "incorrect credentials");
   503 	end
   503 	end
   504 
   504 
   572 	if not verify_client_secret(params.client_id, params.client_secret) then
   572 	if not verify_client_secret(params.client_id, params.client_secret) then
   573 		module:log("debug", "client_secret mismatch");
   573 		module:log("debug", "client_secret mismatch");
   574 		return oauth_error("invalid_client", "incorrect credentials");
   574 		return oauth_error("invalid_client", "incorrect credentials");
   575 	end
   575 	end
   576 
   576 
   577 	local code = codes:get(params.client_id .. "#" .. params.device_code);
   577 	local code = codes:get("device_code:" .. params.device_code);
   578 	if type(code) ~= "table" or code_expired(code) then
   578 	if type(code) ~= "table" or code_expired(code) then
   579 		return oauth_error("expired_token");
   579 		return oauth_error("expired_token");
   580 	elseif code.error then
   580 	elseif code.error then
   581 		return code.error;
   581 		return code.error;
   582 	elseif not code.granted_jid then
   582 	elseif not code.granted_jid then
   583 		return oauth_error("authorization_pending");
   583 		return oauth_error("authorization_pending");
   584 	end
   584 	end
   585 	codes:set(client.client_hash .. "#" .. params.device_code, nil);
   585 	codes:set("device_code:" .. params.device_code, nil);
   586 
   586 
   587 	return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token));
   587 	return json.encode(new_access_token(code.granted_jid, code.granted_role, code.granted_scopes, client, code.id_token));
   588 end
   588 end
   589 
   589 
   590 -- RFC 7636 Proof Key for Code Exchange by OAuth Public Clients
   590 -- RFC 7636 Proof Key for Code Exchange by OAuth Public Clients
   880 		-- Notify client of rejection
   880 		-- Notify client of rejection
   881 		if redirect_uri == device_uri then
   881 		if redirect_uri == device_uri then
   882 			local is_device, device_state = verify_device_token(params.state);
   882 			local is_device, device_state = verify_device_token(params.state);
   883 			if is_device then
   883 			if is_device then
   884 				local device_code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
   884 				local device_code = b64url(hashes.hmac_sha256(verification_key, device_state.user_code));
   885 				local code = codes:get(params.client_id .. "#" .. device_code);
   885 				local code = codes:get("device_code:" .. params.client_id .. "#" .. device_code);
   886 				code.error = oauth_error("access_denied");
   886 				code.error = oauth_error("access_denied");
   887 				code.expires = os.time() + 60;
   887 				code.expires = os.time() + 60;
   888 				codes:set(params.client_id .. "#" .. device_code, code);
   888 				codes:set("device_code:" .. params.client_id .. "#" .. device_code, code);
   889 			end
   889 			end
   890 		end
   890 		end
   891 		return error_response(request, redirect_uri, oauth_error("access_denied"));
   891 		return error_response(request, redirect_uri, oauth_error("access_denied"));
   892 	end
   892 	end
   893 	-- else auth_state.consent == true
   893 	-- else auth_state.consent == true
   967 
   967 
   968 	-- TODO better code generator, this one should be easy to type from a
   968 	-- TODO better code generator, this one should be easy to type from a
   969 	-- screen onto a phone
   969 	-- screen onto a phone
   970 	local user_code = (id.tiny() .. "-" .. id.tiny()):upper();
   970 	local user_code = (id.tiny() .. "-" .. id.tiny()):upper();
   971 	local collisions = 0;
   971 	local collisions = 0;
   972 	while codes:get(user_code) do
   972 	while codes:get("authorization_code:" .. device_uri .. "#" .. user_code) do
   973 		collisions = collisions + 1;
   973 		collisions = collisions + 1;
   974 		if collisions > 10 then
   974 		if collisions > 10 then
   975 			return oauth_error("temporarily_unavailable");
   975 			return oauth_error("temporarily_unavailable");
   976 		end
   976 		end
   977 		user_code = (id.tiny() .. "-" .. id.tiny()):upper();
   977 		user_code = (id.tiny() .. "-" .. id.tiny()):upper();
   979 	-- device code should be derivable after consent but not guessable by the user
   979 	-- device code should be derivable after consent but not guessable by the user
   980 	local device_code = b64url(hashes.hmac_sha256(verification_key, user_code));
   980 	local device_code = b64url(hashes.hmac_sha256(verification_key, user_code));
   981 	local verification_uri = module:http_url() .. "/device";
   981 	local verification_uri = module:http_url() .. "/device";
   982 	local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code });
   982 	local verification_uri_complete = verification_uri .. "?" .. http.formencode({ user_code = user_code });
   983 
   983 
   984 	local dc_ok = codes:set(params.client_id .. "#" .. device_code, { expires = os.time() + 1200 });
   984 	local dc_ok = codes:set("device_code:" .. params.client_id .. "#" .. device_code, { expires = os.time() + 1200 });
   985 	local uc_ok = codes:set(user_code,
   985 	local uc_ok = codes:set("user_code:" .. user_code,
   986 		{ user_code = user_code; expires = os.time() + 600; client_id = params.client_id;
   986 		{ user_code = user_code; expires = os.time() + 600; client_id = params.client_id;
   987     scope = requested_scopes:concat(" ") });
   987     scope = requested_scopes:concat(" ") });
   988 	if not dc_ok or not uc_ok then
   988 	if not dc_ok or not uc_ok then
   989 		return oauth_error("temporarily_unavailable");
   989 		return oauth_error("temporarily_unavailable");
   990 	end
   990 	end
  1007 	local params = strict_formdecode(request.url.query);
  1007 	local params = strict_formdecode(request.url.query);
  1008 	if not params or not params.user_code then
  1008 	if not params or not params.user_code then
  1009 		return render_page(templates.device, { client = false });
  1009 		return render_page(templates.device, { client = false });
  1010 	end
  1010 	end
  1011 
  1011 
  1012 	local device_info = codes:get(params.user_code);
  1012 	local device_info = codes:get("user_code:" .. params.user_code);
  1013 	if not device_info or code_expired(device_info) or not codes:set(params.user_code, nil) then
  1013 	if not device_info or code_expired(device_info) or not codes:set("user_code:" .. params.user_code, nil) then
  1014 		return render_error(oauth_error("expired_token", "Incorrect or expired code"));
  1014 		return render_error(oauth_error("expired_token", "Incorrect or expired code"));
  1015 	end
  1015 	end
  1016 
  1016 
  1017 	return {
  1017 	return {
  1018 		status_code = 303;
  1018 		status_code = 303;