Microsoft.IdentityModel.JsonWebTokens создаёт и проверяет JWT с помощью JsonWebTokenHandler и объекта TokenValidationParameters; AddJwtBearer в ASP.NET Core пропускает через него запросы, поэтому его настройки по умолчанию становятся настройками по умолчанию большинства API на .NET. В версии 8.23.0 подпись и проверка работали для семейств HS, RS, PS и ES; типа ключа безопасности для EdDSA в пакете нет.
По умолчанию ValidAlgorithms равно null, и всё решает ключ: токен RS256 и токен HS256, подделанный с текстом PEM ключа EC в качестве секрета, при проверке ключом EC завершились ошибкой IDX10517: Signature validation failed. Неподписанные токены отклоняются с ошибкой IDX10504, если не установить RequireSignedTokens в false. Слабое место — привязка к кривой: токен ES256 удалось подписать ключом P-384, и тот же обработчик принял его, тогда как другие библиотеки (jose, PyJWT, golang-jwt) такой токен отклоняют. Задайте ValidAlgorithms и используйте ключи нужного размера, как это делает пример.
Правила срока действия противоположны принятым в большинстве библиотек: exp обязателен (IDX10225), а TokenValidationParameters.DefaultClockSkew равен 5 минутам, поэтому токен продолжает работать до 5 минут после истечения срока действия. Для короткоживущих токенов доступа это большой запас; задайте ClockSkew явно. CreateToken добавляет exp, даже если Expires не задан.
Подписи ES256 — это 64-байтовый формат raw r‖s, которого требует JWS и который совпадает с форматом по умолчанию собственного класса ECDsa в .NET; подпись DER не проходит проверку с той же ошибкой IDX10517, что и подделка. Сообщения об ошибках скрывают содержимое токена за [PII of type ... is hidden], если при отладке не включить IdentityModelEventSource.ShowPII; вместо этого декодируйте токен в отладчике JWT, а ключи поставщика проверьте с помощью проверки JWKS.