We use cookies to enhance your experience on the site
CodeWorlds

Authentication strategies

In previous parts, we discussed the basics of authentication and implemented simple authorization using JWT. Now we will dive into different authentication strategies you can apply in your applications.

Token-based vs. session-based authentication

The two main approaches to authentication are:

1. Session-based Authentication

In this approach, the server maintains session state for each logged-in user:

1// Example session configuration in Express
2import express from 'express';
3import session from 'express-session';
4
5const app = express();
6
7app.use(session({
8  secret: 'secret-session-key',
9  resave: false,
10  saveUninitialized: false,
11  cookie: { secure: true, maxAge: 3600000 } // 1 hour
12}));
13
14app.post('/login', (req, res) => {
15  // After successful authentication
16  req.session.authenticated = true;
17  req.session.userId = user.id;
18  res.send({ success: true });
19});
20
21app.get('/protected', (req, res) => {
22  if (req.session.authenticated) {
23    // User has a valid session
24    res.send('Protected data');
25  } else {
26    res.status(401).send('Access denied');
27  }
28});

Advantages:

  • Ease of implementation and maintenance
  • Ability to immediately invalidate a session
  • Does not require additional processing on the client side

Disadvantages:

  • Server load (state storage)
  • Scalability issues in distributed architecture
  • Difficulties in implementation for mobile applications

2. Token-based Authentication

In this approach, the server generates a token that the client stores and sends with each request:

1// Example JWT implementation in Express
2import express from 'express';
3import jwt from 'jsonwebtoken';
4
5const app = express();
6const JWT_SECRET = 'secret-jwt-key';
7
8app.post('/login', (req, res) => {
9  // After successful authentication
10  const token = jwt.sign(
11    { userId: user.id, email: user.email },
12    JWT_SECRET,
13    { expiresIn: '1h' }
14  );
15  
16  res.send({ token });
17});
18
19// Token verification middleware
20const verifyToken = (req, res, next) => {
21  const token = req.headers.authorization?.split(' ')[1];
22  
23  if (!token) {
24    return res.status(401).send('Missing authorization token');
25  }
26  
27  try {
28    const decoded = jwt.verify(token, JWT_SECRET);
29    req.user = decoded;
30    next();
31  } catch (error) {
32    return res.status(401).send('Invalid token');
33  }
34};
35
36app.get('/protected', verifyToken, (req, res) => {
37  res.send('Protected data');
38});

Advantages:

  • Statelessness - easier scaling
  • Works well in mobile apps and SPAs
  • Ability to store additional information in the token

Disadvantages:

  • More difficult to invalidate a token before expiration
  • Vulnerability to theft (if adequate security measures are not applied)
  • Larger tokens may increase network load

OAuth 2.0 and social authentication

OAuth 2.0 is an authorization protocol standard that allows applications to access user resources on another server without sharing credentials.

Implementing authentication with Google

1// OAuth implementation with Google in Express and Passport
2import express from 'express';
3import passport from 'passport';
4import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
5
6const app = express();
7
8// Passport configuration
9passport.use(new GoogleStrategy({
10    clientID: 'YOUR_GOOGLE_CLIENT_ID',
11    clientSecret: 'YOUR_GOOGLE_CLIENT_SECRET',
12    callbackURL: 'http://localhost:3000/auth/google/callback'
13  },
14  function(accessToken, refreshToken, profile, done) {
15    // Find or create the user in the database
16    User.findOrCreate({ googleId: profile.id }, function (err, user) {
17      return done(err, user);
18    });
19  }
20));
21
22// Authentication routes
23app.get('/auth/google',
24  passport.authenticate('google', { scope: ['profile', 'email'] }));
25
26app.get('/auth/google/callback', 
27  passport.authenticate('google', { failureRedirect: '/login' }),
28  (req, res) => {
29    // Redirect after successful authentication
30    res.redirect('/dashboard');
31  }
32);

Multi-factor authentication (MFA)

Multi-Factor Authentication (MFA) increases security by requiring the user to provide two or more proofs of identity.

Example MFA implementation using TOTP (Time-based One-Time Password)

1// 2FA implementation with TOTP in Node.js
2import express from 'express';
3import speakeasy from 'speakeasy';
4import QRCode from 'qrcode';
5
6const app = express();
7app.use(express.json());
8
9// Generating a secret key for the user
10app.post('/generate-2fa', (req, res) => {
11  const secret = speakeasy.generateSecret({
12    name: 'MyApp:' + req.user.email
13  });
14  
15  // Save secret.base32 in the database for this user
16  saveUserSecret(req.user.id, secret.base32);
17  
18  // Generate the QR code
19  QRCode.toDataURL(secret.otpauth_url, (err, imageUrl) => {
20    if (err) {
21      return res.status(500).send('QR code generation error');
22    }
23    
24    res.json({
25      secret: secret.base32,
26      qrCode: imageUrl
27    });
28  });
29});
30
31// One-time code verification
32app.post('/verify-2fa', (req, res) => {
33  const { token } = req.body;
34  
35  // Fetch the user's secret from the database
36  const userSecret = getUserSecret(req.user.id);
37  
38  const verified = speakeasy.totp.verify({
39    secret: userSecret,
40    encoding: 'base32',
41    token: token
42  });
43  
44  if (verified) {
45    // Enable 2FA for the user in the database
46    enableUser2FA(req.user.id);
47    res.json({ success: true });
48  } else {
49    res.status(400).json({ success: false, message: 'Invalid code' });
50  }
51});

Single Sign-On (SSO)

Single Sign-On is an authentication process that allows a user to access multiple applications after a single login.

Implementing SSO with SAML

1// Example SAML SSO implementation in Express
2import express from 'express';
3import passport from 'passport';
4import { Strategy as SAMLStrategy } from 'passport-saml';
5
6const app = express();
7
8passport.use(new SAMLStrategy({
9  path: '/login/callback',
10  entryPoint: 'https://sso.example.com/saml2/idp/SSOService.php',
11  issuer: 'my-application',
12  cert: 'IDENTITY_PROVIDER_CERTIFICATE'
13}, (profile, done) => {
14  // Find or create the user in the database
15  return done(null, {
16    id: profile.nameID,
17    email: profile.email,
18    name: profile.displayName
19  });
20}));
21
22app.get('/login',
23  passport.authenticate('saml', { failureRedirect: '/login/fail' }),
24  (req, res) => res.redirect('/')
25);
26
27app.post('/login/callback',
28  passport.authenticate('saml', { failureRedirect: '/login/fail' }),
29  (req, res) => res.redirect('/')
30);

Authentication security best practices

  1. Secure passwords

    • Require strong passwords (length, complexity)
    • Store only encrypted passwords (use strong algorithms like bcrypt)
    • Implement login attempt limits
  2. Token security

    • Store JWT tokens securely (HttpOnly cookies)
    • Use short token validity periods
    • Implement token rotation and invalidation
  3. Transport

    • Always use HTTPS to transmit credentials
    • Implement HSTS (HTTP Strict Transport Security)
    • Secure cookies with the Secure and SameSite flags
  4. General rules

    • Regularly perform security audits
    • Implement activity logging and monitoring
    • Always apply the principle of least privilege

Summary

Choosing the right authentication strategy depends on many factors, such as:

  • Application security requirements
  • Target user group
  • Type of application (mobile, web, hybrid)
  • Scalability and performance

Remember that authentication security is an ongoing process requiring regular updates and adaptation to new threats and security standards.

In the next lessons, we will move on to implementing advanced authorization mechanisms and user permission management.

Go to CodeWorlds