Menu
CoddyTech

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();
}
challenge icon

Tantangan

Mudah

Sekarang 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.

  1. Di server.js, impor jwtVerify dari jose.
  2. Tulis middleware requireUser(req, res, next). Middleware ini membaca header Authorization. Header itu berisi kata Bearer, spasi, lalu token. Ambil token dari header tersebut.
  3. Periksa token dengan jwtVerify, menggunakan kunci yang sama dengan kunci yang kamu gunakan untuk menandatangani token. jwtVerify melempar 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"}.
  4. Jika token valid, tetapkan req.user sebagai object dengan id yang diambil dari klaim sub token, dan username yang diambil dari klaim username. Lalu panggil next().
  5. Tambahkan GET /me dengan requireUser. Endpoint ini mengembalikan req.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');
});
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Autentikasi dan Keamanan

Latihan sendiri: Playground Express