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-publicpublic3003Anonymous sessions with scoped tools, Streamable HTTP + JSON fallbackpnpm nx serve demo-e2e-public --port 3003
demo-e2e-orchestratedorchestrated (local)3005Stateful sessions, consent UI, incremental auth controlspnpm nx serve demo-e2e-orchestrated --port 3005
demo-e2e-transparenttransparent3004Proxies tokens to a remote IdP while keeping local consent + transport policiespnpm nx serve demo-e2e-transparent --port 3004
All commands assume pnpm install has already been run at the repo root. Replace the port if it conflicts with another service.

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 pnpm nx serve <project> --port <port> and wait for the Listening on log. Requests are available at http://localhost:<port>.
curl http://localhost:<port>/health returns ok when the server is ready.

demo-e2e-public: instant anonymous access

apps/e2e/demo-e2e-public/src/main.ts
@FrontMcp({
  auth: {
    mode: 'public',
    sessionTtl: 3600,
    anonymousScopes: ['anonymous'],
  },
  transport: {
    protocol: 'full',  // All protocols enabled, relaxed sessions for testing
  },
});
  • Exercises the auth.publicAccess path without any login UI.
  • Uses the full protocol preset—all transports enabled with relaxed session requirements—for easy 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({
  auth: {
    mode: 'orchestrated',
    type: 'local',
    consent: {
      enabled: true,
      groupByApp: true,
      showDescriptions: true,
      requireSelection: true,
    },
    allowDefaultPublic: false,
  },
  transport: {
    protocol: 'full',  // All protocols enabled, relaxed sessions for testing
    sessionMode: 'stateful',
  },
});
  • 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',
    remote: {
      provider: process.env.IDP_PROVIDER_URL!,
      dcrEnabled: false,
    },
    expectedAudience: process.env.IDP_EXPECTED_AUDIENCE!,
    requiredScopes: [],
    allowAnonymous: false,
  },
  transport: {
    protocol: 'full',  // All protocols enabled, relaxed sessions for testing
  },
});
  • Validates upstream JWTs while reusing the same consent UI and transport policies.
  • 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 remote.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.