NikFaraday
@NikFaraday
Student full-stack Developer

Почему fetch отдаёт 401 при JWT авторизации на сервер asp.net core web api?

Пытаюсь сделать аутентификацию на основе JWT-токенов. Сервер - asp.net core 8 web api, клиент - ReactJS. Отправляют по fetch'ем.

Вот часть конфигурации проекта (Сразу после регистрации dbContext):
services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.RequireHttpsMetadata = false;

        string key = builder.Configuration["JwtOptions:Key"];
        byte[] byteKey = Encoding.UTF8.GetBytes(key);

        options.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            IssuerSigningKey = new SymmetricSecurityKey(byteKey),
            ValidateIssuerSigningKey = true
        };
    });
services.AddAuthorization();


В той же конфигурации настройка Cors:
services.AddCors(options =>
{
    options.AddPolicy("AllowTeachToolClient",
        builder =>
        {
            builder.WithOrigins("http://localhost:3000");
            builder.AllowAnyHeader();
            builder.AllowAnyMethod();
        });
});

// ...

var app = builder.Build();

app.UseCors("AllowTeachToolClient");

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();


Тестовый контроллер для проверки аутентифицированных запросов:
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    [HttpGet]
    [Route("TestAction")]
    public async Task<IActionResult> TestAction()
    {
        return Ok(new { success = true });
    }
}


Запрос на React:
const testPostAction = async () => {
        let headers = new Headers({
            "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVjMzM2MGIxLTk3NjctNDdkYS1iODliLWRkOTE1ZmNkZTNkNyIsImZpcnN0IG5hbWUiOiJOaWsiLCJsYXN0IG5hbWUiOiJGYXJhZGF5IiwibWlkZGxlIG5hbWUiOiJtaWRkbGUiLCJlbWFpbCI6Im15TWFpbEBnbWFpbC5jb20iLCJsb2dpbiI6IkxPR0lOIiwicGhvbmUiOiJteVBob25lIn0.gi5l7dB-rGTIH_5fWB8li4KxS_pYs1-xTX1xJ51kuIQ",
            "Access-Control-Allow-Origin": "https://localhost:3000"
        })
        console.log(headers);

        let response = await fetch('https://localhost:7155/api/Test/TestAction', {
            headers: headers
        })
        console.log(response)

        let data = await response.json()
        console.log(data)
    }


При отправке запроса получаю ответ HttpStatusCode 401 (Cors). Более детально:

GET https://localhost:7155/api/Test/TestAction 401 (Unauthorized)
...
body: (...)
bodyUsed: true
headers: Headers {}
ok: false
redirected: false
status: 401
statusText: ""
type: "cors"
url: "https://localhost:7155/api/Test/TestAction"
  • Вопрос задан
  • 289 просмотров
Решения вопроса 1
NikFaraday
@NikFaraday Автор вопроса
Student full-stack Developer
Если вкратце, что и почему работает:


  1. Настройка CORS. Для того, что бы настроить cors для React(Next)JS Application, нужно указывать адрес https и это важно, поскольку при запуске приложения React(Next)JS адрес начинается с http. Пример:
    // ...
    services.AddCors(options =>
    {
        options.AddPolicy("AllowTeachToolClient",
            builder =>
            {
                builder.WithOrigins("http://localhost:3000", "https://localhost:3000");
                builder.AllowAnyHeader();
                builder.AllowAnyMethod();
            });
    });
    // ...



  2. При генерации Jwt токена, нужно использовать именно SymmetricSecurityKey. Это для тех, кто захочет аналогично как я попробовать использовать что-то типа RSA(2048)

    Я не говорю, что они не работают, просто для начала с этим могут быть проблемы с реализацией



  3. Самое главное это заголовки. Лично у меня проблема решилась при наличии следующих header'ов:
    • Get:
      'Accept': '*/*',
      'Host': 'http://localhost:3000',
      'Content-Type': 'application/json',
      'Authorization': 'Bearer {token}'



    • Post:
      'Accept': '*/*',
      'Host': "http://localhost:3000",
      'Content-Type': 'application/json;'



      Понятно, что наличие заголовка
    Authorization зависит от того, куда кидаете запрос. Обычно используется, для доступа к [Authorize] контроллерам/эндпоинтам



  4. Настройка Cors (Нюанс, для тех, кто с таким может столкнуться). Код для регистрации политики Cors:
    services.AddCors(options =>
    {
        options.AddPolicy("AllowTeachToolClient",
            builder =>
            {
                builder.WithOrigins("http://localhost:3000");
                builder.AllowAnyHeader();
                builder.AllowAnyMethod();
            });
    });


    И самое главное, что ниже эту же политику нужно подключить перед тем, как вы будете использовать app.UseAuthentication(); и app.UseAuthorization();. Вырезка с самого вопроса:

    var app = builder.Build();
    
    app.UseCors("AllowTeachToolClient");
    
    app.UseHttpsRedirection();
    
    app.UseAuthentication();
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();



Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Maksclub
@Maksclub
maksfedorov.ru
Коротко при работе с лкоального хоста с доменом, который ограничивает CORS — настраиваете прокси (если ангуляр, есть из коробки возможность настроить), если реакт — погуглите как.

Работает так:
- запрос с фронта идет на прокс-сервер
- прокси-сервер не делает запрос через браузер, а server2server
- получает овтет и отдает фронту

Например запрос на домен api.foo.com/users настраивается так, что запрос идет на localhost:3001/api/users, который под капотом проксирует весь запрос на api.foo.com/users
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы