conf 整合說明文件 ← 返回首頁
Overview

架構說明

本 2FA 元件採用純 conf 物件溝通架構。
conf 是爬蟲端與系統端(2FA Modal)之間的共用變數。

爬蟲端負責寫入大部分 conf 欄位(auth_2fa_needauth_2fa_optionsauth_2fa_titleauth_2fa_msg 等),並讀取系統端回寫的結果。
系統端(Modal)負責讀取 conf 設定,僅寫入兩個欄位:auth_2fa_method(使用者從選項中選擇)與 auth_2fa_code(使用者輸入的驗證碼)。
爬蟲端 ──寫入設定──▶ conf 物件 ◀──讀取設定── 系統端 Modal
系統端 Modal ──寫入 method / code──▶ conf 物件 ◀──讀取結果── 爬蟲端
ℹ️ 重點:爬蟲端負責寫入所有設定欄位與訊息,系統端(Modal)僅負責讀取設定並回寫 auth_2fa_methodauth_2fa_code 兩個使用者操作結果。
Step 1

conf 物件設定欄位(爬蟲端寫入)

爬蟲端會將以下欄位寫入 conf 物件,系統端應依以下內容彈出 Modal:

conf 欄位 型別 說明 必填
conf.auth_2fa_need boolean 是否需要進行兩步驟驗證。true → 顯示彈窗;false / null → 不需要。 必填
conf.auth_2fa_options string[] 可用的驗證方式列表,Modal 會依此動態產生按鈕。
可用值:"sms"(手機簡訊)、"app"(驗證器 App)、"email"(信箱)
範例:["sms", "app", "email"]["sms"]
必填
conf.auth_2fa_title string 銀行或服務名稱,顯示於 Modal 標題列。例如 "CUBE 網路銀行""台新 Richart" 必填
conf.auth_2fa_msg string 動態訊息,由爬蟲端寫入。用於顯示發送目的地等資訊。
例如:"已傳送簡訊驗證碼至 0907****48""已傳送驗證碼至 exam**@gmail.com"
Modal 在每次狀態變化時會自動讀取並更新顯示。初始可為空字串。
選填
conf.clientid_parentid string 客戶識別碼,格式如 #TW_065 必填
JavaScript
// 爬蟲端初始化 conf 範例
const conf = {
  auth_2fa_need:       true,
  auth_2fa_options:    ["sms", "app", "email"],
  auth_2fa_title:      "CUBE 網路銀行",
  auth_2fa_msg:        "",            // 初始為空,爬蟲端會動態更新
  clientid_parentid:   "#TW_065"
};

if (conf.auth_2fa_need) {
  openModal();   // 觸發 2FA 彈窗
}
Step 2

共享全域變數(爬蟲端 ↔ 系統端 Modal)

以下為爬蟲端與系統端(Modal)之間共用的全域變數:

🔵 爬蟲端寫入 → 系統端讀取

變數名稱 型別 說明
auth_2fa_need boolean true / false / null 爬蟲端寫入。
true:需要驗證(觸發 Modal)
false / null:驗證已完成或不需要
auth_2fa_options string[] ["sms","app","email"] 爬蟲端寫入。設定可用驗證方式,Modal 據此渲染按鈕
auth_2fa_title string 銀行名稱字串 爬蟲端寫入。顯示於 Modal 標題列的服務名稱
auth_2fa_msg string 動態訊息字串 爬蟲端寫入,可隨時更新此變數來顯示訊息。
Modal 在狀態變化時自動讀取顯示。
常見用途:顯示發送目的地(如 "已傳送至 0907****48"

🟢 系統端(Modal)寫入 → 爬蟲端讀取

變數名稱 型別 說明
auth_2fa_method string "sms" / "email" / "app" / "" 系統端(Modal)寫入。使用者從 auth_2fa_options 中選擇的驗證方式。
空字串代表尚未選擇
auth_2fa_code string 6~10 位數字字串 系統端(Modal)寫入。使用者輸入的驗證碼,按下確定時寫入
Step 3

狀態流程

0
爬蟲端初始化 conf
爬蟲端寫入 auth_2fa_need = trueauth_2fa_optionsauth_2fa_titleauth_2fa_msg
→ 系統端偵測到 auth_2fa_need == true,呼叫 openModal() 彈出驗證視窗
1
使用者選擇驗證方式
Modal 依 auth_2fa_options 顯示可用按鈕(簡訊 / 驗證器 App / Email)
→ 使用者點選後,Modal 寫入 auth_2fa_method = "sms"(或 "email" / "app"
2
爬蟲端更新 auth_2fa_msg
爬蟲端偵測到 auth_2fa_method 變化後,負責發送驗證碼,並更新 auth_2fa_msg
例如:auth_2fa_msg = "已傳送簡訊驗證碼至 0907****48"
→ Modal 自動讀取並顯示此訊息
3
使用者輸入驗證碼並確定
→ Modal 寫入 auth_2fa_code = "xxxxxx"
→ 爬蟲端讀取 auth_2fa_code,進行驗證
驗證成功
爬蟲端驗證通過後,設定 auth_2fa_need = false
→ Modal 顯示成功動畫並關閉
→ 爬蟲端繼續後續流程
驗證失敗 → 爬蟲端觸發重試
系統端不知道驗證成功或失敗。爬蟲端自行判斷後,若需重試,只需同時清空 auth_2fa_methodauth_2fa_code(維持 auth_2fa_need = true
→ Modal 偵測到三項條件同時成立即自動重新彈出:
  auth_2fa_need == true AND auth_2fa_method == "" AND auth_2fa_code == ""
Step 4 — 爬蟲端偵測狀態變化(JS 範例)

爬蟲端偵測系統端回寫的變數

爬蟲端透過輪詢或 Object.defineProperty 監聽系統端(Modal)回寫的 auth_2fa_methodauth_2fa_code

JavaScript
// 方法 A:輪詢(簡單)
const waitFor2FA = () => new Promise(resolve => {
  const timer = setInterval(() => {
    if (!auth_2fa_need) {
      clearInterval(timer);
      resolve();
    }
  }, 500);
});

// 等待驗證完成後繼續
await waitFor2FA();
console.log('驗證完成,繼續登入流程');


// 方法 B:property watch(進階)
let _2fa_need = true;
Object.defineProperty(window, 'auth_2fa_need', {
  get: () => _2fa_need,
  set: val => {
    _2fa_need = val;
    if (!val) onVerified();   // 驗證完成 callback
  }
});
ℹ️ 注意:auth_2fa_method == ""auth_2fa_need == true, 代表使用者尚未選擇驗證方式,爬蟲端應確保 Modal 保持開啟狀態。
Complete Example — 完整整合範例(JS)

完整整合範例

JavaScript
// ── 爬蟲端初始化 conf ──
const conf = {
  auth_2fa_need:     true,
  auth_2fa_options:  ["sms", "email"],
  auth_2fa_title:    "國泰世華 CUBE",
  auth_2fa_msg:      "",
  clientid_parentid: "#TW_065"
};

// ── 系統端讀取 conf 並觸發 Modal ──
auth_2fa_options = conf.auth_2fa_options;
auth_2fa_title   = conf.auth_2fa_title;
openModal();

// ── 使用者在 Modal 選擇 sms ──
// Modal 寫入 auth_2fa_method = "sms"

// ── 爬蟲端偵測到 method 變化,發送驗證碼並更新訊息 ──
auth_2fa_msg = "已傳送簡訊驗證碼至 0907****48";

// ── 使用者在 Modal 輸入驗證碼並按確定 ──
// Modal 寫入 auth_2fa_code = "123456"

// ── 爬蟲端讀取 auth_2fa_code 進行驗證 ──

// 驗證成功:
auth_2fa_need = false;

// 驗證失敗:只需清空 method,維持 need=true → Modal 自動回到步驟 0
auth_2fa_method = "";   // auth_2fa_need 仍為 true
⚠️ 注意:驗證碼的發送與驗證皆由爬蟲端處理,系統端(Modal)僅負責顯示介面並回寫 auth_2fa_methodauth_2fa_code。所有狀態透過 conf 變數傳遞。
POC

PHP 系統端整合範例

以下示範系統端如何以 PHP 讀取 conf 設定、監聽 Modal 回寫的變數,並完成驗證碼核驗流程。

ℹ️ PHP 透過頁面注入初始 conf,再由頁內 JS bridge 輪詢 auth_2fa_method / auth_2fa_code 並以 AJAX 傳回 PHP 端驗證。

① 頁面初始化 — 注入 conf

PHP
// init_2fa.php — 由系統端產生,嵌入頁面 <head>
<?php
// 從 session / 資料庫取得當前使用者的 2FA 設定
$options = get_user_2fa_methods($user_id);  // e.g. ['sms','email']
$title   = '國泰世華 CUBE';
$msg     = '';
?>
<script>
// 由 PHP 寫入 conf — Modal 會讀取這些值
var auth_2fa_need    = true;
var auth_2fa_options = <?= json_encode($options) ?>;
var auth_2fa_title   = <?= json_encode($title)   ?>;
var auth_2fa_msg     = <?= json_encode($msg)     ?>;
var auth_2fa_method  = "";
var auth_2fa_code    = "";
</script>

② 傳送驗證碼 — 監聽 method 並呼叫發送 API

PHP + JS Bridge
// bridge.js(嵌入同一頁面)
// 監聽 auth_2fa_method,偵測到選擇後呼叫 PHP 發送驗證碼
let _sentMethod = "";
setInterval(() => {
  if (auth_2fa_need && auth_2fa_method && auth_2fa_method !== _sentMethod) {
    _sentMethod = auth_2fa_method;
    fetch('/api/2fa/send.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ method: auth_2fa_method })
    })
    .then(r => r.json())
    .then(res => {
      // PHP 回傳發送目的地,更新提示訊息
      auth_2fa_msg = res.message;  // e.g. "已傳送至 0907****48"
    });
  }
}, 300);

// send.php — 發送驗證碼
<?php
$data   = json_decode(file_get_contents('php://input'), true);
$method = $data['method'] ?? '';

switch ($method) {
  case 'sms':
    send_sms_otp($user_phone);
    $message = '已傳送簡訊至 ' . mask_phone($user_phone);
    break;
  case 'email':
    send_email_otp($user_email);
    $message = '已傳送 Email 至 ' . mask_email($user_email);
    break;
  case 'app':
    $message = '請開啟驗證器 App 取得驗證碼';
    break;
}

header('Content-Type: application/json');
echo json_encode(['message' => $message]);
?>

③ 轉送驗證碼 — 監聽 code 並轉送給爬蟲端

PHP + JS Bridge
// bridge.js(續)
// 監聽 auth_2fa_code,偵測到後轉送至 PHP,再由爬蟲端決定成功或重試
// 系統端不判斷驗證成功或失敗
let _sending = false;
setInterval(() => {
  if (auth_2fa_need && auth_2fa_code && !_sending) {
    _sending = true;
    const code   = auth_2fa_code;
    const method = auth_2fa_method;
    auth_2fa_code = "";   // 立即清空,避免重複送出
    _sending      = false;

    fetch('/api/2fa/submit.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ method, code })
    });
    // 不處理回應 — 爬蟲端自行判斷結果
    // 若爬蟲端認為需要重試,會清空 auth_2fa_method + auth_2fa_code
    // → Modal 自動偵測並重新彈出
  }
}, 300);

// submit.php — 接收驗證碼並轉交爬蟲端,不回傳驗證結果
<?php
$data   = json_decode(file_get_contents('php://input'), true);
$method = $data['method'] ?? '';
$code   = $data['code']   ?? '';

// 將 method + code 寫入 session / queue,供爬蟲端取用
$_SESSION['2fa_submit'] = [
  'method' => $method,
  'code'   => $code,
  'at'     => time(),
];

header('Content-Type: application/json');
echo json_encode(['ok' => true]);
?>

④ 爬蟲端判斷結果並更新 conf

PHP(爬蟲端)
// crawler_handler.php — 爬蟲端讀取 session 中的 code 後自行驗證
<?php
$submit = $_SESSION['2fa_submit'] ?? null;
if ($submit) {
  $valid = bank_verify_otp($submit['method'], $submit['code']);

  if ($valid) {
    // 驗證成功:通知前端關閉 Modal
    set_conf('auth_2fa_need', false);

  } else {
    // 驗證失敗:清空 method + code → Modal 自動重新彈出
    // 條件:need==true AND method=="" AND code=="" → popup
    set_conf('auth_2fa_method', '');
    set_conf('auth_2fa_code',   '');
    // auth_2fa_need 維持 true(不需要額外設定)
  }

  unset($_SESSION['2fa_submit']);
}
?>
⚠️ 重點:系統端(submit.php)只負責接收並轉存 code,不判斷驗證成功或失敗。 爬蟲端取得 code 後自行驗證,失敗時只需同時清空 auth_2fa_methodauth_2fa_code, Modal 偵測到三項條件(need=true + method="" + code="")即自動重新彈出。