Core Concepts β
Understanding the core concepts behind OAuth Callback will help you build robust OAuth integrations in your CLI tools, desktop applications, and MCP clients. This page covers the fundamental patterns, architectural decisions, and key abstractions that power the library.
The Authorization Code Flow β
OAuth Callback implements the OAuth 2.0 Authorization Code Flow, the most secure flow for applications that can protect client secrets. This flow involves three key participants:
Why Authorization Code Flow? β
The authorization code flow provides several security benefits:
- No token exposure: Access tokens never pass through the browser
- Short-lived codes: Authorization codes expire quickly (typically 10 minutes)
- Server verification: The auth server can verify the client's identity
- Refresh capability: Supports refresh tokens for long-lived access
The Localhost Callback Pattern β
The core innovation of OAuth Callback is making the localhost callback pattern trivially simple to implement. This pattern, standardized in RFC 8252, solves a fundamental problem: how can native applications receive OAuth callbacks without a public web server?
The Problem β
Traditional web applications have public URLs where OAuth providers can send callbacks:
https://myapp.com/oauth/callback?code=xyz123
But CLI tools and desktop apps don't have public URLs. They run on the user's machine behind firewalls and NAT.
The Solution β
OAuth Callback creates a temporary HTTP server on localhost that:
- Binds locally: Only accepts connections from
127.0.0.1
- Uses dynamic ports: Works with any available port
- Auto-terminates: Shuts down after receiving the callback
- Handles edge cases: Timeouts, errors, user cancellation
// This single function handles all the complexity
const result = await getAuthCode(authorizationUrl);
Architecture Overview β
OAuth Callback is built on a layered architecture that separates concerns and enables flexibility:
Core Components β
1. The HTTP Server (server.ts
) β
The heart of OAuth Callback is a lightweight HTTP server that:
- Listens on localhost for the OAuth callback
- Parses query parameters from the redirect
- Serves success/error HTML pages
- Implements proper cleanup on completion
// Internally, the server handles:
- Request routing (/callback path matching)
- Query parameter extraction (code, state, error)
- HTML template rendering with placeholders
- Graceful shutdown after callback
2. The Authorization Handler (getAuthCode
) β
The main API surface that orchestrates the entire flow:
interface GetAuthCodeOptions {
authorizationUrl: string; // OAuth provider URL
port?: number; // Server port (default: 3000)
timeout?: number; // Timeout in ms (default: 30000)
openBrowser?: boolean; // Auto-open browser (default: true)
signal?: AbortSignal; // For cancellation
// ... more options
}
3. Error Management (OAuthError
) β
Specialized error handling for OAuth-specific failures:
class OAuthError extends Error {
error: string; // OAuth error code
error_description?: string; // Human-readable description
error_uri?: string; // Link to more information
}
Common OAuth errors are properly typed and handled:
access_denied
- User declined authorizationinvalid_scope
- Requested scope is invalidserver_error
- Authorization server errortemporarily_unavailable
- Server is overloaded
Token Management β
For applications that need to persist OAuth tokens, OAuth Callback provides a flexible storage abstraction:
Storage Abstraction β
The TokenStore
interface enables different storage strategies:
interface TokenStore {
get(key: string): Promise<Tokens | undefined>;
set(key: string, tokens: Tokens): Promise<void>;
delete(key: string): Promise<void>;
}
Built-in Implementations β
In-Memory Store β
Ephemeral storage for maximum security:
const store = inMemoryStore();
// Tokens exist only during process lifetime
// Perfect for CLI tools that authenticate per-session
File Store β
Persistent storage for convenience:
const store = fileStore("~/.myapp/tokens.json");
// Tokens persist across sessions
// Ideal for desktop apps with returning users
Token Lifecycle β
MCP Integration Pattern β
The Model Context Protocol (MCP) integration showcases advanced OAuth patterns:
Dynamic Client Registration β
OAuth Callback supports RFC 7591 Dynamic Client Registration, allowing apps to register OAuth clients on-the-fly:
This eliminates the need for users to manually register OAuth applications.
The Provider Pattern β
The browserAuth()
function returns an OAuthClientProvider
that integrates with MCP SDK:
interface OAuthClientProvider {
// Called by MCP SDK when authentication is needed
authenticate(params: AuthenticationParams): Promise<AuthenticationResult>;
// Manages token refresh automatically
refreshToken?(params: RefreshParams): Promise<RefreshResult>;
}
Request/Response Lifecycle β
Understanding the complete lifecycle helps when debugging OAuth flows:
State Management β
OAuth Callback handles multiple types of state throughout the flow:
Server State β
The HTTP server maintains minimal state:
- Active: Server is listening for callbacks
- Received: Callback has been received
- Shutdown: Server is closing
OAuth State β
The OAuth flow tracks:
- Authorization URL: Where to send the user
- Expected state: For CSRF validation
- Timeout timer: For abandonment detection
- Abort signal: For cancellation support
Token State β
When using token storage:
- No tokens: Need to authenticate
- Valid tokens: Can make API calls
- Expired tokens: Need refresh
- Refresh failed: Need re-authentication
Security Architecture β
Security is built into every layer of OAuth Callback:
Network Security β
// Localhost-only binding
server.listen(port, "127.0.0.1");
// IPv6 localhost support
server.listen(port, "::1");
// Reject non-localhost connections
if (!isLocalhost(request.socket.remoteAddress)) {
return reject();
}
OAuth Security β
- State parameter: Prevents CSRF attacks
- PKCE support: Protects authorization codes
- Timeout enforcement: Limits exposure window
- Automatic cleanup: Reduces attack surface
Token Security β
- Memory storage option: No persistence
- File permissions: Restrictive when using file store
- No logging: Tokens never logged or exposed
- Refresh handling: Automatic token refresh
Template System β
OAuth Callback includes a simple but powerful template system for success/error pages:
Placeholder Substitution β
Templates support syntax:
<h1>Error: {{error_description}}</h1>
Placeholders are automatically escaped to prevent XSS attacks.
Built-in Templates β
The library includes professional templates with:
- Animated success checkmark
- Clear error messages
- Responsive design
- Accessibility features
Custom Templates β
Applications can provide custom HTML:
{
successHtml: "<h1>Welcome back!</h1>",
errorHtml: "<h1>Oops! {{error}}</h1>"
}
Cross-Runtime Compatibility β
OAuth Callback achieves cross-runtime compatibility through Web Standards APIs:
Universal APIs β
// Using Web Standards instead of Node.js-specific APIs
new Request(); // Instead of http.IncomingMessage
new Response(); // Instead of http.ServerResponse
new URL(); // Instead of url.parse()
new URLSearchParams(); // Instead of querystring
Runtime Detection β
The library adapts to the runtime environment:
// Node.js
import { createServer } from "node:http";
// Deno
Deno.serve({ port: 3000 });
// Bun
Bun.serve({ port: 3000 });
Performance Considerations β
OAuth Callback is designed for optimal performance:
Fast Startup β
- Minimal dependencies (only
open
package) - Lazy loading of heavy modules
- Pre-compiled HTML templates
Efficient Memory Use β
- Server resources freed immediately after use
- No persistent connections
- Minimal state retention
Quick Response β
- Immediate browser redirect handling
- Non-blocking I/O operations
- Parallel browser launch and server start
Extension Points β
While OAuth Callback provides sensible defaults, it offers multiple extension points:
Custom Storage β
Implement the TokenStore
interface for custom storage:
class RedisStore implements TokenStore {
async get(key: string) {
/* Redis logic */
}
async set(key: string, tokens: Tokens) {
/* Redis logic */
}
async delete(key: string) {
/* Redis logic */
}
}
Request Interception β
Monitor or modify requests with callbacks:
{
onRequest: (req) => {
console.log(`OAuth: ${req.method} ${req.url}`);
// Add telemetry, logging, etc.
};
}
Browser Control β
Customize browser launching:
{
openBrowser: false, // Manual browser opening
// Or provide custom launcher
}
Best Practices β
Error Handling β
Always handle both OAuth errors and unexpected failures:
try {
const result = await getAuthCode(authUrl);
} catch (error) {
if (error instanceof OAuthError) {
// Handle OAuth-specific errors
} else {
// Handle unexpected errors
}
}
State Validation β
Always validate the state parameter:
const state = crypto.randomUUID();
// Include in auth URL
const result = await getAuthCode(authUrl);
if (result.state !== state) throw new Error("CSRF detected");
Token Storage β
Choose storage based on security requirements:
- CLI tools: Use
inMemoryStore()
for per-session auth - Desktop apps: Use
fileStore()
for user convenience - Sensitive apps: Always use in-memory storage
Timeout Configuration β
Set appropriate timeouts for your use case:
- Interactive apps: 30-60 seconds
- Automated tools: 5-10 seconds
- First-time setup: 2-5 minutes