Skip to content

Commit de1def7

Browse files
committed
signed session: Add example of usage to AuthenticationManager
1 parent 0a9d24d commit de1def7

10 files changed

Lines changed: 97 additions & 40 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ API usage
5959
```py
6060
import sys
6161
import asyncio
62-
from httpx import AsyncClient, HTTPStatusError
62+
from httpx import HTTPStatusError
63+
from xbox.webapi.common.signed_session import SignedSession
6364
from xbox.webapi.api.client import XboxLiveClient
6465
from xbox.webapi.authentication.manager import AuthenticationManager
6566
from xbox.webapi.authentication.models import OAuth2TokenResponse
@@ -75,7 +76,7 @@ tokens_file = TOKENS_FILE
7576
For doing authentication, see xbox/webapi/scripts/authenticate.py
7677
"""
7778
async def async_main():
78-
async with AsyncClient() as session:
79+
async with SignedSession() as session:
7980
auth_mgr = AuthenticationManager(
8081
session, client_id, client_secret, ""
8182
)

readme_example.py

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import sys
21
import asyncio
3-
from httpx import AsyncClient, HTTPStatusError
2+
import sys
3+
4+
from httpx import HTTPStatusError
5+
46
from xbox.webapi.api.client import XboxLiveClient
57
from xbox.webapi.authentication.manager import AuthenticationManager
68
from xbox.webapi.authentication.models import OAuth2TokenResponse
9+
from xbox.webapi.common.signed_session import SignedSession
710
from xbox.webapi.scripts import CLIENT_ID, CLIENT_SECRET, TOKENS_FILE
811

912
# This uses the default client identification by OpenXbox
@@ -15,55 +18,58 @@
1518
"""
1619
For doing authentication, see xbox/webapi/scripts/authenticate.py
1720
"""
21+
22+
1823
async def async_main():
19-
async with AsyncClient() as session:
20-
auth_mgr = AuthenticationManager(
21-
session, client_id, client_secret, ""
22-
)
24+
async with SignedSession() as session:
25+
auth_mgr = AuthenticationManager(session, client_id, client_secret, "")
2326

2427
try:
25-
with open(tokens_file, mode="r") as f:
26-
tokens = f.read()
28+
with open(tokens_file) as f:
29+
tokens = f.read()
2730
auth_mgr.oauth = OAuth2TokenResponse.parse_raw(tokens)
2831
except FileNotFoundError:
29-
print(f'File {tokens_file} isn`t found or it doesn`t contain tokens!')
32+
print(f"File {tokens_file} isn`t found or it doesn`t contain tokens!")
3033
exit(-1)
31-
34+
3235
try:
33-
await auth_mgr.refresh_tokens()
36+
await auth_mgr.refresh_tokens()
3437
except HTTPStatusError:
35-
print("Could not refresh tokens")
36-
sys.exit(-1)
38+
print("Could not refresh tokens")
39+
sys.exit(-1)
3740

3841
with open(tokens_file, mode="w") as f:
39-
f.write(auth_mgr.oauth.json())
40-
print(f'Refreshed tokens in {tokens_file}!')
42+
f.write(auth_mgr.oauth.json())
43+
print(f"Refreshed tokens in {tokens_file}!")
4144

4245
xbl_client = XboxLiveClient(auth_mgr)
4346

4447
# Some example API calls
4548
# Get friendslist
4649
friendslist = await xbl_client.people.get_friends_own()
47-
print('Your friends:')
50+
print("Your friends:")
4851
print(friendslist)
4952
print()
5053

5154
# Get presence status (by list of XUID)
52-
presence = await xbl_client.presence.get_presence_batch(["2533274794093122", "2533274807551369"])
53-
print('Statuses of some random players by XUID:')
55+
presence = await xbl_client.presence.get_presence_batch(
56+
["2533274794093122", "2533274807551369"]
57+
)
58+
print("Statuses of some random players by XUID:")
5459
print(presence)
5560
print()
5661

5762
# Get messages
5863
messages = await xbl_client.message.get_inbox()
59-
print('Your messages:')
64+
print("Your messages:")
6065
print(messages)
6166
print()
6267

6368
# Get profile by GT
6469
profile = await xbl_client.profile.get_profile_by_gamertag("SomeGamertag")
65-
print('Profile under SomeGamertag gamer tag:')
70+
print("Profile under SomeGamertag gamer tag:")
6671
print(profile)
6772
print()
6873

69-
asyncio.run(async_main())
74+
75+
asyncio.run(async_main())

tests/conftest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from datetime import datetime
22

33
from ecdsa.keys import SigningKey, VerifyingKey
4-
from httpx import AsyncClient
54
import pytest
65
import pytest_asyncio
76

@@ -13,6 +12,7 @@
1312
XSTSResponse,
1413
)
1514
from xbox.webapi.common.request_signer import RequestSigner
15+
from xbox.webapi.common.signed_session import SignedSession
1616

1717
from tests.common import get_response
1818

@@ -21,7 +21,7 @@
2121

2222
@pytest_asyncio.fixture(scope="function")
2323
async def auth_mgr(event_loop):
24-
session = AsyncClient()
24+
session = SignedSession()
2525
mgr = AuthenticationManager(session, "abc", "123", "http://localhost")
2626
mgr.oauth = OAuth2TokenResponse.parse_raw(get_response("auth_oauth2_token"))
2727
mgr.user_token = XAUResponse.parse_raw(get_response("auth_user_token"))
@@ -45,10 +45,12 @@ def ecdsa_signing_key_str() -> str:
4545
def ecdsa_signing_key(ecdsa_signing_key_str: str) -> SigningKey:
4646
return SigningKey.from_pem(ecdsa_signing_key_str)
4747

48+
4849
@pytest.fixture(scope="session")
4950
def ecdsa_verifying_key(ecdsa_signing_key: SigningKey) -> VerifyingKey:
5051
return ecdsa_signing_key.get_verifying_key()
5152

53+
5254
@pytest.fixture(scope="session")
5355
def synthetic_request_signer(ecdsa_signing_key) -> RequestSigner:
5456
return RequestSigner(ecdsa_signing_key)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"IssueInstant": "2010-10-10T03:04:29.6037497Z",
3+
"NotAfter": "2999-10-24T03:04:29.6037497Z",
4+
"Token": "eyJhYoToken",
5+
"DisplayClaims": {
6+
"xdi": {
7+
"did": "F9E13DC7B0997D0D",
8+
"dcs": "0"
9+
}
10+
}
11+
}

tests/test_auth.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime, timedelta, timezone
2+
import uuid
23

34
from httpx import Response
45
import pytest
@@ -60,7 +61,7 @@ async def test_refresh_tokens(respx_mock, auth_mgr):
6061

6162

6263
@pytest.mark.asyncio
63-
async def test_refresh_tokens_still_valid(respx_mock, auth_mgr):
64+
async def test_refresh_tokens_still_valid(auth_mgr):
6465
now = datetime.now(timezone.utc)
6566
auth_mgr.oauth.issued = now
6667
auth_mgr.user_token.not_after = now + timedelta(days=1)
@@ -96,6 +97,17 @@ async def test_get_title_endpoints(respx_mock, auth_mgr):
9697
assert route.called
9798

9899

100+
@pytest.mark.asyncio
101+
async def test_get_device_token(respx_mock, auth_mgr):
102+
route = respx_mock.post(
103+
"https://device.auth.xboxlive.com/device/authenticate"
104+
).mock(return_value=Response(200, json=get_response_json("auth_device_token")))
105+
resp = await auth_mgr.request_device_token(
106+
uuid.UUID("9c493431-5462-4a4a-a247-f6420396318d")
107+
)
108+
assert route.called
109+
110+
99111
@pytest.mark.asyncio
100112
async def test_xsts_properties(auth_mgr):
101113
assert auth_mgr.xsts_token.xuid == "2669321029139235"

xbox/webapi/authentication/manager.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,20 @@
55
"""
66
import logging
77
from typing import List, Optional
8+
import uuid
89

910
import httpx
1011
from yarl import URL
1112

1213
from xbox.webapi.authentication.models import (
1314
OAuth2TokenResponse,
1415
TitleEndpointsResponse,
16+
XADResponse,
1517
XAUResponse,
1618
XSTSResponse,
1719
)
1820
from xbox.webapi.common.exceptions import AuthenticationException
21+
from xbox.webapi.common.signed_session import SignedSession
1922

2023
log = logging.getLogger("authentication")
2124

@@ -25,13 +28,13 @@
2528
class AuthenticationManager:
2629
def __init__(
2730
self,
28-
client_session: httpx.Client,
31+
client_session: SignedSession,
2932
client_id: str,
3033
client_secret: str,
3134
redirect_uri: str,
3235
scopes: Optional[List[str]] = None,
3336
):
34-
self.session: httpx.Client = client_session
37+
self.session: SignedSession = client_session
3538
self._client_id: str = client_id
3639
self._client_secret: str = client_secret
3740
self._redirect_uri: str = redirect_uri
@@ -160,3 +163,23 @@ async def request_xsts_token(
160163
raise AuthenticationException()
161164
resp.raise_for_status()
162165
return XSTSResponse(**resp.json())
166+
167+
async def request_device_token(self, device_id: uuid.UUID) -> XADResponse:
168+
url = "https://device.auth.xboxlive.com/device/authenticate"
169+
headers = {"x-xbl-contract-version": "1"}
170+
data = {
171+
"RelyingParty": "http://auth.xboxlive.com",
172+
"TokenType": "JWT",
173+
"Properties": {
174+
"AuthMethod": "ProofOfPossession",
175+
"Id": str(device_id).upper(),
176+
"DeviceType": "Win32",
177+
"Version": "10.0.22000.194",
178+
"ProofKey": self.session.request_signer.proof_field,
179+
},
180+
}
181+
182+
request = httpx.Request("POST", url, headers=headers, json=data)
183+
resp = await self.session.send_signed(request)
184+
resp.raise_for_status()
185+
return XADResponse(**resp.json())

xbox/webapi/scripts/authenticate.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
from urllib.parse import parse_qs, urlparse
1212
import webbrowser
1313

14-
from httpx import AsyncClient
15-
1614
from xbox.webapi.authentication.manager import AuthenticationManager
1715
from xbox.webapi.authentication.models import OAuth2TokenResponse
16+
from xbox.webapi.common.signed_session import SignedSession
1817
from xbox.webapi.scripts import CLIENT_ID, CLIENT_SECRET, REDIRECT_URI, TOKENS_FILE
1918

2019
QUEUE = queue.Queue(1)
@@ -74,14 +73,14 @@ def do_GET(self):
7473
async def do_auth(
7574
client_id: str, client_secret: str, redirect_uri: str, token_filepath: str
7675
):
77-
async with AsyncClient() as session:
76+
async with SignedSession() as session:
7877
auth_mgr = AuthenticationManager(
7978
session, client_id, client_secret, redirect_uri
8079
)
8180

8281
# Refresh tokens if we have them
8382
if os.path.exists(token_filepath):
84-
with open(token_filepath, mode="r") as f:
83+
with open(token_filepath) as f:
8584
tokens = f.read()
8685
auth_mgr.oauth = OAuth2TokenResponse.parse_raw(tokens)
8786
await auth_mgr.refresh_tokens()

xbox/webapi/scripts/change_gamertag.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import sys
88

9-
from httpx import AsyncClient, HTTPStatusError
9+
from httpx import HTTPStatusError
1010

1111
from xbox.webapi.api.client import XboxLiveClient
1212
from xbox.webapi.api.provider.account.models import (
@@ -15,6 +15,7 @@
1515
)
1616
from xbox.webapi.authentication.manager import AuthenticationManager
1717
from xbox.webapi.authentication.models import OAuth2TokenResponse
18+
from xbox.webapi.common.signed_session import SignedSession
1819
from xbox.webapi.scripts import CLIENT_ID, CLIENT_SECRET, TOKENS_FILE
1920

2021

@@ -50,12 +51,12 @@ async def async_main():
5051
print("No token file found, run xbox-authenticate")
5152
sys.exit(-1)
5253

53-
async with AsyncClient() as session:
54+
async with SignedSession() as session:
5455
auth_mgr = AuthenticationManager(
5556
session, args.client_id, args.client_secret, ""
5657
)
5758

58-
with open(args.tokens, mode="r") as f:
59+
with open(args.tokens) as f:
5960
tokens = f.read()
6061
auth_mgr.oauth = OAuth2TokenResponse.parse_raw(tokens)
6162
try:

xbox/webapi/scripts/friends.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
from pprint import pprint
88
import sys
99

10-
from httpx import AsyncClient, HTTPStatusError
10+
from httpx import HTTPStatusError
1111

1212
from xbox.webapi.api.client import XboxLiveClient
1313
from xbox.webapi.authentication.manager import AuthenticationManager
1414
from xbox.webapi.authentication.models import OAuth2TokenResponse
15+
from xbox.webapi.common.signed_session import SignedSession
1516
from xbox.webapi.scripts import CLIENT_ID, CLIENT_SECRET, TOKENS_FILE
1617

1718

@@ -42,12 +43,12 @@ async def async_main():
4243
print("No token file found, run xbox-authenticate")
4344
sys.exit(-1)
4445

45-
async with AsyncClient() as session:
46+
async with SignedSession() as session:
4647
auth_mgr = AuthenticationManager(
4748
session, args.client_id, args.client_secret, ""
4849
)
4950

50-
with open(args.tokens, mode="r") as f:
51+
with open(args.tokens) as f:
5152
tokens = f.read()
5253
auth_mgr.oauth = OAuth2TokenResponse.parse_raw(tokens)
5354
try:

xbox/webapi/scripts/search.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
from pprint import pprint
77
import sys
88

9-
from httpx import AsyncClient, HTTPStatusError
9+
from httpx import HTTPStatusError
1010

1111
from xbox.webapi.api.client import XboxLiveClient
1212
from xbox.webapi.authentication.manager import AuthenticationManager
13+
from xbox.webapi.common.signed_session import SignedSession
1314

1415

1516
async def async_main():
@@ -18,7 +19,7 @@ async def async_main():
1819

1920
args = parser.parse_args()
2021

21-
async with AsyncClient() as session:
22+
async with SignedSession() as session:
2223
auth_mgr = AuthenticationManager(session, "", "", "")
2324

2425
# No Auth necessary for catalog searches

0 commit comments

Comments
 (0)