🔐

Authentication

Säker autentisering och auktorisering för moderna webbapplikationer

Vad du hittar här

Kompletta exempel på säker autentisering, från lösenordshantering till OAuth och tvåfaktorsautentisering. Lär dig implementera robusta säkerhetslösningar för dina applikationer.

Innehåll

4
Auth Examples
3
Security Patterns
4
Best Practices
4
Vulnerabilities

Authentication Examples

🔐

JWT Token Generation

Generera och validera JWT tokens för säker autentisering.

const jwt = require('jsonwebtoken');

// Generera token
function generateToken(user) {
  const payload = {
    id: user.id,
    email: user.email,
    role: user.role
  };
  
  const token = jwt.sign(
    payload,
    process.env.JWT_SECRET,
    { expiresIn: '24h' }
  );
  
  return token;
}

// Verifiera token
function verifyToken(token) {
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    return { valid: true, user: decoded };
  } catch (error) {
    return { valid: false, error: error.message };
  }
}

💡 Förklaring: Använder jsonwebtoken för att skapa säkra, tidsbegränsade tokens

🔒

Password Hashing med Bcrypt

Säker lösenordshantering med bcrypt för hashning och salt.

const bcrypt = require('bcrypt');

// Hash lösenord
async function hashPassword(password) {
  const saltRounds = 10;
  const hashedPassword = await bcrypt.hash(password, saltRounds);
  return hashedPassword;
}

// Validera lösenord
async function validatePassword(plainPassword, hashedPassword) {
  const isValid = await bcrypt.compare(plainPassword, hashedPassword);
  return isValid;
}

// Exempel användning
const password = 'mySecurePassword123';
const hash = await hashPassword(password);
console.log('Hashed:', hash);

const isValid = await validatePassword(password, hash);
console.log('Valid:', isValid); // true

💡 Förklaring: Bcrypt lägger automatiskt till salt och använder en långsam hash-algoritm för säkerhet

🔑

OAuth 2.0 Implementation

Implementera OAuth 2.0 för social login (Google exempel).

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: "/auth/google/callback"
  },
  async (accessToken, refreshToken, profile, done) => {
    try {
      // Kolla om användare finns
      let user = await User.findOne({ googleId: profile.id });
      
      if (!user) {
        // Skapa ny användare
        user = await User.create({
          googleId: profile.id,
          email: profile.emails[0].value,
          name: profile.displayName,
          avatar: profile.photos[0].value
        });
      }
      
      return done(null, user);
    } catch (error) {
      return done(error, null);
    }
  }
));

// Routes
app.get('/auth/google',
  passport.authenticate('google', { scope: ['profile', 'email'] })
);

app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/login' }),
  (req, res) => {
    // Generera JWT token
    const token = generateToken(req.user);
    res.redirect(`/dashboard?token=${token}`);
  }
);

💡 Förklaring: Passport.js förenklar implementation av OAuth-strategier för olika providers

🍪

Session Management

Hantera sessioner med express-session och Redis.

const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');

// Skapa Redis client
const redisClient = redis.createClient({
  host: process.env.REDIS_HOST,
  port: process.env.REDIS_PORT,
  password: process.env.REDIS_PASSWORD
});

// Konfigurera sessions
app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production', // HTTPS i produktion
    httpOnly: true, // Skydda mot XSS
    maxAge: 1000 * 60 * 60 * 24 * 7, // 7 dagar
    sameSite: 'strict' // CSRF-skydd
  }
}));

// Login route
app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await authenticateUser(email, password);
  
  if (user) {
    req.session.userId = user.id;
    req.session.userRole = user.role;
    res.json({ success: true, user });
  } else {
    res.status(401).json({ error: 'Invalid credentials' });
  }
});

// Logout route
app.post('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      res.status(500).json({ error: 'Could not log out' });
    } else {
      res.clearCookie('connect.sid');
      res.json({ success: true });
    }
  });
});

💡 Förklaring: Redis som session store ger snabb åtkomst och skalbarhet

Security Patterns

Role-Based Access Control (RBAC)

Implementera rollbaserad åtkomstkontroll för olika användarnivåer.

// Middleware för rollbaserad åtkomst
function authorize(roles = []) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Unauthorized' });
    }
    
    if (roles.length && !roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    
    next();
  };
}

// Använd i routes
app.get('/api/admin/users', 
  authenticate, 
  authorize(['admin']), 
  getUsers
);

app.get('/api/profile', 
  authenticate, 
  authorize(['user', 'admin']), 
  getProfile
);

app.delete('/api/posts/:id',
  authenticate,
  authorize(['admin', 'moderator']),
  deletePost
);

Two-Factor Authentication (2FA)

Implementera tvåfaktorsautentisering med TOTP.

const speakeasy = require('speakeasy');
const QRCode = require('qrcode');

// Aktivera 2FA
app.post('/api/2fa/enable', authenticate, async (req, res) => {
  const secret = speakeasy.generateSecret({
    name: `MyApp (${req.user.email})`
  });
  
  // Spara secret tillfälligt
  await redis.setex(
    `2fa_temp_${req.user.id}`,
    300, // 5 minuter
    JSON.stringify(secret)
  );
  
  // Generera QR-kod
  const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);
  
  res.json({
    secret: secret.base32,
    qrCode: qrCodeUrl
  });
});

// Verifiera och aktivera 2FA
app.post('/api/2fa/verify', authenticate, async (req, res) => {
  const { token } = req.body;
  
  // Hämta tillfällig secret
  const tempSecret = await redis.get(`2fa_temp_${req.user.id}`);
  if (!tempSecret) {
    return res.status(400).json({ error: 'No pending 2FA activation' });
  }
  
  const secret = JSON.parse(tempSecret);
  
  // Verifiera token
  const verified = speakeasy.totp.verify({
    secret: secret.base32,
    encoding: 'base32',
    token,
    window: 1
  });
  
  if (verified) {
    // Spara secret permanent
    await User.update(req.user.id, {
      twoFactorSecret: secret.base32,
      twoFactorEnabled: true
    });
    
    await redis.del(`2fa_temp_${req.user.id}`);
    res.json({ success: true });
  } else {
    res.status(400).json({ error: 'Invalid token' });
  }
});

Rate Limiting

Skydda API:et mot brute force-attacker med rate limiting.

const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');

// Global rate limiter
const apiLimiter = rateLimit({
  store: new RedisStore({
    client: redis,
    prefix: 'rl:api:'
  }),
  windowMs: 15 * 60 * 1000, // 15 minuter
  max: 100, // Max 100 requests per IP
  message: 'Too many requests from this IP'
});

// Striktare limiter för login
const loginLimiter = rateLimit({
  store: new RedisStore({
    client: redis,
    prefix: 'rl:login:'
  }),
  windowMs: 15 * 60 * 1000,
  max: 5, // Max 5 login-försök
  skipSuccessfulRequests: true, // Räkna bara misslyckade försök
  keyGenerator: (req) => {
    // Använd både IP och email för nyckeln
    return `${req.ip}:${req.body.email || ''}`;
  }
});

// Applicera limiters
app.use('/api/', apiLimiter);
app.post('/api/login', loginLimiter, loginHandler);

Best Practices

Använd HTTPS Överallt

All autentiseringsdata måste skickas över krypterade anslutningar

💡 Exempel: Tvinga HTTPS med middleware och sätt secure-flaggan på cookies

Implementera Token Rotation

Förnya tokens regelbundet för att minimera risken vid kompromiss

💡 Exempel: Använd refresh tokens med kort livstid för access tokens

Logga Säkerhetshändelser

Spara alla autentiseringsförsök och säkerhetsrelaterade händelser

💡 Exempel: Logga misslyckade inloggningar, lösenordsändringar och behörighetsfel

Implementera Account Lockout

Lås konton efter upprepade misslyckade inloggningsförsök

💡 Exempel: Lås efter 5 försök och kräv email-verifiering för att låsa upp

Vanliga Sårbarheter

⚠️ Session Fixation

Återanvänd inte session-ID efter inloggning

🔧 Lösning: Regenerera session-ID vid varje inloggning med req.session.regenerate()

⚠️ Password Reset Poisoning

Validera alltid host-header vid lösenordsåterställning

🔧 Lösning: Använd vit-lista för tillåtna domäner och validera redirect-URLs

⚠️ JWT Algorithm Confusion

Specificera alltid algoritm vid JWT-verifiering

🔧 Lösning: Använd { algorithms: ['HS256'] } i verify() för att undvika 'none' algoritm

⚠️ Timing Attacks

Använd konstant-tids jämförelser för känslig data

🔧 Lösning: Använd crypto.timingSafeEqual() för token- och lösenordsjämförelser

🔒Säkerhetstips för Autentisering

Aldrig spara lösenord i klartext

Använd alltid bcrypt eller liknande för att hasha lösenord med salt.

Implementera säkerhetshuvuden

Använd helmet.js för att sätta viktiga HTTP-säkerhetshuvuden automatiskt.

Regelbundna säkerhetsgranskningar

Kör npm audit och håll alla säkerhetsrelaterade paket uppdaterade.