3 releases

0.1.2 Dec 21, 2023
0.1.1 Dec 20, 2023
0.1.0 Nov 12, 2022

#406 in Authentication

MIT license

160KB
2.5K SLoC

xal - Xbox Authentication Library for Rust

Crates.io Docs.rs CI

Authenticate with Xbox Live

Documentation

Find the documentation here: https://docs.rs/xal

Examples

Check out xal-examples

Minimum supported Rust version

This crate requires at least Rust 1.70 (stable).

Disclaimer

This is an unofficial library not endorsed by Microsoft. Use at your own risk!


lib.rs:

XAL - Xbox Live Authentication Library for Rust

This library aims at giving a high range of configurability to the user, so that authentication can be targeted inidividually for each required scenario.

Features:

Quick Start

Authenticate and save tokens to JSON file tokens.json

use xal::{XalAuthenticator, Flows, CliCallbackHandler};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut authenticator = XalAuthenticator::default();

    // Do full SISU auth flow
    let mut token_store = Flows::xbox_live_sisu_full_flow(
        &mut authenticator,
        CliCallbackHandler
    ).await?;

    // User will be prompted on commandline to proceed with authentication

    token_store.update_timestamp();
    token_store.save_to_file("tokens.json")?;
    
    Ok(())
}

Load tokens from file and refresh them

use xal::{XalAuthenticator, Flows, CliCallbackHandler};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Trying to refresh tokens...");
    let mut token_store = match Flows::try_refresh_live_tokens_from_file("tokens.json").await {
        Ok((mut authenticator, ts)) => {
            println!("Tokens refreshed succesfully, proceeding with Xbox Live Authorization");
            Flows::xbox_live_sisu_authorization_flow(&mut authenticator, ts.live_token)
                .await?
        },
        Err(err) => {
            eprintln!("Refreshing tokens failed err={err}");
            let mut authenticator = XalAuthenticator::default();
            println!("Authentication via SISU");
            Flows::xbox_live_sisu_full_flow(&mut authenticator, CliCallbackHandler)
                .await?
        }
    };

    token_store.update_timestamp();
    token_store.save_to_file("tokens.json")?;
    Ok(())
}

Make use of acquired XSTS token

use xal::{XalAuthenticator, Flows, CliCallbackHandler};
use xal::extensions::JsonExDeserializeMiddleware;
use xal::oauth2::TokenResponse;
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create an authenticator with minecraft client parameters
    let mut authenticator = XalAuthenticator::new(
        xal::app_params::MC_BEDROCK_SWITCH(),
        xal::client_params::CLIENT_NINTENDO(),
        "RETAIL".into(),
    );

    // Do full SISU authentication flow
    let mut token_store = Flows::xbox_live_sisu_full_flow(
        &mut authenticator,
        CliCallbackHandler
    ).await?;

    // Authorize to XSTS endpoint via Minecraft RelyingParty
    let xsts_mc_services = authenticator
        .get_xsts_token(
            token_store.device_token.as_ref(),
            token_store.title_token.as_ref(),
            token_store.user_token.as_ref(),
            "rp://api.minecraftservices.com/"
        )
        .await?;

    let identity_token = xsts_mc_services.authorization_header_value();
    println!("identityToken: {identity_token}");
    
    /* Minecraft stuff */
    // Exchange XSTS Token against Minecraft Token
    let mc_token = reqwest::Client::new()
        .post("https://api.minecraftservices.com/authentication/login_with_xbox")
        .json(&json!({"identityToken": identity_token}))
        .send()
        .await?
        .json_ex::<xal::oauth2::basic::BasicTokenResponse>()
        .await?;
    println!("MC: {mc_token:?}");
    
    // Get minecraft profile, use Minecraft Token as Bearer Auth
    let profile = reqwest::Client::new()
        .get("https://api.minecraftservices.com/minecraft/profile")
        .bearer_auth(mc_token.access_token().secret())
        .send()
        .await?
        .text()
        .await?;
    println!("Profile: {profile}");
    Ok(())
}

Loading tokens from file and sending a signed a request

use xal::{
    RequestSigner, TokenStore, Error,
    extensions::{
        SigningReqwestBuilder,
        CorrelationVectorReqwestBuilder,
        
    },
    cvlib::CorrelationVector,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load tokens from JSON
    let token_store = TokenStore::load_from_file("tokens.json")?;

    // Create new instances of Correlation vector and request signer
    let mut cv = CorrelationVector::new();
    let mut signer = RequestSigner::new();

    // Check if XSTS token exists
    let xsts_token = token_store.authorization_token
        .ok_or(Error::GeneralError("No XSTS token was acquired".into()))?;

    // Send a http request
    // Request will get signed and MS-CV header populated
    let userpresence = reqwest::Client::new()
        .get("https://userpresence.xboxlive.com/users/me?level=all")
        .header("x-xbl-contract-version", "3")
        .header("Authorization", xsts_token.authorization_header_value())
        .add_cv(&mut cv)?
        .sign(&mut signer, None)
        .await?
        .send()
        .await?;
   
    println!("{:?}", userpresence);   
    Ok(())
}

Examples

Check out the xal-examples.

Advanced

For advanced usage, see crate::XalAuthenticator.

Dependencies

~9–21MB
~334K SLoC