阿里云主机折上折
  • 微信号
Current Site:Index > Implementation of authentication and authorization schemes

Implementation of authentication and authorization schemes

Author:Chuan Chen 阅读数:31338人阅读 分类: Node.js

Basic Concepts of Authentication and Authorization

Authentication is the process of verifying a user's identity, while authorization determines whether a user has permission to access specific resources. In Express applications, these two concepts are often used together to protect routes and API endpoints. Common authentication methods include session-based authentication, JWT (JSON Web Token), and OAuth, while authorization is typically implemented through role or permission systems.

Implementation of Session-Based Authentication

In Express, you can use the express-session middleware to implement session-based authentication. First, install the dependency:

npm install express-session

Then configure the middleware:

const express = require('express');
const session = require('express-session');

const app = express();

app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: false } // In production, set to true and enable HTTPS
}));

When a user logs in, you can set the session like this:

app.post('/login', (req, res) => {
  const { username, password } = req.body;
  
  // Validate user credentials (in a real project, use a database)
  if (username === 'admin' && password === 'password') {
    req.session.user = { username, role: 'admin' };
    return res.send('Login successful');
  }
  
  res.status(401).send('Authentication failed');
});

Example of middleware to protect routes:

function requireAuth(req, res, next) {
  if (req.session.user) {
    return next();
  }
  res.status(401).send('Login required');
}

app.get('/protected', requireAuth, (req, res) => {
  res.send('This is protected content');
});

Implementation of JWT Authentication

JWT is a stateless authentication mechanism suitable for RESTful APIs. First, install jsonwebtoken:

npm install jsonwebtoken

Example of generating and verifying tokens:

const jwt = require('jsonwebtoken');
const secret = 'your-jwt-secret';

// Login route to generate a token
app.post('/api/login', (req, res) => {
  const { username, password } = req.body;
  
  if (username === 'user' && password === 'pass') {
    const token = jwt.sign(
      { username, role: 'user' },
      secret,
      { expiresIn: '1h' }
    );
    return res.json({ token });
  }
  
  res.status(401).json({ error: 'Authentication failed' });
});

// Middleware to verify the token
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  
  if (!token) return res.sendStatus(401);
  
  jwt.verify(token, secret, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
}

// Protected route
app.get('/api/protected', authenticateToken, (req, res) => {
  res.json({ message: `Welcome, ${req.user.username}` });
});

Role-Based Authorization Control

On top of authentication, you can add role checks for authorization:

// Role-check middleware
function requireRole(role) {
  return (req, res, next) => {
    if (req.user?.role !== role) {
      return res.status(403).send('Access denied');
    }
    next();
  };
}

// Admin-only route
app.get('/admin/dashboard', 
  authenticateToken,
  requireRole('admin'),
  (req, res) => {
    res.send('Admin dashboard');
  }
);

OAuth2.0 Integration

Express can integrate Passport.js for OAuth authentication. Example with GitHub OAuth:

npm install passport passport-github2

Configuration example:

const passport = require('passport');
const GitHubStrategy = require('passport-github2').Strategy;

passport.use(new GitHubStrategy({
    clientID: 'your-client-id',
    clientSecret: 'your-client-secret',
    callbackURL: 'http://localhost:3000/auth/github/callback'
  },
  (accessToken, refreshToken, profile, done) => {
    // Find or create the user here
    return done(null, profile);
  }
));

// Serialize and deserialize the user
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));

// Initialize Passport
app.use(passport.initialize());
app.use(passport.session());

// Authentication routes
app.get('/auth/github', passport.authenticate('github'));

app.get('/auth/github/callback',
  passport.authenticate('github', { failureRedirect: '/login' }),
  (req, res) => res.redirect('/')
);

Permission Management System

More complex systems may require Permission-Based Access Control (PBAC):

// Permission-check middleware
function checkPermission(permission) {
  return (req, res, next) => {
    const userPermissions = getUserPermissions(req.user.id); // Hypothetical function
    
    if (!userPermissions.includes(permission)) {
      return res.status(403).send('Unauthorized to perform this action');
    }
    next();
  };
}

// Usage example
app.delete('/api/posts/:id',
  authenticateToken,
  checkPermission('delete_post'),
  (req, res) => {
    // Logic to delete the post
  }
);

Security Best Practices

  1. Always use HTTPS for sensitive data transmission.
  2. Set secure cookie attributes:
app.use(session({
  // ...other configurations
  cookie: {
    secure: true,
    httpOnly: true,
    sameSite: 'strict',
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  }
}));
  1. Store passwords using libraries like bcrypt for hashing:
const bcrypt = require('bcrypt');
const saltRounds = 10;

// Hash password during registration
app.post('/register', async (req, res) => {
  const { username, password } = req.body;
  const hashedPassword = await bcrypt.hash(password, saltRounds);
  
  // Store username and hashedPassword in the database
});
  1. Implement rate limiting to prevent brute-force attacks:
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5 // Max 5 requests per IP
});

app.use('/login', limiter);

Error Handling

Dedicated middleware for authentication errors:

function authErrorHandler(err, req, res, next) {
  if (err.name === 'UnauthorizedError') {
    return res.status(401).json({ error: 'Invalid token' });
  }
  if (err.name === 'ForbiddenError') {
    return res.status(403).json({ error: 'Access denied' });
  }
  next(err);
}

app.use(authErrorHandler);

Testing Authentication and Authorization

Use Supertest to test endpoints:

const request = require('supertest');
const app = require('../app');

describe('Authentication Tests', () => {
  it('Unauthenticated user accessing protected route should return 401', async () => {
    const res = await request(app).get('/protected');
    expect(res.statusCode).toEqual(401);
  });

  it('Valid token should grant access', async () => {
    const loginRes = await request(app)
      .post('/login')
      .send({ username: 'user', password: 'pass' });
    
    const token = loginRes.body.token;
    
    const res = await request(app)
      .get('/protected')
      .set('Authorization', `Bearer ${token}`);
    
    expect(res.statusCode).toEqual(200);
  });
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.