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
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.