Memverifikasi Token
Termasuk bagian Autentikasi dan Keamanan dalam perjalanan Node.js di Coddy. Pelajaran 16 dari 24.
Klien API mengirim tokennya dalam header pada setiap permintaan: Authorization: Bearer <token>. Membacanya dengan decodeJwt akan menerima token apa pun yang diketik siapa saja. jwtVerify(token, key) menghitung ulang tanda tangan dengan rahasiamu dan menghasilkan error jika tanda tangan tidak cocok, jika token sudah kedaluwarsa, atau jika token itu bukan JWT. Kamu baru boleh memercayai payload setelah fungsi itu mengembalikan hasil.
async function requireDevice(req, res, next) {
const token = (req.get('authorization') ?? '').replace(/^Bearer /, '');
try {
const { payload } = await jwtVerify(token, deviceKey);
req.device = payload.sub;
} catch {
return res.status(401).json({ error: 'Unknown device' });
}
next();
}Tantangan
MudahSekarang terima token yang kamu tanda tangani. Sebuah aplikasi mengirim tokennya di header pada setiap permintaan, dan server memeriksa tanda tangan sebelum memercayai apa pun di dalamnya.
- Di
server.js, imporjwtVerifydarijose. - Tulis middleware
requireUser(req, res, next). Middleware ini membaca headerAuthorization. Header itu berisi kataBearer, spasi, lalu token. Ambil token dari header tersebut. - Periksa token dengan
jwtVerify, menggunakan kunci yang sama dengan kunci yang kamu gunakan untuk menandatangani token.jwtVerifymelempar error jika tanda tangannya tidak cocok, jika token sudah kedaluwarsa, atau jika teksnya sama sekali bukan token. Dalam semua kasus tersebut, dan ketika tidak ada header, kirim respons dengan status 401 dan{"error": "Log in first"}. - Jika token valid, tetapkan
req.usersebagai object denganidyang diambil dari klaimsubtoken, danusernameyang diambil dari klaimusername. Lalu panggilnext(). - Tambahkan
GET /medenganrequireUser. Endpoint ini mengembalikanreq.user:{"id": "66f1c2a9e4b0a1b2c3d4e5f6", "username": "ada"}.
Coba: Tekan tombol Jalankan. Kirim GET /me: status 401 dan {"error": "Log in first"}. Kirim POST /signup, lalu POST /token, keduanya dengan {"username": "ada", "password": "correct horse"}, lalu salin tokennya. Di kolom Headers, tambahkan header bernama Authorization dengan nilai Bearer, spasi, lalu tokenmu. Kirim GET /me: status 200, dengan id-mu dan "username": "ada". Sekarang ubah satu huruf di bagian tengah token, di antara kedua titik, lalu kirim GET /me lagi: status 401, karena tanda tangannya tidak lagi cocok.
Coba sendiri
import express from 'express';
import { SignJWT, decodeJwt } from 'jose';
import { users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const app = express();
app.use(express.json());
app.post('/signup', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const password = String(req.body.password ?? '');
if (!username || password.length < 8) {
return res.status(400).json({ error: 'Pick a username and a password of at least 8 characters' });
}
if (await users.findOne({ username })) {
return res.status(409).json({ error: 'That username is taken' });
}
await users.insertOne({ username, passwordHash: await hashPassword(password) });
res.status(201).json({ username });
});
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
res.json({ username: user.username });
});
app.post('/token', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
const token = await new SignJWT({ username: user.username })
.setProtectedHeader({ alg: 'HS256' })
.setSubject(user._id.toHexString())
.setIssuedAt()
.setExpirationTime('1h')
.sign(secret);
res.json({ token });
});
// Menampilkan isi token: GET /inspect?token=...
app.get('/inspect', (req, res) => {
try {
res.json(decodeJwt(String(req.query.token ?? '')));
} catch {
res.status(400).json({ error: 'Not a JWT' });
}
});
// Halaman admin. Kamu tidak boleh memercayai klaim apa pun sebelum tanda tangannya diperiksa.
app.get('/admin', (req, res) => {
res.status(403).json({ error: 'Admins only' });
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Autentikasi dan Keamanan
Latihan sendiri: Playground Express