@inai-dev/hono 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,318 @@
1
+ # @inai-dev/hono
2
+
3
+ Full Hono integration for InAI Auth. Includes middleware with automatic token refresh, route protection with RBAC, and API route handlers. Works on Cloudflare Workers, Node.js, Deno, and Bun.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @inai-dev/hono
9
+ ```
10
+
11
+ ## Environment Variables
12
+
13
+ ```env
14
+ # Required — your publishable key
15
+ INAI_PUBLISHABLE_KEY=pk_live_...
16
+ ```
17
+
18
+ > On Cloudflare Workers, set this as a secret: `wrangler secret put INAI_PUBLISHABLE_KEY`
19
+
20
+ ## Setup
21
+
22
+ ### 1. Middleware
23
+
24
+ ```ts
25
+ import { Hono } from "hono";
26
+ import { inaiAuthMiddleware } from "@inai-dev/hono/middleware";
27
+
28
+ const app = new Hono();
29
+
30
+ app.use(
31
+ "*",
32
+ inaiAuthMiddleware({
33
+ publicRoutes: ["/", "/health", "/api/public/*"],
34
+ })
35
+ );
36
+ ```
37
+
38
+ The middleware automatically:
39
+ - Skips public routes (supports glob patterns with `*`)
40
+ - Validates the auth token from `Authorization: Bearer <token>` header or cookies
41
+ - Refreshes expired tokens when a refresh token exists in cookies
42
+ - Sets `c.get("inaiAuth")` with the `AuthObject` for authenticated requests
43
+ - Returns `401 Unauthorized` for unauthenticated requests on protected routes
44
+ - Uses `hono/cookie` for cookie parsing (works across all runtimes)
45
+
46
+ #### Configuration
47
+
48
+ ```ts
49
+ inaiAuthMiddleware({
50
+ // Auth mode: "app" (default) or "platform" (admin panel auth)
51
+ authMode: "app",
52
+
53
+ // Routes that don't require authentication
54
+ publicRoutes: ["/", "/health", "/api/public/*"],
55
+ // Or use a function for dynamic matching
56
+ publicRoutes: (path) => path.startsWith("/public"),
57
+
58
+ // Custom unauthorized handler (default: 401 JSON response)
59
+ onUnauthorized: (c) => c.json({ error: "Please sign in" }, 401),
60
+
61
+ // JWKS endpoint for ES256 token verification (optional)
62
+ jwksUrl: "https://apiauth.inai.dev/.well-known/jwks.json",
63
+
64
+ // InAIAuthClient config (optional if using env vars)
65
+ publishableKey: "pk_live_...",
66
+ });
67
+ ```
68
+
69
+ > All tokens are cryptographically verified using ES256 (ECDSA P-256). Public keys are fetched from the JWKS endpoint and cached for 5 minutes.
70
+
71
+ ### 2. Route Protection (RBAC)
72
+
73
+ ```ts
74
+ import { requireAuth } from "@inai-dev/hono/middleware";
75
+
76
+ // Require any authenticated user
77
+ app.get("/api/profile", requireAuth(), (c) => {
78
+ const auth = c.get("inaiAuth");
79
+ return c.json({ userId: auth!.userId });
80
+ });
81
+
82
+ // Require a specific role
83
+ app.get("/api/admin", requireAuth({ role: "admin" }), (c) => {
84
+ return c.json({ message: "Admin area" });
85
+ });
86
+
87
+ // Require a specific permission
88
+ app.post("/api/posts", requireAuth({ permission: "posts:write" }), (c) => {
89
+ return c.json({ message: "Post created" });
90
+ });
91
+ ```
92
+
93
+ `requireAuth()` returns:
94
+ - `401` if no auth or no `userId`
95
+ - `403` if the user lacks the required `role` or `permission`
96
+
97
+ ### 3. API Routes
98
+
99
+ ```ts
100
+ import { createAuthRoutes } from "@inai-dev/hono/api-routes";
101
+
102
+ const authApp = createAuthRoutes({
103
+ publishableKey: "pk_live_...",
104
+ });
105
+
106
+ app.route("/api/auth", authApp);
107
+ ```
108
+
109
+ Handles the following endpoints automatically:
110
+ - `POST /api/auth/login` — User login (returns `{ user }` or `{ mfa_required, mfa_token }`)
111
+ - `POST /api/auth/register` — User registration
112
+ - `POST /api/auth/mfa-challenge` — MFA verification
113
+ - `POST /api/auth/refresh` — Token refresh
114
+ - `POST /api/auth/logout` — User logout
115
+
116
+ ### 4. Auth Helpers
117
+
118
+ #### `getAuth(c)`
119
+
120
+ Returns the `AuthObject` from the context (populated by middleware).
121
+
122
+ ```ts
123
+ import { getAuth } from "@inai-dev/hono";
124
+
125
+ app.get("/api/me", (c) => {
126
+ const auth = getAuth(c);
127
+ if (!auth?.userId) {
128
+ return c.json({ error: "Not signed in" }, 401);
129
+ }
130
+ return c.json({ userId: auth.userId, orgId: auth.orgId });
131
+ });
132
+ ```
133
+
134
+ **`AuthObject`:**
135
+
136
+ | Property | Type | Description |
137
+ |---|---|---|
138
+ | `userId` | `string \| null` | Current user ID |
139
+ | `tenantId` | `string \| null` | Tenant ID |
140
+ | `appId` | `string \| null` | Application ID |
141
+ | `envId` | `string \| null` | Environment ID |
142
+ | `orgId` | `string \| null` | Active organization ID |
143
+ | `orgRole` | `string \| null` | Role in active organization |
144
+ | `sessionId` | `string \| null` | Session ID |
145
+ | `getToken()` | `() => Promise<string \| null>` | Get the access token |
146
+ | `has(params)` | `({ role?, permission? }) => boolean` | Check role or permission |
147
+
148
+ #### Cookie Helpers
149
+
150
+ For advanced use cases (custom auth flows, manual token management):
151
+
152
+ ```ts
153
+ import { setAuthCookies, clearAuthCookies } from "@inai-dev/hono";
154
+
155
+ // Set auth cookies after manual authentication
156
+ setAuthCookies(c, tokens, user);
157
+
158
+ // Clear all auth cookies (manual logout)
159
+ clearAuthCookies(c);
160
+ ```
161
+
162
+ #### Token Extraction
163
+
164
+ ```ts
165
+ import { getTokenFromContext, getRefreshTokenFromContext } from "@inai-dev/hono";
166
+
167
+ // Get access token from Authorization header or cookies
168
+ const token = getTokenFromContext(c);
169
+
170
+ // Get refresh token from cookies
171
+ const refreshToken = getRefreshTokenFromContext(c);
172
+ ```
173
+
174
+ ## Full Example
175
+
176
+ ```ts
177
+ import { Hono } from "hono";
178
+ import {
179
+ inaiAuthMiddleware,
180
+ requireAuth,
181
+ createAuthRoutes,
182
+ getAuth,
183
+ } from "@inai-dev/hono";
184
+
185
+ const app = new Hono();
186
+
187
+ // Auth middleware — protects all routes except public ones
188
+ app.use(
189
+ "*",
190
+ inaiAuthMiddleware({
191
+ publicRoutes: ["/", "/health", "/api/auth/*"],
192
+ })
193
+ );
194
+
195
+ // Auth API routes
196
+ app.route("/api/auth", createAuthRoutes());
197
+
198
+ // Public route
199
+ app.get("/health", (c) => c.json({ status: "ok" }));
200
+
201
+ // Protected route — any authenticated user
202
+ app.get("/api/profile", (c) => {
203
+ const auth = getAuth(c);
204
+ return c.json({ userId: auth?.userId });
205
+ });
206
+
207
+ // Protected route — admin only
208
+ app.get("/api/admin", requireAuth({ role: "admin" }), (c) => {
209
+ return c.json({ message: "Welcome, admin" });
210
+ });
211
+
212
+ export default app;
213
+ ```
214
+
215
+ ### Cloudflare Workers
216
+
217
+ ```ts
218
+ // src/index.ts
219
+ import { Hono } from "hono";
220
+ import { inaiAuthMiddleware, createAuthRoutes } from "@inai-dev/hono";
221
+
222
+ const app = new Hono();
223
+
224
+ app.use(
225
+ "*",
226
+ inaiAuthMiddleware({
227
+ publicRoutes: ["/", "/api/auth/*"],
228
+ publishableKey: "pk_live_...",
229
+ })
230
+ );
231
+
232
+ app.route("/api/auth", createAuthRoutes());
233
+
234
+ app.get("/api/hello", (c) => {
235
+ const auth = c.get("inaiAuth");
236
+ return c.json({ userId: auth?.userId });
237
+ });
238
+
239
+ export default app;
240
+ ```
241
+
242
+ ## Platform Mode (Admin Panel)
243
+
244
+ For admin panels that authenticate against the InAI platform API:
245
+
246
+ ```ts
247
+ app.use(
248
+ "*",
249
+ inaiAuthMiddleware({
250
+ authMode: "platform",
251
+ publicRoutes: ["/login"],
252
+ })
253
+ );
254
+ ```
255
+
256
+ In platform mode, the middleware uses `platformRefresh()` and `platformGetMe()` for token refresh, targeting the platform auth endpoints instead of app user endpoints.
257
+
258
+ ## Type Augmentation
259
+
260
+ The package augments Hono's `ContextVariableMap` so `c.get("inaiAuth")` is fully typed:
261
+
262
+ ```ts
263
+ // Automatic — just import the package
264
+ import "@inai-dev/hono";
265
+
266
+ // c.get("inaiAuth") is typed as AuthObject | null
267
+ ```
268
+
269
+ ## Exports Reference
270
+
271
+ ### `@inai-dev/hono`
272
+
273
+ | Export | Kind | Description |
274
+ |---|---|---|
275
+ | `inaiAuthMiddleware` | Function | Auth middleware |
276
+ | `requireAuth` | Function | RBAC route guard |
277
+ | `createAuthRoutes` | Function | Auth API route handlers |
278
+ | `getAuth` | Function | Get `AuthObject` from context |
279
+ | `setAuthCookies` | Function | Set auth cookies |
280
+ | `clearAuthCookies` | Function | Clear auth cookies |
281
+ | `getTokenFromContext` | Function | Extract access token |
282
+ | `getRefreshTokenFromContext` | Function | Extract refresh token |
283
+
284
+ ### `@inai-dev/hono/middleware`
285
+
286
+ | Export | Kind | Description |
287
+ |---|---|---|
288
+ | `inaiAuthMiddleware` | Function | Auth middleware with token refresh |
289
+ | `requireAuth` | Function | RBAC route guard |
290
+
291
+ ### `@inai-dev/hono/api-routes`
292
+
293
+ | Export | Kind | Description |
294
+ |---|---|---|
295
+ | `createAuthRoutes` | Function | Create Hono sub-app with auth endpoints |
296
+
297
+ ## Exported Types
298
+
299
+ ```ts
300
+ import type {
301
+ InAIHonoMiddlewareConfig,
302
+ RequireAuthConfig,
303
+ } from "@inai-dev/hono";
304
+
305
+ import type {
306
+ AuthObject,
307
+ UserResource,
308
+ OrganizationResource,
309
+ } from "@inai-dev/hono";
310
+ ```
311
+
312
+ ## Questions & Support
313
+
314
+ Visit [https://inai.dev](https://inai.dev) for documentation, guides, and support.
315
+
316
+ ## License
317
+
318
+ [MIT](../../LICENSE)
package/dist/index.cjs CHANGED
@@ -118,6 +118,8 @@ function inaiAuthMiddleware(config = {}) {
118
118
  } = config;
119
119
  const client = new import_backend.InAIAuthClient(authClientConfig);
120
120
  const isPlatform = authMode === "platform";
121
+ const jwksUrl = authClientConfig.jwksUrl ?? `${authClientConfig.apiUrl ?? import_shared2.DEFAULT_API_URL}/.well-known/jwks.json`;
122
+ const jwksClient = new import_shared2.JWKSClient(jwksUrl);
121
123
  const defaultUnauthorized = (c) => c.json({ error: "Unauthorized" }, 401);
122
124
  const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;
123
125
  return async function middleware(c, next) {
@@ -135,7 +137,7 @@ function inaiAuthMiddleware(config = {}) {
135
137
  const tokens = isPlatform ? await client.platformRefresh(refreshToken) : await client.refresh(refreshToken);
136
138
  const { data: user } = isPlatform ? await client.platformGetMe(tokens.access_token) : await client.getMe(tokens.access_token);
137
139
  setAuthCookies(c, tokens, user);
138
- const authObj2 = (0, import_backend.buildAuthObjectFromToken)(tokens.access_token);
140
+ const authObj2 = await (0, import_backend.buildAuthObjectFromToken)(tokens.access_token, jwksClient);
139
141
  c.set("inaiAuth", authObj2);
140
142
  await next();
141
143
  return;
@@ -146,7 +148,7 @@ function inaiAuthMiddleware(config = {}) {
146
148
  }
147
149
  return handleUnauthorized(c);
148
150
  }
149
- const authObj = (0, import_backend.buildAuthObjectFromToken)(token);
151
+ const authObj = await (0, import_backend.buildAuthObjectFromToken)(token, jwksClient);
150
152
  if (!authObj) {
151
153
  return handleUnauthorized(c);
152
154
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/helpers.ts","../src/api-routes.ts"],"sourcesContent":["import \"./types\";\n\nexport { inaiAuthMiddleware, requireAuth } from \"./middleware\";\nexport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\n\nexport {\n getAuth,\n setAuthCookies,\n clearAuthCookies,\n getTokenFromContext,\n getRefreshTokenFromContext,\n} from \"./helpers\";\n\nexport { createAuthRoutes } from \"./api-routes\";\n\nexport type {\n AuthObject,\n UserResource,\n OrganizationResource,\n} from \"@inai-dev/types\";\n","import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = buildAuthObjectFromToken(tokens.access_token);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = buildAuthObjectFromToken(token);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n","import { Hono } from \"hono\";\nimport type { InAIAuthConfig, TokenPair, UserResource, LoginResult } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n setAuthCookies,\n clearAuthCookies,\n getRefreshTokenFromContext,\n} from \"./helpers\";\n\nexport function createAuthRoutes(config: InAIAuthConfig = {}) {\n const app = new Hono();\n const client = new InAIAuthClient(config);\n\n app.post(\"/login\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.login({\n email: body.email,\n password: body.password,\n }) as LoginResult & { user?: UserResource };\n\n if (result.mfa_required) {\n return c.json({ mfa_required: true, mfa_token: result.mfa_token });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Login failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/register\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.register({\n email: body.email,\n password: body.password,\n firstName: body.firstName,\n lastName: body.lastName,\n });\n\n if (!result.access_token) {\n return c.json({ needs_email_verification: true, user: result.user });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Registration failed\";\n return c.json({ error: message }, 400);\n }\n });\n\n app.post(\"/mfa-challenge\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const tokens = await client.mfaChallenge({\n mfa_token: body.mfa_token,\n code: body.code,\n });\n\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"MFA verification failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/refresh\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (!refreshToken) {\n clearAuthCookies(c);\n return c.json({ error: \"No refresh token\" }, 401);\n }\n\n const tokens = await client.refresh(refreshToken);\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch {\n clearAuthCookies(c);\n return c.json({ error: \"Refresh failed\" }, 401);\n }\n });\n\n app.post(\"/logout\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n if (refreshToken) {\n await client.logout(refreshToken).catch(() => {});\n }\n clearAuthCookies(c);\n return c.json({ success: true });\n } catch {\n clearAuthCookies(c);\n return c.json({ success: true });\n }\n });\n\n return app;\n}\n"],"mappings":"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","names":["import_shared","authObj","import_backend"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/helpers.ts","../src/api-routes.ts"],"sourcesContent":["import \"./types\";\n\nexport { inaiAuthMiddleware, requireAuth } from \"./middleware\";\nexport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\n\nexport {\n getAuth,\n setAuthCookies,\n clearAuthCookies,\n getTokenFromContext,\n getRefreshTokenFromContext,\n} from \"./helpers\";\n\nexport { createAuthRoutes } from \"./api-routes\";\n\nexport type {\n AuthObject,\n UserResource,\n OrganizationResource,\n} from \"@inai-dev/types\";\n","import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired, JWKSClient, DEFAULT_API_URL } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const jwksUrl = authClientConfig.jwksUrl\n ?? `${authClientConfig.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;\n const jwksClient = new JWKSClient(jwksUrl);\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = await buildAuthObjectFromToken(tokens.access_token, jwksClient);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = await buildAuthObjectFromToken(token, jwksClient);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n","import { Hono } from \"hono\";\nimport type { InAIAuthConfig, TokenPair, UserResource, LoginResult } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n setAuthCookies,\n clearAuthCookies,\n getRefreshTokenFromContext,\n} from \"./helpers\";\n\nexport function createAuthRoutes(config: InAIAuthConfig = {}) {\n const app = new Hono();\n const client = new InAIAuthClient(config);\n\n app.post(\"/login\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.login({\n email: body.email,\n password: body.password,\n }) as LoginResult & { user?: UserResource };\n\n if (result.mfa_required) {\n return c.json({ mfa_required: true, mfa_token: result.mfa_token });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Login failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/register\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.register({\n email: body.email,\n password: body.password,\n firstName: body.firstName,\n lastName: body.lastName,\n });\n\n if (!result.access_token) {\n return c.json({ needs_email_verification: true, user: result.user });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Registration failed\";\n return c.json({ error: message }, 400);\n }\n });\n\n app.post(\"/mfa-challenge\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const tokens = await client.mfaChallenge({\n mfa_token: body.mfa_token,\n code: body.code,\n });\n\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"MFA verification failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/refresh\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (!refreshToken) {\n clearAuthCookies(c);\n return c.json({ error: \"No refresh token\" }, 401);\n }\n\n const tokens = await client.refresh(refreshToken);\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch {\n clearAuthCookies(c);\n return c.json({ error: \"Refresh failed\" }, 401);\n }\n });\n\n app.post(\"/logout\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n if (refreshToken) {\n await client.logout(refreshToken).catch(() => {});\n }\n clearAuthCookies(c);\n return c.json({ success: true });\n } catch {\n clearAuthCookies(c);\n return c.json({ success: true });\n }\n });\n\n return app;\n}\n"],"mappings":"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","names":["import_shared","authObj","import_backend"]}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/middleware.ts
2
2
  import { InAIAuthClient, buildAuthObjectFromToken } from "@inai-dev/backend";
3
- import { isTokenExpired } from "@inai-dev/shared";
3
+ import { isTokenExpired, JWKSClient, DEFAULT_API_URL } from "@inai-dev/shared";
4
4
 
5
5
  // src/helpers.ts
6
6
  import { getCookie, setCookie, deleteCookie } from "hono/cookie";
@@ -90,6 +90,8 @@ function inaiAuthMiddleware(config = {}) {
90
90
  } = config;
91
91
  const client = new InAIAuthClient(authClientConfig);
92
92
  const isPlatform = authMode === "platform";
93
+ const jwksUrl = authClientConfig.jwksUrl ?? `${authClientConfig.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;
94
+ const jwksClient = new JWKSClient(jwksUrl);
93
95
  const defaultUnauthorized = (c) => c.json({ error: "Unauthorized" }, 401);
94
96
  const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;
95
97
  return async function middleware(c, next) {
@@ -107,7 +109,7 @@ function inaiAuthMiddleware(config = {}) {
107
109
  const tokens = isPlatform ? await client.platformRefresh(refreshToken) : await client.refresh(refreshToken);
108
110
  const { data: user } = isPlatform ? await client.platformGetMe(tokens.access_token) : await client.getMe(tokens.access_token);
109
111
  setAuthCookies(c, tokens, user);
110
- const authObj2 = buildAuthObjectFromToken(tokens.access_token);
112
+ const authObj2 = await buildAuthObjectFromToken(tokens.access_token, jwksClient);
111
113
  c.set("inaiAuth", authObj2);
112
114
  await next();
113
115
  return;
@@ -118,7 +120,7 @@ function inaiAuthMiddleware(config = {}) {
118
120
  }
119
121
  return handleUnauthorized(c);
120
122
  }
121
- const authObj = buildAuthObjectFromToken(token);
123
+ const authObj = await buildAuthObjectFromToken(token, jwksClient);
122
124
  if (!authObj) {
123
125
  return handleUnauthorized(c);
124
126
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/helpers.ts","../src/api-routes.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = buildAuthObjectFromToken(tokens.access_token);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = buildAuthObjectFromToken(token);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n","import { Hono } from \"hono\";\nimport type { InAIAuthConfig, TokenPair, UserResource, LoginResult } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n setAuthCookies,\n clearAuthCookies,\n getRefreshTokenFromContext,\n} from \"./helpers\";\n\nexport function createAuthRoutes(config: InAIAuthConfig = {}) {\n const app = new Hono();\n const client = new InAIAuthClient(config);\n\n app.post(\"/login\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.login({\n email: body.email,\n password: body.password,\n }) as LoginResult & { user?: UserResource };\n\n if (result.mfa_required) {\n return c.json({ mfa_required: true, mfa_token: result.mfa_token });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Login failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/register\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.register({\n email: body.email,\n password: body.password,\n firstName: body.firstName,\n lastName: body.lastName,\n });\n\n if (!result.access_token) {\n return c.json({ needs_email_verification: true, user: result.user });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Registration failed\";\n return c.json({ error: message }, 400);\n }\n });\n\n app.post(\"/mfa-challenge\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const tokens = await client.mfaChallenge({\n mfa_token: body.mfa_token,\n code: body.code,\n });\n\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"MFA verification failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/refresh\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (!refreshToken) {\n clearAuthCookies(c);\n return c.json({ error: \"No refresh token\" }, 401);\n }\n\n const tokens = await client.refresh(refreshToken);\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch {\n clearAuthCookies(c);\n return c.json({ error: \"Refresh failed\" }, 401);\n }\n });\n\n app.post(\"/logout\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n if (refreshToken) {\n await client.logout(refreshToken).catch(() => {});\n }\n clearAuthCookies(c);\n return c.json({ success: true });\n } catch {\n clearAuthCookies(c);\n return c.json({ success: true });\n }\n });\n\n return app;\n}\n"],"mappings":"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","names":["authObj","InAIAuthClient","InAIAuthClient"]}
1
+ {"version":3,"sources":["../src/middleware.ts","../src/helpers.ts","../src/api-routes.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired, JWKSClient, DEFAULT_API_URL } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const jwksUrl = authClientConfig.jwksUrl\n ?? `${authClientConfig.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;\n const jwksClient = new JWKSClient(jwksUrl);\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = await buildAuthObjectFromToken(tokens.access_token, jwksClient);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = await buildAuthObjectFromToken(token, jwksClient);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n","import { Hono } from \"hono\";\nimport type { InAIAuthConfig, TokenPair, UserResource, LoginResult } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n setAuthCookies,\n clearAuthCookies,\n getRefreshTokenFromContext,\n} from \"./helpers\";\n\nexport function createAuthRoutes(config: InAIAuthConfig = {}) {\n const app = new Hono();\n const client = new InAIAuthClient(config);\n\n app.post(\"/login\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.login({\n email: body.email,\n password: body.password,\n }) as LoginResult & { user?: UserResource };\n\n if (result.mfa_required) {\n return c.json({ mfa_required: true, mfa_token: result.mfa_token });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Login failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/register\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const result = await client.register({\n email: body.email,\n password: body.password,\n firstName: body.firstName,\n lastName: body.lastName,\n });\n\n if (!result.access_token) {\n return c.json({ needs_email_verification: true, user: result.user });\n }\n\n const tokens = result as unknown as TokenPair;\n const user =\n result.user ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Registration failed\";\n return c.json({ error: message }, 400);\n }\n });\n\n app.post(\"/mfa-challenge\", async (c) => {\n try {\n const body = await c.req.json<Record<string, string>>();\n const tokens = await client.mfaChallenge({\n mfa_token: body.mfa_token,\n code: body.code,\n });\n\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"MFA verification failed\";\n return c.json({ error: message }, 401);\n }\n });\n\n app.post(\"/refresh\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (!refreshToken) {\n clearAuthCookies(c);\n return c.json({ error: \"No refresh token\" }, 401);\n }\n\n const tokens = await client.refresh(refreshToken);\n const { data: user } = await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n return c.json({ user });\n } catch {\n clearAuthCookies(c);\n return c.json({ error: \"Refresh failed\" }, 401);\n }\n });\n\n app.post(\"/logout\", async (c) => {\n try {\n const refreshToken = getRefreshTokenFromContext(c);\n if (refreshToken) {\n await client.logout(refreshToken).catch(() => {});\n }\n clearAuthCookies(c);\n return c.json({ success: true });\n } catch {\n clearAuthCookies(c);\n return c.json({ success: true });\n }\n });\n\n return app;\n}\n"],"mappings":"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","names":["authObj","InAIAuthClient","InAIAuthClient"]}
@@ -110,6 +110,8 @@ function inaiAuthMiddleware(config = {}) {
110
110
  } = config;
111
111
  const client = new import_backend.InAIAuthClient(authClientConfig);
112
112
  const isPlatform = authMode === "platform";
113
+ const jwksUrl = authClientConfig.jwksUrl ?? `${authClientConfig.apiUrl ?? import_shared2.DEFAULT_API_URL}/.well-known/jwks.json`;
114
+ const jwksClient = new import_shared2.JWKSClient(jwksUrl);
113
115
  const defaultUnauthorized = (c) => c.json({ error: "Unauthorized" }, 401);
114
116
  const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;
115
117
  return async function middleware(c, next) {
@@ -127,7 +129,7 @@ function inaiAuthMiddleware(config = {}) {
127
129
  const tokens = isPlatform ? await client.platformRefresh(refreshToken) : await client.refresh(refreshToken);
128
130
  const { data: user } = isPlatform ? await client.platformGetMe(tokens.access_token) : await client.getMe(tokens.access_token);
129
131
  setAuthCookies(c, tokens, user);
130
- const authObj2 = (0, import_backend.buildAuthObjectFromToken)(tokens.access_token);
132
+ const authObj2 = await (0, import_backend.buildAuthObjectFromToken)(tokens.access_token, jwksClient);
131
133
  c.set("inaiAuth", authObj2);
132
134
  await next();
133
135
  return;
@@ -138,7 +140,7 @@ function inaiAuthMiddleware(config = {}) {
138
140
  }
139
141
  return handleUnauthorized(c);
140
142
  }
141
- const authObj = (0, import_backend.buildAuthObjectFromToken)(token);
143
+ const authObj = await (0, import_backend.buildAuthObjectFromToken)(token, jwksClient);
142
144
  if (!authObj) {
143
145
  return handleUnauthorized(c);
144
146
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/helpers.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = buildAuthObjectFromToken(tokens.access_token);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = buildAuthObjectFromToken(token);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n"],"mappings":"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","names":["import_shared","authObj"]}
1
+ {"version":3,"sources":["../src/middleware.ts","../src/helpers.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired, JWKSClient, DEFAULT_API_URL } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const jwksUrl = authClientConfig.jwksUrl\n ?? `${authClientConfig.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;\n const jwksClient = new JWKSClient(jwksUrl);\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = await buildAuthObjectFromToken(tokens.access_token, jwksClient);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = await buildAuthObjectFromToken(token, jwksClient);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n"],"mappings":"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","names":["import_shared","authObj"]}
@@ -1,6 +1,6 @@
1
1
  // src/middleware.ts
2
2
  import { InAIAuthClient, buildAuthObjectFromToken } from "@inai-dev/backend";
3
- import { isTokenExpired } from "@inai-dev/shared";
3
+ import { isTokenExpired, JWKSClient, DEFAULT_API_URL } from "@inai-dev/shared";
4
4
 
5
5
  // src/helpers.ts
6
6
  import { getCookie, setCookie, deleteCookie } from "hono/cookie";
@@ -90,6 +90,8 @@ function inaiAuthMiddleware(config = {}) {
90
90
  } = config;
91
91
  const client = new InAIAuthClient(authClientConfig);
92
92
  const isPlatform = authMode === "platform";
93
+ const jwksUrl = authClientConfig.jwksUrl ?? `${authClientConfig.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;
94
+ const jwksClient = new JWKSClient(jwksUrl);
93
95
  const defaultUnauthorized = (c) => c.json({ error: "Unauthorized" }, 401);
94
96
  const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;
95
97
  return async function middleware(c, next) {
@@ -107,7 +109,7 @@ function inaiAuthMiddleware(config = {}) {
107
109
  const tokens = isPlatform ? await client.platformRefresh(refreshToken) : await client.refresh(refreshToken);
108
110
  const { data: user } = isPlatform ? await client.platformGetMe(tokens.access_token) : await client.getMe(tokens.access_token);
109
111
  setAuthCookies(c, tokens, user);
110
- const authObj2 = buildAuthObjectFromToken(tokens.access_token);
112
+ const authObj2 = await buildAuthObjectFromToken(tokens.access_token, jwksClient);
111
113
  c.set("inaiAuth", authObj2);
112
114
  await next();
113
115
  return;
@@ -118,7 +120,7 @@ function inaiAuthMiddleware(config = {}) {
118
120
  }
119
121
  return handleUnauthorized(c);
120
122
  }
121
- const authObj = buildAuthObjectFromToken(token);
123
+ const authObj = await buildAuthObjectFromToken(token, jwksClient);
122
124
  if (!authObj) {
123
125
  return handleUnauthorized(c);
124
126
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/helpers.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = buildAuthObjectFromToken(tokens.access_token);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = buildAuthObjectFromToken(token);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n"],"mappings":"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","names":["authObj"]}
1
+ {"version":3,"sources":["../src/middleware.ts","../src/helpers.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"hono\";\nimport type { InAIAuthConfig } from \"@inai-dev/types\";\nimport { InAIAuthClient, buildAuthObjectFromToken } from \"@inai-dev/backend\";\nimport { isTokenExpired, JWKSClient, DEFAULT_API_URL } from \"@inai-dev/shared\";\nimport type { InAIHonoMiddlewareConfig, RequireAuthConfig } from \"./types\";\nimport {\n getTokenFromContext,\n getRefreshTokenFromContext,\n setAuthCookies,\n clearAuthCookies,\n getAuth,\n} from \"./helpers\";\n\nfunction matchesRoute(pathname: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith(\"*\")) {\n return pathname.startsWith(pattern.slice(0, -1));\n }\n return pathname === pattern;\n });\n}\n\nfunction isPublicRoute(\n path: string,\n publicRoutes: string[] | ((path: string) => boolean),\n): boolean {\n if (typeof publicRoutes === \"function\") return publicRoutes(path);\n return matchesRoute(path, publicRoutes);\n}\n\nexport function inaiAuthMiddleware(\n config: InAIHonoMiddlewareConfig & InAIAuthConfig = {},\n): MiddlewareHandler {\n const {\n authMode = \"app\",\n publicRoutes = [],\n onUnauthorized,\n ...authClientConfig\n } = config;\n\n const client = new InAIAuthClient(authClientConfig);\n const isPlatform = authMode === \"platform\";\n\n const jwksUrl = authClientConfig.jwksUrl\n ?? `${authClientConfig.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;\n const jwksClient = new JWKSClient(jwksUrl);\n\n const defaultUnauthorized = (c: Parameters<MiddlewareHandler>[0]) =>\n c.json({ error: \"Unauthorized\" }, 401);\n\n const handleUnauthorized = onUnauthorized ?? defaultUnauthorized;\n\n return async function middleware(c, next) {\n const path = new URL(c.req.url).pathname;\n\n if (isPublicRoute(path, publicRoutes)) {\n c.set(\"inaiAuth\", null);\n await next();\n return;\n }\n\n const token = getTokenFromContext(c);\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = getRefreshTokenFromContext(c);\n\n if (refreshToken) {\n try {\n const tokens = isPlatform\n ? await client.platformRefresh(refreshToken)\n : await client.refresh(refreshToken);\n const { data: user } = isPlatform\n ? await client.platformGetMe(tokens.access_token)\n : await client.getMe(tokens.access_token);\n setAuthCookies(c, tokens, user);\n\n const authObj = await buildAuthObjectFromToken(tokens.access_token, jwksClient);\n c.set(\"inaiAuth\", authObj);\n\n await next();\n return;\n } catch {\n clearAuthCookies(c);\n return handleUnauthorized(c);\n }\n }\n\n return handleUnauthorized(c);\n }\n\n const authObj = await buildAuthObjectFromToken(token, jwksClient);\n if (!authObj) {\n return handleUnauthorized(c);\n }\n\n c.set(\"inaiAuth\", authObj);\n await next();\n };\n}\n\nexport function requireAuth(config: RequireAuthConfig = {}): MiddlewareHandler {\n return async function middleware(c, next) {\n const auth = getAuth(c);\n\n if (!auth?.userId) {\n return c.json({ error: \"Unauthorized\" }, 401);\n }\n\n if (config.role || config.permission) {\n const hasAccess = auth.has({\n role: config.role,\n permission: config.permission,\n });\n\n if (!hasAccess) {\n return c.json({ error: \"Forbidden\" }, 403);\n }\n }\n\n await next();\n };\n}\n","import type { Context } from \"hono\";\nimport { getCookie, setCookie, deleteCookie } from \"hono/cookie\";\nimport type { AuthObject, TokenPair, UserResource, PlatformUserResource } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\nexport function getAuth(c: Context): AuthObject | null {\n return c.get(\"inaiAuth\") ?? null;\n}\n\nexport function getTokenFromContext(c: Context): string | null {\n const authHeader = c.req.header(\"Authorization\");\n if (authHeader?.startsWith(\"Bearer \")) {\n return authHeader.slice(7);\n }\n\n return getCookie(c, COOKIE_AUTH_TOKEN) ?? null;\n}\n\nexport function getRefreshTokenFromContext(c: Context): string | null {\n return getCookie(c, COOKIE_REFRESH_TOKEN) ?? null;\n}\n\nexport function setAuthCookies(\n c: Context,\n tokens: TokenPair,\n user: UserResource | PlatformUserResource,\n): void {\n const isProduction =\n typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n const claims = decodeJWTPayload(tokens.access_token);\n const expiresAt = claims\n ? new Date(claims.exp * 1000).toISOString()\n : new Date(Date.now() + tokens.expires_in * 1000).toISOString();\n\n setCookie(c, COOKIE_AUTH_TOKEN, tokens.access_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n\n setCookie(c, COOKIE_REFRESH_TOKEN, tokens.refresh_token, {\n httpOnly: true,\n secure: isProduction,\n sameSite: \"Strict\",\n path: \"/api/auth\",\n maxAge: 7 * 24 * 60 * 60,\n });\n\n setCookie(\n c,\n COOKIE_AUTH_SESSION,\n JSON.stringify({\n user,\n expiresAt,\n permissions: claims?.permissions ?? [],\n orgId: claims?.org_id,\n orgRole: claims?.org_role,\n appId: claims?.app_id,\n envId: claims?.env_id,\n }),\n {\n httpOnly: false,\n secure: isProduction,\n sameSite: \"Lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n },\n );\n}\n\nexport function clearAuthCookies(c: Context): void {\n deleteCookie(c, COOKIE_AUTH_TOKEN, { path: \"/\" });\n deleteCookie(c, COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n deleteCookie(c, COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n"],"mappings":"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","names":["authObj"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inai-dev/hono",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Hono integration for InAI Auth SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -35,9 +35,9 @@
35
35
  "prepublishOnly": "npm run build"
36
36
  },
37
37
  "dependencies": {
38
- "@inai-dev/types": "^1.1.0",
39
- "@inai-dev/shared": "^1.1.0",
40
- "@inai-dev/backend": "^1.2.0"
38
+ "@inai-dev/types": "^1.3.0",
39
+ "@inai-dev/shared": "^1.3.0",
40
+ "@inai-dev/backend": "^1.4.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "hono": ">=4.0.0"