Authentication Guide
This guide covers authentication methods, security best practices, and implementation details for Trae IDE's authentication system.
Overview
Trae IDE supports multiple authentication methods to ensure secure access to your development environment:
- Local Authentication: Username/password with local storage
- OAuth 2.0: Integration with popular providers (GitHub, Google, Microsoft)
- SAML 2.0: Enterprise single sign-on (SSO)
- LDAP/Active Directory: Enterprise directory integration
- Multi-Factor Authentication (MFA): Enhanced security with 2FA/TOTP
- API Keys: Programmatic access for automation
- JWT Tokens: Stateless authentication for distributed systems
Quick Start
Basic Setup
javascript
// Initialize authentication
const auth = new TraeAuth({
provider: 'oauth2',
clientId: 'your-client-id',
redirectUri: 'http://localhost:3000/callback',
scopes: ['read:user', 'repo']
});
// Login
auth.login().then(user => {
console.log('Authenticated user:', user);
}).catch(error => {
console.error('Authentication failed:', error);
});Environment Configuration
bash
# .env file
TRAE_AUTH_PROVIDER=oauth2
TRAE_AUTH_CLIENT_ID=your-client-id
TRAE_AUTH_CLIENT_SECRET=your-client-secret
TRAE_AUTH_REDIRECT_URI=http://localhost:3000/callback
TRAE_JWT_SECRET=your-jwt-secret
TRAE_SESSION_TIMEOUT=3600Authentication Methods
OAuth 2.0 Integration
GitHub OAuth
javascript
// GitHub OAuth configuration
const githubAuth = {
provider: 'github',
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
scope: 'user:email repo',
endpoints: {
authorization: 'https://github.com/login/oauth/authorize',
token: 'https://github.com/login/oauth/access_token',
userInfo: 'https://api.github.com/user'
}
};
// Implementation
class GitHubAuthProvider {
async authenticate(code) {
// Exchange code for access token
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
client_id: this.clientId,
client_secret: this.clientSecret,
code: code
})
});
const { access_token } = await tokenResponse.json();
// Get user information
const userResponse = await fetch('https://api.github.com/user', {
headers: {
'Authorization': `token ${access_token}`,
'User-Agent': 'Trae-IDE'
}
});
const user = await userResponse.json();
return {
id: user.id,
username: user.login,
email: user.email,
name: user.name,
avatar: user.avatar_url,
provider: 'github',
accessToken: access_token
};
}
}Google OAuth
javascript
// Google OAuth configuration
const googleAuth = {
provider: 'google',
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
scope: 'openid email profile',
endpoints: {
authorization: 'https://accounts.google.com/o/oauth2/v2/auth',
token: 'https://oauth2.googleapis.com/token',
userInfo: 'https://www.googleapis.com/oauth2/v2/userinfo'
}
};
// Google Auth implementation
class GoogleAuthProvider {
async authenticate(code) {
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: this.clientId,
client_secret: this.clientSecret,
code: code,
grant_type: 'authorization_code',
redirect_uri: this.redirectUri
})
});
const { access_token, id_token } = await tokenResponse.json();
// Verify and decode ID token
const userInfo = this.decodeJWT(id_token);
return {
id: userInfo.sub,
username: userInfo.email,
email: userInfo.email,
name: userInfo.name,
avatar: userInfo.picture,
provider: 'google',
accessToken: access_token
};
}
decodeJWT(token) {
const [header, payload, signature] = token.split('.');
return JSON.parse(atob(payload));
}
}Microsoft OAuth
javascript
// Microsoft OAuth configuration
const microsoftAuth = {
provider: 'microsoft',
clientId: process.env.MICROSOFT_CLIENT_ID,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
tenant: process.env.MICROSOFT_TENANT || 'common',
scope: 'openid email profile User.Read',
endpoints: {
authorization: `https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize`,
token: `https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token`,
userInfo: 'https://graph.microsoft.com/v1.0/me'
}
};
// Microsoft Auth implementation
class MicrosoftAuthProvider {
async authenticate(code) {
const tokenResponse = await fetch(
`https://login.microsoftonline.com/${this.tenant}/oauth2/v2.0/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: this.clientId,
client_secret: this.clientSecret,
code: code,
grant_type: 'authorization_code',
redirect_uri: this.redirectUri,
scope: this.scope
})
}
);
const { access_token } = await tokenResponse.json();
// Get user profile
const userResponse = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: {
'Authorization': `Bearer ${access_token}`
}
});
const user = await userResponse.json();
return {
id: user.id,
username: user.userPrincipalName,
email: user.mail || user.userPrincipalName,
name: user.displayName,
provider: 'microsoft',
accessToken: access_token
};
}
}SAML 2.0 Integration
SAML Configuration
javascript
// SAML 2.0 configuration
const samlConfig = {
entryPoint: 'https://your-idp.com/sso/saml',
issuer: 'trae-ide',
callbackUrl: 'https://your-app.com/auth/saml/callback',
cert: process.env.SAML_CERT, // IdP certificate
privateCert: process.env.SAML_PRIVATE_CERT, // SP private key
identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
signatureAlgorithm: 'sha256',
digestAlgorithm: 'sha256'
};
// SAML implementation
class SAMLAuthProvider {
constructor(config) {
this.config = config;
this.saml = new SAML(config);
}
async getLoginUrl() {
return new Promise((resolve, reject) => {
this.saml.getAuthorizeUrl({}, (err, loginUrl) => {
if (err) reject(err);
else resolve(loginUrl);
});
});
}
async validateResponse(samlResponse) {
return new Promise((resolve, reject) => {
this.saml.validatePostResponse({
SAMLResponse: samlResponse
}, (err, profile) => {
if (err) reject(err);
else {
resolve({
id: profile.nameID,
username: profile.nameID,
email: profile.email || profile.nameID,
name: profile.displayName || profile.name,
attributes: profile.attributes,
provider: 'saml'
});
}
});
});
}
}SAML Metadata
xml
<!-- Service Provider Metadata -->
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
entityID="trae-ide">
<md:SPSSODescriptor AuthnRequestsSigned="true"
WantAssertionsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate><!-- Your certificate --></ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://your-app.com/auth/saml/callback"
index="1" />
</md:SPSSODescriptor>
</md:EntityDescriptor>LDAP/Active Directory Integration
LDAP Configuration
javascript
// LDAP configuration
const ldapConfig = {
url: 'ldap://your-ldap-server:389',
bindDN: 'cn=admin,dc=company,dc=com',
bindCredentials: process.env.LDAP_PASSWORD,
searchBase: 'ou=users,dc=company,dc=com',
searchFilter: '(uid={{username}})',
searchAttributes: ['uid', 'cn', 'mail', 'memberOf'],
tlsOptions: {
rejectUnauthorized: false
}
};
// LDAP implementation
class LDAPAuthProvider {
constructor(config) {
this.config = config;
this.client = ldap.createClient({
url: config.url,
tlsOptions: config.tlsOptions
});
}
async authenticate(username, password) {
try {
// Bind with admin credentials
await this.bind(this.config.bindDN, this.config.bindCredentials);
// Search for user
const user = await this.searchUser(username);
if (!user) {
throw new Error('User not found');
}
// Authenticate user
await this.bind(user.dn, password);
return {
id: user.uid,
username: user.uid,
email: user.mail,
name: user.cn,
groups: user.memberOf || [],
provider: 'ldap'
};
} catch (error) {
throw new Error(`LDAP authentication failed: ${error.message}`);
}
}
async bind(dn, password) {
return new Promise((resolve, reject) => {
this.client.bind(dn, password, (err) => {
if (err) reject(err);
else resolve();
});
});
}
async searchUser(username) {
const searchFilter = this.config.searchFilter.replace('{{username}}', username);
return new Promise((resolve, reject) => {
this.client.search(this.config.searchBase, {
filter: searchFilter,
attributes: this.config.searchAttributes,
scope: 'sub'
}, (err, res) => {
if (err) {
reject(err);
return;
}
let user = null;
res.on('searchEntry', (entry) => {
user = {
dn: entry.dn.toString(),
...entry.object
};
});
res.on('end', () => {
resolve(user);
});
res.on('error', (err) => {
reject(err);
});
});
});
}
}Multi-Factor Authentication (MFA)
TOTP Implementation
javascript
// TOTP (Time-based One-Time Password) implementation
class TOTPProvider {
constructor() {
this.speakeasy = require('speakeasy');
this.qrcode = require('qrcode');
}
generateSecret(username) {
const secret = this.speakeasy.generateSecret({
name: `Trae IDE (${username})`,
issuer: 'Trae IDE',
length: 32
});
return {
secret: secret.base32,
qrCodeUrl: secret.otpauth_url,
backupCodes: this.generateBackupCodes()
};
}
async generateQRCode(secret) {
return await this.qrcode.toDataURL(secret.qrCodeUrl);
}
verifyToken(secret, token) {
return this.speakeasy.totp.verify({
secret: secret,
encoding: 'base32',
token: token,
window: 2 // Allow 2 time steps (60 seconds) of drift
});
}
generateBackupCodes() {
const codes = [];
for (let i = 0; i < 10; i++) {
codes.push(Math.random().toString(36).substring(2, 10).toUpperCase());
}
return codes;
}
}
// MFA workflow
class MFAManager {
async enableMFA(userId) {
const totp = new TOTPProvider();
const secret = totp.generateSecret(userId);
// Store secret temporarily (user needs to verify)
await this.storeTempSecret(userId, secret);
return {
secret: secret.secret,
qrCode: await totp.generateQRCode(secret),
backupCodes: secret.backupCodes
};
}
async verifyAndActivateMFA(userId, token) {
const tempSecret = await this.getTempSecret(userId);
const totp = new TOTPProvider();
if (totp.verifyToken(tempSecret.secret, token)) {
// Move from temp to permanent storage
await this.activateMFA(userId, tempSecret);
await this.deleteTempSecret(userId);
return true;
}
return false;
}
async verifyMFA(userId, token) {
const user = await this.getUser(userId);
if (!user.mfaEnabled) {
return true; // MFA not required
}
const totp = new TOTPProvider();
// Check TOTP token
if (totp.verifyToken(user.mfaSecret, token)) {
return true;
}
// Check backup codes
if (user.backupCodes.includes(token)) {
await this.useBackupCode(userId, token);
return true;
}
return false;
}
}SMS/Email MFA
javascript
// SMS MFA implementation
class SMSMFAProvider {
constructor(twilioConfig) {
this.twilio = require('twilio')(twilioConfig.accountSid, twilioConfig.authToken);
this.fromNumber = twilioConfig.fromNumber;
}
async sendCode(phoneNumber) {
const code = Math.floor(100000 + Math.random() * 900000).toString();
await this.twilio.messages.create({
body: `Your Trae IDE verification code is: ${code}`,
from: this.fromNumber,
to: phoneNumber
});
// Store code with expiration (5 minutes)
await this.storeCode(phoneNumber, code, 300);
return true;
}
async verifyCode(phoneNumber, code) {
const storedCode = await this.getStoredCode(phoneNumber);
if (storedCode && storedCode === code) {
await this.deleteStoredCode(phoneNumber);
return true;
}
return false;
}
}
// Email MFA implementation
class EmailMFAProvider {
constructor(emailConfig) {
this.nodemailer = require('nodemailer');
this.transporter = this.nodemailer.createTransporter(emailConfig);
}
async sendCode(email) {
const code = Math.floor(100000 + Math.random() * 900000).toString();
await this.transporter.sendMail({
from: 'noreply@trae-ide.com',
to: email,
subject: 'Trae IDE Verification Code',
html: `
<h2>Verification Code</h2>
<p>Your verification code is: <strong>${code}</strong></p>
<p>This code will expire in 5 minutes.</p>
`
});
// Store code with expiration
await this.storeCode(email, code, 300);
return true;
}
async verifyCode(email, code) {
const storedCode = await this.getStoredCode(email);
if (storedCode && storedCode === code) {
await this.deleteStoredCode(email);
return true;
}
return false;
}
}API Key Authentication
API Key Management
javascript
// API Key implementation
class APIKeyManager {
async generateAPIKey(userId, name, permissions = []) {
const key = this.generateSecureKey();
const hashedKey = await this.hashKey(key);
const apiKey = {
id: this.generateId(),
userId: userId,
name: name,
keyHash: hashedKey,
permissions: permissions,
createdAt: new Date(),
lastUsed: null,
isActive: true
};
await this.storeAPIKey(apiKey);
// Return the plain key only once
return {
id: apiKey.id,
key: key,
name: name,
permissions: permissions
};
}
async validateAPIKey(key) {
const hashedKey = await this.hashKey(key);
const apiKey = await this.getAPIKeyByHash(hashedKey);
if (!apiKey || !apiKey.isActive) {
return null;
}
// Update last used timestamp
await this.updateLastUsed(apiKey.id);
return {
id: apiKey.id,
userId: apiKey.userId,
permissions: apiKey.permissions
};
}
generateSecureKey() {
const crypto = require('crypto');
return 'trae_' + crypto.randomBytes(32).toString('hex');
}
async hashKey(key) {
const crypto = require('crypto');
return crypto.createHash('sha256').update(key).digest('hex');
}
async revokeAPIKey(keyId) {
await this.updateAPIKey(keyId, { isActive: false });
}
async listAPIKeys(userId) {
const keys = await this.getAPIKeysByUser(userId);
return keys.map(key => ({
id: key.id,
name: key.name,
permissions: key.permissions,
createdAt: key.createdAt,
lastUsed: key.lastUsed,
isActive: key.isActive
}));
}
}
// API Key middleware
function apiKeyAuth(requiredPermissions = []) {
return async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'API key required' });
}
const apiKey = authHeader.substring(7);
const keyManager = new APIKeyManager();
try {
const validatedKey = await keyManager.validateAPIKey(apiKey);
if (!validatedKey) {
return res.status(401).json({ error: 'Invalid API key' });
}
// Check permissions
if (requiredPermissions.length > 0) {
const hasPermission = requiredPermissions.every(permission =>
validatedKey.permissions.includes(permission)
);
if (!hasPermission) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
}
req.user = { id: validatedKey.userId };
req.apiKey = validatedKey;
next();
} catch (error) {
return res.status(500).json({ error: 'Authentication error' });
}
};
}JWT Token Authentication
JWT Implementation
javascript
// JWT token manager
class JWTManager {
constructor(secret, options = {}) {
this.jwt = require('jsonwebtoken');
this.secret = secret;
this.options = {
expiresIn: options.expiresIn || '1h',
issuer: options.issuer || 'trae-ide',
audience: options.audience || 'trae-users'
};
}
generateToken(payload) {
return this.jwt.sign(payload, this.secret, this.options);
}
generateRefreshToken(userId) {
return this.jwt.sign(
{ userId, type: 'refresh' },
this.secret,
{ expiresIn: '7d' }
);
}
verifyToken(token) {
try {
return this.jwt.verify(token, this.secret);
} catch (error) {
throw new Error(`Invalid token: ${error.message}`);
}
}
refreshAccessToken(refreshToken) {
try {
const decoded = this.jwt.verify(refreshToken, this.secret);
if (decoded.type !== 'refresh') {
throw new Error('Invalid refresh token');
}
// Generate new access token
return this.generateToken({
userId: decoded.userId,
type: 'access'
});
} catch (error) {
throw new Error(`Token refresh failed: ${error.message}`);
}
}
decodeToken(token) {
return this.jwt.decode(token);
}
}
// JWT middleware
function jwtAuth(options = {}) {
return (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Access token required' });
}
const token = authHeader.substring(7);
const jwtManager = new JWTManager(process.env.JWT_SECRET);
try {
const decoded = jwtManager.verifyToken(token);
if (decoded.type !== 'access') {
return res.status(401).json({ error: 'Invalid token type' });
}
req.user = { id: decoded.userId };
req.token = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
};
}Session Management
Session Configuration
javascript
// Session management
class SessionManager {
constructor(options = {}) {
this.redis = require('redis').createClient(options.redis);
this.sessionTimeout = options.timeout || 3600; // 1 hour
this.maxSessions = options.maxSessions || 5;
}
async createSession(userId, metadata = {}) {
const sessionId = this.generateSessionId();
const session = {
id: sessionId,
userId: userId,
createdAt: new Date(),
lastActivity: new Date(),
ipAddress: metadata.ipAddress,
userAgent: metadata.userAgent,
isActive: true
};
// Store session
await this.redis.setex(
`session:${sessionId}`,
this.sessionTimeout,
JSON.stringify(session)
);
// Manage session limit
await this.enforceSessionLimit(userId);
return sessionId;
}
async getSession(sessionId) {
const sessionData = await this.redis.get(`session:${sessionId}`);
if (!sessionData) {
return null;
}
const session = JSON.parse(sessionData);
// Update last activity
session.lastActivity = new Date();
await this.redis.setex(
`session:${sessionId}`,
this.sessionTimeout,
JSON.stringify(session)
);
return session;
}
async destroySession(sessionId) {
await this.redis.del(`session:${sessionId}`);
}
async destroyAllUserSessions(userId) {
const sessions = await this.getUserSessions(userId);
for (const session of sessions) {
await this.destroySession(session.id);
}
}
async enforceSessionLimit(userId) {
const sessions = await this.getUserSessions(userId);
if (sessions.length >= this.maxSessions) {
// Remove oldest sessions
const sessionsToRemove = sessions
.sort((a, b) => new Date(a.lastActivity) - new Date(b.lastActivity))
.slice(0, sessions.length - this.maxSessions + 1);
for (const session of sessionsToRemove) {
await this.destroySession(session.id);
}
}
}
generateSessionId() {
const crypto = require('crypto');
return crypto.randomBytes(32).toString('hex');
}
}Session Security
javascript
// Session security measures
class SessionSecurity {
constructor(sessionManager) {
this.sessionManager = sessionManager;
this.suspiciousActivityThreshold = 5;
this.geoLocationService = new GeoLocationService();
}
async validateSession(sessionId, request) {
const session = await this.sessionManager.getSession(sessionId);
if (!session) {
throw new Error('Session not found');
}
// Check for suspicious activity
await this.checkSuspiciousActivity(session, request);
// Validate IP address (optional)
if (process.env.STRICT_IP_VALIDATION === 'true') {
await this.validateIPAddress(session, request.ip);
}
// Check for concurrent sessions
await this.checkConcurrentSessions(session.userId);
return session;
}
async checkSuspiciousActivity(session, request) {
const activities = await this.getRecentActivities(session.userId);
// Check for rapid location changes
const currentLocation = await this.geoLocationService.getLocation(request.ip);
const lastLocation = activities.length > 0 ? activities[0].location : null;
if (lastLocation && this.isRapidLocationChange(lastLocation, currentLocation)) {
await this.flagSuspiciousActivity(session.userId, 'rapid_location_change');
}
// Check for unusual user agent
if (session.userAgent !== request.headers['user-agent']) {
await this.flagSuspiciousActivity(session.userId, 'user_agent_change');
}
}
async flagSuspiciousActivity(userId, type) {
const flags = await this.getSuspiciousFlags(userId);
flags.push({
type: type,
timestamp: new Date(),
resolved: false
});
await this.storeSuspiciousFlags(userId, flags);
// If too many flags, require re-authentication
if (flags.filter(f => !f.resolved).length >= this.suspiciousActivityThreshold) {
await this.sessionManager.destroyAllUserSessions(userId);
await this.notifySecurityTeam(userId, flags);
}
}
}Security Best Practices
Password Security
javascript
// Password hashing and validation
class PasswordManager {
constructor() {
this.bcrypt = require('bcrypt');
this.saltRounds = 12;
this.minLength = 8;
this.maxLength = 128;
}
async hashPassword(password) {
this.validatePassword(password);
return await this.bcrypt.hash(password, this.saltRounds);
}
async verifyPassword(password, hash) {
return await this.bcrypt.compare(password, hash);
}
validatePassword(password) {
if (password.length < this.minLength) {
throw new Error(`Password must be at least ${this.minLength} characters`);
}
if (password.length > this.maxLength) {
throw new Error(`Password must be no more than ${this.maxLength} characters`);
}
// Check for common patterns
if (this.isCommonPassword(password)) {
throw new Error('Password is too common');
}
// Strength requirements
const strength = this.calculatePasswordStrength(password);
if (strength < 3) {
throw new Error('Password is too weak');
}
}
calculatePasswordStrength(password) {
let score = 0;
// Length bonus
if (password.length >= 12) score++;
if (password.length >= 16) score++;
// Character variety
if (/[a-z]/.test(password)) score++;
if (/[A-Z]/.test(password)) score++;
if (/[0-9]/.test(password)) score++;
if (/[^A-Za-z0-9]/.test(password)) score++;
// Patterns (negative)
if (/(..).*\1/.test(password)) score--; // Repeated patterns
if (/^\d+$/.test(password)) score--; // Only numbers
if (/^[a-zA-Z]+$/.test(password)) score--; // Only letters
return Math.max(0, score);
}
isCommonPassword(password) {
const commonPasswords = [
'password', '123456', 'password123', 'admin', 'qwerty',
'letmein', 'welcome', 'monkey', '1234567890'
];
return commonPasswords.includes(password.toLowerCase());
}
}Rate Limiting
javascript
// Rate limiting for authentication endpoints
class RateLimiter {
constructor(redis) {
this.redis = redis;
this.limits = {
login: { requests: 5, window: 900 }, // 5 attempts per 15 minutes
register: { requests: 3, window: 3600 }, // 3 attempts per hour
passwordReset: { requests: 3, window: 3600 }, // 3 attempts per hour
mfaVerify: { requests: 10, window: 300 } // 10 attempts per 5 minutes
};
}
async checkLimit(identifier, action) {
const limit = this.limits[action];
if (!limit) return true;
const key = `ratelimit:${action}:${identifier}`;
const current = await this.redis.get(key);
if (current && parseInt(current) >= limit.requests) {
const ttl = await this.redis.ttl(key);
throw new Error(`Rate limit exceeded. Try again in ${ttl} seconds.`);
}
// Increment counter
const multi = this.redis.multi();
multi.incr(key);
multi.expire(key, limit.window);
await multi.exec();
return true;
}
async resetLimit(identifier, action) {
const key = `ratelimit:${action}:${identifier}`;
await this.redis.del(key);
}
}
// Rate limiting middleware
function rateLimitMiddleware(action) {
return async (req, res, next) => {
const rateLimiter = new RateLimiter(req.app.locals.redis);
const identifier = req.ip; // or req.user?.id for authenticated requests
try {
await rateLimiter.checkLimit(identifier, action);
next();
} catch (error) {
res.status(429).json({ error: error.message });
}
};
}CSRF Protection
javascript
// CSRF token management
class CSRFProtection {
constructor() {
this.crypto = require('crypto');
}
generateToken(sessionId) {
const token = this.crypto.randomBytes(32).toString('hex');
const timestamp = Date.now();
const signature = this.crypto
.createHmac('sha256', process.env.CSRF_SECRET)
.update(`${token}:${sessionId}:${timestamp}`)
.digest('hex');
return `${token}:${timestamp}:${signature}`;
}
validateToken(token, sessionId) {
if (!token) return false;
const [tokenValue, timestamp, signature] = token.split(':');
if (!tokenValue || !timestamp || !signature) {
return false;
}
// Check if token is expired (1 hour)
if (Date.now() - parseInt(timestamp) > 3600000) {
return false;
}
// Verify signature
const expectedSignature = this.crypto
.createHmac('sha256', process.env.CSRF_SECRET)
.update(`${tokenValue}:${sessionId}:${timestamp}`)
.digest('hex');
return signature === expectedSignature;
}
}
// CSRF middleware
function csrfProtection(req, res, next) {
if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
return next();
}
const csrf = new CSRFProtection();
const token = req.headers['x-csrf-token'] || req.body._csrf;
const sessionId = req.session?.id;
if (!csrf.validateToken(token, sessionId)) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
next();
}Troubleshooting
Common Issues
OAuth Callback Errors
javascript
// OAuth error handling
class OAuthErrorHandler {
static handleCallback(error, req, res) {
switch (error.code) {
case 'access_denied':
return res.redirect('/login?error=access_denied');
case 'invalid_request':
console.error('OAuth invalid request:', error);
return res.redirect('/login?error=configuration_error');
case 'server_error':
console.error('OAuth server error:', error);
return res.redirect('/login?error=server_error');
default:
console.error('Unknown OAuth error:', error);
return res.redirect('/login?error=unknown_error');
}
}
static validateState(receivedState, sessionState) {
if (!receivedState || !sessionState) {
throw new Error('Missing state parameter');
}
if (receivedState !== sessionState) {
throw new Error('Invalid state parameter - possible CSRF attack');
}
}
}Session Issues
javascript
// Session debugging
class SessionDebugger {
static async diagnoseSession(sessionId) {
const sessionManager = new SessionManager();
const session = await sessionManager.getSession(sessionId);
if (!session) {
return {
status: 'not_found',
message: 'Session does not exist or has expired'
};
}
const now = new Date();
const lastActivity = new Date(session.lastActivity);
const timeSinceActivity = now - lastActivity;
return {
status: 'found',
session: {
id: session.id,
userId: session.userId,
createdAt: session.createdAt,
lastActivity: session.lastActivity,
timeSinceActivity: `${Math.floor(timeSinceActivity / 1000)} seconds`,
isActive: session.isActive
}
};
}
static async listUserSessions(userId) {
const sessionManager = new SessionManager();
return await sessionManager.getUserSessions(userId);
}
}MFA Issues
javascript
// MFA troubleshooting
class MFATroubleshooter {
static async diagnoseMFA(userId) {
const user = await getUserById(userId);
if (!user.mfaEnabled) {
return {
status: 'disabled',
message: 'MFA is not enabled for this user'
};
}
const totp = new TOTPProvider();
const currentTime = Math.floor(Date.now() / 1000);
const timeStep = Math.floor(currentTime / 30);
return {
status: 'enabled',
mfaInfo: {
secretLength: user.mfaSecret.length,
currentTimeStep: timeStep,
backupCodesRemaining: user.backupCodes.length,
lastUsed: user.mfaLastUsed
},
troubleshooting: {
timeSync: 'Ensure device time is synchronized',
appRecommendation: 'Use Google Authenticator or Authy',
backupCodes: 'Use backup codes if TOTP fails'
}
};
}
static generateTestToken(secret) {
const totp = new TOTPProvider();
return totp.speakeasy.totp({
secret: secret,
encoding: 'base32'
});
}
}Logging and Monitoring
javascript
// Authentication logging
class AuthLogger {
constructor(logger) {
this.logger = logger;
}
logLoginAttempt(userId, success, metadata = {}) {
this.logger.info('Login attempt', {
userId,
success,
ipAddress: metadata.ipAddress,
userAgent: metadata.userAgent,
provider: metadata.provider,
timestamp: new Date().toISOString()
});
}
logMFAAttempt(userId, success, method) {
this.logger.info('MFA attempt', {
userId,
success,
method,
timestamp: new Date().toISOString()
});
}
logSuspiciousActivity(userId, activity, metadata = {}) {
this.logger.warn('Suspicious activity detected', {
userId,
activity,
metadata,
timestamp: new Date().toISOString()
});
}
logPasswordChange(userId, success) {
this.logger.info('Password change', {
userId,
success,
timestamp: new Date().toISOString()
});
}
logAPIKeyUsage(keyId, userId, endpoint) {
this.logger.info('API key usage', {
keyId,
userId,
endpoint,
timestamp: new Date().toISOString()
});
}
}This authentication guide provides comprehensive coverage of authentication methods, security best practices, and troubleshooting techniques for Trae IDE. Regular security audits and updates ensure the authentication system remains secure against evolving threats.