Section 5 · Step 1
Three patterns. That's the engineering surface.
MCP server auth middleware. Agent-side token acquisition. Connection token fetching in tool handlers. Everything else is configuration in Descope.
loan-ops-mcp/src/middleware/auth.ts
ts// loan-ops-mcp/src/middleware/auth.ts
import { jwtVerify, createRemoteJWKSet } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://auth.northwind.com/.well-known/jwks.json"),
);
export async function requireUser(req, res, next) {
const token = req.headers.authorization?.replace(/^Bearer\s+/i, "");
if (!token) return res.status(401).json({ error: "no_token" });
try {
const { payload } = await jwtVerify(token, JWKS, {
issuer: "https://auth.northwind.com",
audience: "loan-ops-mcp",
});
req.user = {
sub: payload.sub,
roles: Array.isArray(payload.roles) ? payload.roles : [],
scopes:
typeof payload.scope === "string"
? payload.scope.split(" ").filter(Boolean)
: Array.isArray(payload.scopes)
? payload.scopes
: [],
clientId: payload.client_id ?? payload.azp ?? "unknown-client",
tenant: payload.tenantId,
};
next();
} catch {
return res.status(401).json({ error: "invalid_token" });
}
}
export function requireScope(required: string) {
return (req, res, next) => {
if (!req.user?.scopes.includes(required)) {
return res.status(403).json({
error: "insufficient_scopes",
required,
had: req.user?.scopes,
});
}
next();
};
}Take-away
Two functions: validate the token, check the scope. The MCP server doesn't own access logic — it validates what Descope decided.
What the audience just saw
- Section 1: An MCP server that didn't need a new permission model — it reads Okta roles via Descope and checks scopes per tool.
- Section 2: Agents that don't hold secrets. Credentials retrieved on demand, user-delegated or machine, same SDK shape.
- Section 3: A single Agent Directory across MCP, AgentCore, n8n, and custom agents — with real revocation that invalidates tokens immediately.
- Section 4: Policy conditions written against the Okta groups Northwind already maintains — extended into the MCP layer.