JSON Web Token: Bảo mật RESTful API với JWT và Cookie httpOnly, Secure.

Ở bài viết trước, chúng ta đã làm rõ về cách lưu trữ token ở đâu trên Client. Và cách nào an toàn hơn và hạn chế được các XSS attack. Do đó ở bài này chúng ta sẽ triển khai xây dựng một RESTful APIs bảo mật token hạn chế việc đánh cắp khi mà càng ngày hackers luôn luôn rình mò ở quanh ta :D. 

Để đọc và hiểu bài này thì những bạn chứ đọc bài trước "JSON Web Token: Lưu trữ và bảo mật tokens trên client như thế nào?" thì nên lướt qua để hiểu vì sao lại có bài viết này. Bắt đầu tạo cho mình một rest api thôi nào. Code full tôi sẽ post sau bài viết. Nhưng các bạn nên theo dõi từng bước trước để hiểu cách run một api thế nào rồi lúc đó download source về cũng chưa muộn nhé. 

Code Full:

Phía Back-End: https://github.com/anonystick/demo-jwt-token 

Phía Client: https://github.com/anonystick/demo-jwt-token-client

Trước tiên tôi tạo một file có tên là users.json. Chứa danh sách user hệ thống. 

1 - Create users.json

[
    {
        "id": 1,
        "name": "anonystick"
    },
    {
        "id": 2,
        "name": "anonymous hackers"
    }
]

Nhiệm vụ của chúng ta khi truy cập vào http://localhost:3000/api/users thì chúng ta sẽ lấy được danh sách đó, tất nhiên là phải xác nhận được token. 

2 - package.json

  "name": "demo-auth-jwt",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "^1.19.1"
  },
  "dependencies": {
    "cookie-parser": "^1.4.4",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "jsonwebtoken": "^8.5.1"
  }
}

Copy file này về rồi các bạn chỉ việc run command.

npm i

Cấu trúc một restful api đơn giản. 

3 - index.js

//Khi user login xác nhận 
app.post('/auth/login', (req, res) => {
    try{
        const email = req.body.email;
        const password = req.body.password;
        if(email !== 'anonystick@gmail.com' || password !== 'password'){
            res.status(400);
            throw new Error('invalid infor')
        }
        const payload = {
            email: email
        }
        const token = jwt.sign(payload, SECRET);
        res.cookie('access_token', token, {
            maxAge: 365 * 24 * 60 * 60 * 100,
            httpOnly: true,
            //secure: true;
        })
        res.status(200).json({ok: true});
    }catch (err){
        throw err
    }
    
})

Nhiệm vụ app.post('/auth/login') là khi user xác nhận pass và email nếu thành công thì sẽ trả về cho user một token, đồng thời server sẽ set một cookiebrowser của client. Và cookie này ở user sẽ không bao giờ đọc được bởi javascript.  Nó sẽ được browser tự động gửi đi khi có yêu cầu.

res.cookie('access_token', token, {
    maxAge: 365 * 24 * 60 * 60 * 100, // thời gian sống 
    httpOnly: true, // chỉ có http mới đọc được token
    //secure: true; //ssl nếu có, nếu chạy localhost thì comment nó lại
})

Nếu thành công thì các bạn có thể thấy như hình ảnh này. 

Các bạn để ý các thuộc tính của một cookie nhé. Tiếp theo viết một service cho phép user request để lấy danh sách user.

app.use('/api/users', (req, res) => {
    const token = req.cookies.access_token;
    try{
        const decoded = jwt.verify(token, SECRET);
        res.status(200).json(users);
    }catch(err){
        res.status(400)
        throw err;
    }
    
})

Chú ý:

const token = req.cookies.access_token

Khi user gọi http://localhost:3000/api/users thì mặc định browser sẽ gửi cookie đi theo nếu trên client cho phép.

const decoded = jwt.verify(token, SECRET);

Nếu verify thành công thì lúc đó client sẽ nhận được danh sách user. Quan trọng chúng ta install những package này vào webservice nhằm đảm bảo cho việc bảo vệ token.

app.use(express.json()) // for parsing application/json
app.use(cookieParser()) //cookie-parser dùng để đọc cookies của request:
app.use(cors({
    origin: 'http://127.0.0.1:5500', //Chan tat ca cac domain khac ngoai domain nay
    credentials: true //Để bật cookie HTTP qua CORS
}))

Code Full:

Phía Back-End: https://github.com/anonystick/demo-jwt-token 

Phía Client: https://github.com/anonystick/demo-jwt-token-client