Vérifier des jetons
Fait partie de la section Authentification et sécurité du Journey Node.js de Coddy. Leçon 11 sur 17.
Un client d’API envoie son jeton dans un en-tête à chaque requête : Authorization: Bearer <token>. Le lire avec decodeJwt accepterait n’importe quel jeton saisi par n’importe qui. jwtVerify(token, key) recalcule la signature avec votre secret et lève une exception si elle ne correspond pas, si le jeton a expiré ou s’il ne s’agit pas du tout d’un JWT. Vous ne pouvez faire confiance à payload qu’après son retour.
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();
}Défi
Facile- Écrivez
requireUser: vérifiez le jetonAuthorization: BeareravecjwtVerifyet définissezreq.user = { id, username }à partir de ses revendications, ou répondez avec le code 401{ "error": "Log in first" }. GET /mel’utilise et renvoiereq.user.
Envoyez GET /me sans jeton. Obtenez-en un avec POST /token, ajoutez l’en-tête Authorization: Bearer <token> et envoyez de nouveau GET /me. Ensuite, changez une lettre du jeton et envoyez-le une fois de plus.
Essayez vous-même
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 });
});
// Affiche le contenu d’un 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' });
}
});
// La page d’administration. Aucune déclaration ne peut être considérée comme fiable avant la vérification de sa signature.
app.get('/admin', (req, res) => {
res.status(403).json({ error: 'Admins only' });
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.