Nest.js - User Auth

크롱·2024년 11월 19일
0

Nest.js

목록 보기
14/15

Intro

email - pw 로 auth를 구현할때
유저의 비밀번호는 보안상 string 그대로 db에 저장하면 안되고 해당 string을 hash,salt를 한 뒤 안전하게 db에 저장해야한다.


유저가 로그인을 할 때 이메일과 제공한 password을
db에 해당 이메일과 함께 저장된 비밀번호 hash 와 비교를 해서 매치되면
유저는 authenticated 된 것이고, 이때 우리는 JWT(JSON Web Token) 토큰을 발급한다.


유저가 authentication이 필요한 기능을 수행할때, 즉 엔드포인트를 통해 request를 보낼때 유저는 항상 JWT 토큰을보내야하고 이 토큰이 올바르면 우리는 해당 request를 허가하면된다.



Hashing & Salting

DB에 비밀번호를 저장할때 필요한 Hashing & Salting 작업

위 표처럼 password는 처음에 string 이였지만 hash를 generate 한 뒤 이걸 db에 저장된 hash와 비교하는것이다. Salt는 hash 에 첨가되는것으로, 해커들이 해킹하기 더 어렵게 만드는것이다.

생성된 hash는 이러한 구조를 가지고있다고합니다

우리는 Bcrypt의 도움을 받을것이다.

The Bcrypt would generate this entire string for you and give it to you to save it in the database whenever you want to compare.

Bcrypt을 통해 password와 db에 저장된 hash 형태의 password를 비교할 수 있고 이를 통해 로그인 기능을 구현할 수 있돠.




Bcrypt

hashingProvider는 abstract class로, 실제로 작업을 하는 코드는 Bcrypt provider에 있다.

왜 추상 클래스 ?
기본적인 구조나 기능을 정의하고, 그 것을 확장하는 자식 클래스들에 특정 메소드 구현을 강제하기 위함이다. 이렇게 함으로 자식 클래스들이 일관된 방식으로 동작하도록 보장할 수 있다.

추상 클래스를 사용하므로써 나중에 Bcrypt보다 더 좋은게 잇으면 그걸로 바꾸기도 용이하다

파일: hashingProvider

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

@Injectable()
export abstract class HashingProvider {
  abstract hashPassword(data: string): Promise<string>;

  abstract comparePassword(data: string, encrypted: string): Promise<boolean>;
}

자식클래스 BcryptProvider는 추상클래스를 implements 해야한다.
자식클래스는 추상클래스에 정의된 모든 메소드를 정의해줘야한다.

파일: BcryptProvider

import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcrypt';

import { HashingProvider } from './hashing.provider';

@Injectable()
export class BcryptProvider implements HashingProvider {
  public async hashPassword(data: string): Promise<string> {
    //Generate Salt
    const salt = await bcrypt.genSalt();
    return bcrypt.hash(data, salt);
  }

  public async comparePassword(
    data: string,
    encrypted: string, //db에 저장된 hash 형태
  ): Promise<boolean> {
    return bcrypt.compare(data, encrypted);
  }
}

bcrypt.hashbcrypt.compare를 통해 pw를 해쉬로 만들고, 로그인할때 해쉬된 pw를 비교해서 매치시키는 함수가 완성되었다

이제 해당 함수를 사용하기위해 module.ts 파일에 등록해야하는데 abstract 은 바로 providers에 넣을 수 없다.
HashingProvider를 useClass를 통해 BcryptProvider로 제공함으로써, HashingProvider를 직접 사용할 수 있다.
이렇게 하면 HashingProvider를 주입받는 다른 서비스나 컴포넌트에서 BcryptProvider의 기능을 사용할 수 있다.

import { Module } from '@nestjs/common';
import { HashingProvider } from './hashing.provider';
import { BcryptProvider } from './bcrypt.provider';
import { SomeService } from './some.service';

@Module({
  providers: [
    {
      provide: HashingProvider,
      useClass: BcryptProvider,
    },
    SomeService,
  ],
  exports: [HashingProvider], // 다른 모듈에서 사용할 수 있도록 export
})
export class AppModule {}





JWT

JSON WEB TOKEN

JWT은 이러한 문자열로 이루어지며 .으로 구분된다
첫번째줄은 header
두번째줄은 payload
세번째줄은 signature of the token

  • header
    The header contains the information about the algorithm that was used for encrypting the token as well as the type of token it is.

  • payload
    A payload can contain information that you want to send back to your user.
    sub은 db에서 id 같은 값
    always remember the payload inside the JWT token is accessible to everyone
    The information is encrypted, but it can be decoded just by visiting the JWT website itself.
    So never ever pass sensitive information inside the payload.

    The payload should ideally contain that information using which you can identify the user like email or id and Based on that you can always fetch the user from your database.

    So any unique value representing the user that helps you identify the user is sufficient inside the payload of the JSON web token.

    아니 모든사람이 accessible 한거면 안전하지않잖아!라고할수있지만 이는 세번째 줄 signature가 해결해준다

  • signature
    Now this signature is created using the algorithm along with URL encoded values for the header and the payload

    Now, if anyone tries to tamper with the information of a JSON web token, the signature gets changed, so the exact signature can only be created using this key. (your-256-bit secret) and this key resides on ur server


From sending the password till responding back from your method with a JWT token.

Once you are confirmed that the user's password and the hash in your table, they match, that's the point when you generate a JWT or a JSON web token. => 즉 로그인할떄 JWT 제너레이트

This JSON web token is generated using a secret key on our server, so the signature of this token can only be validated and recreated using this particular key.

NEXT STEP

Whenever user wants to access certain resources or authorize a certain route endpoint, he uses the token instead of the password. And whenever they want to request some information, they can send the token along with authorization headers.

Every time the user sends in a JWT token, you just check the signature of the token to be valid or invalid

user들은 뭔가 요청을 보내거나 인증이필요할때 pw를 안보내고 jwt로 인증요청하면된다.


There is an expiration time or time to live attached with a JWT token, so any requests within that time span would be considered as authentic requests. So until the time the JSON web token does not expire, the user is authenticated to use the resources.

The user would add this token to the authorization headers while sending any request.


@nestjs/jwt 설치 ㄱ

claim

  • aud : JWT의 수신인, 즉 토큰 대상자(Audience)입니다. JWT를 처리하려는 각 주체는 해당 값으로 자신을 식별해야 하며, 요청을 처리하는 주체가 'aud'값으로 자신을 식별하지 않으면 JWT는 거부됩니다.


if the token will expire, users would have to re-authenticate. 이메일,비번 입력해야함.

즉 re issue token

profile
👩‍💻안녕하세요🌞

0개의 댓글