Server-Side Authentication
This guide demonstrates how to implement server-side authentication with Crossmint, including session management, token refresh, and protected routes.
Demo Repositories
Next.js SSR
Complete Next.js server-side auth example
Node.js + Express
Express server with auth middleware
Installation
npm install @crossmint/server-sdk
Environment Variables
# Server-side API key (keep this secret!)
SERVER_CROSSMINT_API_KEY=your_server_api_key
# Optional: Port configuration
PORT=3001
Never expose your server API key in client-side code or commit it to version control.
Node.js Implementation
Basic HTTP Server
Create a basic authentication server with Node.js:
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
import http from "http";
import dotenv from "dotenv";
dotenv.config();
const crossmint = createCrossmint({
apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
});
const crossmintAuth = CrossmintAuth.from(crossmint);
const server = http.createServer(async (req, res) => {
// Handle token refresh
if (req.method === "POST" && req.url === "/refresh") {
try {
await crossmintAuth.handleCustomRefresh(req, res);
} catch (error) {
console.error("Error refreshing token", error);
}
res.end();
return;
}
// Handle logout
if (req.method === "POST" && req.url === "/logout") {
try {
await crossmintAuth.logout(req, res);
} catch (error) {
console.error("Error logging out", error);
}
res.end();
return;
}
// Protected route - validate session
try {
const { jwt, refreshToken, userId } = await crossmintAuth.getSession(req, res);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ jwt, refreshToken, userId }));
} catch (error) {
console.error(error);
res.writeHead(500, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: error.message }));
}
});
const PORT = process.env.PORT || 3001;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Express Server
Implement authentication with Express middleware:
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
import express from "express";
import dotenv from "dotenv";
dotenv.config();
const app = express();
// Create Crossmint instances
const crossmint = createCrossmint({
apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
});
const crossmintAuth = CrossmintAuth.from(crossmint);
app.use(express.json());
// Token refresh endpoint
app.post("/refresh", async (req, res) => {
try {
await crossmintAuth.handleCustomRefresh(req, res);
} catch (error) {
console.error("Error refreshing token", error);
}
res.end();
});
// Logout endpoint
app.post("/logout", async (req, res) => {
try {
await crossmintAuth.logout(req, res);
} catch (error) {
console.error("Error logging out", error);
}
res.end();
});
// Authentication middleware
const authMiddleware = async (req, res, next) => {
if (req.method !== "GET") {
next();
return;
}
try {
const { jwt, userId } = await crossmintAuth.getSession(req, res);
req.user = { userId, jwt };
next();
} catch (error) {
console.error(error);
res.status(401).json({ error: "Authentication failed" });
}
};
app.use(authMiddleware);
// Protected route
app.get("/protected", (req, res) => {
res.json({ message: "Protected route", userId: req.user.userId });
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Next.js Implementation
API Routes
Implement authentication API routes in Next.js:
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
import type { NextRequest } from "next/server";
export async function POST(request: NextRequest) {
try {
const crossmint = createCrossmint({
apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
});
const crossmintAuth = CrossmintAuth.from(crossmint, {
cookieOptions: {
httpOnly: true, // Secure cookies
},
});
const response = await crossmintAuth.handleCustomRefresh(request);
return response as Response;
} catch (error) {
console.error("Refresh error:", error);
return Response.json(
{ error: "Authentication failed" },
{ status: 401 }
);
}
}
Server Components
Access auth session in Next.js Server Components:
import { cookies } from "next/headers";
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
export async function getAuthSession(refreshRoute?: string) {
const cookieStore = await cookies();
const jwt = cookieStore.get("crossmint-jwt")?.value;
const refreshToken = cookieStore.get("crossmint-refresh-token")?.value;
if (!refreshToken) {
return null;
}
try {
const crossmint = createCrossmint({
apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
});
const crossmintAuth = CrossmintAuth.from(crossmint, {
refreshRoute,
});
const session = await crossmintAuth.getSession({
jwt,
refreshToken,
});
return session;
} catch (error) {
console.error("Session error:", error);
return null;
}
}
export async function requireAuth(refreshRoute?: string) {
const session = await getAuthSession(refreshRoute);
if (!session) {
throw new Error("Authentication required");
}
return session;
}
Middleware for Protected Routes
Protect multiple routes with middleware:
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
// Configure which routes need authentication
export const config = {
matcher: [
"/dashboard/:path*",
"/profile/:path*",
"/settings/:path*",
],
};
export async function middleware(request: NextRequest) {
const jwt = request.cookies.get("crossmint-jwt")?.value;
const refreshToken = request.cookies.get("crossmint-refresh-token")?.value;
// Redirect to login if no refresh token
if (!refreshToken) {
return NextResponse.redirect(new URL("/login", request.url));
}
try {
const crossmint = createCrossmint({
apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
});
const crossmintAuth = CrossmintAuth.from(crossmint);
// Validate and refresh session
const { jwt: newJwt, refreshToken: newRefreshToken } =
await crossmintAuth.getSession({
jwt,
refreshToken,
});
const response = NextResponse.next();
// Update cookies if tokens changed
if (newJwt !== jwt) {
response.cookies.set("crossmint-jwt", newJwt);
}
if (newRefreshToken.secret !== refreshToken) {
response.cookies.set("crossmint-refresh-token", newRefreshToken.secret);
}
return response;
} catch (error) {
console.error("Auth middleware error:", error);
// Clear invalid cookies and redirect to login
const response = NextResponse.redirect(new URL("/login", request.url));
response.cookies.delete("crossmint-jwt");
response.cookies.delete("crossmint-refresh-token");
return response;
}
}
Session Management
Get User Session
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
const crossmint = createCrossmint({
apiKey: process.env.SERVER_CROSSMINT_API_KEY,
});
const crossmintAuth = CrossmintAuth.from(crossmint);
// From request/response objects
const session = await crossmintAuth.getSession(req, res);
console.log(session.userId);
console.log(session.jwt);
// Or with explicit tokens
const session = await crossmintAuth.getSession({
jwt: "...",
refreshToken: "...",
});
Handle Token Refresh
// Automatic refresh handling
const crossmintAuth = CrossmintAuth.from(crossmint, {
refreshRoute: "/api/refresh",
});
// The SDK will automatically refresh tokens when they expire
const session = await crossmintAuth.getSession(req, res);
Cookie Configuration
const crossmintAuth = CrossmintAuth.from(crossmint, {
cookieOptions: {
httpOnly: true, // Prevent XSS attacks
secure: true, // HTTPS only
sameSite: "strict", // CSRF protection
path: "/",
maxAge: 60 * 60 * 24 * 7, // 7 days
},
});
Security Best Practices
Use httpOnly Cookies
Always use httpOnly cookies for storing tokens to prevent XSS attacks.
Validate Sessions
Validate sessions on every protected route or API call.
Secure Your API Keys
Never expose server API keys in client code or version control.
Use HTTPS
Always use HTTPS in production to protect tokens in transit.
Error Handling
try {
const session = await crossmintAuth.getSession(req, res);
// Session is valid
} catch (error) {
if (error.code === "SESSION_EXPIRED") {
// Token expired, try refresh
await crossmintAuth.handleCustomRefresh(req, res);
} else if (error.code === "INVALID_TOKEN") {
// Invalid token, require re-authentication
return res.status(401).json({ error: "Please login again" });
} else {
// Other error
console.error("Auth error:", error);
return res.status(500).json({ error: "Authentication failed" });
}
}
Next Steps
Next.js Integration
Full Next.js authentication setup
API Reference
Server SDK API reference
Wallet Creation
Add wallets to your auth flow
Authentication Guide
Complete authentication guide
Troubleshooting
Session validation failing
- Verify your server API key is correct
- Check that cookies are being sent with requests
- Ensure the refresh token hasn’t expired
Configure your CORS settings to allow credentials:app.use(cors({
origin: 'https://yourdomain.com',
credentials: true
}));
Check your middleware configuration matcher to ensure it includes the routes you want to protect.