# FR-048 Phase 2a — Auth-group 授權守門盤點（factual inventory）

> 範圍：`api/auth`、`api/user_auth_provider`、`api/captcha`。
> 只列 WRITE 方法（post/put/delete/patch）與敏感 GET（download/sample、otp-qrcode、totp-secret、token）。
> URL prefix = `/api/1.0`。純事實盤點，不做 axis 判定。
> 掃描 branch：`fix/v1.8.0-bugs`。

**「現況守門」定義**：`無 JWT` / `僅 JWT` / `僅 JWT (delegate jedi-auth package service, 主專案無守門)` / `已有: <guard> @ <file:line>`。
所有 route 皆為 thin wrapper；已 TRACE 進主專案 `app/auth/service/...` service，確認**無**任何 `require_platform_admin` / `require_super_admin` / `assert_project_manager` / 手動 `ForbiddenError`(role/is_admin) 守門。

---

## api/auth

| Route path | Method | Route file:line | @jwt_required? | 呼叫的 service method | 現況守門 | 備註 |
|---|---|---|---|---|---|---|
| `/api/1.0/login` | POST | `api/auth/routes/login_route.py:39` (LoginRoute) | 否 | `app/auth/service/login_service.py:LoginService.process_login` (主專案，wrap jedi_login) + `CaptchaService.verify_turnstile_token` | 無 JWT | 公開端點(login)，intentional；先驗 turnstile captcha |
| `/api/1.0/refresh` | POST | `api/auth/routes/login_route.py:104` (LoginRefreshRoute) | 是 (`@jwt_required(refresh=True)`) | `jedi_login...LoginTokenService.refresh_user_token` (jedi 套件) | 僅 JWT (delegate jedi package service, 主專案無守門) | 公開流程(refresh)，靠 refresh token；jedi-login package service |
| `/api/1.0/logout` | POST | `api/auth/routes/login_route.py:115` (LogoutRoute) | 是 | `jedi_login...LoginTokenService.revoke_token` (jedi 套件) | 僅 JWT (delegate jedi package service, 主專案無守門) | 自己登出，僅需 JWT |
| `/api/1.0/users` | POST | `api/auth/routes/user_route.py:43` (UsersRoute) | 是 | `app/auth/service/user_app_service.py:19 UserAppService.get_users_and_pager` (主專案) | 僅 JWT | POST 實為分頁查詢(讀)；service 無守門(user_app_service.py:19) |
| `/api/1.0/user`, `/api/1.0/user/<uid>` | POST | `api/auth/routes/user_route.py:80` (UserRoute) | 是 | `jedi_auth...UserService.add_user` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 新增使用者；route/service 皆無角色守門 |
| `/api/1.0/user`, `/api/1.0/user/<uid>` | PUT | `api/auth/routes/user_route.py:95` (UserRoute) | 是 | `jedi_auth...UserService.update_user(is_admin=curr_user.is_admin,...)` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 更新使用者；is_admin 僅作 param 傳入 jedi service，route 層不擋 |
| `/api/1.0/user`, `/api/1.0/user/<uid>` | DELETE | `api/auth/routes/user_route.py:107` (UserRoute) | 是 | `jedi_auth...UserService.update_user_status(uid, REVOKE)` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 撤銷使用者(軟刪)；無角色守門 |
| `/api/1.0/user-profile/<uid>` | PUT | `api/auth/routes/user_route.py:146` (UserProfileRoute) | 是 | `jedi_auth...UserService.update_user` (jedi 套件) | 已有: `if uid != current_user.uid: raise UnauthorizedError` @ `api/auth/routes/user_route.py:148-149` | 僅限本人改自己 profile；route 內建 self-check(非 role guard) |
| `/api/1.0/user/change-status/<uid>` | POST | `api/auth/routes/user_route.py:135` (UserChangeStatusRoute) | 是 | `jedi_auth...UserService.update_user_status(uid, status)` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 任意使用者可改他人狀態；無角色守門 |
| `/api/1.0/user/batch-upload` | POST | `api/auth/routes/user_import_route.py:85` (UserBatchUploadRoute) | 是 | `jedi_auth...ImportUserService.check_upload_payload` + `UserService.add_user` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 批次新增使用者；無角色守門 |
| `/api/1.0/user/validate` | POST | `api/auth/routes/user_import_route.py:52` (UserValidateRoute) | 是 | `jedi_auth...ImportUserService.check_excel_user_data` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 上傳資料單筆驗證(不寫入)；無角色守門 |
| `/api/1.0/user/download/sample` | GET | `api/auth/routes/user_import_route.py:64` (UserUploadSampleDownloadRoute) | 是 | `app/auth/service/user_import_template_app_service.py:49 UserImportTemplateAppService.generate` (主專案) | 僅 JWT | 敏感 GET(download/sample)；依下載者權限動態產生 scoped 內容，service 無 role guard(user_import_template_app_service.py:49) |
| `/api/1.0/users/upload` | POST | `api/auth/routes/user_import_route.py:33` (UserUploadRoute) | 是 | `jedi_auth...ImportUserService.import_user_from_excel` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | Excel 批次上傳(寫入)；無角色守門 |
| `/api/1.0/roles` | POST | `api/auth/routes/role_route.py:38` (RolesRoute) | 是 | `app/auth/service/role_app_service.py:18 RoleAppService.get_roles_and_pager` (主專案) | 僅 JWT | POST 實為分頁查詢(讀)；service 無守門(role_app_service.py:18) |
| `/api/1.0/role/<uid>`, `/api/1.0/role` | POST | `api/auth/routes/role_route.py:67` (RoleRoute) | 是 | `jedi_auth...RoleService.add_role` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 新增角色；無角色守門 |
| `/api/1.0/role/<uid>`, `/api/1.0/role` | PUT | `api/auth/routes/role_route.py:80` (RoleRoute) | 是 | `jedi_auth...RoleService.update_role` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 更新角色；無角色守門 |
| `/api/1.0/role/<uid>`, `/api/1.0/role` | DELETE | `api/auth/routes/role_route.py:92` (RoleRoute) | 是 | `jedi_auth...RoleService.delete_role` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 刪除角色；無角色守門 |
| `/api/1.0/role/status/<uid>` | PUT | `api/auth/routes/role_route.py:103` (RoleStatusRoute) | 是 | `jedi_auth...RoleService.change_role_status` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 變更角色狀態；無角色守門 |
| `/api/1.0/tenants` | POST | `api/auth/routes/tenant_route.py:48` (TenantsRoute) | 是 | `jedi_auth...TenantService.get_tenants_and_pager` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | POST 實為分頁查詢(讀)；無守門 |
| `/api/1.0/tenant`, `/api/1.0/tenant/<uid>` | POST | `api/auth/routes/tenant_route.py:77` (TenantRoute) | 是 | `app/auth/service/tenant_provisioning_service.py:57 TenantProvisioningService.add_tenant_with_defaults` (主專案) | 僅 JWT | 新增租戶(連帶 seed 部門+管理員角色)；service TRACE 確認無守門(tenant_provisioning_service.py:57-85) |
| `/api/1.0/tenant`, `/api/1.0/tenant/<uid>` | PUT | `api/auth/routes/tenant_route.py:106` (TenantRoute) | 是 | `jedi_auth...TenantService.update_tenant` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 修改租戶；無角色守門 |
| `/api/1.0/tenant`, `/api/1.0/tenant/<uid>` | DELETE | `api/auth/routes/tenant_route.py:118` (TenantRoute) | 是 | `jedi_auth...TenantService.delete_tenant` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 刪除租戶；無角色守門 |
| `/api/1.0/org-units` | POST | `api/auth/routes/org_unit_route.py:40` (OrgUnitsRoute) | 是 | `jedi_auth...OrgUnitService.get_org_units_and_pager` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | POST 實為分頁查詢(讀)；無守門 |
| `/api/1.0/org-unit`, `/api/1.0/org-unit/<uid>` | POST | `api/auth/routes/org_unit_route.py:66` (OrgUnitRoute) | 是 | `jedi_auth...OrgUnitService.add_org_unit` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 新增組織單位；無角色守門 |
| `/api/1.0/org-unit`, `/api/1.0/org-unit/<uid>` | PUT | `api/auth/routes/org_unit_route.py:79` (OrgUnitRoute) | 是 | `jedi_auth...OrgUnitService.update_org_unit` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 修改組織單位；無角色守門 |
| `/api/1.0/org-unit`, `/api/1.0/org-unit/<uid>` | DELETE | `api/auth/routes/org_unit_route.py:91` (OrgUnitRoute) | 是 | `jedi_auth...OrgUnitService.delete_org_unit` (jedi 套件) | 僅 JWT (delegate jedi-auth package service, 主專案無守門) | 刪除組織單位；無角色守門 |
| `/api/1.0/user/change-pwd-req/<uid>` | GET | `api/auth/routes/change_password_route.py:51` (UserChangePasswordRequestRoute) | 否 | `jedi_auth...UserChangePasswordService.verify_and_get_user_change_password_request` (jedi 套件) | 無 JWT | 取忘記密碼申請資料(靠 req uid 為 secret)；公開流程(forget-password 一環) |
| `/api/1.0/user/change-pwd-by-req` | POST | `api/auth/routes/change_password_route.py:21` (ChangePasswordByChangeRequestRoute) | 否 | `jedi_auth...UserChangePasswordService.change_password_by_request` (jedi 套件) | 無 JWT | 憑 req_uid 改密碼；公開流程(forget-password)，intentional |
| `/api/1.0/forget-password` | POST | `api/auth/routes/change_password_route.py:39` (ForgetPasswordRoute) | 否 | `jedi_auth...UserChangePasswordService.request_change_password` (jedi 套件) | 無 JWT | 公開端點(forget-password)，intentional |
| `/api/1.0/totp-secret` | POST | `api/auth/routes/otp_route.py:27` (TotpSecretRoute) | 是 | `jedi_auth...UserService.get_user` + `jedi_mfa...TotpMfaService.register` (jedi 套件) | 僅 JWT (delegate jedi package service, 主專案無守門) | 敏感 GET/寫(totp-secret，取本人 TOTP secret)；用 get_user_context() 綁本人 |
| `/api/1.0/otp-qrcode/<uid>` | GET | `api/auth/routes/otp_route.py:48` (TotpQrcodeRoute) | **否** | `jedi_auth...UserService.get_user(uid)` + `jedi_mfa...TotpMfaService.get_register_qr_code_img` (jedi 套件) | **無 JWT** | **敏感 GET(otp-qrcode 無 JWT)** — 已確認 route 無 `@jwt_required`，任意 uid 皆可取 TOTP 註冊 QR(SEC A-族) |
| `/api/1.0/otp-verify` | POST | `api/auth/routes/otp_route.py:66` (MfaVerifyRoute) | 否 | `jedi_mfa...(Totp/Email)MfaService.verify_otp` + `jedi_login...LoginService.generate_jwt_token` (jedi 套件) | 無 JWT | 公開流程(MFA 驗證，登入第二步發 token)，intentional |
| `/api/1.0/otp-resend` | POST | `api/auth/routes/otp_route.py:118` (OtpCodeResendRoute) | 否 | `jedi_auth...UserService.get_user` + `jedi_mfa...EmailMfaService.gen_mfa_code` (jedi 套件) | 無 JWT | 公開流程(otp-resend)，intentional |
| `/api/1.0/send-user-test-mail` | POST | `api/auth/routes/user_route.py:161` (SendTestMailFromJediAuth) | **否** | `jedi_auth...UserService.test_send_mail(hardcoded email)` (jedi 套件) | **無 JWT** | 無 `@jwt_required`（只有 `@inject`）；寄測試信到 hardcoded 地址；用 get_user_context() 但不擋未認證(可能 debug 殘留) |

---

## api/user_auth_provider

| Route path | Method | Route file:line | @jwt_required? | 呼叫的 service method | 現況守門 | 備註 |
|---|---|---|---|---|---|---|
| `/api/1.0/auth-provider`, `/api/1.0/auth-provider/<uid>` | POST | `api/user_auth_provider/routes/user_auth_provider_route.py:42` (UserAuthProviderRoute) | 是 | `jedi_login...UserAuthProviderService.register_user_auth_provider` (jedi 套件) | 僅 JWT (delegate jedi package service, 主專案無守門) | 新增使用者第三方登入註冊(可指定任意 user_uid)；無角色守門 |
| `/api/1.0/auth-provider`, `/api/1.0/auth-provider/<uid>` | DELETE | `api/user_auth_provider/routes/user_auth_provider_route.py:82` (UserAuthProviderRoute) | 是 | `jedi_login...UserAuthProviderService.delete_user_auth_provider` (jedi 套件) | 僅 JWT (delegate jedi package service, 主專案無守門) | 刪除第三方登入註冊；無角色守門 |
| `/api/1.0/ldap/connect-test` | POST | `api/user_auth_provider/routes/ldap_route.py:19` (LdapConnectTestRoute) | 是 | `app/user_auth_provider/service/ldap_service.py:22 LdapService.test_connect` (主專案) | 僅 JWT | 測試 LDAP 連線(不寫入)；service TRACE 確認無守門(ldap_service.py:22) |

---

## api/captcha

| Route path | Method | Route file:line | @jwt_required? | 呼叫的 service method | 現況守門 | 備註 |
|---|---|---|---|---|---|---|
| `/api/1.0/verify-captcha` | POST | `api/captcha/routes/captcha_route.py:41` (VerifyCaptchaRoute) | 否 | `jedi_captcha...CaptchaService.verify_graph_captcha` (jedi 套件) | 無 JWT | 公開端點(captcha 驗證，登入前置)，intentional |

> 註：`GET /api/1.0/captcha`（captcha_route.py:25）為圖形驗證碼產生(讀，公開)，非 WRITE 亦非清單指定的敏感 GET，未列入。
