Skip to content

feat(auth): Entra sign-in with MSAL beside GitHub (step 5c) - #8

Merged
JasonPaff merged 5 commits into
mainfrom
feat/entra-sign-in
Sep 15, 2026
Merged

JasonPaff merged 5 commits into
mainfrom
feat/entra-sign-in

Conversation

@JasonPaff

Copy link
Copy Markdown
Collaborator

Step 5c of the ATK Azure build-out (docs/Direction.md in the monorepo): an Entra sign-in with MSAL beside the existing GitHub sign-in, Entra as the primary option, and the API's GitHub-auth sunset signals surfaced. Nothing changes for GitHub sessions, and no environment has a sunset date set.

What changed

  • @azure/msal-browser 5.21.0 (pinned, no msal-react) behind an EntraClient interface in src/lib/entra.ts, with createFakeEntraClient() for tests. Auth-code + PKCE redirect flow through the v5 redirect bridge: auth-redirect.html is a second Vite entry whose only script calls broadcastResponseToMainFrame(), so the hash router is re-entered at the route that started sign-in and never sees #code=. Constants in code (tenant, ES ATK Web client id, API scope); no new VITE_* variables.
  • SessionProvider runs both providers behind one status machine: startup awaits initialize() + handleRedirectPromise() before reporting anything but verifying; signIn(provider, returnPath?); per-scheme signOut (Entra: logoutRedirect, ends the Microsoft web session); a notice store (entra_expired, github_auth_retired, sign_in_failed) shown on the landing; 403 guest_not_allowed joins non-member; 401 github_auth_retired keeps the API's message verbatim. SessionUser is now the /me principal and token left the context (nothing read it).
  • API client takes a credential ({ scheme: 'github', token } or { scheme: 'entra', getToken }); the Entra getter calls acquireTokenSilent on every request and turns an interaction-required failure into a 401 session_expired that ends the session (never an interactive flow inside a request). New 401/403 texts.
  • UI: landing with "Sign in with your Emergent account" (primary) and "Sign in with GitHub"; header starts Entra; /sign-in starts Entra, /sign-in?provider=github GitHub; SunsetBanner under the header for GitHub sessions with githubAuthSunset ("Switch now", dismiss per tab); RequireAuth label "Checking your access…"; per-scheme not-authorized copy; author pre-fill from the display name for Entra users; publish permission text.
  • Regenerated client for the 5a contract (/me now types id, email, githubAuthSunset); docs (docs/deployment.md §2.4/2.5, PROJECT_OVERVIEW.md §7, .env.example).

Registration change already applied to ES ATK Web: the two auth-redirect.html SPA redirect URIs, the localhost URIs corrected to include /agentic-toolkit-web/ (Vite serves the base path in dev too), and the login_hint optional ID-token claim.

Verification

  • pnpm lint && pnpm typecheck && pnpm test && pnpm build clean (341 tests; dist/auth-redirect.html emitted; pnpm dev serves it at /agentic-toolkit-web/auth-redirect.html).
  • Locally against the dev API: Emergent sign-in from /#/bundles back to /#/bundles with the UPN in the header and no "GitHub" wording; reload keeps the session; a new tab shows the landing and the Emergent button completes without a prompt; browse, asset detail with files, .skill download (200, zip), bundle detail; Contribute dry run (plan prBody "Published by Jason Paff (jasonp@emergentsoftware.net) via Entra"); one real publish: registry PR EmergentSoftware/agentic-toolkit-registry#313 opened by JasonPaff, commit authored as Jason Paff jasonp@emergentsoftware.net, publisher line present, closed unmerged. Sign out → Microsoft → landing signed out (clean URL), the Emergent button prompts again and a fresh sign-in returns to the app. GitHub: sign in → /auth/callback → member, Contribute dry run ("@JasonPaff via GitHub"), author pre-fill "JasonPaff".
  • Guest refusal cannot be exercised without a guest account in the tenant; covered by unit tests only.
  • Sunset banner / github_auth_retired in the browser and the live check on Pages: see the PR comments.

🤖 Generated with Claude Code

JasonPaff and others added 5 commits September 15, 2026 13:57
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds @azure/msal-browser 5.21.0 with the v5 redirect bridge (auth-redirect.html
as a second Vite entry), an EntraClient interface in src/lib/entra.ts with a
fake for tests, and a SessionProvider that runs both providers behind one
status machine: Entra is the primary sign-in, GitHub stays unchanged until the
cutoff. The API client takes a credential (GitHub token or Entra token getter),
403 guest_not_allowed joins the non-member status, 401 github_auth_retired and
an expired Entra session become notices on the landing, and a SunsetBanner
shows the API's githubAuthSunset for GitHub sessions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tHub cutoff

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
nuqs's react-router adapter reads window.location.search, which is empty under
HashRouter because the query lives inside the fragment, so /sign-in?provider=github
started the Entra sign-in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@JasonPaff
JasonPaff merged commit f4a7502 into main Sep 15, 2026
1 check passed
@JasonPaff
JasonPaff deleted the feat/entra-sign-in branch September 15, 2026 19:16
@JasonPaff

Copy link
Copy Markdown
Collaborator Author

Sunset checks (dev, Atk__Auth__GitHubSunset set temporarily on func-atk-dev, GitHub session in the browser):

  • Date three days out: GET /me carries githubAuthSunset; after a reload the banner "GitHub sign-in to ATK ends on 2026-09-18. Switch to your Emergent account before then." shows under the header on every page; dismiss hides it and survives navigation within the tab (atk:session:sunset-dismissed), and it returns after a reload once the flag is cleared. Switch now dropped the GitHub token, went through the Microsoft sign-in and landed back on the same route in an Entra session with no banner.
  • Date yesterday: signing in with GitHub in a fresh tab returns to the landing with the API's 401 github_auth_retired message verbatim ("GitHub sign-in to ATK ended on 2026-09-14. Update the CLI … or sign in to the web app with your Emergent account."), the token cleared; the Emergent button on that landing completed silently (live Microsoft session) into an Entra session with no banner.
  • The setting is being removed again; the next atk-infra apply would drop it anyway.

Also fixed on this branch since the description was written: /sign-in?provider=github had started the Entra sign-in because nuqs's react-router adapter reads window.location.search, which is empty under HashRouter; the route now reads the query through react-router (163eb91). The "cancel on the Microsoft page" case is not exercisable for a pre-authorized app with no consent screen (browser Back makes MSAL treat it as a back-navigation and the app simply shows the landing); a rejected redirect is covered by the sign_in_failed unit tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant