Неправильный Jwt. Запрос аутентификации к серверу.

Вопрос или проблема

Я создаю полный стек проекта, используя Angular, NestJS и PostgreSQL. Пытаюсь реализовать аутентификацию и авторизацию с помощью Auth0. Пользователь может хранить данные в базе данных, одним из столбцов является уникальный sub id от Auth0. Моя идея заключается в следующем: когда пользователь авторизован на клиенте и хочет извлечь данные, он делает запрос к серверу, где должен проверяться клиентский JWT-Sub с DB-Sub.

Так что вот в чем проблема. Токен клиента получен, отправлен на сервер, но возникает ошибка в этом Guard:

PS: Быстрое исправление. Когда я передаю JWT-токен в Postman вручную, запрос проходит, данные извлекаются полностью. Возможно, проблема на стороне клиента?

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  handleRequest(
    ...args: Parameters<
      InstanceType<ReturnType<typeof AuthGuard>>['handleRequest']
    >
  ) {
    console.log(args);
    return super.handleRequest(...args);
  }
}

Вот ошибка:

[
  null,
  false,
  JsonWebTokenError: jwt malformed
      at module.exports [as verify] (C:\Projects\dnd-server\node_modules\jsonwebtoken\verify.js:70:17)
      at module.exports [as JwtVerifier] (C:\Projects\dnd-server\node_modules\passport-jwt\lib\verify_jwt.js:4:16)
      at C:\Projects\dnd-server\node_modules\passport-jwt\lib\strategy.js:104:25
      at JwtStrategy.secretProvider [as _secretOrKeyProvider] (C:\Projects\dnd-server\node_modules\jwks-rsa\src\integrations\passport.js:42:14)
      at JwtStrategy.authenticate (C:\Projects\dnd-server\node_modules\passport-jwt\lib\strategy.js:99:10)
      at attempt (C:\Projects\dnd-server\node_modules\passport\lib\middleware\authenticate.js:378:16)
      at authenticate (C:\Projects\dnd-server\node_modules\passport\lib\middleware\authenticate.js:379:7)
      at C:\Projects\dnd-server\node_modules\@nestjs\passport\dist\auth.guard.js:88:3
      at new Promise (<anonymous>)
      at C:\Projects\dnd-server\node_modules\@nestjs\passport\dist\auth.guard.js:80:83,
  ExecutionContextHost {
    args: [ [IncomingMessage], [ServerResponse], [Function: next] ],
    constructorRef: [class CharacterController],
    handler: [AsyncFunction: findOne],
    contextType: 'http',
    getRequest: [Function: getRequest],
    getResponse: [Function: getResponse],
    getNext: [Function: getNext]
  },
  undefined
]

Вот мой auth.strategy.ts:

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import * as jwksRsa from 'jwks-rsa';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      // Извлечение JWT из заголовка Авторизации
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),

      // Проверка издателя и аудитории токена
      issuer: `https://${process.env.AUTH0_DOMAIN}/`,
      audience: process.env.AUTH0_AUDIENCE,

      // Использование открытого ключа Auth0 для проверки подписи токена
      secretOrKeyProvider: jwksRsa.passportJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
      }),

      algorithms: ['RS256'],
    });
  }

  async validate(payload: any) {
    return {
      sub: payload.sub, // утверждение 'sub'
    };
  }
}

Вот мой character.controller.ts, где я извлекаю данные из БД:

import {
  Body,
  Controller,
  Get,
  Param,
  Post,
  Req,
  UseGuards,
} from '@nestjs/common';
import { CharacterService } from './character.service';
import { Character } from './character.model';
import { JwtAuthGuard } from 'src/auth/auth.guard';

@Controller('character')
export class CharacterController {
  constructor(private readonly characterService: CharacterService) {}

  @Post()
  async create(@Body() character: Character): Promise<Character> {
    const createdCharacter = await this.characterService.create(character);
    console.log('Персонаж успешно создан:', createdCharacter);
    return createdCharacter;
  }

  @Get(':id')
  @UseGuards(JwtAuthGuard)
  async findOne(@Param('id') id: string, @Req() req): Promise<Character> {
    const sub = req.user?.sub;
    if (!sub) {
      console.error("Параметр 'sub' неопределен или равен null.");
      throw new Error("Недействительный параметр 'sub'");
    }

    console.log('Серверный SUB:', sub);

    const character = await this.characterService.findOne(+id);
    if (!character) {
      console.error(`Персонаж с ID ${id} не найден.`);
      throw new Error('Персонаж не найден');
    }

    if (character.sub !== sub) {
      console.error(
        `Серверный SUB ${sub} не соответствует персонажу sub ${character.sub}.`,
      );
      throw new Error('Несанкционированный доступ');
    }

    return character;
  }
}

А вот часть моего creator.component.ts, где я пытаюсь сделать запрос к серверу:

saveFormDataToFile() {
    const data = this.characterCreationForm.value;
    this.http.post("http://localhost:3000/character", data).subscribe(
      (response) => {
        console.log("Форма данных успешно сохранена:", response);
      },
      (error) => {
        console.error("Ошибка при сохранении данных формы:", error);
      }
    );
  }

  retrieveFormDataFromServer(id: number) {
    const authToken = this.auth.idTokenClaims$;

    authToken.subscribe((token) => {
      if (token) {
        const headers = new HttpHeaders().set(
          "Authorization",
          `Bearer ${token.__raw}`
        );

        console.log("ПОЛНЫЙ ТОКЕН:", token);
        console.log("Токен доступа успешно получен:", token.__raw);
        this.http
          .get(`http://localhost:3000/character/${id}`, { headers })
          .subscribe(
            (response: any) => {
              console.log("Данные формы успешно извлечены:", response);
              this.characterCreationForm.patchValue(response);
            },
            (error) => {
              console.error("Ошибка при извлечении данных формы:", error);
            }
          );
      } else {
        console.error("Ошибка при извлечении токена доступа.");
      }
    });
  }

Я идиот. В моем Angular app.module.ts было: allowedList: [“http://localhost:3000/*”],

Удалил “*” и теперь все работает.

Ответ или решение

Проблема, с которой вы столкнулись, связана с ошибкой "jwt malformed", возникающей при попытке валидации токена JWT на стороне сервера. В вашем проекте, построенном на Angular, NestJS и PostgreSQL, вы используете Auth0 для аутентификации и авторизации пользователей. Давайте проанализируем проблему и предложим способы ее устранения.

Причины возникновения ошибки "jwt malformed"

  1. Неверный формат токена: Если токен, который отправляется на сервер, не соответствует стандартному формату JWT, сервер не сможет его распознать. JWT состоит из трех частей, разделенных точками (например, header.payload.signature). Ошибка "jwt malformed" указывает на то, что токен не не соответствует этому формату.

  2. Отсутствие Authorization заголовка: Если при запросе на сервер не передается заголовок Authorization с Bearer токеном, то сервер не сможет извлечь токен. Убедитесь, что заголовок Authorization правильно установлен.

  3. Проблемы с кодировкой токена: Если токен был неправильно закодирован или модифицирован перед отправкой, это также может привести к тому, что сервер не сможет его распознать.

Решение проблем

Исходя из вашего кода, следует проверить несколько моментов:

  1. Отправка токена: В вашем методе retrieveFormDataFromServer(id: number) в клиентском приложении вы правильно извлекаете токен из подписки и добавляете его в заголовок запроса. Однако убедитесь, что token.__raw действительно содержит корректный JWT. Используйте console.log("FULL TOKEN:", token.__raw); для тестирования.

  2. Проверка конфигурации CORS: Вы упомянули, что в вашем app.module.ts была неправильная конфигурация allowedList: ["http://localhost:3000/*"]. Заменив на allowedList: ["http://localhost:3000"], вы решили проблему, что указывает на то, что возможная ошибка могла быть связана с тем, как CORS обрабатывал ваши запросы и предоставлял заголовки. Убедитесь, что вас не блокируют недопустимые источники или настройки.

  3. Обработка ошибок: В вашем JwtAuthGuard вы можете добавить дополнительное логирование для отслеживания, какие именно параметры были получены на стороне сервера. Ваше текущее console.log(args); может быть полезным, но стоит добавить проверку, что именно передается в Authorization заголовке.

Резюме

Проблема "jwt malformed" в вашем приложении, вероятно, была вызвана неправильной отправкой токена или конфигурацией CORS. Убедитесь, что токен, который вы отправляете, корректный и не искажен. Также важно следить за настройками CORS, чтобы они соответствовали вашему приложению. Ваша реализация аутентификации с использованием NestJS и Auth0 в целом выглядит корректной; проблемы возникают ввиду конфигурации или передачи данных. Продолжайте тестирование и логирование для улучшения диагностики в будущем.

Если проблемы будут продолжаться, рассмотрите возможность использования инструментов для отладки запросов (например, Postman или других) и анализа JWT (например, jwt.io) для дальнейшей проверки формата токена.

Оцените материал
Добавить комментарий

Капча загружается...