Skip to content

WebAuthn Passkey 案例研究

本案例研究透過複雜功能範例展示規格框架:為影像監控平台後端新增 WebAuthn passkey 身分驗證。本案例研究突顯與第三方身分識別提供者(AWS Cognito)整合以及管理破壞性約束的挑戰。

高階規格(提案)

提案捕捉業務意圖、技術約束與破壞性變更影響。

markdown
# Change Proposal: Add WebAuthn Passkey Support

## Why

Users increasingly expect passwordless authentication options for better security
and user experience. Passkeys (WebAuthn) provide phishing-resistant authentication
that's more secure than traditional passwords and easier for users. AWS Cognito
added native support for passwordless authentication including passkeys in
November 2024, making this a viable enhancement for the platform.

## What Changes

- Add WebAuthn/passkey authentication as an alternative first-factor method
- Implement passkey registration workflow (up to 20 passkeys per user)
- Add GraphQL APIs for passkey management (register, list, delete)
- Modify authentication flow to support password and passwordless paths
- Add organization-level setting `passkeyEnabled` (default false)

## Impact

### Affected Specs
- NEW: authentication/passkey-management
- MODIFIED: organization-settings (add passkeyEnabled flag with MFA constraint)

### Breaking Changes
- **BREAKING**: Organizations with `isRequiredMFA=true` cannot enable passkey
  authentication (AWS Cognito constraint: MFA and passwordless are mutually exclusive)
- Organizations must choose between enforced MFA or passkey support
- Users with personal MFA enabled cannot authenticate with passkeys

### Dependencies
- AWS Cognito Essentials or Plus plan (Lite plan does not support passkeys)
- Frontend must implement WebAuthn browser APIs
- Choice-based authentication flow configuration in Cognito

### Migration
- No data migration required for existing users
- Organizations wanting passkeys must disable MFA enforcement first
- Feature flag for controlled rollout

關鍵特性:

  • 明確識別平台約束(Cognito MFA/passkey 互斥性)
  • 以清楚的影響範圍分類破壞性變更
  • 列出影響實作選擇的外部相依性

設計文件

設計文件捕捉由外部平台約束驅動的架構決策。

markdown
# Design Document: WebAuthn Passkey Support

## Context

Vortex Backend uses AWS Cognito for user authentication with two MFA mechanisms:
1. **User-level MFA**: Individual users enable TOTP via `AdminSetUserMFAPreference`
2. **Organization-level MFA**: Organizations require all users to use MFA via
   `isRequiredMFA` flag

AWS Cognito constraint (November 2024 documentation):
> "If you require multi-factor authentication (MFA) in your user pool,
> you cannot use passwordless authentication."

## Goals / Non-Goals

### Goals
- Enable passwordless authentication using WebAuthn passkeys
- Support passkey registration, listing, and deletion
- Maintain backward compatibility with password + MFA authentication
- Provide clear configuration options for organizations

### Non-Goals
- Replace existing password-based authentication
- Modify existing TOTP MFA implementation
- Custom WebAuthn implementation (will use Cognito native support)
- Passkey-based MFA (AWS Cognito doesn't support this)

## Decisions

### Decision 1: Use Cognito Native Passkey Support
**Choice:** Use AWS Cognito's native WebAuthn support rather than custom implementation.

**Rationale:**
- Reduces implementation complexity and security risks
- Leverages AWS's managed infrastructure and compliance
- Native integration with existing user pools
- Built-in support for standard WebAuthn APIs

**Alternatives Considered:**
- Custom WebAuthn with Lambda: Rejected due to security audit requirements
- Third-party service (Okta): Rejected to avoid vendor migration costs

### Decision 2: MFA and Passkey Mutual Exclusivity at Organization Level
**Choice:** Add `passkeyEnabled` boolean (default false) with validation:
`isRequiredMFA && passkeyEnabled` cannot both be true.

**Rationale:**
- AWS Cognito enforces this at the user pool level
- Clear separation prevents user confusion
- Explicit opt-in for passkey functionality

**Alternatives Considered:**
- Auto-disable MFA when passkeys enabled: Too dangerous (security downgrade)
- Separate user pool for passkey users: Too complex (data sync issues)

### Decision 3: Support Both ES256 and RS256 Algorithms
**Choice:** Accept both ES256 (-7) and RS256 (-257) for passkey registration.

**Rationale:**
- Maximizes device compatibility (different platforms prefer different algorithms)
- Cognito supports both; no security downside

### Decision 4: Support Synced and Device-bound Passkeys
**Choice:** Allow both device-bound and synced passkeys (iCloud Keychain, Google).

**Rationale:**
- Better UX (passkeys work across user's devices)
- Industry trend toward synced passkeys for usability
- Users choose based on security/convenience preference

## Risks

### Risk 1: Organizations Cannot Use Both MFA and Passkeys
**Impact:** Organizations requiring MFA for compliance must choose between
enforcement and passkey convenience.

**Mitigation:**
- Document constraint clearly in admin UI
- Provide migration guide
- Monitor AWS Cognito roadmap for future support

### Risk 2: User Lockout
**Impact:** Users might lose access if they lose passkey device without backup.

**Mitigation:**
- Allow multiple passkeys per user (up to 20)
- Encourage synced passkeys
- Always keep password authentication as backup

關鍵特性:

  • 將外部平台約束記錄為一等關注點
  • 每個決策追溯至平台限制或安全考量
  • 風險承認團隊無法控制的約束

領域規格

領域規格以涵蓋約束的情境定義需求。

markdown
# Authentication Capability: WebAuthn Passkey Support

## Requirement: Passkey Registration Challenge
The system SHALL provide an API to initiate passkey registration.

#### Scenario: User requests passkey registration challenge
- **GIVEN** a user is authenticated
- **AND** the organization has `passkeyEnabled=true`
- **WHEN** the user requests a registration challenge via `registerPasskeyChallenge`
- **THEN** the system SHALL return challenge ID and WebAuthn options
- **AND** the challenge SHALL be valid for 5 minutes

#### Scenario: Organization has passkeys disabled
- **GIVEN** a user belongs to an organization with `passkeyEnabled=false`
- **WHEN** the user requests a passkey registration challenge
- **THEN** the system SHALL return error "PASSKEYS_NOT_ENABLED"

## Requirement: Passkey Registration Completion
The system SHALL accept and verify WebAuthn credentials.

#### Scenario: Valid passkey credential submitted
- **GIVEN** a user has requested a registration challenge
- **WHEN** the user submits valid signed credential within 5 minutes
- **THEN** the system SHALL verify the credential with AWS Cognito
- **AND** store the passkey in the user's account
- **AND** return success with passkey metadata

#### Scenario: Maximum passkeys limit reached
- **GIVEN** a user has 20 registered passkeys
- **WHEN** the user attempts to register another passkey
- **THEN** the system SHALL return error "MAX_PASSKEYS_REACHED"

## Requirement: Passkey Listing
The system SHALL provide API to list user's registered passkeys.

#### Scenario: User lists their passkeys
- **GIVEN** a user has registered passkeys
- **WHEN** the user queries via `listPasskeys`
- **THEN** the system SHALL return passkey metadata including:
  - Passkey ID, Credential ID, Friendly name
  - Creation timestamp, Last used timestamp
  - Supported transports

## Requirement: MFA and Passkey Mutual Exclusivity
The system SHALL enforce mutual exclusivity between MFA requirement and passkeys.

#### Scenario: Enable passkeys when MFA not required
- **GIVEN** an organization has `isRequiredMFA=false`
- **WHEN** admin sets `passkeyEnabled=true`
- **THEN** the system SHALL update the setting
- **AND** users can register passkeys

#### Scenario: Attempt to enable passkeys with required MFA
- **GIVEN** an organization has `isRequiredMFA=true`
- **WHEN** admin attempts to set `passkeyEnabled=true`
- **THEN** the system SHALL reject with error "MFA_PASSKEY_CONFLICT"
- **AND** return message explaining the constraint

#### Scenario: Attempt to require MFA with passkeys enabled
- **GIVEN** an organization has `passkeyEnabled=true`
- **WHEN** admin attempts to set `isRequiredMFA=true`
- **THEN** the system SHALL reject with error "MFA_PASSKEY_CONFLICT"

關鍵特性:

  • 約束情境(MFA/passkey 衝突)視為一等需求
  • 為前端整合指定錯誤碼
  • 包含安全需求(稽核記錄)

規格可追溯性

Passkey 功能展示透過約束驅動實作的可追溯性:

意圖:「使用者需要無密碼身分驗證以獲得更好的安全性與使用者體驗」

    ├── 提案(proposal.md)
    │   ├── 識別 AWS Cognito 約束
    │   └── 分類破壞性變更(MFA/passkey 互斥性)

    ├── 設計文件(design.md)
    │   ├── 4 個架構決策
    │   ├── 外部約束記錄為一等關注點
    │   └── 合規性驅動組織的風險分析

    ├── 領域規格
    │   ├── authentication/passkey-management(NEW)
    │   │   ├── 6 個需求、15+ 個情境
    │   │   └── 約束情境作為需求
    │   └── organization-settings(MODIFIED)
    │       └── passkeyEnabled 旗標含驗證

    └── 實作任務(tasks.md)
        └── 12 個任務群組、50+ 個個別任務

結案報告:策略相依性與隱形成本

結果: Passkey 提案最終因以下原因取消:(1) 沒有 Cognito 方案升級的預算,以及 (2)「One Account」計畫被認定為長期解決方案。

此結果揭露了規格驅動工作流程應處理的關鍵組織模式:

隱藏的相依性問題

發現時程(AI 加速):
───────────────────────────────────────
第 1 天:工程提出 passkey 功能
第 2 天:AI 輔助調查發現 Cognito MFA/passkey 約束
第 3 天:AI 輔助成本分析與設計文件完成
第 6 天:PM Team 透露「One Account」(Okta 遷移)已在規劃中(但無實際時程跟計劃)
        → 工程團隊不知道這會影響所有身分驗證相關變更
        → 提案以「拋棄式工作」為由取消

AI 如何加速發現

規格驅動工作流程搭配 AI 代理,將傳統需要數月的工作壓縮至數天:

階段傳統時程AI 輔助時程加速倍數
提案草稿1-2 週1 天7-14x
技術調查2-4 週1 天14-28x
設計文件1-2 週1 天7-14x
任務拆解(50+ 任務)1 週數小時10-20x
到達決策點總計5-9 週4 天9-16x

悖論: AI 加速比傳統流程更快地暴露了策略阻擋因素(One Account)。若無 AI 輔助,團隊可能花費 5-9 週進行調查才發現相同的阻擋因素——這是顯著更高的沉沒成本。

AI 加速的真正價值

快速失敗優於緩慢失敗。在 4 天內發現策略阻擋因素相較於 5-9 週,節省了大量組織資源並讓團隊能快速轉向。

AI 輔助的規格工作即使在提案取消時也產生有價值的產出物:

  • 完整的技術調查已記錄(Cognito 約束、MFA 互動)
  • 設計決策與替代方案保留供未來參考
  • 50+ 實作任務已定義——若計畫解除阻擋即可使用
  • 決策理由已捕捉供組織學習
  • 投入並非完全「浪費」——知識以機器可讀格式保留

關鍵學習

  1. 策略計畫的可見性:長期計畫(One Account、Privilege 2.0)必須在共享路線圖中可見,工程在建立提案時可查閱

  2. 等待的成本:「等待 X」的決策應包含 X 被延遲或取消的風險評估

  3. 需求管道:需要明確流程處理前線驅動與工程驅動的改善

  4. 模式識別:組織應追蹤「被未來計畫阻擋」的模式以識別系統性規劃問題

框架新增: 被策略計畫阻擋的規格應以「BLOCKED_BY_INITIATIVE」狀態歸檔,並設定定期審查觸發器以重新評估阻擋計畫是否仍在正軌。

為何此案例研究重要

  1. 外部約束:展示如何在規格中處理第三方平台限制(AWS Cognito MFA/passkey 互斥性)

  2. 破壞性變更:展示影響組織配置的破壞性變更的明確分類與溝通

  3. 合規性影響:說明安全合規性需求(MFA 強制)如何與功能啟用互動

  4. 多層級協調:後端變更需要前端 WebAuthn 實作、基礎設施配置與謹慎部署

  5. 代理脈絡:AI 代理需要完整的約束鏈才能產生正確的驗證邏輯與錯誤處理

另一個案例: E-Map 平面圖