Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,43 @@
namespace Trax.Samples.GameServer.Api;

/// <summary>
/// Maps a Google-issued id-token onto a Trax principal. The JWT bearer
/// handler has already verified signature, issuer, audience, and lifetime
/// against Google's JWKS before this runs.
/// Sample-only resolver that grants every authenticated Google user the
/// <c>Player</c> role so the GameServer sample trains can be exercised by
/// anyone who can sign in. <b>You almost certainly don't need a class like
/// this in a real app.</b>
/// </summary>
/// <remarks>
/// Demo only: every authenticated Google user is granted the Player role so
/// sample trains work out of the box. A real deployment would look up the
/// principal in the game's user table and assign roles based on account
/// state, revoke unknown subjects, etc.
/// <para>
/// <b>When you don't need a custom resolver.</b> For most apps, the one-line
/// call <c>services.AddTraxJwtAuth(authority, audience)</c> is the whole
/// integration. Trax's <c>DefaultJwtPrincipalResolver</c> already maps the
/// standard OIDC claims (<c>sub</c>, <c>name</c>, <c>preferred_username</c>,
/// <c>email</c>, <c>role</c>/<c>roles</c>) onto a <c>TraxPrincipal</c>. Any
/// provider that emits standard claims works without additional code.
/// </para>
/// <para>
/// <b>When a custom resolver IS justified.</b> Reach for this only when one
/// of the following is true:
/// <list type="bullet">
/// <item>Roles live in a non-standard claim the default mapper doesn't
/// recognize (e.g. tenant-specific Entra app roles or Okta group URIs).</item>
/// <item>You need to enrich the principal from your own database — fetch
/// the user's tenant, permissions, feature flags, etc.</item>
/// <item>You need to reject unknown subjects: allow-list of provisioned
/// users, revocation cache, suspension check.</item>
/// </list>
/// In those cases the pattern is exactly what's below: implement
/// <c>ITraxPrincipalResolver&lt;JwtTokenInput&gt;</c>, read claims off
/// <c>input.Principal</c> (already signature/issuer/audience/lifetime
/// validated by <c>JwtBearerHandler</c>), and return either a
/// <c>TraxPrincipal</c> or <c>null</c> to reject.
/// </para>
/// <para>
/// <b>This sample uses a custom resolver PURELY for the role-assignment
/// hack</b> — hardcoded <c>Player</c> so the trains work out of the box.
/// Remove the generic overload in Program.cs and the default resolver
/// will do the rest.
/// </para>
/// </remarks>
internal sealed class GoogleJwtResolver : ITraxPrincipalResolver<JwtTokenInput>
{
Expand Down
21 changes: 19 additions & 2 deletions samples/LocalWorkers/Trax.Samples.GameServer.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@
// companion app (samples/LocalWorkers/trax-samples-gameserver-web)
// signs users in with Google via NextAuth, then forwards the id-token
// to this API. Trax validates against Google's JWKS. Enable by setting
// Google:ClientId in appsettings.json to your OAuth 2.0 client id from
// Google Cloud Console.
// Google:ClientId (via `dotnet user-secrets set "Google:ClientId" <id>`)
// to your OAuth 2.0 client id from Google Cloud Console.
//
// For a real app, the whole integration is one line:
// services.AddTraxJwtAuth("https://accounts.google.com", googleClientId);
// The default resolver handles standard OIDC claims. This sample uses a
// custom resolver only to hand every Google user the Player role so the
// trains work out of the box — see GoogleJwtResolver.cs for the rationale
// and the "when do I need a custom resolver?" guidance.
//
// Both schemes feed the same TraxPrincipal, so [TraxAuthorize] works against
// either credential type.
Expand Down Expand Up @@ -125,6 +132,16 @@
// them via NextAuth and sends them as Authorization: Bearer <id-token>.
// Signature validation happens against Google's published JWKS; only tokens
// minted for our specific OAuth client id are accepted (aud claim check).
//
// The one-line path below is enough for most apps — the package's default
// resolver maps sub → Id, name/email → DisplayName, and role claims → Roles.
// We override with GoogleJwtResolver here ONLY because this sample needs to
// grant every signed-in user the Player role so the sample trains work.
// A real deployment would keep the default and look up roles in its user
// database via a scoped resolver — see GoogleJwtResolver.cs for the shape.
//
// Default-resolver version (swap in for a real app):
// builder.Services.AddTraxJwtAuth("https://accounts.google.com", googleClientId);
var googleClientId = builder.Configuration["Google:ClientId"];
if (!string.IsNullOrWhiteSpace(googleClientId))
{
Expand Down
9 changes: 6 additions & 3 deletions samples/LocalWorkers/trax-samples-gameserver-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ with Google via NextAuth, then forwards the Google-issued id-token to the
Trax GraphQL API as an `Authorization: Bearer` credential. The API validates
the token against Google's JWKS via `AddTraxJwtAuth("https://accounts.google.com", googleClientId)`.

> **Demo only.** The resolver in `GoogleJwtResolver.cs` grants the `Player`
> role to every authenticated Google user so the sample's trains work out of
> the box. Do not copy that pattern into anything real.
> **Most apps don't need a custom resolver.** The actual integration is one
> line — `services.AddTraxJwtAuth("https://accounts.google.com", clientId)` —
> and Trax's default resolver handles standard OIDC claims. This sample only
> has a `GoogleJwtResolver` class because it hard-assigns the `Player` role
> to every signed-in user so the trains are exercisable; see the comments on
> that class for "when do I actually need a custom resolver?"

## Prerequisites

Expand Down
Loading