I have written an illustrative proof-of-concept implementation in pure, self-contained bash+curl+sed
.
It is heavily inspired by plagiarized/condensed from the xbox.webapi.authentication.manager
module from Team OpenXbox's Xbox-Webapi, which you should probably just use instead*. Theirs is such a good API, covering so much arcana that Microsoft… simply fails to document; that it would be worth strongly considering switching your project to Python just for this library, if it relies on Microsoft's Xbox Live API for its core functionality.
In a nutshell, to hit this API, it appears that you must:
Register an Application in Azure
- if your application can bind to
localhost:8080
on the system of the user who is going to authorize the application, or you otherwise have their co-operation (specifically: they're able to paste the code
parameter into the program from a URL in their browser), you may skip this step, use client_id=0000000048093EE3
, and completely omit client_secret
. (In this case, you do not even need an Azure account.)
Get any** Xbox Live user to provide the Xboxlive.signin
and Xboxlive.offline_access
scopes to your application via OAuth2
Use this authorization and a Bearer token to get a so-called "User Token" from https://user.auth.xboxlive.com/user/authenticate
Use that token to authenticate yourself to https://xsts.auth.xboxlive.com/xsts/authorize
to get an "XToken"
Use that token to authenticate yourself to https://profile.xboxlive.com
for the actual endpoints you're interested in, such as /users/gt({gamertag})/profile/settings
(which contains, among other attributes, the XUID, as a decimal string, at property "id"
)
(**Obviously, if you're hitting privileged endpoints, such those that view private information or modify user account settings, you'll have additional requirements on whose authorization you need and what scopes you'll have to request; but, for the general case of gamertag-to-XUID lookup, a mere sign-in from any user is fine.)
*For this, it'd be something like:
from asyncio import run as arun
from sys import argv
from os import path, environ
import subprocess
from aiohttp import ClientSession
from xbox.webapi.api.client import XboxLiveClient
from xbox.webapi.authentication.manager import AuthenticationManager
from xbox.webapi.authentication.models import OAuth2TokenResponse
async def gamertags_to_xuids(gamertags, client_id=environ["client_id"], client_secret=environ["client_secret"], token_jar=path.join(environ["HOME"], ".local", "share", "xbox", "tokens.json")):
assert len(gamertags) >= 1
global session
auth_mgr = AuthenticationManager(session, client_id, client_secret, "")
await freshen_tokens(auth_mgr, token_jar)
xbl_client = XboxLiveClient(auth_mgr)
ids = []
for gamertag in gamertags:
profile = await xbl_client.profile.get_profile_by_gamertag(gamertag)
ids.append(int(profile.profile_users[0].id))
xuids = [f"{id:016X}" for id in ids]
return xuids
async def freshen_tokens(auth_mgr, token_jar):
with open(token_jar, "r+") as f:
auth_mgr.oauth = OAuth2TokenResponse.parse_raw(f.read())
await auth_mgr.refresh_tokens()
f.seek(0)
f.truncate()
f.write(auth_mgr.oauth.json())
async def amain(args):
global session
subprocess.run(["xbox-authenticate"], check=True)#TODO: avoid this system call
async with ClientSession() as session:
return await gamertags_to_xuids(args)
def main(args=argv[1:]):
return arun(amain(args))
if __name__ == '__main__':
for xuid in main():
print(xuid)