Session Management
Atlantic ID entegrasyonunda session yönetimi best practices.
Session Storage
Backend Session
Önerilen yaklaşım: Token'ları backend session'da saklayın.
// Express.js
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // XSS koruması
sameSite: 'lax', // CSRF koruması
maxAge: 86400000 // 24 saat
},
store: new RedisStore({
client: redisClient,
prefix: 'sess:'
})
}));
// Token'ları session'a kaydet
req.session.accessToken = tokens.access_token;
req.session.refreshToken = tokens.refresh_token;
req.session.user = userInfo;
Cookie-Based
// httpOnly cookie ile token gönder
res.cookie('access_token', tokens.access_token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 900000 // 15 dakika
});
Session Lifecycle
1. Session Creation
app.get('/auth/callback', async (req, res) => {
// Token exchange
const tokens = await exchangeCode(req.query.code);
const userInfo = await getUserInfo(tokens.access_token);
// Session oluştur
req.session.regenerate((err) => {
if (err) return res.status(500).send('Session error');
req.session.userId = userInfo.sub;
req.session.accessToken = tokens.access_token;
req.session.refreshToken = tokens.refresh_token;
req.session.expiresAt = Date.now() + (tokens.expires_in * 1000);
req.session.save((err) => {
if (err) return res.status(500).send('Session save error');
res.redirect('/dashboard');
});
});
});
2. Session Validation
function requireAuth(req, res, next) {
if (!req.session.userId) {
return res.redirect('/login');
}
// Token expired kontrolü
if (Date.now() >= req.session.expiresAt) {
return refreshSession(req, res, next);
}
next();
}
async function refreshSession(req, res, next) {
try {
const tokens = await refreshAccessToken(req.session.refreshToken);
req.session.accessToken = tokens.access_token;
req.session.refreshToken = tokens.refresh_token;
req.session.expiresAt = Date.now() + (tokens.expires_in * 1000);
await req.session.save();
next();
} catch (error) {
req.session.destroy();
res.redirect('/login');
}
}
3. Session Refresh
async function refreshAccessToken(refreshToken) {
const response = await fetch('https://id.codeatlantis.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET
})
});
if (!response.ok) {
throw new Error('Refresh failed');
}
return await response.json();
}
4. Session Destruction
app.post('/logout', (req, res) => {
const refreshToken = req.session.refreshToken;
// Token'ı revoke et
revokeToken(refreshToken).catch(console.error);
// Session'ı yok et
req.session.destroy((err) => {
if (err) console.error('Session destroy error:', err);
res.clearCookie('connect.sid');
res.redirect('/');
});
});
Session Security
Session Fixation Prevention
// Login sonrası session ID değiştir
req.session.regenerate((err) => {
req.session.userId = user.id;
});
Session Hijacking Prevention
// IP ve User-Agent kontrolü
req.session.ip = req.ip;
req.session.userAgent = req.headers['user-agent'];
function validateSession(req) {
if (req.session.ip !== req.ip) {
throw new Error('IP mismatch');
}
if (req.session.userAgent !== req.headers['user-agent']) {
throw new Error('User-Agent mismatch');
}
}
Inactivity Timeout
const INACTIVITY_TIMEOUT = 30 * 60 * 1000; // 30 dakika
function checkInactivity(req, res, next) {
const lastActivity = req.session.lastActivity;
if (lastActivity && Date.now() - lastActivity > INACTIVITY_TIMEOUT) {
req.session.destroy();
return res.redirect('/login?reason=timeout');
}
req.session.lastActivity = Date.now();
next();
}
Multi-Tab Support
Aynı kullanıcının birden fazla tab'de oturum açması:
// Frontend: BroadcastChannel ile tab'ler arası iletişim
const authChannel = new BroadcastChannel('auth_channel');
authChannel.onmessage = (event) => {
if (event.data === 'logout') {
window.location.href = '/login';
}
};
// Logout olunca diğer tab'leri bilgilendir
function logout() {
authChannel.postMessage('logout');
// ... logout logic
}
Session Storage Options
1. In-Memory (Development Only)
app.use(session({
secret: 'dev-secret',
resave: false,
saveUninitialized: false
}));
❌ Production'da kullanmayın - restart'ta kaybolur
2. Redis (Önerilen)
const RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
}));
✅ Hızlı, scalable, distributed systems için ideal
3. Database
const MySQLStore = require('express-mysql-session')(session);
app.use(session({
store: new MySQLStore(dbConfig),
secret: process.env.SESSION_SECRET
}));
✅ Persistent, backup kolay
Best Practices
- Regenerate on login - Session fixation koruması
- Use secure cookies - httpOnly, secure, sameSite
- Implement timeout - Inactivity ve absolute timeout
- Store minimal data - Sadece gerekli bilgiler
- Use Redis/DB - In-memory production'da yok
- Monitor sessions - Aktif session sayısı, ortalama süre
İlgili: Security, Token Refresh, Logout