Skip to main content

Waypoint Authentication — Current-State Review

Status: Real findings from a direct review of Waypoint's actual source (API + frontend), 2026-09-12. Conducted specifically to answer whether Waypoint is genuinely the lower-cost, lower-risk first candidate for real Keystone integration, as keystone-framing.md §8's sequencing (Waypoint/Relay before RoundTrip) assumed in the abstract. Headline correction: Waypoint's auth was recalled as "bare-minimum scaffolding, not something I'm attached to." The real code shows something more substantial — a working (if flawed) multi-tenant identity system underneath a genuinely large, real application. This document exists to replace that recollection with what's actually there.


1. Scale reality check​

Not a thin shell. A real Clean Architecture solution: 60+ endpoints across five genuine bounded contexts (Workspace, HR/Employees, Financial, Documents, Team), a standard Microsoft.Identity.Web-based API, and a real MSAL-authenticated React frontend (TipTap rich-text editing, Recharts, file uploads, React Query). Auth sits underneath a real, working application — a migration touches more surface than "swap out a login screen."


2. Urgent, unrelated to Keystone — action needed now​

Waypoint.API.Web/appsettings.Development.json contains a live-looking Microsoft Graph API client secret in plain text (GraphApi:ClientSecret). Believed to be excluded from source control and local-only — worth confirming directly (a git log/git status check) rather than assuming, and rotating in the Entra app registration is cheap insurance regardless, given it was already shared outside the local machine once.


3. WAY-49, confirmed directly rather than secondhand​

appsettings.Development.json's real AzureAd:Instance is https://roundtripapp.ciamlogin.com/ — the exact same shared CIAM tenant RoundTrip uses. This is worse than the abstract description suggested: it's resolved independently in two separate places —

  • TenantProvisioningMiddleware.cs: context.User.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid") ?? context.User.FindFirst("tid")
  • CurrentTenantService.cs: identical pattern, its own independent implementation

Since every Waypoint user authenticates against that one shared tenant, this claim is genuinely constant across everyone today — there is no real mechanism for two different Waypoint customers to receive different workspaces.

Fixed by the Keystone migration, structurally, by construction — once tenant identity comes from Command Center's own real TenantId rather than an Entra claim, this problem doesn't just get mitigated, it stops being possible to have at all.


4. Zero authorization anywhere — real, independent of Keystone​

Searched the entire Waypoint.API.Web endpoint layer for any role-based authorization construct (Roles(...), .Authorize, RequireAuthorization, anything). None exist, anywhere. Concretely: InviteUserEndpoint lets any authenticated person invite a new user with any role, including WaypointAdmin — no check on the caller's own role at all.

Not automatically fixed by replacing the identity provider. This is a real, easy trap worth naming plainly: whether an endpoint checks the caller's role before acting has nothing to do with which service issued their token. Swapping Entra for Keystone changes nothing about this unless someone deliberately adds real authorization checks as an explicit part of the migration's own scope.

Real context, not a flaw in judgment: per Pete, this reflects the app still being in "build it out and let me see the UI" mode, not a deliberate decision to ship without authorization.


5. CurrentUserService never resolves an actual WaypointUser​

public sealed class CurrentUserService(IHttpContextAccessor httpContextAccessor)
: ICurrentUserService
{
public string? Name => httpContextAccessor.HttpContext?.User.FindFirst("name")?.Value;
public string? EntraObjectId => httpContextAccessor.HttpContext?.User.FindFirst(
"http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value
?? httpContextAccessor.HttpContext?.User.FindFirst("oid")?.Value;
}

Only ever exposes raw JWT claims — no lookup of a real, persisted WaypointUser row (with its actual role, actual status) exists anywhere reviewed.

Not automatically fixed either. The migration creates a natural opportunity — a stable KeystoneUserId needs a real lookup regardless — but that lookup needs to be explicitly designed and built as part of the migration's scope, not assumed as an automatic side effect.


6. The invite flow uses Entra's guest-invitation API — the same pattern RoundTrip already found broken​

GraphUserService.InviteUserAsync:

var result = await _graphClient.Invitations.PostAsync(invitation, cancellationToken: cancellationToken);

_graphClient.Invitations.PostAsync is unambiguously Microsoft Graph's B2B guest-invitation API. This is the same class of pattern RoundTrip's own TRA-223 already found unreliable (guest accounts intermittently failing app-role assignment with 403s) and replaced with CreateMemberUserAsync.

Conditionally fixed — only if the invite flow itself gets rebuilt to go through Keystone's own registration/linking patterns as part of the migration. If the migration narrowly swaps just login/token-validation and leaves this call in place for any reason, this specific bug survives untouched.


7. What this means for the Waypoint-vs-RoundTrip sequencing question​

The original case for "Waypoint first" was low cost, nothing real to lose. That's not quite accurate anymore — there's substantial real application logic sitting on top of this auth model. But the case is arguably stronger from a different angle: the current system has real, concrete, independently-worth-fixing problems (a genuine tenant-collision risk, a real missing-authorization gap, a known-bad invite pattern) that a real Keystone migration resolves as much-needed side effects, not just relocates elsewhere.

Recommendation, not yet a decision: proceed with Waypoint as the first real Keystone integration, but scope the migration explicitly to include real authorization checks and a real WaypointUser lookup service — not just an identity-provider swap. Treat Section 4 and Section 5's findings as required work items within that migration, not assumed-free byproducts of it.