# Token-Based Authentication JWT ## JWT 介紹 1. JSON Web Token (JWT) 是一種基於 JSON 的開放標準(RFC 7519),基於 JSON object 的編碼,並透過這個編碼進行傳遞資訊。 2. JWT會透過HMAC、RSA、ECDS等演算法進行加密 [https://jwt.io/](https://jwt.io/) ## JWT 組成 JWT 分為 Header, Payload, Signature 三個部份以 Base64 編碼, 並且以 . 分開。 eg: `xxxxx.yyyyy.zzzzz` * **Header** Header 由 alg, typ 兩個欄位所組成 1. alg: Signature 所要使用的加密演算法 2. typ: token 種類,基本上是 JWT eg: ```json= { "alg": "HS256", "typ": "JWT" } ``` * **Payload** Payload 是用來傳送訊息(claim)的地方(也可以空白),主要有三種內容 1. Registered claims: 一些已經標準公認的訊息,建議放,但不強迫 * iss: JWT簽發者 * iat: JWT簽發時間 * exp: JWT的過期時間 * ... 2. Public claims: 可以向官方申請公開聲明 3. Private claims: 自定義的訊息 eg: ```json= { "sub": "1234567890", "name": "John Doe", "admin": true } ``` :::warning **由於加密只作用於 Signature ,因此 Payload 不應該存放隱私資訊** ::: * **Signature** Signature 由三個部份組成 1. base64UrlEncode(header) 2. base64UrlEncode(payload) 3. secret ``` HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) ``` signature 是用來確保訊息不會在傳送途中被竄改,並且有一個 private secret key 用來確保 JWT 發行者的正確性 ## JWT 使用 * 流程 ![](https://i.imgur.com/7uX2Hxh.png) * client 端使用方法 當使用需要驗證的 api 時,在 Authorization header 使用 Bearer schema ``` Authorization: Bearer <token> ``` ## Server 範例 ``` javascript import jsonwebtoken from "jsonwebtoken"; import express, { urlencoded } from "express"; import "dotenv/config"; const app = express(); app.use(urlencoded({ extended: true })); app.post("/login", async (req, res, next) => { const { id } = req.body; const token = jsonwebtoken.sign({ id: id }, process.env.JWT_SIGN_SECRET, { noTimestamp: true, algorithm: process.env.JWT_ALGO, }); return res.status(200).json({ jwt: token }); }); app.get("/hello", async (req, res, next) => { try { const token = req.header("Authorization").replace("Bearer ", ""); const decoded = jsonwebtoken.verify(token, process.env.JWT_SIGN_SECRET, { algorithm: process.env.JWT_ALGO, }); return res.status(200).send(`Hello ${decoded.id}`); } catch (e) { return res.status(404).send({ error: "Please authenticate." }); } }); app.listen(8080, () => { console.log(` ################################################ 🛡️ HTTP server listening on port: 8080 🛡️ ################################################ `); }); ``` ## JWT with Nginx [https://github.com/TeslaGov/ngx-http-auth-jwt-module](https://github.com/TeslaGov/ngx-http-auth-jwt-module) [https://github.com/auth0/nginx-jwt](https://github.com/auth0/nginx-jwt) 待更新 ## 流程 ![](https://i.imgur.com/5UOpEwX.png) ```javascript= import express, { Router, urlencoded, json } from "express"; import { createProxyMiddleware } from "http-proxy-middleware"; import jsonwebtoken from "jsonwebtoken"; import morgan from "morgan"; // Create Express Server const app = express(); // Configuration const PORT = 3000; const HOST = "localhost"; const API_SERVICE_URL = "http://127.0.0.1:4444/"; const route = Router(); app.post("/login", async (req, res, next) => { const { id } = req.body; const token = jsonwebtoken.sign({ id: id }, "ABCDEFG"); return res.status(200).json({ jwt: token }); }); // Proxy endpoints app.use("/wd/hub/session/", morgan("common"), route); route.post( "/", createProxyMiddleware({ target: API_SERVICE_URL, changeOrigin: true, onProxyReq(proxyReq, req, res) { try { let data = ""; let token; req.on("data", (chunk) => { data += chunk; }); req.on("end", () => { //console.log(JSON.parse(data).capabilities.auth); token = JSON.parse(data).capabilities.auth; console.log(token); jsonwebtoken.verify(token, "ABCDEFG"); proxyReq.end(); }); proxyReq.setHeader("content-type", "application/json; charset=utf-8"); proxyReq.write(data); } catch (e) { console.log(e); return res.status(404).send({ error: "Please authenticate." }); } }, }) ); route.post( "/:id", morgan("common"), createProxyMiddleware({ target: API_SERVICE_URL, changeOrigin: true, }) ); // Start Proxy app.listen(PORT, HOST, () => { console.log(`Starting Proxy at ${HOST}:${PORT}`); }); ``` ###### tags: JWT Nginx