Using @warpy-auth-sdk/core with pure Node.js HTTP
The Node.js HTTP adapter provides integration of @warpy-auth-sdk/core with the native Node.js http module. This adapter has zero framework dependencies and gives you maximum control and flexibility.
npm install @warpy-auth-sdk/core
# or
yarn add @warpy-auth-sdk/core
# or
pnpm add @warpy-auth-sdk/coreNo additional dependencies required!
import { createServer } from "http";
import { createAuthHandler } from "@warpy-auth-sdk/core/adapters/node";
import { google } from "@warpy-auth-sdk/core";
// Configure authentication
const { handler, requireAuth } = createAuthHandler({
secret: process.env.AUTH_SECRET!,
provider: google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
redirectUri: "http://localhost:3000/api/auth/callback/google",
}),
}, {
basePath: "/api/auth",
successRedirect: "/dashboard",
errorRedirect: "/login",
});
// Create HTTP server
const server = createServer(async (req, res) => {
// Try auth handler first
const handled = await handler(req, res);
if (handled) return;
// Your custom routes
if (req.url === "/api/user" && req.method === "GET") {
const authenticated = await requireAuth(req, res);
if (!authenticated) return; // Already sent 401
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ user: req.session.user }));
return;
}
// 404
res.writeHead(404);
res.end("Not Found");
});
server.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});Creates an authentication handler and middleware for Node.js HTTP servers.
AuthConfig): Authentication configurationsecret: JWT signing secret (min 32 characters)provider: OAuth provider (Google, GitHub, etc.)adapter: Optional database adaptercallbacks: Optional callbacksNodeAuthOptions): Optional configurationbasePath: Base path for auth routes (default: /auth)successRedirect: Redirect after successful auth (default: /)errorRedirect: Redirect after auth failure (default: /login)mcp: MCP configurationAn object with:
handler: Async function to handle auth routesrequireAuth: Middleware function to protect routesWhen a request matches these routes, the handler returns true:
GET {basePath}/session - Get current sessionPOST {basePath}/signout - Sign out userGET {basePath}/signin/:provider - Provider-specific sign-inGET {basePath}/callback/:provider - Provider-specific callbackPOST {basePath}/mcp - MCP tools endpoint (if enabled)const server = createServer(async (req, res) => {
const handled = await handler(req, res);
if (handled) return;
// Protected API endpoint
if (req.url === "/api/user" && req.method === "GET") {
const authenticated = await requireAuth(req, res);
if (!authenticated) return; // requireAuth sends 401 response
// Access session from req.session
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ user: req.session.user }));
return;
}
// Public endpoint
if (req.url === "/api/health") {
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ status: "ok" }));
return;
}
res.writeHead(404);
res.end("Not Found");
});import { readFileSync } from "fs";
import { join } from "path";
const server = createServer(async (req, res) => {
const handled = await handler(req, res);
if (handled) return;
const url = new URL(req.url!, `http://${req.headers.host}`);
// Serve HTML
if (url.pathname === "/") {
const html = readFileSync(join(__dirname, "public/index.html"));
res.writeHead(200, { "content-type": "text/html" });
res.end(html);
return;
}
// Serve static assets
if (url.pathname.startsWith("/public/")) {
try {
const file = readFileSync(join(__dirname, url.pathname));
res.writeHead(200);
res.end(file);
return;
} catch {
res.writeHead(404);
res.end("Not Found");
}
}
});const { handler, requireAuth } = createAuthHandler({
secret: process.env.AUTH_SECRET!,
provider: google({ /* config */ }),
callbacks: {
async user(oauthUser) {
// Create or update user in your database
return await db.user.findOrCreate({
where: { email: oauthUser.email },
defaults: {
name: oauthUser.name,
picture: oauthUser.picture,
},
});
},
jwt(token) {
token.role = "user";
return token;
},
session(session) {
session.customField = "value";
return session;
},
},
});import { PrismaAdapter } from "@warpy-auth-sdk/core";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
const { handler, requireAuth } = createAuthHandler({
secret: process.env.AUTH_SECRET!,
provider: google({ /* config */ }),
adapter: PrismaAdapter(prisma),
});const { handler, requireAuth } = createAuthHandler({
secret: process.env.AUTH_SECRET!,
provider: google({ /* config */ }),
}, {
basePath: "/api/auth",
mcp: {
enabled: true,
path: "/api/mcp",
shield: {
secret: process.env.AUTH_SECRET!,
warpy: {
apiKey: process.env.WARPY_API_KEY,
},
},
},
});
// MCP tools now available at POST /api/mcpimport { parse } from "url";
const server = createServer(async (req, res) => {
const handled = await handler(req, res);
if (handled) return;
const { pathname } = parse(req.url || "/", true);
// Route matching
switch (pathname) {
case "/":
// Home page
break;
case "/api/user":
const authenticated = await requireAuth(req, res);
if (!authenticated) return;
// Handle user API
break;
default:
res.writeHead(404);
res.end("Not Found");
}
});async function parseJSON(req: IncomingMessage): Promise<any> {
return new Promise((resolve, reject) => {
let body = "";
req.on("data", (chunk) => {
body += chunk.toString();
});
req.on("end", () => {
try {
resolve(JSON.parse(body));
} catch (err) {
reject(err);
}
});
});
}
const server = createServer(async (req, res) => {
const handled = await handler(req, res);
if (handled) return;
if (req.url === "/api/data" && req.method === "POST") {
try {
const body = await parseJSON(req);
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ received: body }));
} catch {
res.writeHead(400);
res.end("Invalid JSON");
}
}
});The adapter includes full TypeScript support:
import type {
IncomingMessage,
ServerResponse,
NodeAuthOptions,
NodeAuthMiddleware,
Session,
} from "@warpy-auth-sdk/core/adapters/node";
// Extend request type with session
declare module "http" {
interface IncomingMessage {
session?: Session;
}
}
// Typed handler
const server = createServer(
async (req: IncomingMessage, res: ServerResponse) => {
// ...
}
);See the complete Node.js example in the repository.
| Feature | Node.js | Express | Fastify | Hono |
|---|---|---|---|---|
| Framework Dependency | None | Yes | Yes | Yes |
| Bundle Size | Smallest | Medium | Medium | Small |
| Learning Curve | Steep | Easy | Easy | Easy |
| Flexibility | Maximum | High | High | High |
| TypeScript Support | ✓ | ✓ | ✓ | ✓ |
| MCP Support | ✓ | ✓ | ✓ | ✓ |
| Best For | Microservices, Serverless | Traditional apps | High performance | Multi-runtime |
Choose the Node.js adapter when:
Choose a framework adapter when:
The adapter automatically handles cookies. Ensure your client sends cookies:
fetch("/api/user", {
credentials: "include", // Important!
});Make sure you call the handler before your custom routes:
const server = createServer(async (req, res) => {
// Call handler FIRST
const handled = await handler(req, res);
if (handled) return;
// Then your routes
// ...
});The adapter handles body parsing for MCP endpoints. For custom endpoints, parse manually:
async function readBody(req): Promise<string> {
return new Promise((resolve) => {
let body = "";
req.on("data", (chunk) => body += chunk);
req.on("end", () => resolve(body));
});
}Add CORS headers manually:
const server = createServer(async (req, res) => {
// CORS headers
res.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
if (req.method === "OPTIONS") {
res.writeHead(204);
res.end();
return;
}
const handled = await handler(req, res);
// ...
});