The same way Unity does it. The game hands its idToken to your server - in its join message, a
header, whatever your netcode uses - and your server checks it without asking us: the token is a
JWT signed with our private key, and the public halves are published.
Check four things, and then sub is the player's id:
| Check | Must be |
|---|---|
| Signature | RS256, by a key in https://www.dasmaffin.com/api/mgds/v1/.well-known/jwks.json (pick it by the token's kid) |
iss |
https://www.dasmaffin.com/api/mgds/v1 |
aud |
Your project's id - so a token from somebody else's game is refused |
exp |
In the future. Tokens last an hour, like the access token |
The token also carries sid (the session), project_id, sign_in_provider (anonymous,
steam, sbox, dasmaffin) and name when the player has one. Cache the key set: it changes only
when we add a key, and an old key stays published for as long as tokens it signed can be alive.
There is a discovery document at https://www.dasmaffin.com/api/mgds/v1/.well-known/openid-configuration
for libraries that want one.
Like Unity's, a token cannot be taken back before it expires: a player who signs out is signed out of
MGDS at once, but a server holding their token believes it until its exp. That is the price of your
server not having to ask, and why it lasts an hour.
C# (a dedicated server, .NET): with the Microsoft.IdentityModel.Protocols.OpenIdConnect and
Microsoft.IdentityModel.JsonWebTokens packages, which fetch and cache the keys themselves:
var keys = new ConfigurationManager<OpenIdConnectConfiguration>(
"https://www.dasmaffin.com/api/mgds/v1/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever());
async Task<string?> WhoIs(string idToken)
{
OpenIdConnectConfiguration config = await keys.GetConfigurationAsync();
TokenValidationResult result = await new JsonWebTokenHandler().ValidateTokenAsync(idToken, new TokenValidationParameters
{
ValidIssuer = "https://www.dasmaffin.com/api/mgds/v1",
ValidAudience = "YOUR-PROJECT-ID",
IssuerSigningKeys = config.SigningKeys,
});
return result.IsValid ? (string)result.Claims["sub"] : null; // the player's id, or not a player
}
Node: with jose:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const keys = createRemoteJWKSet(new URL('https://www.dasmaffin.com/api/mgds/v1/.well-known/jwks.json'));
const { payload } = await jwtVerify(idToken, keys, {
issuer: 'https://www.dasmaffin.com/api/mgds/v1',
audience: 'YOUR-PROJECT-ID',
});
// payload.sub is the player's id; jwtVerify throws for anything else