Token-based authorization is a pretty nifty way to secure access to various resources by validating a token. It's a popular choice in web apps for managing user authentication and authorization. Let’s look at how to implement this using JavaScript, complete with code snippets for both the server and client sides.
First up, let's set up a new project and install the necessary packages:
mkdir secure-token-auth
cd secure-token-auth
npm init -y
npm install express jsonwebtoken bcrypt
Start with server.js
.
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const app = express();
const PORT = process.env.PORT || 3000;
const SECRET_KEY = 'your_secret_key';
app.use(express.json());
const users = []; // We're keeping things simple with in-memory storage. Use a real DB for production!
// Error-handling Middleware
app.use((err, req, res, next) => {
res.status(err.status || 500).json({ error: { message: err.message } });
});
Add a route for user registration that handles password hashing.
app.post('/register', async (req, res, next) => {
try {
const { username, password } = req.body;
if (!(username && password)) {
throw new Error('Username and Password are required');
}
const hashedPassword = await bcrypt.hash(password, 10);
users.push({ username, password: hashedPassword });
res.status(201).send('User Registered');
} catch (err) {
next(err);
}
});
Create a route for logging in, which checks the password and issues a token.
app.post('/login', async (req, res, next) => {
try {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(404).send('User Not Found');
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).send('Invalid Credentials');
}
const token = jwt.sign({ username: user.username }, SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
} catch (err) {
next(err);
}
});
Add middleware to verify tokens.
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (token == null) {
return res.status(401).send('Token Required');
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).send('Invalid Token');
}
req.user = user;
next();
});
};
Create a route that can only be accessed with a valid token.
app.get('/protected', authenticateToken, (req, res) => {
res.send(`Hello ${req.user.username}, you have accessed a protected route!`);
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Using the Fetch API, we can interact with our server to register a user.
async function register(username, password) {
const response = await fetch('http://localhost:3000/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
if (!response.ok) {
throw new Error('Registration Failed');
}
console.log('User Registered');
}
async function login(username, password) {
const response = await fetch('http://localhost:3000/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
if (!response.ok) {
throw new Error('Login Failed');
}
const data = await response.json();
localStorage.setItem('token', data.token);
console.log('User Logged In', data.token);
}
async function accessProtectedResource() {
const token = localStorage.getItem('token');
if (!token) {
throw new Error('Authorization Token Not Found');
}
const response = await fetch('http://localhost:3000/protected', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!response.ok) {
throw new Error('Failed to Access Resource');
}
const data = await response.text();
console.log(data);
}
This guide walks you through implementing a secure token-based authorization system using JavaScript. It covers both the server and client sides. On the server, we use Express to handle HTTP requests, bcrypt to hash passwords, and jsonwebtoken to manage tokens. On the client side, the Fetch API helps us interact with the server for registration, login, and protected resource access.
Keep in mind a couple of recommendations: