OAuth 2.1
Let users sign in with an identity provider, and protect tools with their tokens
OAuth 2.1 lets the people who use your MCP server sign in with an identity provider they already use, such as Auth0, Okta, Microsoft Entra ID, or Google. The client gets a short-lived access token from that provider and sends it with every request; your server checks the token and knows who is calling and what they're allowed to do.
NitroStack's OAuthModule does the server side for you. It validates JWT signatures against the provider's keys, checks expiry, audience, and issuer, publishes the metadata clients need to start a sign-in (RFC 9728), and supports token introspection for opaque tokens. You add the module to your app and put @UseGuards(OAuthGuard) on the tools that need a signed-in user.
Quick start
The fastest way to see OAuth working is the OAuth template, which has everything below already wired up.
Create the project
Scaffold a new project from the OAuth template:
npx @nitrostack/cli init my-oauth-server --template typescript-oauth
cd my-oauth-serverSet up your identity provider
In your provider, create an application for your MCP clients and an API whose identifier is your server's RESOURCE_URI. Add the scopes your tools need, such as read, write, and admin. The template's OAUTH_SETUP.md walks through this for Auth0, and the OAuth 2.1 template page lists the environment variables.
Configure the module
OAuthModule.forRoot() tells NitroStack where your provider is and how to check its tokens. Every value comes from .env, so the same code runs in development and production.
import { McpApp, Module, ConfigModule, OAuthModule } from '@nitrostack/core';
@McpApp({
module: AppModule,
server: { name: 'my-oauth-server', version: '1.0.0' },
})
@Module({
name: 'app',
imports: [
ConfigModule.forRoot(),
OAuthModule.forRoot({
// Reject calls without a valid token. Off by default for local development
required: process.env.OAUTH_REQUIRED === 'true',
// Your server's public identifier; tokens must be issued for it (RFC 8707)
resourceUri: process.env.RESOURCE_URI || 'https://mcplocal',
// Your identity provider
authorizationServers: [process.env.AUTH_SERVER_URL!],
scopesSupported: ['read', 'write', 'admin'],
// Verify JWT signatures with the provider's public keys
jwksUri: process.env.JWKS_URI,
// Optional extra checks
audience: process.env.TOKEN_AUDIENCE,
issuer: process.env.TOKEN_ISSUER,
}),
],
})
export class AppModule {}Start the server
Run the server in development mode:
npm run devWith OAuth enabled, the server publishes its sign-in metadata over HTTP even while it talks MCP over STDIO. In STDIO mode the metadata is served on port 3005 by default; set OAUTH_DISCOVERY_PORT to change it.
Sign in from NitroStudio
Open the project in NitroStudio, go to Authentication, and add your provider's details for the project. Register nitrostudio://oauth/callback as a redirect URI in your provider. After you sign in, call a protected tool and it runs as you.
Turn enforcement on before you deploy
While OAUTH_REQUIRED isn't true, OAuthGuard lets calls through without a token, which is convenient locally. Set OAUTH_REQUIRED=true in every deployed environment.
OAuth topics
Protect Tools
Require sign-in and scopes on a tool.
How It Works
Discovery endpoints, token checks, and the sign-in flow.
Configuration
Introspection, custom validation, and security features.
Testing and Production
Test sign-in locally, then deploy safely.
