How to add license keys to a .NET or Unity tool
Short answer: dotnet add package SunLicense, check the key at start-up with ValidateAsync, stop (or switch to the free features) when SunLicense says no, and keep working when it can't be reached. The SDK targets netstandard2.0, so the same code runs in .NET 6+, .NET Framework 4.7.2+, Mono and Unity.
using SunLicense;
using var client = new SunLicenseClient("https://api.sunlicense.com", token: ApiKey); // API key from your build
try
{
var r = await client.ValidateAsync(licenseKey, new Dictionary<string, string> { ["host"] = MachineId() });
if (r.StatusCode == 400) // EXPIRED, REVOKED, TOO_MANY_MACHINES…: Body has the code
{
Console.Error.WriteLine($"License rejected: {r.Body}");
return 1;
}
}
catch (SunLicenseException e)
{
Console.Error.WriteLine($"SunLicense can't be reached, running anyway: {e.Message}");
}ValidateAsync returns IsValid, StatusCode and Body; it throws SunLicenseException only when SunLicense can't be reached.
Or let your AI agent do it: paste the setup prompt into Claude Code, Cursor, Codex or Copilot in your solution's folder.
Free plan: 3 products, 100 licenses, no credit card. Create a free account.
Step by step
- In the dashboard: a product, a policy (for example 2 machines) and an API key, which can only validate.
- Install:
dotnet add package SunLicense. In Unity, putSunLicense.dll(from the NuGet package'slib/netstandard2.0) in anEditor/orPlugins/folder. - Your API key goes in at build time (an MSBuild property written into a generated class, or a CI secret), never in the repository.
- The buyer's key: a settings file in their app data folder (
Environment.SpecialFolder.ApplicationData), or in a Unity editor toolEditorPrefs.SetString("MyTool.License", key)from a small activation window. - Machine id as
host: hashEnvironment.MachineNamewith the OS and architecture; in Unity,SystemInfo.deviceUniqueIdentifieris already stable per machine. Choosing a machine id. - Results: 200 runs, 400 stops with the reason (mask keys in logs and UI), anything else runs and checks again later. Desktop users go offline: accept a recent good check, or hand out a signed offline license file.
- Reuse one client. It wraps an
HttpClient; create one per app (or pass your ownHttpClientto the constructor).
Unity editor tools
[InitializeOnLoad]
static class MyToolLicense
{
public static bool Active { get; private set; } = true;
static MyToolLicense() => _ = Check();
static async Task Check()
{
var key = EditorPrefs.GetString("MyTool.License", "");
if (key == "") { Active = false; return; }
using var client = new SunLicenseClient("https://api.sunlicense.com", token: ApiKey);
try
{
var r = await client.ValidateAsync(key, new Dictionary<string, string> { ["host"] = SystemInfo.deviceUniqueIdentifier });
Active = r.StatusCode != 400;
}
catch (SunLicenseException) { /* offline: stay active */ }
}
}Your menu items and windows check MyToolLicense.Active. Only the editor needs the check: tools that never go into the player build don't ship it to the games your buyers make.
Unity Asset Store: its submission guidelines restrict DRM, registration and features locked behind a key, so a package sold there shouldn't check a license. Use this for tools you sell yourself (itch.io, Gumroad, Lemon Squeezy, Payhip, your own site), and read the current Asset Store submission guidelines before you list.
Going further
- Protection: seal part of the tool so a copy with the check removed has nothing to run.
- Selling a FiveM resource in C#? The same SDK works there; the FiveM guide covers Tebex and escrow.
- More on licensing game dev tools and desktop apps.
