From bceaebde98ab69fb85c3abe6a7c10ea4ab2318ad Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 Jul 2026 19:09:46 +0800 Subject: [PATCH] =?UTF-8?q?revert:=20=E5=9B=9E=E6=BB=9A=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=88=B0=E4=B8=8A=E5=8D=88=E7=89=88=E6=9C=AC?= =?UTF-8?q?(400ce3d)=20-=20=E6=81=A2=E5=A4=8D=E6=89=AB=E7=A0=81=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=92=8C=E8=B4=A6=E5=8F=B7=E5=AF=86=E7=A0=81+OTP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/agents.py | 8 +--- backend/app/api/auth_qrcode.py | 59 +++++++++++++++++----------- frontend-admin/src/api/mfa.ts | 60 ----------------------------- frontend-admin/src/views/Login.vue | 62 ------------------------------ 4 files changed, 37 insertions(+), 152 deletions(-) diff --git a/backend/app/api/agents.py b/backend/app/api/agents.py index bc1e52e..14d5e10 100644 --- a/backend/app/api/agents.py +++ b/backend/app/api/agents.py @@ -291,20 +291,14 @@ async def agent_login( # BUG-001 修复: 签发半认证 token,使前端可以调用 otp-bind / otp-verify # 这些端点需要 Bearer token(get_current_user 认证),否则流程完全阻断 from app.services.token_service import TokenService - from app.services.role_mapping_service import RoleMappingService from app.dependencies import get_redis redis_client = await get_redis() token_service = TokenService(redis_client) - # BUGFIX: 从 UserRole 表查询真实角色,而非硬编码 ["agent"] - role_service = RoleMappingService(db) - roles = await role_service.get_user_roles(agent.user_id) - if not roles: - roles = ["agent"] # 无角色时默认 fallback bind_token = await token_service.create_token( employee_id=agent.user_id, name=agent.name, - roles=roles, + roles=["agent"], avatar=avatar, login_source="agent_pending_otp", ) diff --git a/backend/app/api/auth_qrcode.py b/backend/app/api/auth_qrcode.py index 1c1813f..adc1c10 100644 --- a/backend/app/api/auth_qrcode.py +++ b/backend/app/api/auth_qrcode.py @@ -236,46 +236,59 @@ async def scan_qrcode( f"employee_id={result['employee_id']}, name={result['name']}" ) - # GET 请求(企微 OAuth 回调)→ 已自动确认,显示成功页 + 自动关闭 + # GET 请求(企微 OAuth 回调)→ 重定向到前端选择页 + # 因为企微 OAuth 流程不在这个端点完成最终登录,只标记 scanned, + # 等用户在坐席端点 confirm 后才能拿到 token。 + # 但企微 WebView 期望看到跳转后的页面,所以这里给个提示页。 from fastapi.responses import HTMLResponse if final_code is not None and final_ticket is not None and body is None: - user_name = result.get('name', '') + # GET 模式:渲染一个 "扫码成功" 的 HTML 提示页 + 引导用户到登录页 html = f""" -登录成功 - IT智能服务台 +扫码成功 - IT智能服务台
- - - + -

登录成功

-
已自动确认登录
-

你好,{user_name}
请返回电脑端查看

- +

扫码成功

+
+ +等待确认登录... +
+

请在电脑端的登录页面点击
「确认登录」 按钮完成登录

+
+
📋 操作指引
+
已在电脑上打开登录页面
+
点击页面上的「确认登录」按钮
+
登录成功后可关闭此页面
+
+
- """ return HTMLResponse(content=html, status_code=200) diff --git a/frontend-admin/src/api/mfa.ts b/frontend-admin/src/api/mfa.ts index ef49994..eeadebd 100644 --- a/frontend-admin/src/api/mfa.ts +++ b/frontend-admin/src/api/mfa.ts @@ -23,42 +23,11 @@ // ============================================================================= import apiClient from './index' -import type { AxiosResponse } from 'axios' // -------------------------------------------------------------------------- // TypeScript 类型定义 // -------------------------------------------------------------------------- -/** POST /auth/otp-bind 响应(用户绑定用) */ -export interface OtpBindData { - /** TOTP 共享密钥(base32) */ - secret: string - /** otpauth:// URI */ - otpauth_url: string - /** 二维码 PNG base64(不含 data: 前缀) */ - qr_code_base64: string -} - -/** POST /auth/otp-verify 请求体 */ -export interface OtpVerifyRequest { - /** 6 位 OTP 动态码 */ - otp_code: string -} - -/** POST /auth/otp-verify 响应 */ -export interface OtpVerifyData { - /** 验证是否通过 */ - verified: boolean - /** 登录 token(首次绑定成功后返回) */ - token?: string - /** 用户 ID */ - user_id?: string - /** 用户姓名 */ - name?: string - /** 用户角色 */ - role?: string -} - /** 单个用户的 OTP 状态条目 */ export interface MfaUserStatus { /** 员工 ID(企微 userid) */ @@ -137,32 +106,3 @@ export async function resetMfa(employeeId: string): Promise { ) return response } - -// -------------------------------------------------------------------------- -// 用户端 OTP 绑定/验证函数(供登录绑定面板使用) -// -------------------------------------------------------------------------- - -/** - * 绑定 OTP — 生成 secret + 二维码 - * 用户首次登录时调用,获取 TOTP 密钥和二维码 - * - * @returns OTP 绑定信息(secret + otpauth_url + base64 PNG) - */ -export async function bindOtp(): Promise { - // 拦截器已返回 inner data,直接返回 - return await apiClient.post('/auth/otp-bind') -} - -/** - * 验证 OTP 并完成绑定 - * 用户扫码后输入 6 位验证码,验证通过后完成绑定 - * 如果是登录流程中的首次绑定,返回 token 等登录信息 - * - * @param otpCode - 6 位 OTP 动态码 - * @returns 验证结果(verified + 可选的登录 token) - */ -export async function verifyOtp(otpCode: string): Promise { - const body: OtpVerifyRequest = { otp_code: otpCode } - // 拦截器已返回 inner data,直接返回 - return await apiClient.post('/auth/otp-verify', body) -} diff --git a/frontend-admin/src/views/Login.vue b/frontend-admin/src/views/Login.vue index 7c2ecf7..07b61d4 100644 --- a/frontend-admin/src/views/Login.vue +++ b/frontend-admin/src/views/Login.vue @@ -130,15 +130,6 @@ IT智能服务台 — 管理员登录页 (v1.2, 2026-07-06) - - - (null) - /** 错误信息 */ const errorMsg = ref('') @@ -344,9 +328,6 @@ async function checkWecomClient(): Promise { /** * 企微免密登录 - * @deprecated 已废弃 - AUTH-04 任务移除免密分支,后端 /api/auth_wecom/jsdk-login 接口已删除 - * @see docs/system_design.md 第347行 AUTH-04 验收标准 - * 此函数仅作占位,暂未删除以保持代码可追溯性 */ async function handleWecomQuickLogin(): Promise { wecomQuickLoading.value = true @@ -455,34 +436,6 @@ function stopPolling(): void { } } -/** - * OTP 绑定成功回调 - * @see docs/system_design.md AUTH-10 验收标准 - */ -function onBindSuccess(token: string, userId: string, name: string, role: string): void { - // 保存 token - localStorage.setItem('admin_token', token) - localStorage.setItem('admin_user_id', userId) - - // 更新 store - adminStore.token = token - adminStore.adminUserId = userId - - ElMessage.success('OTP 绑定成功,已登录') - router.push('/') -} - -/** - * OTP 绑定取消回调 - * 清除绑定状态,返回登录表单 - */ -function onBindCancel(): void { - requireOtpBind.value = false - otpBindUser.value = null - // 登出以清除半认证 token - adminStore.logout() -} - /** * 处理登录 */ @@ -501,7 +454,6 @@ async function handleLogin(): Promise { ) } catch (error: unknown) { // 检查是否需要 OTP 验证 - // @see docs/system_design.md AUTH-10 验收标准 if (error instanceof Error && error.message === 'require_otp') { requireOtp.value = true loginForm.otpCode = '' @@ -509,20 +461,6 @@ async function handleLogin(): Promise { return } - // 检查是否需要 OTP 首次绑定 - if (error instanceof Error && error.message === 'require_otp_bind') { - // 从 adminStore 获取用户信息(登录时已保存) - requireOtpBind.value = true - otpBindUser.value = { - user_id: adminStore.adminUserId || loginForm.userId, - name: adminStore.adminInfo?.name || '', - role: adminStore.adminInfo?.role || 'admin', - } - // 隐藏登录表单 - showPasswordPanel.value = false - return - } - const errMsg = error instanceof Error ? error.message : '登录失败,请重试' errorMsg.value = errMsg }