theauth
Skill20 starsChanged 26 days agoMITView on GitHub
Install and use theAuth (@glinr/theauth), the TypeScript auth SDK for AI agents and humans. Use when adding agent identity, scoped permissions, delegation, audit logs, human sign-in plugins, or an MCP OAuth 2.1 authorization server to a Node, Bun, Deno or edge project.
- Reads credentials
- Installs packages
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Very specific: line 14
Look for an existing setup: search for createTheAuth( or call the inspect tool.
- Distinctive: line 15
Never print or commit secrets.
- Distinctive: line 16
Verify against the docs page or the installed types.
- Very specific: line 37
Create it once at startup and reuse it.
- Distinctive: line 165
Run the project's typecheck after edits.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
theAuth
theAuth is a TypeScript SDK where the AI agent is the primary identity. Each agent has an owner (a user row), a bearer token, scoped permissions, optional delegation to sub-agents, and an audit row for every decision. Human sign-in comes from plugins. An MCP OAuth 2.1 authorization server is available through @glinr/theauth/mcp.
Docs: https://docs.theauth.dev (full text at https://docs.theauth.dev/llms-full.txt). If the theauth MCP server is connected, prefer its search_docs, get_doc, add_plugin and inspect tools over guessing.
Before you change anything
- Look for an existing setup: search for
createTheAuth(or call theinspecttool. Extend it, do not add a second instance. - Never print or commit secrets. Session and MCP signing secrets come from environment variables.
- Verify against the docs page or the installed types. Do not invent options.
Install
pnpm add @glinr/theauth better-sqlite3npm install and yarn add work the same. Use the matching database provider on Bun, Deno or Workers (see https://docs.theauth.dev/database). Tables are created on startup.
Create the instance
import { createTheAuth } from "@glinr/theauth";
export const theauth = await createTheAuth({
database: { provider: "sqlite-native", url: "theauth.db" }, // or { provider: "postgres", url: process.env.DATABASE_URL! }
agents: { enabled: true, maxPerUser: 10, auditAll: true, tokenExpiry: "24h" },
});Create it once at startup and reuse it. Functions that can fail return { success: true, data } | { success: false, error } in several modules, so check success rather than assuming a throw.
Adapter choice
Pick the adapter for the framework already in the project. Each returns the routes (agents, delegations, audit, authorize, MCP) for you to mount.
| Framework | Install | Mount |
| Hono | @glinr/theauth-hono | app.route("/api/theauth", theAuthHono(theauth, { authenticate })) |
| Express | @glinr/theauth-express | app.use("/api/theauth", theAuthExpress(theauth, { authenticate })) |
| Fastify | @glinr/theauth-fastify | fastify.register(theAuthFastify(theauth, { authenticate }), { prefix: "/api/theauth" }) |
| Next.js | @glinr/theauth-nextjs | const handlers = theAuthNextjs(theauth, { authenticate }) in app/api/theauth/[...theauth]/route.ts, then export GET, POST, PATCH, DELETE, OPTIONS from it |
Other adapters: Astro, NestJS, Nuxt, SolidStart, SvelteKit, TanStack (see https://docs.theauth.dev/adapters).
The adapters refuse to start without a way to authenticate callers on the management routes. Pass authenticate, configure auth.session, or set allowUnauthenticated: true for local development only:
const authenticate = async (request: Request) => {
const user = await theauth.auth.resolveUser(request);
return user ? { id: user.id } : null;
};Agent identity, permissions, audit
const agent = await theauth.agent.create({
ownerId: "user-123", // must exist in theauth_users
name: "github-reader",
type: "autonomous", // or "delegated" | "service"
permissions: [{ resource: "mcp:github:*", actions: ["read"] }],
});
// agent.token starts with "ta_" (legacy "kv_" tokens still work) and is shown once. Store it in a secrets manager.
const decision = await theauth.authorize(agent.id, { action: "read", resource: "mcp:github:repos" });
if (!decision.allowed) throw new Error(`Denied: ${decision.reason}`);
// With only a bearer token from an incoming request:
await theauth.authorizeByToken(agent.token, { action: "read", resource: "mcp:github:repos" });
const logs = await theauth.audit.query({ agentId: agent.id });
const rotated = await theauth.agent.rotate(agent.id); // old token stops working immediatelyConstraints such as requireApproval: true and maxCallsPerHour go in a permission's constraints. Resources support * wildcards.
Delegation
An agent can pass a subset of its own permissions to another agent. Escalation is rejected.
const sub = await theauth.agent.create({ ownerId: "user-123", name: "sub-reader", type: "delegated", permissions: [] });
await theauth.delegate({
fromAgent: agent.id,
toAgent: sub.id,
permissions: [{ resource: "mcp:github:issues", actions: ["read"] }],
expiresAt: new Date(Date.now() + 3_600_000),
maxDepth: 2,
});
const effective = await theauth.delegation.getEffectivePermissions(sub.id);Plugins (human auth and extras)
Pass plugins to createTheAuth({ plugins: [...] }). Plugins that issue sessions (magicLink, emailOtp, anonymousAuth) throw at startup unless auth: { session: { secret: process.env.THEAUTH_SESSION_SECRET! } } is set (32+ characters).
import { createTheAuth, passkey, twoFactor, organization } from "@glinr/theauth";
import { emailPassword } from "@glinr/theauth-email"; // pnpm add @glinr/theauth-email
const theauth = await createTheAuth({
database: { provider: "sqlite-native", url: "theauth.db" },
auth: { session: { secret: process.env.THEAUTH_SESSION_SECRET! } },
plugins: [
emailPassword({
appUrl: process.env.APP_URL!,
sendVerificationEmail: async (email, _token, url) => sendMail(email, "Verify your email", url),
sendResetEmail: async (email, _token, url) => sendMail(email, "Reset your password", url),
}),
passkey({ rpName: "My App", rpId: "example.com", origin: "https://example.com" }),
twoFactor(),
organization(),
],
});Exported from @glinr/theauth: magicLink, emailOtp, passkey, twoFactor, organization, apiKeys, anonymousAuth, admin, gdpr, agentRegistration, oauth. Email and password lives in @glinr/theauth-email. Plugin endpoints take standard Request objects: theauth.plugins.handleRequest(request). Use the add_plugin MCP tool for the exact snippet of any of them.
Headless agents (CI, workers) can register themselves with a one-time token from the agentRegistration() plugin: https://docs.theauth.dev/agent-registration-tokens.
MCP auth (OAuth 2.1 authorization server)
The OAuth server is a separate module. It has no database of its own: you supply storage callbacks (storeClient, findClient, storeAuthorizationCode, consumeAuthorizationCode, storeToken, findTokenByRefreshToken, revokeToken, resolveUserId). Copy the in-memory example from https://docs.theauth.dev/mcp and back it with a real database in production.
import { createMcpModule } from "@glinr/theauth/mcp";
export const mcp = createMcpModule({
config: {
enabled: true,
issuer: process.env.AUTH_BASE_URL!,
baseUrl: `${process.env.AUTH_BASE_URL!}/api/theauth`, // public origin plus the adapter mount path
signingSecret: process.env.MCP_SIGNING_SECRET!, // 32+ characters
resource: process.env.MCP_RESOURCE_URL!, // audience this server accepts
scopes: ["mcp:read", "mcp:execute"],
loginPage: `${process.env.AUTH_BASE_URL!}/login`,
},
...mcpStore,
});
// Mount it: theAuthHono(theauth, { mcp, authenticate })Protect a route:
const check = await mcp.requireScopes(request, ["mcp:read"]);
if (!check.authorized) return check.response; // 401 or 403 with the right challenge
const session = check.session; // userId, clientId, scopes, resource, expiresAtGotchas: the well-known documents are served relative to the mount point, so add root-level routes for /.well-known/oauth-authorization-server (mcp.getMetadata()) and /.well-known/oauth-protected-resource (mcp.getProtectedResourceMetadata()) when mounted under a prefix. PKCE S256 is the only challenge method. resource (RFC 8707) is required at the authorize and token endpoints.
Checklist
- One
createTheAuthinstance, secrets from the environment, nothing secret committed. - Management routes protected by
authenticateorauth.session. - Agent tokens stored once, rotated with
theauth.agent.rotate. - Run the project's typecheck after edits. The package ships its own types.
In this repository3 other files it gives its agents.
- docs/llms-full.txtllms.txt
- docs/llms.txtllms.txt
- theauthSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

