Surprenant.Grunt
0.0.0.1-beta
Prefix Reserved
See the version list below for details.
dotnet add package Surprenant.Grunt --version 0.0.0.1-beta
NuGet\Install-Package Surprenant.Grunt -Version 0.0.0.1-beta
<PackageReference Include="Surprenant.Grunt" Version="0.0.0.1-beta" />
paket add Surprenant.Grunt --version 0.0.0.1-beta
#r "nuget: Surprenant.Grunt, 0.0.0.1-beta"
// Install Surprenant.Grunt as a Cake Addin #addin nuget:?package=Surprenant.Grunt&version=0.0.0.1-beta&prerelease // Install Surprenant.Grunt as a Cake Tool #tool nuget:?package=Surprenant.Grunt&version=0.0.0.1-beta&prerelease
🪐 Grunt API - The Halo API Wrapper
Your one-stop-shop for the official undocumented Halo API
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.
👋 NOTE
In case you are wondering, this API is simply a wrapper over the Halo Waypoint API requests that the game executes while running. Nothing here required looking at anything more complex than a Fiddler trace to see what calls the game makes. To use this API, you will need to go through the authentication flow with your very own credentials.
This projects does not circumvent and has no goals of circumventing any of the existing authentication mechanisms or in-game protections.
⚠️ WARNING
This project wraps the undocumented Halo Waypoint APIs and requires use of your account credentials/tokens. While 343 Industries has not yet raised any concerns over the use of these APIs, I cannot guarantee 343 Industries won't change their position (e.g., ban your account). Use at your own risk.
This API enables you to:
- Get stats on matches you played.
- Get your personal player stats.
- Track your campaign progress.
- Track map and game mode popularity (see -
openspartan-data-snapshots
).
And more!
Table of contents
Platform Support
.NET
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. It's a project where wrapped APIs from Grunt are tested in a more real scenario. |
Grunt.Librarian |
Tool used to auto-generate code stubs for Halo Infinite API endpoints. It's a very "brute"-ish way to produce 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.
⚠️ WARNING
The Spartan token is associated with your identity and your account. Do not share it with anyone, under any circumstances. The API wrapper does not explicitly store it anywhere. It's your responsibility to make sure that it's secure and not available to anyone else.
There are two ways to experiment with the library:
- 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 (what Julia Evans described in her blog post), or by grabbing it from the Halo Waypoint site. Read more on that in the section below.
- 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. You are still using your own identity and account, but will be generating a new Spartan token for your requests. 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 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.
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, but you can also grab it from the Halo Waypoint site. If you look for it in the network inspector, you will get the 343-clearance
header as well.
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
.
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 getting403 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
Q1: Is this in any way endorsed by 343 Industries or Microsoft?
No. Not at all. This is something that I've put together myself by inspecting network traffic. This project is not funded, supported, or otherwise endorsed by either 343 Industries or Microsoft.
Q2: 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, and will never be.
Q3: 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.
Q4: How do I contact the author?
Open an issue or reach out on Twitter.
Q5: Can this be used for commercial purposes?
Absolutely not. This project is exploratory in nature. It has no guarantees, implied or otherwise, of your ability to consume the API. It does not give you any permission to use this in commercial projects, and neither does it guarantee API access or stability. If you are looking at building something serious using the Halo API, you need to reach out to 343 Industries.
Contributions
Contributions are welcome, but please first open an issue so that we can discuss before writing any code.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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. |
-
net8.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 |
---|---|---|
0.0.0.5-beta | 91 | 3/4/2024 |
0.0.0.4-beta | 71 | 2/14/2024 |
0.0.0.3-beta | 64 | 2/12/2024 |
0.0.0.2-beta | 57 | 2/11/2024 |
0.0.0.1-beta | 63 | 2/11/2024 |
Initial release of the OpenSpartan.Grunt package.