Files
gatehouse-api/tests/integration/test_webauthn_workflows.py
T
nexgen_mirrors 015c622016 test: add comprehensive integration test suite for IAM platform
Add 162 integration tests covering authentication flows, TOTP MFA,
SSH key/certificate management, organization workflows, multi-org
access, self-service features, admin operations, authorization,
security edge cases, department/principal management, CA management,
policy compliance, WebAuthn passkeys, and ZeroTier network access.

Includes:
- Reusable API client library with session management
- Test fixtures for users, organizations, memberships, and CAs
- Helper functions for SSH key generation and verification
- Documentation for running and writing tests

Also update test configuration to disable conflicting maas plugins
and configure WebAuthn/session settings for localhost testing.
2026-04-23 15:41:37 +09:30

119 lines
4.9 KiB
Python

"""WebAuthn passkey integration tests.
Covers WebAuthn registration, login, and credential management.
These tests mock the cryptographic operations since real WebAuthn
requires a browser environment.
"""
import pytest
from unittest.mock import patch, MagicMock
from tests.integration.client.base import ApiError
def assert_success(response: dict, message_contains: str = "") -> dict:
data = response.get("data", {})
assert response.get("success") is not False, (
f"Expected success but got error: {response.get('message')}"
)
if message_contains:
assert message_contains.lower() in response.get("message", "").lower()
return data
class TestWebAuthnRegistration:
"""Test WebAuthn passkey registration."""
def test_begin_registration_positive(self, integration_client, create_test_user):
"""TEST: WEBAUTHN-01 — Begin passkey registration.
WHAT: POST /auth/webauthn/register/begin.
WHY: First step of passkey enrollment.
EXPECTED: 200 OK with challenge options.
"""
user = create_test_user(password="MyPassword123!")
integration_client.auth.login(email=user["email"], password="MyPassword123!")
result = integration_client.post("/auth/webauthn/register/begin")
# Endpoint returns jsonify directly, not api_response wrapper
assert "rp" in result or result.get("success") is not False
def test_complete_registration_mocked_positive(self, integration_app, integration_client, create_test_user):
"""TEST: WEBAUTHN-02 — Complete passkey registration (mocked).
WHAT: POST /auth/webauthn/register/complete with mocked verification.
WHY: Full registration flow requires mocking crypto.
EXPECTED: 201 Created when verification succeeds.
"""
from gatehouse_app.models.auth.authentication_method import AuthenticationMethod
user = create_test_user(password="MyPassword123!")
integration_client.auth.login(email=user["email"], password="MyPassword123!")
with patch("gatehouse_app.api.v1.auth.webauthn.WebAuthnService.verify_registration_response") as mock_verify:
mock_auth_method = MagicMock()
mock_auth_method.to_webauthn_dict.return_value = {"id": "cred-123", "type": "public-key"}
mock_verify.return_value = mock_auth_method
import base64
client_data = base64.urlsafe_b64encode(b'{"challenge":"test-challenge"}').rstrip(b"=").decode()
result = integration_client.post(
"/auth/webauthn/register/complete",
data={
"id": "cred-123",
"rawId": "raw-123",
"response": {
"clientDataJSON": client_data,
"attestationObject": "o2Nmb",
},
"type": "public-key",
},
)
# Mock path may return 201 or wrapped response depending on flow
assert result.get("success") is not False or result.get("code") == 201
def test_list_credentials_positive(self, integration_client, create_test_user):
"""TEST: WEBAUTHN-03 — List WebAuthn credentials.
WHAT: GET /auth/webauthn/credentials.
WHY: Security page displays registered passkeys.
EXPECTED: 200 OK with credentials array.
"""
user = create_test_user(password="MyPassword123!")
integration_client.auth.login(email=user["email"], password="MyPassword123!")
result = integration_client.get("/auth/webauthn/credentials")
assert_success(result)
class TestWebAuthnLogin:
"""Test WebAuthn login flow."""
def test_begin_login_positive(self, integration_client, create_test_user):
"""TEST: WEBAUTHN-04 — Begin WebAuthn login.
WHAT: POST /auth/webauthn/login/begin with email.
WHY: First step of passkey authentication.
EXPECTED: 200 OK with challenge options (or 404 if no passkeys).
"""
user = create_test_user(password="MyPassword123!")
try:
result = integration_client.post("/auth/webauthn/login/begin", data={"email": user["email"]})
assert "challenge" in result
except ApiError as exc:
# Accept 404 when user has no passkeys registered
assert exc.status_code == 404, f"Expected 200 or 404, got {exc.status_code}"
def test_get_webauthn_status_positive(self, integration_client, create_test_user):
"""TEST: WEBAUTHN-05 — Get WebAuthn status.
WHAT: GET /auth/webauthn/status.
WHY: Security page shows whether passkeys are enabled.
EXPECTED: 200 OK.
"""
user = create_test_user(password="MyPassword123!")
integration_client.auth.login(email=user["email"], password="MyPassword123!")
result = integration_client.get("/auth/webauthn/status")
assert_success(result)