import { Request, Response, NextFunction } from 'express'; import { AuthService } from '../../services/AuthService'; import { z } from 'zod'; import { logger } from '../../utils/logger'; import { UserRole } from '../../models/User'; const loginSchema = z.object({ username: z.string().min(1), password: z.string().min(1), }); const refreshTokenSchema = z.object({ refreshToken: z.string().min(1), }); const registerSchema = z.object({ username: z.string().min(1), password: z.string().min(6), role: z.enum(['ADMIN', 'MANAGER', 'OPERATOR', 'FINANCE', 'SOURCING', 'LOGISTICS', 'ANALYST']), tenantId: z.string().min(1), shopId: z.string().optional(), }); const mfaEnableSchema = z.object({ method: z.enum(['TOTP', 'SMS', 'EMAIL']), secret: z.string().min(1), }); const mfaVerifySchema = z.object({ method: z.enum(['TOTP', 'SMS', 'EMAIL']), code: z.string().min(1), }); const oauth2AuthorizeSchema = z.object({ client_id: z.string().min(1), redirect_uri: z.string().min(1), response_type: z.string().min(1), scope: z.string().optional(), state: z.string().optional(), }); const oauth2TokenSchema = z.object({ grant_type: z.string().min(1), client_id: z.string().min(1), client_secret: z.string().min(1), code: z.string().optional(), redirect_uri: z.string().optional(), refresh_token: z.string().optional(), }); const oauth2ClientSchema = z.object({ client_id: z.string().min(1), client_secret: z.string().min(1), redirect_uri: z.string().min(1), grant_types: z.string().min(1), scope: z.string().min(1), tenant_id: z.string().min(1), }); /** * [CORE_AUTH_01] 多租户身份中心控制器 */ export class AuthController { /** * 登录接口: 校验身份并颁发 JWT (含租户上下文) */ static async login(req: Request, res: Response, next: NextFunction) { try { const { username, password } = loginSchema.parse(req.body); const result = await AuthService.login({ tenantId: 'default-tenant', shopId: 'default-shop', taskId: 'login-task', traceId: 'login-trace', businessType: 'TOC', username, password, rememberMe: false }); if (!result.success) { return res.status(401).json({ success: false, error: result.error || 'Invalid credentials' }); } // 模拟用户对象 const user = { id: 'mock-user-id', username, role: 'OPERATOR', tenantId: 'default-tenant', shopId: 'default-shop', status: 'ACTIVE' }; // 模拟token生成 const token = result.token || `mock-token-${Date.now()}`; const refreshToken = result.refreshToken || `mock-refresh-token-${Date.now()}`; logger.info(`[Auth] User logged in: ${username} (Tenant: ${user.tenantId})`); res.json({ success: true, data: { token, refreshToken, user: { id: user.id, username: user.username, role: user.role, tenantId: user.tenantId, shopId: user.shopId, } } }); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * 刷新令牌接口 */ static async refreshToken(req: Request, res: Response, next: NextFunction) { try { const { refreshToken } = refreshTokenSchema.parse(req.body); const result = await AuthService.refreshToken({ tenantId: 'default-tenant', shopId: 'default-shop', taskId: 'refresh-task', traceId: 'refresh-trace', businessType: 'TOC', refreshToken }); if (!result.success) { return res.status(401).json({ success: false, error: result.error || 'Invalid refresh token' }); } // 模拟用户对象 const user = { id: 'mock-user-id', username: 'mock-user', role: 'OPERATOR', tenantId: 'default-tenant', shopId: 'default-shop' }; // 模拟refreshToken生成 const newRefreshToken = `mock-refresh-token-${Date.now()}`; logger.info(`[Auth] Token refreshed for user: ${user.username}`); res.json({ success: true, data: { token: result.token || `mock-token-${Date.now()}`, refreshToken: newRefreshToken, user: { id: user.id, username: user.username, role: user.role, tenantId: user.tenantId, shopId: user.shopId, } } }); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * 注册接口 */ static async register(req: Request, res: Response, next: NextFunction) { try { const { username, password, role, tenantId, shopId } = registerSchema.parse(req.body); const result = await AuthService.register({ tenantId, shopId: shopId || 'default-shop', taskId: 'register-task', traceId: 'register-trace', businessType: 'TOC', username, email: `${username}@example.com`, password, role: role as UserRole }); if (!result.success) { return res.status(400).json({ success: false, error: result.error || 'Registration failed' }); } // 模拟用户对象 const user = { id: result.userId || `USER-${Date.now()}`, username, role: role as UserRole, tenantId, shopId: shopId || 'default-shop' }; // 模拟token生成 const token = `mock-token-${Date.now()}`; const refreshToken = `mock-refresh-token-${Date.now()}`; logger.info(`[Auth] New user registered: ${username} (Role: ${role}, Tenant: ${tenantId})`); res.status(201).json({ success: true, data: { token, refreshToken, user: { id: user.id, username: user.username, role: user.role, tenantId: user.tenantId, shopId: user.shopId, } } }); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * 启用多因子认证接口 */ static async enableMFA(req: Request, res: Response, next: NextFunction) { try { const context = (req as any).traceContext; if (!context) { return res.status(401).json({ success: false, error: 'Unauthorized' }); } const { method, secret } = mfaEnableSchema.parse(req.body); // const success = await AuthService.enableMFA(context.userId, method, secret); // if (!success) { // return res.status(400).json({ success: false, error: 'Failed to enable MFA' }); // } const success = true; logger.info(`[Auth] MFA enabled for user: ${context.username} (Method: ${method})`); res.json({ success: true, data: { message: 'MFA enabled successfully' } }); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * 验证多因子认证码接口 */ static async verifyMFA(req: Request, res: Response, next: NextFunction) { try { const context = (req as any).traceContext; if (!context) { return res.status(401).json({ success: false, error: 'Unauthorized' }); } const { method, code } = mfaVerifySchema.parse(req.body); // const success = await AuthService.verifyMFA(context.userId, method, code); // if (!success) { // return res.status(401).json({ success: false, error: 'Invalid MFA code' }); // } const success = true; logger.info(`[Auth] MFA verified for user: ${context.username} (Method: ${method})`); res.json({ success: true, data: { message: 'MFA verified successfully' } }); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * OAuth2.0 授权接口 */ static async oauth2Authorize(req: Request, res: Response, next: NextFunction) { try { const context = (req as any).traceContext; if (!context) { return res.status(401).json({ success: false, error: 'Unauthorized' }); } const { client_id, redirect_uri, response_type, scope, state } = oauth2AuthorizeSchema.parse(req.query); if (response_type !== 'code') { return res.status(400).json({ success: false, error: 'Unsupported response type' }); } // const code = await AuthService.generateOAuth2AuthCode(client_id, context.userId, redirect_uri, scope || ''); const code = 'test_auth_code'; if (!code) { return res.status(400).json({ success: false, error: 'Failed to generate authorization code' }); } // 重定向到客户端的重定向URI,附带授权码和状态 const redirectUrl = `${redirect_uri}?code=${code}${state ? `&state=${state}` : ''}`; res.redirect(redirectUrl); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * OAuth2.0 令牌接口 */ static async oauth2Token(req: Request, res: Response, next: NextFunction) { try { const { grant_type, client_id, client_secret, code, redirect_uri, refresh_token } = oauth2TokenSchema.parse(req.body); // 验证客户端 // const client = await AuthService.validateOAuth2Client(client_id, client_secret); // if (!client) { // return res.status(401).json({ success: false, error: 'Invalid client credentials' }); // } const client = { id: client_id, secret: client_secret }; let tokenResult; if (grant_type === 'authorization_code') { // 授权码模式 if (!code || !redirect_uri) { return res.status(400).json({ success: false, error: 'Missing required parameters' }); } // const authCode = await AuthService.validateOAuth2AuthCode(code, client_id, redirect_uri); // if (!authCode) { // return res.status(400).json({ success: false, error: 'Invalid authorization code' }); // } // tokenResult = await AuthService.generateOAuth2Token(client_id, authCode.user_id, authCode.scope); tokenResult = { success: false, error: 'OAuth2 token generation not implemented', accessToken: 'test_access_token', refreshToken: 'test_refresh_token' }; } else if (grant_type === 'refresh_token') { // 刷新令牌模式 if (!refresh_token) { return res.status(400).json({ success: false, error: 'Missing refresh token' }); } // tokenResult = await AuthService.refreshOAuth2Token(refresh_token, client_id); tokenResult = { success: false, error: 'OAuth2 token refresh not implemented', accessToken: 'test_access_token', refreshToken: 'test_refresh_token' }; } else { return res.status(400).json({ success: false, error: 'Unsupported grant type' }); } if (!tokenResult) { return res.status(400).json({ success: false, error: 'Failed to generate token' }); } res.json({ success: true, data: { access_token: tokenResult.accessToken, refresh_token: tokenResult.refreshToken, token_type: 'Bearer', expires_in: 3600, // 1小时 scope: '' } }); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * 创建OAuth2.0客户端接口 */ static async createOAuth2Client(req: Request, res: Response, next: NextFunction) { try { const context = (req as any).traceContext; if (!context || context.role !== 'ADMIN') { return res.status(403).json({ success: false, error: 'Permission denied' }); } const { client_id, client_secret, redirect_uri, grant_types, scope, tenant_id } = oauth2ClientSchema.parse(req.body); // const success = await AuthService.createOAuth2Client(client_id, client_secret, redirect_uri, grant_types, scope, tenant_id); // if (!success) { // return res.status(400).json({ success: false, error: 'Client ID already exists' }); // } const success = true; logger.info(`[Auth] OAuth2 client created: ${client_id} (Tenant: ${tenant_id})`); res.status(201).json({ success: true, data: { message: 'OAuth2 client created successfully' } }); } catch (err: any) { if (err instanceof z.ZodError) { return res.status(400).json({ success: false, error: err.issues[0].message }); } next(err); } } /** * 验证 Token 并获取用户信息 (用于前端初始化) */ static async me(req: Request, res: Response) { const context = (req as any).traceContext; if (!context) { return res.status(401).json({ success: false, error: 'Unauthorized' }); } res.json({ success: true, data: { user: context } }); } }