• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openwallet-foundation / acapy-vc-authn-oidc / 19741749098

27 Nov 2025 03:50PM UTC coverage: 85.943% (-6.5%) from 92.397%
19741749098

push

github

web-flow
Feat: robust multi-tenant webhook registration (#915)

* feat: robust multi-tenant webhook registration

Signed-off-by: Yuki I <omoge.real@gmail.com>

* fix: ensure api package has init file

Signed-off-by: Yuki I <omoge.real@gmail.com>

* test: maximize coverage for main.py startup logic

Signed-off-by: Yuki I <omoge.real@gmail.com>

* test: add integration tests for main.py routes and middleware

Signed-off-by: Yuki I <omoge.real@gmail.com>

* style(core): apply black code formatting

Signed-off-by: Yuki I <omoge.real@gmail.com>

* test: add comprehensive coverage for config.py validation logic

Signed-off-by: Yuki I <omoge.real@gmail.com>

* refactor: extract webhook registration logic to core/webhook_utils.py

Signed-off-by: Yuki I <omoge.real@gmail.com>

* refactor: address code review feedback (imports, retries, cleanup)

Signed-off-by: Yuki I <omoge.real@gmail.com>

* refactor: remove unused import

Signed-off-by: Yuki I <omoge.real@gmail.com>

---------

Signed-off-by: Yuki I <omoge.real@gmail.com>
Co-authored-by: Yuki I <omoge.real@gmail.com>

46 of 50 new or added lines in 3 files covered. (92.0%)

1828 of 2127 relevant lines covered (85.94%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/oidc-controller/api/core/webhook_utils.py
1
import asyncio
1✔
2
import structlog
1✔
3
import requests
1✔
4

5

6
logger: structlog.typing.FilteringBoundLogger = structlog.getLogger(__name__)
1✔
7

8

9
async def register_tenant_webhook(
1✔
10
    wallet_id: str,
11
    webhook_url: str,
12
    admin_url: str,
13
    api_key: str | None,
14
    admin_api_key: str | None,
15
    admin_api_key_name: str | None,
16
):
17
    """
18
    Registers the controller's webhook URL with the ACA-Py Agent Tenant.
19
    Includes retries for agent startup and validation for configuration.
20
    """
21
    if not webhook_url or not wallet_id:
1✔
22
        logger.warning(
1✔
23
            "Multi-tenant mode enabled but CONTROLLER_WEB_HOOK_URL or MT_ACAPY_WALLET_ID is missing. "
24
            "Verification callbacks may not work."
25
        )
26
        return
1✔
27

28
    if not webhook_url.startswith("http"):
1✔
29
        logger.error(
1✔
30
            f"Invalid webhook URL format: {webhook_url}. Must start with http:// or https://"
31
        )
32
        return
1✔
33

34
    # Prepare URL with Authentication
35
    # Ensure API key is in the URL if configured.
36
    # ACA-Py supports this by appending #key to the URL
37
    if api_key and "#" not in webhook_url:
1✔
38
        webhook_url = f"{webhook_url}#{api_key}"
1✔
39

40
    headers = {}
1✔
41
    if admin_api_key_name and admin_api_key:
1✔
42
        headers[admin_api_key_name] = admin_api_key
1✔
43

44
    target_url = f"{admin_url}/multitenancy/wallet/{wallet_id}"
1✔
45
    payload = {"wallet_webhook_urls": [webhook_url]}
1✔
46

47
    max_retries = 5
1✔
48
    base_delay = 2  # seconds
1✔
49

50
    logger.info(f"Attempting to register webhook for wallet {wallet_id}...")
1✔
51

52
    for attempt in range(0, max_retries):
1✔
53
        try:
1✔
54
            response = requests.put(
1✔
55
                target_url, json=payload, headers=headers, timeout=5
56
            )
57

58
            if response.status_code == 200:
1✔
59
                logger.info("Successfully registered webhook URL with ACA-Py tenant")
1✔
60
                return
1✔
61
            elif response.status_code in [401, 403]:
1✔
62
                logger.error(
1✔
63
                    f"Webhook registration failed: Unauthorized (401/403). Check AGENT_ADMIN_API_KEY configuration."
64
                )
65
                return
1✔
NEW
66
            elif response.status_code >= 500:
×
67
                # Retry on server errors
NEW
68
                logger.warning(
×
69
                    f"Webhook registration failed with server error {response.status_code}: {response.text}. Retrying..."
70
                )
71
            else:
NEW
72
                logger.warning(
×
73
                    f"Webhook registration returned status {response.status_code}: {response.text}"
74
                )
NEW
75
                return
×
76

77
        except requests.exceptions.ConnectionError:
1✔
78
            logger.warning(f"ACA-Py Agent unreachable at {admin_url}")
1✔
79
        except Exception as e:
1✔
80
            logger.error(f"Unexpected error during webhook registration: {str(e)}")
1✔
81
            return
1✔
82

83
        if attempt < max_retries - 1:
1✔
84
            delay = base_delay * (2**attempt)
1✔
85
            logger.debug(
1✔
86
                f"Retrying webhook registration in {delay} seconds (Attempt {attempt + 1}/{max_retries})"
87
            )
88
            await asyncio.sleep(delay)
1✔
89

90
    logger.error(
1✔
91
        "Failed to register webhook after multiple attempts. Agent notification may fail."
92
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc