Next.js course Β· Module 7: Authentication and Authorization
Authentication strategies
In this lesson6
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
Secure passwords
- Require strong passwords (length, complexity)
- Store only encrypted passwords (use strong algorithms like bcrypt)
- Implement login attempt limits
Token security
- Store JWT tokens securely (HttpOnly cookies)
- Use short token validity periods
- Implement token rotation and invalidation
Transport
- Always use HTTPS to transmit credentials
- Implement HSTS (HTTP Strict Transport Security)
- Secure cookies with the Secure and SameSite flags
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.
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. NextAuth.js is:
2. JWT (JSON Web Token) is:
Hands-on tasks in the game
- Vertical ordering
Arrange the stages of OAuth 2.0 Authorization Code Flow:
- Click in order
Arrange the useSession destructuring syntax in NextAuth