Memverifikasi Token
Termasuk bagian Autentikasi dan Keamanan dalam perjalanan Node.js di Coddy. Pelajaran 11 dari 17.
client API mengirim tokennya di 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 secret-mu dan menghasilkan error saat tanda tangan tidak cocok, saat token kedaluwarsa, atau saat token tersebut 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
Mudah- Tulis
requireUser: verifikasi tokenAuthorization: BearerdenganjwtVerifydan aturreq.user = { id, username }berdasarkan klaimnya, atau kirim respons 401{ "error": "Log in first" }. GET /memenggunakan fungsi ini dan mengirim responsreq.user.
Kirim GET /me tanpa token. Dapatkan token dari POST /token, tambahkan header Authorization: Bearer <token>, lalu kirim GET /me lagi. Setelah itu, ubah satu huruf pada token dan kirim sekali lagi.
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. Jangan percayai 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
4Pemilik dan Keamanan
Hanya CatatanmuPeran AdminLatihan sendiri: Playground Express