feat(V2): IBKR live session token mint via DH key exchange
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64 as _b64
|
||||
import re
|
||||
import time
|
||||
|
||||
import pytest
|
||||
from cerbero_mcp.exchanges.ibkr.oauth import (
|
||||
OAuth1aSigner,
|
||||
build_signature_base_string,
|
||||
)
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding, rsa
|
||||
from pytest_httpx import HTTPXMock
|
||||
|
||||
|
||||
def test_signature_base_string_canonical_order():
|
||||
@@ -83,3 +87,47 @@ def test_oauth_signer_signs_with_rsa(tmp_path):
|
||||
padding.PKCS1v15(),
|
||||
hashes.SHA256(),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_live_session_token_mint(httpx_mock: HTTPXMock, tmp_path):
|
||||
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||
pem = key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
(tmp_path / "sig.pem").write_bytes(pem)
|
||||
(tmp_path / "enc.pem").write_bytes(pem)
|
||||
|
||||
# Encrypt fake "real" secret with our public key
|
||||
raw_secret = b"my_real_token_secret"
|
||||
encrypted = key.public_key().encrypt(raw_secret, padding.PKCS1v15())
|
||||
encrypted_hex = encrypted.hex()
|
||||
|
||||
httpx_mock.add_response(
|
||||
url=re.compile(r".*/oauth/live_session_token"),
|
||||
json={
|
||||
"diffie_hellman_response": "ff",
|
||||
"live_session_token_signature": "00" * 20,
|
||||
"live_session_token_expiration": int(time.time() * 1000) + 86400000,
|
||||
},
|
||||
)
|
||||
|
||||
from cerbero_mcp.exchanges.ibkr.oauth import OAuth1aSigner
|
||||
signer = OAuth1aSigner(
|
||||
consumer_key="TEST_CK",
|
||||
access_token="TEST_AT",
|
||||
access_token_secret=encrypted_hex,
|
||||
signature_key_path=str(tmp_path / "sig.pem"),
|
||||
encryption_key_path=str(tmp_path / "enc.pem"),
|
||||
dh_prime="ff",
|
||||
)
|
||||
lst = await signer.get_live_session_token(
|
||||
base_url="https://api.ibkr.com/v1/api"
|
||||
)
|
||||
assert isinstance(lst, str) and len(lst) > 0
|
||||
lst2 = await signer.get_live_session_token(
|
||||
base_url="https://api.ibkr.com/v1/api"
|
||||
)
|
||||
assert lst == lst2 # cached
|
||||
|
||||
Reference in New Issue
Block a user