Skip to content

認証ガイド

このガイドでは、Trae IDEの認証システムの認証方法、セキュリティベストプラクティス、実装詳細について説明します。

概要

Trae IDEは、開発環境への安全なアクセスを確保するために複数の認証方法をサポートしています:

  • ローカル認証: ローカルストレージを使用したユーザー名/パスワード
  • OAuth 2.0: 人気プロバイダー(GitHub、Google、Microsoft)との統合
  • SAML 2.0: エンタープライズシングルサインオン(SSO)
  • LDAP/Active Directory: エンタープライズディレクトリ統合
  • 多要素認証(MFA): 2FA/TOTPによる強化されたセキュリティ
  • APIキー: 自動化のためのプログラマティックアクセス
  • JWTトークン: 分散システム用のステートレス認証

クイックスタート

基本セットアップ

javascript
// 認証を初期化
const auth = new TraeAuth({
    provider: 'oauth2',
    clientId: 'your-client-id',
    redirectUri: 'http://localhost:3000/callback',
    scopes: ['read:user', 'repo']
});

// ログイン
auth.login().then(user => {
    console.log('認証されたユーザー:', user);
}).catch(error => {
    console.error('認証に失敗しました:', error);
});

環境設定

bash
# .envファイル
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=3600

認証方法

OAuth 2.0 Integration

GitHub OAuth

javascript
// GitHub OAuth設定
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'
    }
};

// 実装
class GitHubAuthProvider {
    async authenticate(code) {
        // コードをアクセストークンに交換
        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();
        
        // ユーザー情報を取得
        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設定
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実装
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設定
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実装
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();
        
        // ユーザープロファイルを取得
        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設定
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証明書
    privateCert: process.env.SAML_PRIVATE_CERT, // SP秘密鍵
    identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
    signatureAlgorithm: 'sha256',
    digestAlgorithm: 'sha256'
};

// SAML実装
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
<!-- サービスプロバイダーメタデータ -->
<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><!-- あなたの証明書 --></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設定
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実装
class LDAPAuthProvider {
    constructor(config) {
        this.config = config;
        this.client = ldap.createClient({
            url: config.url,
            tlsOptions: config.tlsOptions
        });
    }
    
    async authenticate(username, password) {
        try {
            // 管理者認証情報でバインド
            await this.bind(this.config.bindDN, this.config.bindCredentials);
            
            // ユーザーを検索
            const user = await this.searchUser(username);
            if (!user) {
                throw new Error('User not found');
            }
            
            // ユーザーを認証
            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(時間ベースワンタイムパスワード)実装
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 // 2タイムステップ(60秒)のドリフトを許可
        });
    }
    
    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.

究極の AI 駆動 IDE 学習ガイド