Отдавая фронт, сервер может установить в нём уникальное значение ключа, и требовать именно это же значение в запросах к api. Скажем, в HTTP-заголовках запроса. Если нет ключа – запрос отклонять.
Значение это может быть привязано ко времени и считаться действительным только в течение часа, например.
Но и это никак не защищает от того, что получив код фронта, «злодей» не станет выполнять запросы с этим ключом из своих скриптов. Уточните вашу задачу, чего хочется добиться.
Авторизация пользователя — этот вариант вполне. Чел логинится на бэке и получает уникальный временный ключ, ассоциированный с его аккаунтом.
Ограничение по частоте запросов к API тоже с ключом решается.