-
Notifications
You must be signed in to change notification settings - Fork 75
Fix: add auth headers and body to multitenancy token request #908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix: add auth headers and body to multitenancy token request #908
Conversation
Pull Request Test Coverage Report for Build 19694902885Details
💛 - Coveralls |
5bb27f0 to
0531ff3
Compare
Signed-off-by: Yuki I <[email protected]>
0531ff3 to
060295c
Compare
|
rebased |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a bug where the OIDC controller failed to authenticate with secured ACA-Py instances when requesting wallet tokens. The fix adds proper authentication headers and the required wallet_key parameter to the multitenancy token request endpoint.
Key Changes:
- Modified
MultiTenantAcapy.get_wallet_tokento include Admin API key headers when configured and addwallet_keyto the request body - Refactored error handling from
assertstatements to explicitExceptionraising with improved logging - Updated
SingleTenantAcapy.get_headersto return empty dict instead of dict withNonevalues when API key is not configured
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| oidc-controller/api/core/acapy/config.py | Added authentication header logic and request body payload to get_wallet_token, improved error handling with explicit exceptions, and modified SingleTenantAcapy.get_headers to handle missing credentials gracefully |
| oidc-controller/api/core/acapy/tests/test_config.py | Added comprehensive tests for authenticated and unauthenticated scenarios, updated existing error test to expect Exception instead of AssertionError, and added test for SingleTenantAcapy with missing API key |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| wallet_id = "wallet_id" | ||
| wallet_key = "wallet_key" | ||
|
|
||
| with mock.patch.object(settings, "MT_ACAPY_WALLET_ID", wallet_id): |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test should also mock MT_ACAPY_WALLET_KEY for consistency and test isolation, similar to the other tests in this file (lines 80-88 and 121-129). Without mocking this setting, the test relies on the actual environment configuration or default value, which could lead to inconsistent test behavior across different environments.
| acapy = MultiTenantAcapy() | ||
| acapy.wallet_id = "wallet_id" |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test doesn't set wallet_key or mock MT_ACAPY_WALLET_KEY, but the implementation now includes wallet_key in the request body (config.py line 42). For better test isolation and consistency with other tests in this file (e.g., lines 70-110, 113-150), consider mocking MT_ACAPY_WALLET_KEY and explicitly setting acapy.wallet_key.
| try: | ||
| resp_raw = requests.post( | ||
| settings.ACAPY_ADMIN_URL | ||
| + f"/multitenancy/wallet/{self.wallet_id}/token", | ||
| headers=headers, | ||
| json=payload, | ||
| ) | ||
|
|
||
| if resp_raw.status_code != 200: | ||
| error_detail = resp_raw.content.decode() | ||
| logger.error( | ||
| f"Failed to get wallet token. Status: {resp_raw.status_code}, Detail: {error_detail}" | ||
| ) | ||
| # Raising Exception to be caught by the except block below or propagated | ||
| raise Exception(f"{resp_raw.status_code}::{error_detail}") | ||
|
|
||
| resp = json.loads(resp_raw.content) | ||
| wallet_token = resp["token"] | ||
|
|
||
| except Exception as e: | ||
| logger.error(f"Exception occurred while getting wallet token: {str(e)}") | ||
| raise | ||
|
|
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The try-except block is redundant. The exception raised at line 58 is immediately caught at line 63, logged, and re-raised. This adds no value since:
- The exception message is already logged at line 54-56 before raising
- The except block at line 63-65 logs the same exception again with
str(e)which produces the same message - The exception is simply re-raised without any transformation
Consider removing the try-except wrapper entirely, or only catch specific exceptions (like requests.RequestException, json.JSONDecodeError) if you need to handle network/parsing errors differently.
| try: | |
| resp_raw = requests.post( | |
| settings.ACAPY_ADMIN_URL | |
| + f"/multitenancy/wallet/{self.wallet_id}/token", | |
| headers=headers, | |
| json=payload, | |
| ) | |
| if resp_raw.status_code != 200: | |
| error_detail = resp_raw.content.decode() | |
| logger.error( | |
| f"Failed to get wallet token. Status: {resp_raw.status_code}, Detail: {error_detail}" | |
| ) | |
| # Raising Exception to be caught by the except block below or propagated | |
| raise Exception(f"{resp_raw.status_code}::{error_detail}") | |
| resp = json.loads(resp_raw.content) | |
| wallet_token = resp["token"] | |
| except Exception as e: | |
| logger.error(f"Exception occurred while getting wallet token: {str(e)}") | |
| raise | |
| resp_raw = requests.post( | |
| settings.ACAPY_ADMIN_URL | |
| + f"/multitenancy/wallet/{self.wallet_id}/token", | |
| headers=headers, | |
| json=payload, | |
| ) | |
| if resp_raw.status_code != 200: | |
| error_detail = resp_raw.content.decode() | |
| logger.error( | |
| f"Failed to get wallet token. Status: {resp_raw.status_code}, Detail: {error_detail}" | |
| ) | |
| # Raising Exception to be caught by the except block below or propagated | |
| raise Exception(f"{resp_raw.status_code}::{error_detail}") | |
| resp = json.loads(resp_raw.content) | |
| wallet_token = resp["token"] |
|
Tested out locally with our usual single tenant/no-key setup and that's still working for me. Some minor Copilot suggestions that could be looked at. Didn't try out any mult-tenant setup but implementation makes sense to me |
esune
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested locally, everything works as expected on a multi-tenant instance of ACA-Py. I think addressing the couple comments from Copilot is beneficial, then we can merge.
Description:
MultiTenantAcapy.get_wallet_tokento include Admin API key headers when configured.wallet_keyto the request body payload as required by the endpoint.SingleTenantAcapy.get_headersto return an empty dict instead ofNonevalues when keys are missing.MultiTenantAcapyto raiseExceptioninstead ofAssertionErrorfor better error handling.401 Unauthorizederrors when the controller attempts to get a wallet token from a secured ACA-Py agent instance.