Menu
CoddyTech

Apa Itu JWT

Termasuk bagian Autentikasi dan Keamanan dalam perjalanan Node.js di Coddy. Pelajaran 9 dari 17.

Sesi memerlukan pencarian database pada setiap permintaan. JWT (JSON Web Token) membawa fakta di dalamnya: tiga bagian base64url yang disambungkan dengan titik. Header menyebutkan algoritmanya, payload berisi klaim (sub untuk menunjukkan token itu milik siapa, exp untuk menunjukkan kapan token kedaluwarsa), dan signature membuktikan bahwa server membuatnya.

Base64url adalah encoding, bukan enkripsi. Siapa pun yang memegang token dapat membaca payload-nya:

const payload = token.split('.')[1];
JSON.parse(Buffer.from(payload, 'base64url').toString());
// { sub: '42', name: 'Lin', exp: 1767225600 }

decodeJwt(token) dari jose melakukan hal yang sama dan melewati signature. Jadi, jangan pernah memasukkan rahasia ke dalam payload, dan jangan pernah mengambil keputusan berdasarkan klaim yang diberikan oleh decodeJwt: siapa pun dapat menulis klaim apa pun.

challenge icon

Tantangan

Mudah
  • GET /inspect?token=... mengembalikan klaim token sebagai JSON, atau 400 { "error": "Not a JWT" }.
  • GET /admin mengembalikan 403 { "error": "Admins only" } untuk setiap permintaan.

Pertama, kirim token Mallory ke GET /admin?token= dan lihat apa yang dibukanya: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYWxsb3J5IiwiYWRtaW4iOnRydWV9.c2lnbmF0dXJl. Perbaiki, lalu kirim token yang sama ke /inspect.

Coba sendiri

import express from 'express';
import { decodeJwt } from 'jose';
import { users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';

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 });
});

// Menampilkan isi token: GET /inspect?token=...
app.get('/inspect', (req, res) => {
    res.status(501).json({ error: 'Not written yet' });
});

// Halaman admin.
app.get('/admin', (req, res) => {
    const claims = decodeJwt(String(req.query.token ?? ''));
    if (claims.admin === true) {
        return res.json({ secret: 'The vault key is 42' });
    }
    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