diff options
| author | Caroline Larimore <caroline@larimo.re> | 2025-08-18 14:37:07 -0700 |
|---|---|---|
| committer | Caroline Larimore <caroline@larimo.re> | 2025-08-18 14:37:07 -0700 |
| commit | 8ca8b344ba9ea4c8eb8c72822120f414bf0d2c72 (patch) | |
| tree | ceafb0249f4060c4c4d9adfb7b307a963c6e6436 /mcsh.sh | |
| parent | 8a012c97b7ba3777d8d4b9c3a79416d5803a0877 (diff) | |
feat: msa authentication
Diffstat (limited to 'mcsh.sh')
| -rwxr-xr-x | mcsh.sh | 91 |
1 files changed, 90 insertions, 1 deletions
@@ -8,6 +8,92 @@ NATIVES_DIR="natives" ASSETS_DIR="assets" GAME_DIR="run" +function auth ( + CLIENT_ID="9e97542c-b7d5-4a02-a656-4559dad4590a" + DEVICE_CODE_URL="https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode" + TOKEN_URL="https://login.microsoftonline.com/consumers/oauth2/v2.0/token" + XBL_AUTH_URL="https://user.auth.xboxlive.com/user/authenticate" + XSTS_TOKEN_URL="https://xsts.auth.xboxlive.com/xsts/authorize" + MC_AUTH_URL="https://api.minecraftservices.com/authentication/login_with_xbox" + + function login ( + printf 'getting device code\n' >&2 + res="$(curl -s -X POST "$DEVICE_CODE_URL" -H 'Content-Type: application/x-www-form-urlencoded' -d "client_id=$CLIENT_ID&scope=XboxLive.signin%20offline_access")" + device_code="$(<<<"$res" jq -r '.device_code')" + message="$(<<<"$res" jq -r '.message')" + + printf '%s\n' "$message" >&2 + + printf 'waiting for token...\n' >&2 + pending=true + while [[ "$pending" == true ]]; do + res="$(curl -s -X POST "$TOKEN_URL" -H 'Content-Type: application/x-www-form-urlencoded' -d "grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id=$CLIENT_ID&device_code=$device_code")" + pending="$(<<<"$res" jq -r '.error == "authorization_pending"')" + sleep 1 + done + + printf '%s\n' "$res" + ) + + if [[ -z "$(jq -r '.refresh_token // ""' auth.json)" ]]; then + printf 'logging in\n' >&2 + res="$(login)" + else + printf 'refreshing msa auth\n' >&2 + refresh_token="$(jq -r '.refresh_token' auth.json)" + res="$(curl -s -X POST "$TOKEN_URL" -H 'Content-Type: application/x-www-form-urlencoded' -d "grant_type=refresh_token&client_id=$CLIENT_ID&refresh_token=$refresh_token")" + fi + + refresh_token="$(<<<"$res" jq -r '.refresh_token')" + msa_token="$(<<<"$res" jq -r '.access_token')" + + auth="$(jq --arg token "$refresh_token" '. + {"refresh_token": $token}' auth.json)" + <<<"$auth" cat > auth.json + + printf 'authenticating with xbox live\n' >&2 + req="$(jq -n --arg token "$msa_token" '{"Properties": {"AuthMethod": "RPS", "SiteName": "user.auth.xboxlive.com", "RpsTicket": "d=\($token)"}, "RelyingParty": "http://auth.xboxlive.com", "TokenType": "JWT"}')" + res="$(curl -s -X POST "$XBL_AUTH_URL" -H 'Content-Type: application/json' -H 'Accept: application/json' -d "$req")" + xuid="$(<<<"$res" jq -r '.DisplayClaims.xui[0].uhs')" + xbl_token="$(<<<"$res" jq -r '.Token')" + + # not 100% sure this is *actually* the xuid, but the game doesnt seem to + # care, even if passed a nonsense value + auth="$(jq --arg xuid "$xuid" '. + {"xuid": $xuid}' auth.json)" + <<<"$auth" cat > auth.json + + printf 'getting xsts token\n' >&2 + req="$(jq -n --arg token "$xbl_token" '{"Properties": {"SandboxId": "RETAIL", "UserTokens": [ $token ]}, "RelyingParty": "rp://api.minecraftservices.com/", "TokenType": "JWT"}')" + res="$(curl -s -X POST "$XSTS_TOKEN_URL" -H 'Content-Type: application/json' -H 'Accept: application/json' -d "$req")" + xsts_token="$(<<<"$res" jq -r '.Token')" + + printf 'authenticating with mincraft\n' >&2 + req="$(jq -n --arg xuid "$xuid" --arg token "$xsts_token" '{"identityToken": "XBL3.0 x=\($xuid);\($token)"}')" + res="$(curl -s -X POST "$MC_AUTH_URL" -H 'Content-Type: application/json' -H 'Accept: application/json' -d "$req")" + mc_token="$(<<<"$res" jq -r '.access_token')" + + auth="$(jq --arg token "$mc_token" '. + {"access_token": $token}' auth.json)" + <<<"$auth" cat > auth.json + + printf '%s\n' "$mc_token" +) + +function update_profile ( + MC_PROFILE_URL="https://api.minecraftservices.com/minecraft/profile" + + token="$1" + + printf 'getting minecraft profile\n' >&2 + res="$(curl -s -X GET "$MC_PROFILE_URL" -H "Authorization: Bearer $token")" + if [[ -n "$(<<<"$res" jq -r '.error // ""')" ]]; then + printf 'error: user does not own minecraft\n' >&2 + exit 1 + fi + + #TODO: hyphenate uuid, for :sparkles: style :sparkles: + auth="$(<<<"$res" jq --slurpfile auth auth.json '$auth[0] + {"player_name": .name, "uuid": .id}')" + <<<"$auth" cat > auth.json +) + function update_metadata ( printf 'updating version manifest\n' >&2 mkdir -p "$VERSIONS_DIR" @@ -29,6 +115,9 @@ function update_metadata ( ) function launch ( + update_profile "$(auth)" + printf '\n' >&2 + meta="$(update_metadata "${1:-}")" export LIBRARIES_DIR @@ -81,7 +170,7 @@ function launch ( -e "s/\${launcher_version}/v0.1.0/g" } - printf 'starting game :3\n' >&2 + printf '\nstarting game :3\n\n' >&2 <<<"$meta" jq -r '.args.jvm + [.main_class] + .args.game | join(" ")' | replace_placeholders | xargs java ) |