# Lesson 8 - Token

Token là một chuỗi ký tự hoặc số đại diện cho một quyền truy cập hoặc một loại thông tin xác thực (được ví như là một loại vé thông hành). Trong ngữ cảnh bảo mật và xác thực, token thường được sử dụng để kiểm tra danh tính người dùng hoặc ứng dụng và quyền truy cập của họ

## Ứng dụng

Sau khi đăng nhập hệ thống, server sẽ trả về cho client một đoạn token, giống một chiếc vé, mỗi khi thực hiện request lên các API, với các API nào mà yêu cầu xác thực thông tin, chỉ cần kiểm tra tấm vé này là có thể xác thực và sử dụng.

Ví như là một tấm vé, vậy tấm vé này, sẽ có một số thông tin như người mua, thời gian mua, thời gian hết hạn, quyền hạn của tấm vé, ... Token cũng như thế, cần phải mang theo các thông tin này theo. Việc token xác thực thông tin, phân quyền sẽ đều được sử dụng chung với một token.

## Loại token

Token thường sẽ có hai loại, là authentication và authorization.

1. **Authentication (Xác thực)**:
   * Authentication liên quan đến việc xác định danh tính của người dùng hoặc ứng dụng. Điều này đảm bảo rằng bạn biết ai đang sử dụng hệ thống hoặc ứng dụng của bạn.
   * Thường được thực hiện bằng cách xác định người dùng thông qua tên người dùng và mật khẩu, token xác thực, hoặc phương thức xác thực khác.
2. **Authorization (Phân quyền)**:
   * Authorization liên quan đến việc quản lý quyền truy cập của người dùng hoặc ứng dụng sau khi họ đã xác thực.
   * Điều này đảm bảo rằng người dùng chỉ có quyền truy cập và thực hiện các hành động cụ thể mà họ được phép thực hiện, dựa trên vai trò hoặc quyền hạn của họ.

Authentication (xác thực) và Authorization (phân quyền) thường được kết hợp lại với nhau trong duy nhất 1 token để đảm bảo tính an toàn và quản lý quyền truy cập hiệu quả.

Token thường sẽ được mã hoá và có thể giải mã.

Token cần được mã hoá vì tính bảo mật và bảo vệ thông tin cá nhân của người dùng. Ngược lại, token cũng sẽ cần phải giải mã để xử lý trong quá trình Authentication và Authorization.

Tuy nhiên, quá trình mã hoá, cũng như để có thể giải mã được, cần tuân thủ nghiêm ngặt các nguyên tắc, cũng như cấu trúc một token có.

## Cấu trúc

Cấu trúc của token thường sẽ có 3 phần:

**Header**: Phần này chứa thông tin về loại token và thuật toán mã hóa được sử dụng. Header thường là một đối tượng JSON.

Ví dụ:

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

**Payload**: Phần này chứa các thông tin xác thực, thông tin người dùng, hoặc thông tin tùy chọn khác. Payload cũng là một đối tượng JSON.

Ví dụ:

```json
{
  "userId": "6512d82bed93da9953dde3f5",
  "name": "John Doe",
  "ROLE": "USER",
  "exp": 1653410000,
  "scope": "read write"
}
```

**Signature**: Phần này chứa chữ ký số học được tạo ra bằng cách ký số học của header và payload bằng một **khóa bí mật (Secret key)**. Signature đảm bảo tính toàn vẹn của token.

## Cài đặt jsonwebtoken

Để có thể tạo được token, với thư viện jsonwebtoken sẽ hỗ trợ nhanh chóng, đơn giản.

Cài đặt:

```
npm install jsonwebtoken
```

Sử dụng:

```javascript
import jwt from 'jsonwebtoken';

// Secret key, thường được lưu trong biến môi trường (env)
const secretKey = 'mysecretkey';

// Dữ liệu mẫu để mã hoá vào JWT
const userData = {
  id: "123",
  username: 'john_doe',
  role: 'user',
};

// Tạo JWT
const token = jwt.sign(userData, secretKey, { expiresIn: '1h' });


// Xác thực JWT
jwt.verify(token, secretKey, (err, decoded) => {
  if (err) {
    console.error('JWT verification failed:', err.message);
  } else {
    console.log('Decoded JWT:');
    console.log(decoded);
  }
});

// ví dụ token
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```

## Access token

* **Access token** là một loại token dùng để xác định quyền truy cập của người dùng hoặc ứng dụng vào một tài nguyên cụ thể, chẳng hạn như một API hoặc dịch vụ web.
* Access token thường có thời hạn, có thể hết hạn sau một khoảng thời gian cố định hoặc sau một số lần sử dụng. Mục đích của nó là đảm bảo rằng quyền truy cập được kiểm soát và bảo mật. Token hết hạn, giống với việc người dùng không được phép sử dụng các dịch vụ được cung cấp bởi server (người dùng đăng xuất khỏi hệ thống).
* Để sử dụng một dịch vụ hoặc tài nguyên bảo mật, người dùng hoặc ứng dụng cần gửi access token cùng với yêu cầu của họ. Server sau đó kiểm tra token để xác định quyền truy cập.
* Thường được trả về khi người dùng sử dụng API dành cho việc đăng nhập hoặc được cấp lại trong Refresh token
* Ví dụ: Khi bạn đăng nhập vào một ứng dụng và sau đó truy cập tài khoản người dùng của bạn hoặc gửi yêu cầu đến một API, access token được sử dụng để xác định bạn có quyền truy cập vào những thông tin đó hay không.

## Refresh token

* **Refresh token** là một loại token được sử dụng để lấy access token mới sau khi access token hiện tại đã hết hạn.
* Mục đích làm giảm thiểu việc yêu cầu người dùng phải đăng nhập lại.
* Refresh token thường có thời hạn lâu hơn so với access token.
* Được trả về ngay cả lúc đăng nhập thành công.
* Ví dụ: Khi đang sử dụng ứng dụng với các API, được báo rằng Access token hết hạn, lúc này phía client sẽ tự động call API phục vụ cho việc gửi lên Refresh token để thực hiện xác thực và nhận về một Access token mới.

{% hint style="info" %}
Cần phải có một API riêng biệt để xử lý Refresh token để làm giảm 'gánh nặng' cho server khi xử lý, cấp lại Access token
{% endhint %}

## Phân biệt AT và RT

Cần phải phân biệt đâu là Access token và đâu là Refresh token, vì mỗi loại token sẽ có tác dụng khác nhau, không được phép dùng chung cho một mục đích

Do cả 2 loại token này đều là token, nên để phân biệt được, ta có thể thêm một key là tokenType trong mỗi payload của token.

<pre class="language-javascript"><code class="lang-javascript">// AT
<strong>const payload ={
</strong>    "userId": "12345",
    "role": "USER"
    "tokenType": "AT"
}

// RT
const payload ={
    "userId": "12345",
    "role": "USER"
    "tokenType": "RF"
}
</code></pre>

## Đính token

Khi gửi request lên server (các API), client sẽ cần phải gửi theo token, để có thể sử dụng được các service cung cấp, vậy khi gửi token, ta cần phải biết được token nên được gửi ở đâu để đảm bảo được an toàn.

Một số cách làm phổ biến:

1. **Trong Header Authorization**: Đây là cách phổ biến để gửi access token. Token thường được đính kèm trong tiêu đề "Authorization" của yêu cầu HTTP dưới dạng chuỗi. Ví dụ:

   ```http
   GET /api/resource HTTP/1.1
   Host: example.com
   Authorization: Bearer your_access_token_here
   ```

   Trong trường hợp của OAuth 2.0, bạn sử dụng chuỗi "Bearer " để đánh dấu access token.
2. **Trong Thân Yêu Cầu (Body)**: Trong một số trường hợp, token cũng có thể được đính kèm trong thân yêu cầu HTTP, đặc biệt là khi bạn sử dụng phương thức HTTP POST hoặc PUT. Dữ liệu thường được truyền dưới dạng JSON hoặc các định dạng dữ liệu khác. Ví dụ:

   ```http
   POST /api/resource HTTP/1.1
   Host: example.com
   Content-Type: application/json

   {
     "access_token": "your_access_token_here"
   }
   ```
3. **Trong Tham Số Truy vấn (Query Parameter)**: Trong một số trường hợp, token cũng có thể được truyền trong URL như một tham số truy vấn. Tuy nhiên, việc này không được khuyến nghị vì thông tin token có thể bị lộ trong lịch sử trình duyệt hoặc các bản ghi nhật ký. Ví dụ:

   ```http
   GET /api/resource?access_token=your_access_token_here HTTP/1.1
   Host: example.com
   ```

Phần mà bạn chọn để đính kèm token trong yêu cầu HTTP phụ thuộc vào thiết kế API của bạn và tiêu chuẩn ủy quyền bạn sử dụng. Phần tiêu đề "Authorization" thường là cách phổ biến và được khuyến nghị cho việc gửi access token.

Ví dụ middleware xác thực token và giải mã với header authorization bearer token:

<pre class="language-javascript"><code class="lang-javascript"><strong>const validateToken = (req, res, next) => {
</strong>  const authHeader = req.headers['authorization'];
  if (authHeader) {
    const token = authHeader.split(' ')[1]; // Tách access token từ chuỗi "Bearer {access_token}"

    // Xác thực access token
    jwt.verify(token, secretKey, (err, decoded) => {
      if (err) {
        return res.status(401).json({ message: 'Access token is invalid' });
      } else {
        // Lưu thông tin người dùng từ access token vào req
        // tham số req sẽ được chuyển tiếp tới các handler tiếp theo
        req.user = decoded; 
        next();
      }
    });
  } else {
    res.status(401).json({ message: 'Access token is missing' });
  }
}
</code></pre>

Cách đính kèm trên Postman:

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/tpUMWqiEH4wrVcsPzIxp/Screenshot%202023-10-12%20at%2016.18.02.png" alt=""><figcaption><p>Đính kèm token trên header Authorization</p></figcaption></figure>

## Thực hành

Sử dụng kiến thức của bài học (access token và refresh token) áp dụng cho bài tập đã làm.

## Kết

Trên đây là kiến thứ về token, một cách hiệu quả để kiếm soát hoạt động truy cập, sử dụng các service của server.
