diff --git a/src/api/server_server.rs b/src/api/server_server.rs
index 21e32f5c21f44db2a20f36f05d71f8559f332c0c..b07c931420b1229792ff24656292bf9d36e914de 100644
--- a/src/api/server_server.rs
+++ b/src/api/server_server.rs
@@ -917,7 +917,7 @@ pub async fn create_join_event_template_route(
 		.as_ref()
 		.map(|join_rules_event| {
 			serde_json::from_str(join_rules_event.content.get()).map_err(|e| {
-				warn!("Invalid join rules event: {e}");
+				warn!("Invalid join rules event: {}", e);
 				Error::bad_database("Invalid join rules event in db.")
 			})
 		})
@@ -940,20 +940,56 @@ pub async fn create_join_event_template_route(
 						.state_cache
 						.is_joined(&body.user_id, &m.room_id)
 						.unwrap_or(false)
-				}) && services()
-				.rooms
-				.state_cache
-				.is_left(&body.user_id, &body.room_id)
-				.unwrap_or(true)
-			{
-				services()
+				}) {
+				if services()
 					.rooms
 					.state_cache
-					.room_members(&body.room_id)
-					.filter_map(Result::ok)
-					.find(|user| user.server_name() == services().globals.server_name())
+					.is_left(&body.user_id, &body.room_id)
+					.unwrap_or(true)
+				{
+					let members: Vec<_> = services()
+						.rooms
+						.state_cache
+						.room_members(&body.room_id)
+						.filter_map(Result::ok)
+						.filter(|user| user.server_name() == services().globals.server_name())
+						.collect();
+
+					let mut auth_user = None;
+
+					for user in members {
+						if services()
+							.rooms
+							.state_accessor
+							.user_can_invite(&body.room_id, &user, &body.user_id, &state_lock)
+							.await
+							.unwrap_or(false)
+						{
+							auth_user = Some(user);
+							break;
+						}
+					}
+					if auth_user.is_some() {
+						auth_user
+					} else {
+						return Err(Error::BadRequest(
+							ErrorKind::UnableToGrantJoin,
+							"No user on this server is able to assist in joining.",
+						));
+					}
+				} else {
+					// If the user has any state other than leave, either:
+					// - the auth_check will deny them (ban, knock - (until/unless MSC4123 is
+					//   merged))
+					// - they are able to join via other methods (invite)
+					// - they are already in the room (join)
+					None
+				}
 			} else {
-				None
+				return Err(Error::BadRequest(
+					ErrorKind::UnableToAuthorizeJoin,
+					"User is not known to be in any required room.",
+				));
 			}
 		} else {
 			None
@@ -1054,6 +1090,7 @@ async fn create_join_event(
 	// We do not add the event_id field to the pdu here because of signature and
 	// hashes checks
 	let room_version_id = services().rooms.state.get_room_version(room_id)?;
+
 	let Ok((event_id, mut value)) = gen_event_id_canonical_json(pdu, &room_version_id) else {
 		// Event could not be converted to canonical json
 		return Err(Error::BadRequest(
@@ -1068,7 +1105,10 @@ async fn create_join_event(
 		&mut value,
 		&room_version_id,
 	)
-	.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event."))?;
+	.map_err(|e| {
+		warn!("Failed to sign event: {e}");
+		Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event.")
+	})?;
 
 	let origin: OwnedServerName = serde_json::from_value(
 		serde_json::to_value(