# JWT - JSON Web Token

## 토큰 기반 인증

![https://velopert.com/wp-content/uploads/2016/12/token-diagram.png](https://2977016459-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M26jG1xuJ_y_q6GUJdp-887967055%2Fuploads%2Fgit-blob-6fced2919c4a663bca7e7bc56ccfbb2caee8f1f7%2Ftoken-diagram.png?alt=media)

토큰 기반 시스템은 stateless하므로 유저의 인증 정보를 서버나 세션에 담아두지 않는다.

1. 유저가 아디이와 비밀번호로 로그인을 한다.
2. 서버측에서 해당 계정정보를 검증한다.
3.  계정정보가 정확하면, 서버측에서 유저에게 signed 토큰을 발급해준다.

    _signed는 해당 토큰이 서버에서 정상적으로 발급된 토큰임을 증명하는 signature이다._
4. 클라이언트 측에서 전달받은 토큰을 저장해두고, 서버에 요청할 때마다, 해당 토큰을 함께 서버에 전달한다.
5. 서버는 토큰을 검증하고 요청에 응답한다.

웹 서버에서 토큰을 서버에 전달할 때는, HTTP 요청의 헤더에 토큰값을 포함시켜서 전달한다.

### 장점

#### 무상태(Stateless)이며 확장성(scalability)가 있다.

토큰은 클라이언트 사이드에 저장하기때문에 완전 stateless하며, 서버 확장하기에 적합한 환경을 제공한다.

#### 보안성

클라이언트가 서버에 요청을 보낼 때 쿠키를 사용함으로 인해 발생하는 취약점이 사라진다.

#### 확장성(Extensibility)

토큰을 사용해 다른 서비스에서도 권한을 공유할 수 있다. 예를 들어서 Fackbook, Naver, Google, Kakao 계정으로 로그인할 수 있다면, 토큰에 선택적인 권한만 부여해 발급할 수 있다.

#### 여러 플랫폼 및 도메인

서비스의 규모가 커지면, 우리는 여러 디바이스를 호환시키고, 더 많은 종류의 서비스를 제공한다. 토큰을 사용한다면, 어떤 디바이스에서도, 도메인에서도 토큰만 유효하다면 요청이 정상적으로 처리된다.

## JSON Web Token

JWT는 웹표준(RFC7519)으로서 JSON 객체를 사용해 가볍고 자가 수용적인 방식으로 정보를 안전성 있게 전달해준다.

* 많은 프로그래밍 언어에서 지원(C, Java, Python, C++, R, JavaScript, Ruby...)
* 자가 수용적(self-contained) : JWT는 필요한 모든 정보를 자체적으로 지니고 있다.
* 쉽게 전달 가능 : 웹 서버의 경우 HTTP 헤더에 넣어 전달할 수 있고, URL의 파라미터로 전달할 수 있다.

주로 회원 인증이나, 안정성있게 정보 교류를 할 때 사용된다.

![https://velopert.com/wp-content/uploads/2016/12/jwt.png](https://2977016459-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M26jG1xuJ_y_q6GUJdp-887967055%2Fuploads%2Fgit-blob-d871c55f6751b02615aba28e3ac4ddcbd7b90fca%2Fjwt.png?alt=media)

### Header

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

* typ : 토큰의 타입을 지정
* alg : 해싱 알고리즘을 지정(HMAC SHA256 , RSA)

### Payload

Payload 부분에는 토큰에 담을 정보가 들어있따. 여기에 담는 정보의 한 조각을 **clame** 이라 하며, 이는 name,value 쌍으로 이루어져있다.

```json
{
  "sub": "1234567890",
  "name": "Dahye Jeong",
  "iat": 1516239022
}
```

클레임은 크게 세분류로 나뉜다.

#### Registered claim

서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기 위해 이미 이름이 정해진 클레임들이다. 등록된 클레임의 사용은 모두 선택적(optional)하다.

| registered claim | 설명                             |
| ---------------- | ------------------------------ |
| iss              | 토큰 발급자(issuer)                 |
| sub              | 토큰 제목(subject)                 |
| aud              | 토큰 대상자(audience)               |
| exp              | 토큰만료시간(expriation)             |
| nbf              | Not Before로 토큰 활성 날짜와 비슷한 개념   |
| iat              | 토큰이 발급된 시간(issued at)          |
| jti              | JWT의 고유 식별자로, 중복처리를 방지하기 위해 사용 |

#### Public Claim

공개 클레임들은 충돌이 방지된 이름을 갖고 있어야한다. 충돌 방지를 위해서는 클레임이름을 URI 형식으로 짓는다.

```json
{
    "https://velopert.com/jwt_claims/is_admin": true
}
```

#### Private Claim

클라이언트 <-> 서버 간에 협의하에 사용되는 클레임들이다. 공개 클레임과 달리 이름이 중복되어 충돌 될 수 있으므로 주의해야한다.

```json
"name": "Dahye Jeong",
```

### Signature

서명은 Header의 인코딩된 값과 Payload의 인코딩된 값을 합친 후 주어진 비밀키로 Hash를 생성한다.

```json
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
);
```

이렇게 만든 해쉬를 base64형태로 나타내면된다.

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```

다음과 같이 생성되는 것을 확인할 수 있다.

### base64

```js
// payload or header 생성하기
const encodedPayload = new Buffer(JSON.stringify(payload))
                            .toString('base64')
                            .replace('=', '');
```

```js
const crypto = require('crypto');
const signature = crypto.createHmac('sha256', 'secret')
             .update(encodedHeader + '.' + encodedPayload)
             .digest('base64')
             .replace('=', '');
```

다음과 같이 base64를 이용해 생성할 수 있다.

### [jwt.io](https://jwt.io/)

## 참고

* [VELOPERT.LOG](https://velopert.com/2350)
