# Pagination 출처

- https://whatthecode.dev/nestjs-typeorm-pagination-step-by-step-guide/

- https://github.com/nestjsx/nestjs-typeorm-paginate


- https://stackoverflow.com/questions/53922503/how-to-implement-pagination-in-nestjs-with-typeorm

async findAll(query): Promise<Paginate> {
    const take = query.take || 10
    const skip = query.skip || 0
    const keyword = query.keyword || ''

    const [result, total] = await this.userRepository.findAndCount(
        {
            where: { name: Like('%' + keyword + '%') }, order: { name: "DESC" },
            take: take,
            skip: skip
        }
    );

    return {
        data: result,
        count: total
    }
}

- https://github.com/bashleigh/nestjs-blog/blob/master/src/user/user.service.ts



# Environment

nestjs-config


# Authentification / JWT / 


# NestJs 참조 링크 모음

1. https://github.com/bashleigh/awesome-nestjs


# 튜토리얼 도전 Top 3

1. [full stack 동영상]https://www.youtube.com/watch?v=nz6yFTyLbAQ&list=PLq1kZ5GbKd4qyDcK3IHGSi4FDAL6fRZeL

2. [config, jwt, passport 백앤드 기초]https://github.com/bashleigh/nestjs-blog(유용할 듯)

3. [front, backend, jwt, config, 기초]https://github.com/Abdallah-khalil/ContactManagerApp



4. [swagger, session 관련] https://blog.exceptionfound.com/2018/06/07/nestjs-basic-auth-and-sessions/ 



# JWT 핵심 정리

https://github.com/appkr/jwt-scratchpad


JSON Web Token. 쿠키 메커니즘을 이용한 세션 유지를 할 수 없는/하지 않는 API 서비스에서 클라이언트를 식별하기 위한 토큰이다.

1.1. 대표적인 무상태 인증 방법

  1. HTTP Basic 인증

    1. 사용자 아이디와 비밀번호를 base64로 인코딩해서 매 요청마다 Authorization 헤더에 붙여 보낸다. (e.g. Authorization: Basic am9obkBleGFtcGxlLmNvbTpwYXNzd29yZA==)
    2. base64 인코딩은 평문이나 마찬가지다. 즉, 네트워크 구간에서 탈취 당하면 '빵' 털린다. (e.g. base64_decode('am9obkBleGFtcGxlLmNvbTpwYXNzd29yZA=='); //"john@example.com:password" )
    3. 인트라넷 등 방화벽 안에서 클라이언트를 식별할 때만 유용하다.
  2. 토큰 기반 인증

    1. Oauth
      1. 도메인 사용자외에 별도의 ClientId와 ClientSecret가 필요하다.
      2. 페이스북 API, 유튜브 API 등 예측할 수 없는 클라이언트가 API를 소비하는 오픈 API 서비스에 적절하다.
      3. 토큰 뿐만아니라, Client* 발급 및 관리, 각 API 엔드포인트에 대한 접근 권한 제어를 포함하는 등, 크고 무겁다.
      4. 즉, 큰 서비스에만 어울린다.
    2. JWT
      1. 토큰만 다룬다. 도메인 사용자를 그대로 사용하고, 도메인 사용자와 토큰간의 맵핑 테이블을 이용한다(주로 속도가 빠른 키-값 저장소 이용).
      2. 네트워크 구간에서 변조가 불가능하다. 변조되면 토큰은 무효화된다.
      3. 네트워크 구간에서 탈취 당해도 유효 기간(ttl) 또는 리프레시 가능 기간(refresh_ttl)이 지나면 무효화된다.
      4. 토큰 안에 사용자를 식별하기 위한 정보를 담고 있다. 즉, 토큰만 해독하면 사용자를 식별할 수 있다.
      5. 캡티브 API, 소형 API 서비스에 어울린다.

Token은 3파트로 나뉘어 암호화 된다.[.으로 구분]
  1. header(회색) -- 토큰 타입과 해시 알고리즘을 담고 있다(우리 프로젝트는 HMAC SHA256).

  2. payload(적색) -- 사용자 아이디 또는 이메일 등 사용자를 식별하기 위한 정보를 담고 있다. 역할 또는 권한을 포함할 수 있는 등, 담을 수 있는 내용에는 제한이 없다. 다만, 많은 정보를 담을수록 토큰 크기는 커지므로, 꼭 필요한 정보만 담아야 한다.(보통 

  3. secret(녹색) -- 해시 키를 담고 있다.
    (1). payload(A사용자의 정보)를 검증하는 용도 : 해당 Token이 A 사용자에 해당하는 Token이 맞는가?
    (2). 서버 내에만 존재하는 Secret Key(해킹하기 어려움)와 Payload의 value(보통 user.id)를 베이스로 암호화 된다. : Secret Key가 서버 내에만 존재하기 때문에, JWT 토큰 생성 및 요청 토큰 검증의 주체도 서버이다.
    (3). 고로 A사용자가 B사용자만 할 수 있는 요청을 해보고 싶어서, A사용자 Token의 validation signature 부분만 잘라내서 B 사용자 Token의 header, payload에 붙인다고 하더라도, 해당 validataion signature는 A사용자의 payload 기반으로 암호화되었기 때문에, 서버는 유효하지 않은 토큰으로 인지하고, 예외를 발생시킨다.

return [
    'ttl' => 60, // 분
    'refresh_ttl' => 20160, // 분
];

[[클라이언트]]는 최초에 사용자이름, 패스워드로 토큰을 요청한다.

POST /api/tokens/create
HOST jwt-scratchpad.dev
Accept: application/json
Content-Type: application/json

{
  "email": "john@example.com",
  "password": "password"
}

[[서버]] 로그인 성공시, 로그인 아이디를 저장하여, Token을 발급한다.

[[클라이언트]] Token을 저장해두고, 아래와같이 Authorization 헤더 속성에 Bearer <Token> 붙여서, 사용자 인증을 대신한다.(서버는 AuthGuard 되어 있기 때문에, 요청자 인증을 해야되는데, Token만 해독해서 맞으면, 해당 Token에 해당하는 UserId를 인증되었다고 판단한다. login sql 쿼리문을 날릴 필요가 없으므로, 빨라진다.)

GET /api/users
HOST jwt-scratchpad.dev
Accept: application/json
Authorization: Bearer eyJ***.eyJ***.ZGS***

[[클라이언트]]의 토큰 리프레시 요청

Accept: application/json
우리의 토큰은 사용자의 아이디를 담고 있다. 
처음 발급 받을 때, 우리의 JWT 컴포넌트는 라라벨의 캐시 저장소에 토큰과 사용자 아이디의 맵핑 테이블을 만들어 두었다.
토큰이 온전히 서버에 전달되어 해독된다면 사용자를 식별하기 위한 SQLite 쿼리는 필요없다는 얘기다. 

현재 설정은 토큰 유효 기간 60분, 리프레시 가능 기간 2주다. 60분이 지나면 기존 토큰으로 새 토큰을 교환 받을 수 있다.

(실질적으로, A라는 토큰으로 새로운 60분짜리 토큰을 받을 수 있으나, 리프레시 토큰을 받는 순간, A토큰은 더이상 사용할 수 없게 되는 구조)

새 토큰을 장착했다고해서 리프레시 가능 기간이 다시 2주가 아니다. 

2주는 최초 발급 시점으로 부터 2주란 뜻이며, 최초 토큰 발급 후 2주가 지나면 무조건 로그인 창을 띄우고 완전 새삥 토큰을 발급 받아야 한다.

토큰이 탈취 당하면, 60분 동안 John 사용자를 빙의하며 John 사용자가 접근할 수 있는 서버의 리소스를 휘젓고 다닐 수 있다. 

또 리프레시 방법을 알고 있다면 2주 동안 토큰을 갱신해 가며 계속 나쁜 짓을 할 수 있다

[[클라이언트]]는 갱신된 토큰을 받으면 기존과 똑같이 로컬 저장소에 저장하고 60분 동안 API 콜을 할 수 있다. 참고로 토큰을 갱신하면 기존 토큰은 폐기

# 사용할 라우트 -- https://github.com/appkr/jwt-scratchpad/blob/master/routes/api.php#L21

GET /api/tokens/refresh
HOST jwt-scratchpad.dev
Accept: application/json
Authorization: Bearer eyJ***.eyJ***.ZGS***

1. localStorage 내 존재하던 accessToken값으로, 원하는 Http 요청을 한다.

2. token_expired 예외를 확인했을 경우

3. 가지고 있던 refreshToken으로 tokenRefresh Http 요청을 다시 수행한다.

4-1. refresh_ttl_finished 예외를 확인했을 경우, Login Page로 route 이동한다.

4-2. 예외 없이 새로운 accessToken을 받았을 경우, 돌려 받은 accessToken 값을

다시 localStorage에 기존 Token 값에 덮어씌우기 한다.

. refreshToken으로 받은 새로운 access Token으로 하려고 했던 Http 요청을 수행한다. (처음 refreshToken은 로그인시 accessToken과 같이 발급되는 듯?)

다음은 이 프로젝트의 오류 메시지와 그에 따른 클라이언트의 동작이다.

응답 코드오류 메시지클라이언트의 구현 사례
400token_not_provided

- 인증이 필요한 라우트다.(AuthGuard로 되어있는경우)

  - 저장된 토큰이 없다면 사용자 로그인 UI를 표시하고 서버에 토큰을 요청한다.
401token_expired- 토큰의 유효 기간이 만료됐다.
  - 토큰 교체를 요청하고 새로 받은 토큰으로 리소스 요청을 다시 한다.
401refresh_ttl_finished- 토큰 교체 가능 기간이 지났다.
  - 사용자 로그인 UI를 표시하고, 새 토큰을 요청한다.
400token_invalid- 토큰이 변조되었다.
  - 사용자 로그인 UI를 표시하고, 새 토큰을 요청한다.
404user_not_found

- 토큰 본문에 포함된 사용자가 현재는 서비스에 없다(탈퇴 등).


# 부가적인 기술 : Refresh Token


사용자가 로그인을 할 때에 AccessToken과 함께 그에 비해 긴 만료 시간을 갖는 RefreshToken을 클라이언트에 함께 발급합니다. 주로 AccessToken은 30분 내외, RefreshToken은 2주에서 한달 정도의 만료 기간을 부여합니다.


클라이언트는 AccessToken이 만료되었다는 오류를 받으면 따로 저장해두었던 RefreshToken을 이용하여 AccessToken의 재발급을 요청합니다. 서버는 유효한 RefreshToken으로 요청이 들어오면 새로운 AccessToken을 발급하고, 만료된 RefreshToken으로 요청이 들어오면 오류를 반환해, 사용자에게 로그인을 요구합니다.


AccessToken은 서버에 따로 저장해 둘 필요가 없지만, RefreshToken의 경우 서버의 stroage에 따로 저장해서 이후 검증에 활용해야 합니다. 그러므로 RefreshToken을 이용한다는 것은 추가적인 I/O 작업이 필요하다는 의미이며, 이는 I/O 작업이 필요없는 빠른 인증 처리를 장점으로 내세우는 JWT의 스펙에 포함되지 않는 부가적인 기술입니다.


RefreshToken은 탈취되어서는 곤란하므로 클라이언트는 보안이 유지되는 공간에 이를 저장해두어야 합니다.


RefreshToken은 서버에서 따로 저장을 하고 있기 때문에 강제로 토큰을 만료시키는 것이 가능합니다.



# NestJs + Passport + Jwt Token 방식 로그인 구현 출처


https://tansfil.tistory.com/58 <- 이해하기 쉬움

https://docs.nestjs.com/techniques/authentication

https://www.codemag.com/Article/2001081/Nest.js-Step-by-Step-Part-3-Users-and-Authentication

https://makemethink.tistory.com/162

https://wanago.io/2020/05/25/api-nestjs-authenticating-users-bcrypt-passport-jwt-cookies/


# NestJs + Config

https://github.com/nestjsx/nestjs-config


config 폴더 내 app.ts , database.ts 생성


app.module.ts 중 Typeorm 연동 


ConfigModule.load(path.resolve(__dirname'config''**/!(*.d).{ts,js}')), //config 폴더 내의 ts 파일을 config로 보겠다
    // TypeOrmModule.forRoot(), //기본 ormconfig.json 사용
    TypeOrmModule.forRootAsync({
      useFactory: async (configConfigService=> config.get('database'),
      inject: [ConfigService],
    }) //Typeorm + Nest-config 조합, config/database.ts 사용


  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기