From inai-auth-skills
Integrate InAI Auth SDK into Astro applications. Use this skill whenever the user wants to add authentication, login, signup, middleware protection, role-based access control (RBAC), MFA, or session management to an Astro app using @inai-dev/astro. Also trigger when the user mentions InAI auth with Astro, asks about protecting routes in Astro, needs server-side auth helpers (auth(), currentUser()), wants to set up auth API routes in Astro, or is building an SSR Astro site that needs authentication. Covers both the integration plugin and direct @inai-dev/backend usage.
How this skill is triggered — by the user, by Claude, or both
Slash command
/inai-auth-skills:inai-astro-sdkThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill guides you through integrating InAI Auth into Astro 6+ applications using the `@inai-dev/astro` package.
This skill guides you through integrating InAI Auth into Astro 6+ applications using the @inai-dev/astro package.
https://apiauth.inai.dev — hardcoded in the SDK, never configurableINAI_PUBLISHABLE_KEY=pk_live_...@inai-dev/astro (depends on @inai-dev/backend and @inai-dev/shared)output: "server" (SSR) mode@inai-dev/react separatelynpm install @inai-dev/astro
inaiAuth() in astro.config.mjsinaiAstroMiddleware() in src/middleware.tscreateAuthRoutes() in src/pages/api/auth/[path].tsServer helpers (auth(), currentUser()) are available in any .astro page or API endpoint.
// astro.config.mjs
import { defineConfig } from "astro/config";
import { inaiAuth } from "@inai-dev/astro";
export default defineConfig({
output: "server", // Required for auth
integrations: [inaiAuth()],
});
The plugin is currently minimal and exists mainly for future extensibility.
// src/middleware.ts
import { inaiAstroMiddleware } from "@inai-dev/astro/middleware";
export const onRequest = inaiAstroMiddleware({
publicRoutes: ["/", "/about", "/pricing"],
signInUrl: "/login",
// jwksUrl: "https://apiauth.inai.dev/.well-known/jwks.json", // optional override
});
auth_token cookieAstro.locals.auth with an AuthObjectsignInUrl (preserves original URL via ?returnTo=)refresh_token exists// src/middleware.ts
import { sequence } from "astro:middleware";
import { inaiAstroMiddleware } from "@inai-dev/astro/middleware";
const authMiddleware = inaiAstroMiddleware({
publicRoutes: ["/", "/about"],
signInUrl: "/login",
});
export const onRequest = sequence(authMiddleware, myOtherMiddleware);
Use Astro's built-in sequence() to chain middleware — the auth middleware populates Astro.locals.auth for downstream middleware and pages.
// src/pages/api/auth/[path].ts
import { createAuthRoutes } from "@inai-dev/astro/api-routes";
const routes = createAuthRoutes({
publishableKey: process.env.INAI_PUBLISHABLE_KEY,
});
export const ALL = routes.ALL;
Creates these endpoints automatically:
POST /api/auth/login — Login, sets httpOnly cookies. Returns { user } or { mfa_required, mfa_token }POST /api/auth/register — RegistrationPOST /api/auth/mfa-challenge — TOTP MFA verificationPOST /api/auth/refresh — Token rotation (also called by middleware automatically)POST /api/auth/logout — Invalidate session, clear cookiesSynchronous. Returns the AuthObject from Astro.locals.auth (populated by middleware).
---
// src/pages/dashboard.astro
import { auth } from "@inai-dev/astro/server";
const authObj = auth(Astro);
if (!authObj?.userId) {
return Astro.redirect("/login");
}
const canManage = authObj.has({ role: "admin" });
---
<h1>Dashboard</h1>
{canManage && <a href="/admin">Admin Panel</a>}
Async. Fetches the full UserResource from the API using the access token.
---
import { currentUser } from "@inai-dev/astro/server";
const user = await currentUser(Astro);
if (!user) return Astro.redirect("/login");
---
<p>{user.email}</p>
<p>{user.firstName} {user.lastName}</p>
You can optionally pass a publishableKey:
const user = await currentUser(Astro, {
publishableKey: process.env.INAI_PUBLISHABLE_KEY,
});
// src/pages/api/profile.ts
import type { APIRoute } from "astro";
import { auth } from "@inai-dev/astro/server";
export const GET: APIRoute = async (context) => {
const authObj = auth(context);
if (!authObj?.userId) {
return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
}
return new Response(JSON.stringify({ userId: authObj.userId }), {
headers: { "Content-Type": "application/json" },
});
};
For advanced use cases (custom auth flows, manual token management):
import { setAuthCookies, clearAuthCookies } from "@inai-dev/astro/server";
// Set auth cookies after manual authentication
setAuthCookies(Astro.cookies, tokens, user);
// Clear all auth cookies (manual logout)
clearAuthCookies(Astro.cookies);
{
userId: string | null
tenantId: string | null
appId: string | null
envId: string | null
orgId: string | null
orgRole: string | null
sessionId: string | null
roles: string[]
permissions: string[]
getToken(): Promise<string | null>
has(params: { role?: string; permission?: string }): boolean
}
Since Astro is server-first, client-side auth is done via fetch to the API routes:
// Login
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
if (data.mfa_required) {
// Show MFA form, then:
await fetch("/api/auth/mfa-challenge", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mfa_token: data.mfa_token, code: totpCode }),
});
}
// Logout
await fetch("/api/auth/logout", { method: "POST" });
If you use React islands in Astro and need client-side auth state, install @inai-dev/react separately:
npm install @inai-dev/react
Then wrap your island with the provider:
// src/components/AuthIsland.tsx
import { InAIAuthProvider, useAuth, SignedIn, SignedOut } from "@inai-dev/react";
export function AuthIsland() {
return (
<InAIAuthProvider>
<SignedIn>
<p>Authenticated!</p>
</SignedIn>
<SignedOut>
<a href="/login">Sign in</a>
</SignedOut>
</InAIAuthProvider>
);
}
---
// src/pages/index.astro
import { AuthIsland } from "../components/AuthIsland";
---
<AuthIsland client:load />
You can use @inai-dev/backend directly without the Astro integration package for full control:
// src/lib/auth-client.ts
import { InAIAuthClient } from "@inai-dev/backend";
export const authClient = new InAIAuthClient({
publishableKey: process.env.INAI_PUBLISHABLE_KEY,
});
---
import { authClient } from "../lib/auth-client";
const token = Astro.cookies.get("auth_token")?.value;
if (!token) return Astro.redirect("/login");
try {
const { data: user } = await authClient.getMe(token);
} catch {
return Astro.redirect("/login");
}
---
<h1>Dashboard</h1>
This approach gives you access to all InAIAuthClient methods (login, register, refresh, MFA, organization management, etc.) but you handle cookies and middleware yourself.
| Cookie | Purpose | httpOnly | Path | MaxAge |
|---|---|---|---|---|
auth_token | Access JWT | Yes | / | Token expiry |
refresh_token | Refresh JWT | Yes | / | 7 days |
auth_session | User data (readable by JS) | No | / | Token expiry |
The auth_session cookie enables React islands to hydrate auth state without a network request.
---
import { auth } from "@inai-dev/astro/server";
const authObj = auth(Astro);
if (!authObj?.userId) return Astro.redirect("/login");
if (!authObj.has({ role: "admin" })) return Astro.redirect("/unauthorized");
---
<h1>Admin Panel</h1>
---
import { auth } from "@inai-dev/astro/server";
// Redirect if already signed in
const authObj = auth(Astro);
if (authObj?.userId) return Astro.redirect("/dashboard");
---
<form id="login-form">
<input type="email" name="email" required />
<input type="password" name="password" required />
<button type="submit">Sign In</button>
</form>
<script>
document.getElementById("login-form")?.addEventListener("submit", async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const data = Object.fromEntries(new FormData(form));
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (res.ok) {
window.location.href = "/dashboard";
}
});
</script>
When you need to check implementation details, the source files are at:
packages/astro/src/middleware.ts — Middleware implementationpackages/astro/src/server.ts — auth(), currentUser(), cookie helperspackages/astro/src/api-routes.ts — createAuthRoutes()packages/astro/src/integration.ts — Astro integration pluginpackages/backend/src/client.ts — InAIAuthClient (core API client)packages/shared/src/constants.ts — Cookie names, URLs, headerspackages/shared/src/jwks.ts — JWKSClient (JWKS key fetching, caching, error throttling)packages/shared/src/jwt.ts — ES256 verification, JWT decodingnpx claudepluginhub inai-team/inai-auth-skills --plugin inai-auth-skillsProvides Astro patterns for integrating Clerk authentication: middleware, SSR pages, island components, API routes, and static vs SSR rendering.
Expert Astro Server Actions — defineAction, astro:actions, Zod validation, ActionError, HTML form actions, accept form, progressive enhancement, redirect patterns. Use when handling form submissions, mutations, or any server-side logic with type safety.
Use when adding authentication or login to any app - detects your stack (React, Next.js, Vue, Nuxt, Angular, Express, Fastify, FastAPI, ASP.NET Core, React Native, Expo, Android, Swift), sets up an Auth0 account if needed, and routes to the correct SDK setup workflow.