Ahwoo.SessionTickets.Server
1.0.0
Prefix Reserved
dotnet add package Ahwoo.SessionTickets.Server --version 1.0.0
NuGet\Install-Package Ahwoo.SessionTickets.Server -Version 1.0.0
<PackageReference Include="Ahwoo.SessionTickets.Server" Version="1.0.0" />
<PackageVersion Include="Ahwoo.SessionTickets.Server" Version="1.0.0" />
<PackageReference Include="Ahwoo.SessionTickets.Server" />
paket add Ahwoo.SessionTickets.Server --version 1.0.0
#r "nuget: Ahwoo.SessionTickets.Server, 1.0.0"
#:package Ahwoo.SessionTickets.Server@1.0.0
#addin nuget:?package=Ahwoo.SessionTickets.Server&version=1.0.0
#tool nuget:?package=Ahwoo.SessionTickets.Server&version=1.0.0
Ahwoo.SessionTickets.Server
Use this package to identify the Ahwoo account that a joining player is signed in to.
The player signs in to the Ahwoo desktop client, a separate Windows application. The joining game
asks that client to mint a session ticket, a short-lived signed assertion of the player's identity.
The game mints the ticket with
Ahwoo.SessionTickets.Client and
sends it to your game server. This package verifies the ticket against Ahwoo's published public
signing keys and gives your game server a stable Ahwoo profile ID.
Your game server holds no Ahwoo secret and never sees the player's access token. Anyone can therefore operate a game server without holding Ahwoo credentials.
dotnet add package Ahwoo.SessionTickets.Server
How the exchange works
- The player's game connects to your game server. Your game server generates a nonce, a single-use random challenge value, and sends it to the game.
- The game asks the Ahwoo desktop client on the player's computer to mint a ticket bound to that nonce.
- The game sends the ticket to your game server over the game's own network connection. Ahwoo provides no transport for this step.
- Your game server verifies the ticket and reads the player's Ahwoo profile ID.
Verifying a ticket
using Ahwoo.SessionTickets.Server;
// Create one verifier per process and keep it for the lifetime of the process.
// The verifier caches Ahwoo's signing keys.
private readonly SessionTicketVerifier _ahwoo = new();
async Task AdmitAsync(Connection connection, CancellationToken cancellationToken)
{
var nonce = SessionTicketNonce.Create();
var ticket = await connection.ChallengeAsync(nonce);
var result = await _ahwoo.VerifyAsync(ticket, nonce, cancellationToken);
if (!result.IsValid)
{
connection.Refuse(result.Rejection);
return;
}
Guid ahwooProfileId = result.Ticket.ProfileId;
if (BanList.Contains(ahwooProfileId))
connection.Refuse();
}
VerifyAsync checks the signature, the issuer, the audience, the expiry, and the nonce that your
game server sent. VerifyAsync returns a result instead of throwing an exception, because a bad
ticket is ordinary traffic on a public server.
VerifyAsync throws SessionTicketKeysUnavailableException, in
Ahwoo.SessionTickets.Server.Exceptions, only when it cannot fetch Ahwoo's signing keys at all.
Refuse the connection and retry. Never admit a player whose ticket your game server has not
verified.
Rejection reasons
Log result.Rejection to record why a ticket failed. Every value calls for the same operational
response: refuse the connection. Add using Ahwoo.SessionTickets.Server.Models; when your code
names the enum values rather than only logging them.
| Reason | Cause |
|---|---|
SignatureNotTrusted |
Ahwoo did not sign the ticket with a published key, or the signature uses the wrong algorithm. |
NonceMismatch |
The game minted the ticket against a different challenge, so the ticket belongs to another connection attempt. |
Expired |
The ticket is older than its life of about 120 seconds. Ask the game to mint a new ticket. |
NotYetValid |
Your game server's clock is behind Ahwoo's clock by more than the configured skew. |
UntrustedIssuer |
Something other than the configured Ahwoo API issued the token. |
WrongAudience |
The token's audience is something other than an Ahwoo session ticket. |
ClaimsMissing |
The ticket is signed and current, but it carries no usable identity. |
Malformed |
The verifier cannot read the ticket, or validation failed for a reason with no more specific value. |
What the security depends on
Use one nonce per connection attempt. Generate each nonce with SessionTicketNonce.Create(), and
consume it as soon as your game server accepts a ticket against it. Never accept two tickets for one
outstanding nonce, and never reuse a nonce across connections. The nonce is what stops an attacker
from replaying a ticket harvested elsewhere.
Key your bans and player records on ProfileId. ProfileId is a Guid, and it stays the same for
the life of the Ahwoo account. DisplayName is only the name that the player used at mint time, and
the player can change it.
Keep the verifier alive for the lifetime of the process. The verifier caches Ahwoo's signing keys and fetches them again when a ticket names a key that the cache does not hold. This is how the verifier survives a key rotation. A verifier per connection defeats the cache, and it calls the key endpoint on every join.
Optionally, store each accepted result.Ticket.TicketId for a few minutes, and refuse a ticket that
repeats one. This check backstops a broken nonce lifecycle. It does not replace the nonce check, and
it has no effect across a fleet of game servers unless those servers share the store.
What a ticket does not tell you
- A ticket asserts identity only. It says nothing about game ownership or entitlements.
- A banned player can create a new Ahwoo account and receive a new
ProfileId. - A ticket is not bound to the game server that it was minted for. A hostile game server can relay a visitor's ticket to another game server within the ticket's life of about 120 seconds. Binding tickets to a destination is future work.
Configuration
This package requires no configuration to verify tickets against Ahwoo's live API. Pass options to point the verifier at a different Ahwoo API.
| Option | Default | Description |
|---|---|---|
ApiBaseUrl |
https://api.ahwoo.com |
The Ahwoo API that mints the tickets. Sets both the issuer that the verifier trusts and the endpoint it fetches signing keys from |
ClockSkew |
30 seconds | Tolerance for clock drift between your game server and Ahwoo |
KeyRefreshCooldown |
30 seconds | Shortest interval between key fetches, so that tickets naming unknown keys cannot pull the key endpoint repeatedly |
var verifier = new SessionTicketVerifier(new SessionTicketVerifierOptions
{
ApiBaseUrl = new Uri("https://api.ahwoo.example")
});
The constructor also accepts an HttpClient if your game server manages its own. Use the
ISessionTicketVerifier interface to substitute the verifier in your own tests.
Support
Email contact@ahwoo.com with questions or bug reports.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.IdentityModel.JsonWebTokens (>= 8.18.0)
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 |
|---|---|---|
| 1.0.0 | 143 | 8/19/2026 |