Skip to main content
FrontMCP bundles three ready-to-serve authentication demos under apps/e2e/**. Each project runs the same Notes + Tasks apps but wires a different auth mode, transport policy, and consent experience so you can validate clients before touching your own code.

Projects at a glance

ProjectModeDefault portHighlightsServe command
demo-e2e-publicpublic3100Anonymous sessions with scoped tools, Streamable HTTP + JSON fallbackPORT=3100 yarn nx serve demo-e2e-public
demo-e2e-orchestratedorchestrated (local)3121Stateful sessions, consent UI, incremental auth controlsPORT=3121 yarn nx serve demo-e2e-orchestrated
demo-e2e-transparenttransparent3109Proxies tokens to a remote IdP while keeping local transport policiesPORT=3109 yarn nx serve demo-e2e-transparent
All commands assume yarn install has already been run at the repo root. The serve target runs the prebuilt dist/.../main.js, so it does not forward an --port flag — set the port via the PORT environment variable (PORT=<n> yarn nx serve <project>).

Run a demo locally

1

Pick a project

Decide which auth mode to explore (demo-e2e-public, demo-e2e-transparent, or demo-e2e-orchestrated).
2

Configure environment

Transparent mode needs an upstream IdP. Export IDP_PROVIDER_URL and IDP_EXPECTED_AUDIENCE before serving:
export IDP_PROVIDER_URL="https://auth.example.com"
export IDP_EXPECTED_AUDIENCE="https://api.example.com"
3

Serve the project

Run PORT=<port> yarn nx serve <project> and wait for the Listening on log. Requests are available at http://localhost:<port>. The serve target runs the prebuilt bundle and does not accept a --port flag, so the port must come from the PORT environment variable.
curl http://localhost:<port>/health returns a JSON body like { "status": "ok", "server": { ... }, "uptime": ... } when the server is ready.

demo-e2e-public: instant anonymous access

apps/e2e/demo-e2e-public/src/main.ts
@FrontMcp({
  info: { name: 'Demo E2E Public', version: '0.1.0' },
  apps: [NotesApp],
  auth: {
    mode: 'public',
  },
});
  • Exercises the auth.publicAccess path without any login UI.
  • Uses framework defaults for transports (Streamable HTTP + SSE + JSON), suitable for smoke tests.
Use this project with @frontmcp/testing when you want fast, anonymous fixtures that still cover transport-level behavior.

demo-e2e-orchestrated: built-in OAuth server

apps/e2e/demo-e2e-orchestrated/src/main.ts
@FrontMcp({
  info: { name: 'Demo Orchestrated Auth', version: '0.1.0' },
  apps: [NotesApp, TasksApp],
  auth: {
    mode: 'local',
    consent: {
      enabled: true,
      groupByApp: true,
      showDescriptions: true,
      allowSelectAll: true,
      requireSelection: true,
      rememberConsent: true,
    },
    tokenStorage: 'memory', // Use { redis: ... } in production
    allowDefaultPublic: false,
    anonymousScopes: ['anonymous'],
  },
  transport: {
    sessionMode: 'stateful',
    protocol: {
      sse: true,
      streamable: true,
      json: true,
      stateless: false,
      legacy: false,
      strictSession: false,
    },
  },
});
  • Spins up the full OAuth 2.1 stack (authorization code + PKCE) entirely inside FrontMCP.
  • Shows the consent UI grouping tools by app, plus incremental auth defaults.
  • Uses the same transport defaults documented in Transport controls so you can compare behavior with production builds.
The demo login accepts any email address. Replace it with a real IdP before exposing the flow to users.

demo-e2e-transparent: pass-through tokens

apps/e2e/demo-e2e-transparent/src/main.ts
@FrontMcp({
  auth: {
    mode: 'transparent',
    provider: process.env.IDP_PROVIDER_URL!,
    providerConfig: {
      dcrEnabled: false,
    },
    expectedAudience: process.env.IDP_EXPECTED_AUDIENCE!,
    requiredScopes: [],
    allowAnonymous: false,
  },
});
  • Validates upstream JWTs while reusing the same transport policies. (Transparent mode has no consent option — the consent UI is a local/remote-mode feature.)
  • Demonstrates how to forward-declare an IdP without shipping secrets—set IDP_PROVIDER_URL/IDP_EXPECTED_AUDIENCE per environment.
  • Keeps anonymous access off so every tool call requires a verified token.
If your IdP exposes a JWKS file, frontload the URL in providerConfig.jwksUri to skip metadata discovery during tests.

Test with @frontmcp/testing

You can point the Jest fixtures at any demo by spinning it up through the built-in TestServer helper:
demo-public.e2e.ts
import { test, expect, TestServer } from '@frontmcp/testing';

let serverProcess;

test.beforeAll(async () => {
  serverProcess = await TestServer.startNx('demo-e2e-public', { port: 4001 });
});

test.afterAll(async () => {
  await serverProcess?.stop();
});

test.use({
  server: serverProcess?.info.baseUrl ?? 'http://localhost:4001',
  transport: 'streamable-http',
});

test('lists demo tools', async ({ mcp }) => {
  const tools = await mcp.tools.list();
  expect(tools).toContainTool('create-note');
});
Pair this with the Transport controls reference to mirror the exact transports your production server exposes.

Authentication Modes

Understand when to reach for public, transparent, or orchestrated auth.

Transport controls

Dive deeper into Streamable, stateful, and stateless HTTP settings.