Authentication
Choose how callers prove who they are: API keys, JWTs, or OAuth 2.1
A server that does anything sensitive needs to know who is calling. NitroStack offers three ways for callers to prove their identity. Each one is a module you configure once, plus a guard you put on the tools that need it. Tools without a guard stay open.
- A long-lived secret per integration
- Simplest to set up
- No user sign-in
- Your server issues signed tokens
- Tokens carry the user and roles
- You run the login
- Auth0, Okta, Entra ID, Google
- Short-lived tokens with scopes
- Required by ChatGPT apps
How it fits together
Every method follows the same pattern. A module holds the configuration, a guard checks each call, and the guard puts the caller's identity on context.auth for your handler to read.
Guards
Guards are the mechanism behind all three methods. A guard is a class with a canActivate() method that returns true to let a call through.
Writing a guard
Guards determine whether a request should be processed. They're used primarily for authentication and authorization.
import { Guard, ExecutionContext } from '@nitrostack/core';
export class JWTGuard implements Guard {
async canActivate(context: ExecutionContext): Promise<boolean> {
// Check if user is authenticated
const token = this.extractToken(context);
if (!token) {
return false;
}
try {
const payload = await this.verifyToken(token);
context.auth = {
subject: payload.sub,
token: token,
...payload
};
return true;
} catch (error) {
return false;
}
}
private extractToken(context: ExecutionContext): string | null {
// Extract from metadata or arguments
return context.metadata?.token || null;
}
private async verifyToken(token: string): Promise<any> {
// Verify JWT token
// Implementation depends on your JWT library
}
}Applying a guard
Add the guard to any tool with @UseGuards. Tools without it stay public.
import { UseGuards } from '@nitrostack/core';
export class UserTools {
@Tool({ name: 'get_profile' })
@UseGuards(JWTGuard) // ← Requires JWT authentication
async getProfile(input: any, ctx: ExecutionContext) {
const userId = ctx.auth?.subject; // ← Available from guard
return await this.userService.findById(userId);
}
@Tool({ name: 'delete_user' })
@UseGuards(JWTGuard, AdminGuard) // ← Multiple guards
async deleteUser(input: any, ctx: ExecutionContext) {
// Requires both JWT auth AND admin role
}
}Methods
API keys
Keys for services and integrations.
JWT
Tokens you issue yourself.
OAuth 2.1
Sign-in with an identity provider.
Access control
Roles, scopes, and combined checks.
