본문 바로가기

프로그램 개발

NestJS 개발 시작하기(로그인 인증 개발); 5. Session, JWT, OAuth 차이

NestJS 개발 시작하기(로그인 인증 개발)

1. NestJS와 Spring의 비교

2. NestJS CLI로 개발시작

3. API개발과 Swagger

4. TypeORM으로 DB연결

5. Session, JWT, OAuth 차이

6. jwt 인증 (1)

7. 암호화와 해쉬 함수

 

6. Session, JWT (JSON Web Token), OAuth

a. Session

앞에서는 사용자가 email과 password를 사용해서 로그인을 하면 서비스의 isLogedin에 정보를 저장했다. account.service.ts의 마지막에 아래와 같이 로그인 상태를 확인하는 함수를 추가하고

 

...
    async isLogedIn(email: string): Promise<boolean> {
        return this.isLogedin[email] === true;
    }
}

 

account.controller.ts의 마지막에 아래를 추가하면, API를 사용하여 로그인(Sign) 상태를 확인할 수 있을 것이다.

 

...
    @Get('check/:id')
    async isSignedIn(@Param('id') id: string, @Res() res) {
        console.debug('Check sign in\n' + 'id = '+ id);
        const result = await this.signService.isLogedIn(id);
        return res.status(result ? HttpStatus.OK : HttpStatus.UNAUTHORIZED).send(result ? 'Signed in' : 'Not Signed in');
    }
}

 

만인 isLogedIn데이터 구조에 boolean이 아니라

 

{"userid": "user@email.com", "cart": "user-20240101", status: "signed"}

 

처럼 정보를 담으면 더 많은 현재 사용자의 상태를 보관할 수 있다. 이런 정보를 서버의 Service에 저장하지 않고 Request의 session cookie에 저장하는 방법도 많이 이용하는 방법이다.

 

...
    @Get('in/:id')
    async signIn(@Param('id') id: string, @Query('password') password: string, @Req req, @Res() res) {
        console.debug('Sign in\n' + 'id = '+ id + ', password = ' + password);
        const result = await this.signService.login(id, password);
        if (result.status) {
            const session = req.session;
            session.userid = id;
            session.cartid = result.cardid;
            session.status = result.status;
            session.cookie.maxAge = 1000;
        }
...

 

이렇게 상태를 가지는 것이 사용자가 서비스에 연결했을 때 사용자에 대한 정보를 보관하고 관리하기 편리하지만, session별로 상태를 관리할 경우 다음과 같이 문제가 복잡해질 수도 있다.

 

  • 민감한 상태 정보가 cookie에 저장될 경우 cooke정보를 탈취당할 수 있다.
  • 만일, 상태를 서버의 Service에 저장할 경우 인스턴스가 늘어나면 상태정보를 동기해야 한다.
  • 동기화하지 않으면, 사용자가 요청을 시작하면 같은 Service로 요청되도록 Sticky Session으로 유지해야 한다.
  • 쿠버네티스 같이 인스턴스를 자동으로 조절하는 경우 Service의 정보는 언제든 유실될 수 있다.
  • 상태정보의 유실을 방지하려면 정보를 persistent storage (파일, DB 등)에 저장해야 한다.

따라서, 가급적 상태를 가지지 않는 서비스 (Stateless Service)를 만드는 것이 개발이나 운영측면에서 편리하고, 최소한의 필수 정보만 Token이라고 불리는 인증수단과 같이 들고 다니는 방식을 이용하는데, 많이 사용하는 방법이 표준(RFC 7519)으로 정의된 JWT(JSON Web Token)이다. JWT는 SAML(Security Assertion Markup Language Tokens) 보다 간결하고, SWT(Simple Web Token) 보다 안전한 것으로 알려져 있다.

b. JWT(JSON Web Toekn)

JWT의 Token은 세 부분으로 분리되어 있다.

 

XXXXX.YYYYY.ZZZZZ

 

XXXXX : 헤더(Header)는 어떤 암호알고리즘을 사용했는지 "alg"와 "JWT"를 값으로 가지는 "typ"로 된 JSON이며 Base64Url로 인코딩 되어 있다.

 

{
  "alg": "HS256",
  "typ": "JWT"
}

 

YYYYY : 페이로드(Payload)는 "iss" 발행자, "exp" 만료시간, "sub" 주제, "aud" 대상 등 이미 등록되어 있는 키워드와 등록되지 않은 Public claims, Private claims 두 종류의 키워드를 사용하여 토큰에 실어 보낼 정보를 저장한다. 역시 Base64Url로 인코딩 되어 있다.

 

{
  "sub": "p1234",
  "exp": 3600000
  "name": "user1",
  "admin": true
}

 

ZZZZZ: Signature로 헤더에 정의된 알고리즘을 사용해서 Private key를 사용해서 만들고 public key를 사용해서 내용을 확인해서 변경된 부분이 없는지 확인한다. JWT토큰의 내용을 보고 싶으면 아래와 같이 Base64Url을 디코딩해서 보기 쉽게 정리해 주는 페이지를 이용하면 편리하다.

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

c. OAuth 2.0

JWT는 Token이라는 정보를 정의하는 표준이라면 OAuth 2.0은 이 토큰을 사용하여 어떻게 인증을 받을 것인지에 대한 절차를 정의한 프로토콜에 대한 표준(RFC 6749)이다. OAuth 2.0를 이해하기 가장 쉽게 RFC 6749의 다이어그램을 보기 쉽게 다시 정리한 것이 아래 그림이다.

그림에 나열된 절차를 간단하게 설명하면, 최초 id, pw로 인증을 하면 Access Token과 Refresh Token (Refresh를 허용할 경우)이 발행되고 Access Token의 유효기간이 만료되면 Refresh Token으로 Access Token을 다시 발행해서 연장하고, Refresh Token도 만료되면 접근이 불가능해지는 절차로 진행된다. 만약 Refresh Token을  Refresh 할 때 다시 발행한다면 계속 연장하는 것도 가능할 것이다.

 

Token을 사용한 OAuth 2.0 방식을 사용했을 때 Token을 획득한 주체는 Token에서 허용하는 모든 권한을 얻을 수 있기 때문에 Token이 유출되지 않도록 주의해야 한다. 만약 Token의 유출이 가능한 상황이라면 Access Token의 만료기간을 줄여 유출로 인한 위험을 최소화하는 것도 고려해야 한다.

 

Autorization Server는 API Service와 동일한 서버에서 개발되고 운영될 수도 있고, API Server와 분리하고 OAuth 2.0을 제공하는 Keycloak과 소프트웨어를 사용하는 것도 가능하다.

 

Keycloak

Single-Sign On Users authenticate with Keycloak rather than individual applications. This means that your applications don't have to deal with login forms, authenticating users, and storing users. Once logged-in to Keycloak, users don't have to login again

www.keycloak.org