OpenSpartan.Grunt 0.1.1

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package OpenSpartan.Grunt --version 0.1.1
NuGet\Install-Package OpenSpartan.Grunt -Version 0.1.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OpenSpartan.Grunt" Version="0.1.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add OpenSpartan.Grunt --version 0.1.1
#r "nuget: OpenSpartan.Grunt, 0.1.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install OpenSpartan.Grunt as a Cake Addin
#addin nuget:?package=OpenSpartan.Grunt&version=0.1.1

// Install OpenSpartan.Grunt as a Cake Tool
#tool nuget:?package=OpenSpartan.Grunt&version=0.1.1

Grunt logo

🪐 Grunt API - The Halo API Wrapper

Your one-stop-shop for the official undocumented Halo API

Stand With Ukraine Publish API Documentation Publish In-Repo NuGet Package Publish NuGet Package

Welcome to Grunt API - the unofficial way to use official undocumented Halo APIs. Here be a lot of dragons and this is not yet ready to be a standalone package, since the changes will be frequent and large. That said, you can use it as a test pad for your own explorations.

This API enables you to:

  • Get stats on matches you played.
  • Get your personal player stats.
  • Track campaign progress.
  • Track map popularity

And more!

Table of contents

Platform Support

.NET

NuGet download link for OpenSpartan.Grunt NuGet download link for OpenSpartan.Grunt with download counter

Python

In development

Node.js

In development

Components

Component Description
Grunt The core library, written in C#, that wraps the Halo Infinite web APIs.
Grunt.Zeta Experimental ground for the Grunt library. This will eventually become the Grunt CLI.
Grunt.Librarian Tool used to auto-generate code stubs for Halo Infinite API endpoints. It's a very "brute"-ish way to generate the code, but it works for now.

Setup & usage

The core requirement to use the endpoints in the library is to have a Spartan token, that is provided by the Halo Infinite service. That being said, there are two ways to experiment with the library:

  1. Bring your own Spartan token. That means that you can obtain it on your own through man-in-the-middle inspection of the app/game traffic, or by grabbing it from the Halo Waypoint site. Read more on that in the section below.
  2. Executing the full authentication flow yourself. This is a bit more complex, but doable because Grunt API wraps all the required methods out-of-the-box. For details, see section below.

Bring your own token

If you want to bring your own token, you carry the responsibility of acquiring and getting an up-to-date version of the Spartan token (they do expire frequently). The easiest way to do that is by looking at the Halo Waypoint site through the lens of your browser's Network Inspector.

Look for API calls that return JSON data, and in some of the request headers you will notice a particularly interesting one - x-343-authorization-spartan. That's what you need.

Acquiring the Spartan token from the Halo Waypoint website

I'll say it again - this token is not long-lived and if you see calls failing with 401 Unauthorized, that means you need a new token.

Some API calls are also requiring you include another header - 343-clearance. This token is obtained through a separate API call that I am yet to document, but you can also grab it from the Halo Waypoint site. An example API call that you can watch for with the Synthwave event going on is this:

https://gamecms-hacs-origin.svc.halowaypoint.com/hi/Progression/file/ChallengeContent/ClientChallengeDefinitions/S1EventSynthwaveChallenges/Normal/NSynthwaveMedalRevive.json

If you look for it in the network inspector, you will get the 343-clearance header as well. It's on my TODO list to document available endpoints and whether they require clearance or not.

Once you have the Spartan and clearance tokens, you are good to go, and can now call the API endpoints from Grunt.

HaloInfiniteClient client = new(<YOUR_SPARTAN_TOKEN>, <YOUR_CLEARANCE_TOKEN>, <YOUR_XUID_REQUIRED_ONLY_FOR_SOME_CALLS>);

// Try getting actual Halo Infinite data.
Task.Run(async () =>
{
    var example = await client.StatsGetMatchStats("21416434-4717-4966-9902-af7097469f74");
    Console.WriteLine("You have data.");
}).GetAwaiter().GetResult();

Authenticate yourself

IMPORTANT: The instructions below are using Visual Studio 2019, but are going to work with Visual Studio 2022, which you can download for free.

If you want to automatically generate the Spartan token, you can do so with the help of Grunt API without having to worry about doing any of the REST API calls yourself. Before you get started, make sure that you register an Azure Active Directory application. You will need it in order to log in with your Microsoft account, that will be used to generate the token. Because this is just for you, you can use https://localhost as the redirect URI when you create the application, unless you're thinking of productizing whatever you're building.

With the application created, in the Grunt.Zeta project create a client.json file, that has the following contents:

{
  "client_id": "<YOUR_CLIENT_ID_FROM_AAD>",
  "client_secret": "<YOUR_SECRET_FROM_AAD>",
  "redirect_url": "<YOUR_REDIRECT_URI_FROM_AAD>"
}

When you add the configuration file to your project, make sure that it's Build Action is set to None and Copy to Output Directory is Copy if newer.

Configuration file for Grunt.Zeta

With the file there, you can now run through the authentication flow, that is powered by Grunt's helper methods:

ConfigurationReader clientConfigReader = new();
var clientConfig = clientConfigReader.ReadConfiguration<ClientConfiguration>("client.json");

XboxAuthenticationClient manager = new();
var url = manager.GenerateAuthUrl(clientConfig.ClientId, clientConfig.RedirectUrl);

HaloAuthenticationClient haloAuthClient = new();

OAuthToken currentOAuthToken = null;

var ticket = new XboxTicket();
var haloTicket = new XboxTicket();
var extendedTicket = new XboxTicket();

var xblToken = string.Empty;
var haloToken = new SpartanToken();

if (System.IO.File.Exists("tokens.json"))
{
    Console.WriteLine("Trying to use local tokens...");
    // If a local token file exists, load the file.
    currentOAuthToken = clientConfigReader.ReadConfiguration<OAuthToken>("tokens.json");
}
else
{
    currentOAuthToken = RequestNewToken(url, manager, clientConfig);
}

Task.Run(async () =>
{
    ticket = await manager.RequestUserToken(currentOAuthToken.AccessToken);
    if (ticket == null)
    {
        // There was a failure to obtain the user token, so likely we need to refresh.
        currentOAuthToken = await manager.RefreshOAuthToken(clientConfig.ClientId, currentOAuthToken.RefreshToken, clientConfig.RedirectUrl, clientConfig.ClientSecret);
        if (currentOAuthToken == null)
        {
            Console.WriteLine("Could not get the token even with the refresh token.");
            currentOAuthToken = RequestNewToken(url, manager, clientConfig);
        }
        ticket = await manager.RequestUserToken(currentOAuthToken.AccessToken);
    }
}).GetAwaiter().GetResult();

Task.Run(async () =>
{
    haloTicket = await manager.RequestXstsToken(ticket.Token);
}).GetAwaiter().GetResult();

Task.Run(async () =>
{
    extendedTicket = await manager.RequestXstsToken(ticket.Token, false);
}).GetAwaiter().GetResult();

if (ticket != null)
{
    xblToken = manager.GetXboxLiveV3Token(haloTicket.DisplayClaims.Xui[0].Uhs, haloTicket.Token);
}

Task.Run(async () =>
{
    haloToken = await haloAuthClient.GetSpartanToken(haloTicket.Token);
    Console.WriteLine("Your Halo token:");
    Console.WriteLine(haloToken.Token);
}).GetAwaiter().GetResult();

HaloInfiniteClient client = new(haloToken.Token, extendedTicket.DisplayClaims.Xui[0].Xid);

// Test getting the clearance for local execution.
string localClearance = string.Empty;
Task.Run(async () =>
{
    var clearance = (await client.SettingsGetClearance("RETAIL", "UNUSED", "222249.22.06.08.1730-0")).Result;
    if (clearance != null)
    {
        localClearance = clearance.FlightConfigurationId;
        client.ClearanceToken = localClearance;
        Console.WriteLine($"Your clearance is {localClearance} and it's set in the client.");
    }
    else
    {
        Console.WriteLine("Could not obtain the clearance.");
    }
}).GetAwaiter().GetResult();

// Try getting actual Halo Infinite data.
Task.Run(async () =>
{
    var example = await client.StatsGetMatchStats("21416434-4717-4966-9902-af7097469f74");
    Console.WriteLine("You have stats.");
}).GetAwaiter().GetResult();

The code above will try to read tokens locally and refresh them, if available.

NOTE: This is worth additional investigation, but it seems that if the clearance (343-clearance header) is used, it needs to be activated at least once with the game before the API access is granted. That is, you need to launch the game at the latest build on your account before you can start querying the API. If you are running into issues with the API and are getting 403 Forbidden errors, make sure that you start Halo Infinite at least once before retrying.

Once you have the Spartan token, you are good to go and can start issuing API requests. Keep in mind that the Spartan token does expire, so you will need to refresh it along other tokens as well.

Endpoints

Complete list of endpoints can be obtained by querying the official Halo Infinite API, that also helpfully contains all the metadata and requirements for each:

https://settings.svc.halowaypoint.com/settings/hipc/e2a0a7c6-6efe-42af-9283-c2ab73250c48

The endpoint above does not require authentication and can be queried in the open. You can also peruse an offline version of the API response in the library.

Documentation

You can read the docs on the Grunt docs website.

FAQ

Is this in any way endorsed by 343 Industries?

No. Not at all. This is something that I've put together myself by inspecting network traffic.

Something is broken and my production site that uses your library doesn't work. Can you help?

Don't use any of this code in production. It's nowhere near stable.

Some API endpoint is not working anymore or returns an unexpected result. What's up with that?

Open an issue so that I can investigate.

How do I contact the author?

Open an issue or reach out on Twitter.

Contributions

Contributions are welcome, but please first open an issue so that we can discuss before writing any code.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated

Initial release of the OpenSpartan.Grunt package.