Skip to Content

인증

ASAPJS는 라우트 수준 인증에 JSON Web Token(JWT)을 사용합니다. jwtVerification 미들웨어는 데코레이터 시스템을 통해 등록된 모든 라우트에 자동으로 적용됩니다. 엄격성은 각 라우트 데코레이터의 auth 옵션으로 제어합니다.

jwtVerification

jwtVerification@asapjs/router에서 내보내는 Express 미들웨어 팩토리입니다. 일반적인 사용에서는 직접 호출할 필요가 없습니다 — RouterController가 등록된 모든 라우트에 대해 내부적으로 호출합니다. IOptionsauth 필드가 동작을 제어하는 공개 API입니다.

import { jwtVerification } from '@asapjs/router';

시그니처

const jwtVerification = (auth?: boolean) => (req: Request, res: Response, next: NextFunction) => void
파라미터타입기본값설명
authbooleanfalsetrue이면 유효한 Bearer 토큰이 필요합니다. false이면 토큰이 있을 경우 수락하지만 필수는 아닙니다.

동작 방식

미들웨어는 모든 요청에서 다음 로직을 실행합니다:

  1. 수신된 요청에서 Authorization 헤더를 읽습니다.
  2. 헤더가 없는 경우:
    • auth === trueHttpException(403, 'NO Token Provided')를 throw합니다. 이 에러는 Express 에러 처리 파이프라인으로 전달됩니다.
    • auth === false — 즉시 next()를 호출합니다; req.user는 설정되지 않습니다.
  3. 헤더가 있는 경우, Bearer <token> 형식에서 토큰을 추출하고 config.auth.jwt_access_token_secret을 사용하여 검증합니다.
  4. 검증이 성공하면 — 디코딩된 페이로드를 req.user에 저장하고 next()를 호출합니다.
  5. 검증이 실패하고 auth === true인 경우 — 에러 유형에 따라 직접 응답을 전송합니다 (아래 응답 형식 참고).
  6. 검증이 실패하고 auth === false인 경우 — req.user를 설정하지 않고 next()를 호출합니다.

에러 응답 형식

jwtVerification은 에러 케이스에 따라 서로 다른 방식으로 응답합니다:

시나리오상태응답 경로바디
토큰 없음, auth: true403throw HttpException → Wrapper errorToResponse(){ status: 403, errorCode: 'LEGACY_HTTP_EXCEPTION', message: 'NO Token Provided' }
유효하지 않은 서명, auth: true403res.json() 직접 응답{ error: true, message: 'invaild signature. please use vaild endpoint' }
만료/유효하지 않은 토큰, auth: true401res.json() 직접 응답{ error: true, message: 'Unauthorized access. Please Refresh Token' }
실패, auth: false요청 계속 진행req.userundefined

참고: 토큰 없음 케이스는 throw를 통해 에러 파이프라인(errorToResponse)으로 처리되어 { status, errorCode, message } 형식이지만, JWT 검증 실패 케이스는 res.json()으로 직접 응답하여 { error, message } 형식입니다. 클라이언트 에러 처리 시 두 가지 형식을 모두 고려해야 합니다.

코드 내 오타: 현재 코드에 'invaild signature' (invalid의 오타)와 'vaild endpoint' (valid의 오타)가 포함되어 있습니다. 이 문자열은 코드 수정 전까지 그대로 반환됩니다.

필수 설정

jwtVerification은 전역 설정 객체에서 시크릿을 읽습니다. 서버가 시작되기 전에 애플리케이션 설정에 auth.jwt_access_token_secret을 반드시 설정해야 합니다.

// 애플리케이션 설정 (Application.run()에 전달) { auth: { jwt_access_token_secret: 'your-secret-key' } }

설정은 런타임에 @asapjs/coregetConfig()를 통해 접근합니다. 키가 없으면 첫 번째 인증 요청 시점에 JWT 검증이 오류를 던집니다.

라우트 데코레이터의 auth 옵션

모든 HTTP 데코레이터(@Get, @Post, @Put, @Delete)는 IOptions 인수에 auth 필드를 받습니다. 이것이 인증을 제어하는 주요 방법입니다:

import { Get, Post } from '@asapjs/router'; // 공개 라우트 — 토큰 불필요 @Get('/posts', { title: 'List posts', auth: false, // 기본값; 생략 가능 }) async getPosts({ paging }: ExecuteArgs) { ... } // 보호된 라우트 — 유효한 Bearer 토큰 필요 @Post('/posts', { title: 'Create post', auth: true, body: CreatePostDto, }) async createPost({ body, user }: ExecuteArgs) { ... }

auth를 생략하면 기본값은 false입니다.

JWT 토큰 형식

클라이언트는 Bearer 방식을 사용하여 Authorization 요청 헤더에 토큰을 전달해야 합니다:

Authorization: Bearer <jwt-token>

미들웨어는 공백 문자를 기준으로 분리하여 두 번째 세그먼트를 토큰 문자열로 사용합니다. 다른 형식(예: Bearer 접두사 누락)은 서명 검증에 실패하는 인식되지 않는 토큰을 생성합니다.

HttpException

HttpException은 라우트 핸들러와 미들웨어 내에서 HTTP 오류 응답을 알리는 에러 클래스입니다.

import { HttpException } from '@asapjs/router';

생성자

class HttpException extends Error { public status: number; public message: string; constructor(status?: number, message?: string); }
파라미터타입기본값설명
statusnumber500클라이언트에 전송할 HTTP 상태 코드.
messagestring'알 수 없는 서버 오류가 발생했습니다.'응답 바디에 포함되는 사람이 읽을 수 있는 오류 메시지.

HttpException 던지기

라우트 핸들러나 서비스 메서드 내에서 HttpException을 던집니다. Wrapper 유틸리티가 이를 잡아 errorToResponse()를 통해 구조화된 JSON 응답을 전송합니다:

import { HttpException } from '@asapjs/router'; async login({ body }: ExecuteArgs) { const user = await this.userService.findByEmail(body.email); if (!user) { throw new HttpException(404, 'User not found'); } const valid = await user.verifyPassword(body.password); if (!valid) { throw new HttpException(401, 'Invalid credentials'); } return this.userService.issueToken(user); }

오류 응답 형식

HttpExceptionWrapper에 의해 잡히면 errorToResponse()를 통해 처리됩니다. HttpException에는 errorCode가 없으므로 레거시 호환 코드가 사용됩니다:

{ "status": 404, "errorCode": "LEGACY_HTTP_EXCEPTION", "message": "User not found" }

타입 안전한 에러 코드와 데이터가 필요한 경우 @asapjs/errorerror() 팩토리를 사용하세요. 자세한 내용은 에러 처리 가이드를 참고하세요.

전체 컨트롤러 예제

import { RouterController, Get, Post, ExecuteArgs } from '@asapjs/router'; import { HttpException } from '@asapjs/router'; import UserApplication from '../application/UserApplication'; import CreateUserDto from '../dto/CreateUserDto'; import LoginRequestDto from '../dto/LoginRequestDto'; import LoginResponseDto from '../dto/LoginResponseDto'; import UserInfoDto from '../dto/UserInfoDto'; export default class UserController extends RouterController { public tag = 'User'; public basePath = '/users'; private userService: UserApplication; constructor() { super(); this.registerRoutes(); this.userService = new UserApplication(); } // 공개 — 토큰 불필요 @Post('/register', { title: 'Register', description: 'Creates a new user account.', body: CreateUserDto, response: UserInfoDto, }) async register({ body }: ExecuteArgs) { return await this.userService.register(body as CreateUserDto); } // 공개 — 토큰 불필요 @Post('/login', { title: 'Login', description: 'Authenticates with email and password, returns a JWT.', body: LoginRequestDto, response: LoginResponseDto, }) async login({ body }: ExecuteArgs) { return await this.userService.login(body as LoginRequestDto); } // 보호된 라우트 — 유효한 Bearer 토큰 필요 @Get('/me', { title: 'Get current user', description: 'Returns the profile of the authenticated user.', auth: true, response: UserInfoDto, }) async getMe({ user }: ExecuteArgs) { if (!user?.id) { throw new HttpException(401, 'Token payload missing user id'); } return await this.userService.getUserInfo(user); } }

관련 항목

  • 라우팅 — HTTP 메서드 데코레이터와 IOptions
  • 요청 처리ExecuteArgs.user와 오류 전파
  • 에러 처리HttpError, error() 팩토리, errorToResponse()
Last updated on