@inai-dev/hono 1.0.0 → 1.1.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 +313 -0
- package/package.json +4 -4
package/README.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
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
|
+
// InAIAuthClient config (optional if using env vars)
|
|
62
|
+
publishableKey: "pk_live_...",
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Route Protection (RBAC)
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { requireAuth } from "@inai-dev/hono/middleware";
|
|
70
|
+
|
|
71
|
+
// Require any authenticated user
|
|
72
|
+
app.get("/api/profile", requireAuth(), (c) => {
|
|
73
|
+
const auth = c.get("inaiAuth");
|
|
74
|
+
return c.json({ userId: auth!.userId });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Require a specific role
|
|
78
|
+
app.get("/api/admin", requireAuth({ role: "admin" }), (c) => {
|
|
79
|
+
return c.json({ message: "Admin area" });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Require a specific permission
|
|
83
|
+
app.post("/api/posts", requireAuth({ permission: "posts:write" }), (c) => {
|
|
84
|
+
return c.json({ message: "Post created" });
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`requireAuth()` returns:
|
|
89
|
+
- `401` if no auth or no `userId`
|
|
90
|
+
- `403` if the user lacks the required `role` or `permission`
|
|
91
|
+
|
|
92
|
+
### 3. API Routes
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { createAuthRoutes } from "@inai-dev/hono/api-routes";
|
|
96
|
+
|
|
97
|
+
const authApp = createAuthRoutes({
|
|
98
|
+
publishableKey: "pk_live_...",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
app.route("/api/auth", authApp);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Handles the following endpoints automatically:
|
|
105
|
+
- `POST /api/auth/login` — User login (returns `{ user }` or `{ mfa_required, mfa_token }`)
|
|
106
|
+
- `POST /api/auth/register` — User registration
|
|
107
|
+
- `POST /api/auth/mfa-challenge` — MFA verification
|
|
108
|
+
- `POST /api/auth/refresh` — Token refresh
|
|
109
|
+
- `POST /api/auth/logout` — User logout
|
|
110
|
+
|
|
111
|
+
### 4. Auth Helpers
|
|
112
|
+
|
|
113
|
+
#### `getAuth(c)`
|
|
114
|
+
|
|
115
|
+
Returns the `AuthObject` from the context (populated by middleware).
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { getAuth } from "@inai-dev/hono";
|
|
119
|
+
|
|
120
|
+
app.get("/api/me", (c) => {
|
|
121
|
+
const auth = getAuth(c);
|
|
122
|
+
if (!auth?.userId) {
|
|
123
|
+
return c.json({ error: "Not signed in" }, 401);
|
|
124
|
+
}
|
|
125
|
+
return c.json({ userId: auth.userId, orgId: auth.orgId });
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**`AuthObject`:**
|
|
130
|
+
|
|
131
|
+
| Property | Type | Description |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `userId` | `string \| null` | Current user ID |
|
|
134
|
+
| `tenantId` | `string \| null` | Tenant ID |
|
|
135
|
+
| `appId` | `string \| null` | Application ID |
|
|
136
|
+
| `envId` | `string \| null` | Environment ID |
|
|
137
|
+
| `orgId` | `string \| null` | Active organization ID |
|
|
138
|
+
| `orgRole` | `string \| null` | Role in active organization |
|
|
139
|
+
| `sessionId` | `string \| null` | Session ID |
|
|
140
|
+
| `getToken()` | `() => Promise<string \| null>` | Get the access token |
|
|
141
|
+
| `has(params)` | `({ role?, permission? }) => boolean` | Check role or permission |
|
|
142
|
+
|
|
143
|
+
#### Cookie Helpers
|
|
144
|
+
|
|
145
|
+
For advanced use cases (custom auth flows, manual token management):
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { setAuthCookies, clearAuthCookies } from "@inai-dev/hono";
|
|
149
|
+
|
|
150
|
+
// Set auth cookies after manual authentication
|
|
151
|
+
setAuthCookies(c, tokens, user);
|
|
152
|
+
|
|
153
|
+
// Clear all auth cookies (manual logout)
|
|
154
|
+
clearAuthCookies(c);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Token Extraction
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { getTokenFromContext, getRefreshTokenFromContext } from "@inai-dev/hono";
|
|
161
|
+
|
|
162
|
+
// Get access token from Authorization header or cookies
|
|
163
|
+
const token = getTokenFromContext(c);
|
|
164
|
+
|
|
165
|
+
// Get refresh token from cookies
|
|
166
|
+
const refreshToken = getRefreshTokenFromContext(c);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Full Example
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { Hono } from "hono";
|
|
173
|
+
import {
|
|
174
|
+
inaiAuthMiddleware,
|
|
175
|
+
requireAuth,
|
|
176
|
+
createAuthRoutes,
|
|
177
|
+
getAuth,
|
|
178
|
+
} from "@inai-dev/hono";
|
|
179
|
+
|
|
180
|
+
const app = new Hono();
|
|
181
|
+
|
|
182
|
+
// Auth middleware — protects all routes except public ones
|
|
183
|
+
app.use(
|
|
184
|
+
"*",
|
|
185
|
+
inaiAuthMiddleware({
|
|
186
|
+
publicRoutes: ["/", "/health", "/api/auth/*"],
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Auth API routes
|
|
191
|
+
app.route("/api/auth", createAuthRoutes());
|
|
192
|
+
|
|
193
|
+
// Public route
|
|
194
|
+
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
195
|
+
|
|
196
|
+
// Protected route — any authenticated user
|
|
197
|
+
app.get("/api/profile", (c) => {
|
|
198
|
+
const auth = getAuth(c);
|
|
199
|
+
return c.json({ userId: auth?.userId });
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Protected route — admin only
|
|
203
|
+
app.get("/api/admin", requireAuth({ role: "admin" }), (c) => {
|
|
204
|
+
return c.json({ message: "Welcome, admin" });
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
export default app;
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Cloudflare Workers
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
// src/index.ts
|
|
214
|
+
import { Hono } from "hono";
|
|
215
|
+
import { inaiAuthMiddleware, createAuthRoutes } from "@inai-dev/hono";
|
|
216
|
+
|
|
217
|
+
const app = new Hono();
|
|
218
|
+
|
|
219
|
+
app.use(
|
|
220
|
+
"*",
|
|
221
|
+
inaiAuthMiddleware({
|
|
222
|
+
publicRoutes: ["/", "/api/auth/*"],
|
|
223
|
+
publishableKey: "pk_live_...",
|
|
224
|
+
})
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
app.route("/api/auth", createAuthRoutes());
|
|
228
|
+
|
|
229
|
+
app.get("/api/hello", (c) => {
|
|
230
|
+
const auth = c.get("inaiAuth");
|
|
231
|
+
return c.json({ userId: auth?.userId });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
export default app;
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Platform Mode (Admin Panel)
|
|
238
|
+
|
|
239
|
+
For admin panels that authenticate against the InAI platform API:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
app.use(
|
|
243
|
+
"*",
|
|
244
|
+
inaiAuthMiddleware({
|
|
245
|
+
authMode: "platform",
|
|
246
|
+
publicRoutes: ["/login"],
|
|
247
|
+
})
|
|
248
|
+
);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
In platform mode, the middleware uses `platformRefresh()` and `platformGetMe()` for token refresh, targeting the platform auth endpoints instead of app user endpoints.
|
|
252
|
+
|
|
253
|
+
## Type Augmentation
|
|
254
|
+
|
|
255
|
+
The package augments Hono's `ContextVariableMap` so `c.get("inaiAuth")` is fully typed:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
// Automatic — just import the package
|
|
259
|
+
import "@inai-dev/hono";
|
|
260
|
+
|
|
261
|
+
// c.get("inaiAuth") is typed as AuthObject | null
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Exports Reference
|
|
265
|
+
|
|
266
|
+
### `@inai-dev/hono`
|
|
267
|
+
|
|
268
|
+
| Export | Kind | Description |
|
|
269
|
+
|---|---|---|
|
|
270
|
+
| `inaiAuthMiddleware` | Function | Auth middleware |
|
|
271
|
+
| `requireAuth` | Function | RBAC route guard |
|
|
272
|
+
| `createAuthRoutes` | Function | Auth API route handlers |
|
|
273
|
+
| `getAuth` | Function | Get `AuthObject` from context |
|
|
274
|
+
| `setAuthCookies` | Function | Set auth cookies |
|
|
275
|
+
| `clearAuthCookies` | Function | Clear auth cookies |
|
|
276
|
+
| `getTokenFromContext` | Function | Extract access token |
|
|
277
|
+
| `getRefreshTokenFromContext` | Function | Extract refresh token |
|
|
278
|
+
|
|
279
|
+
### `@inai-dev/hono/middleware`
|
|
280
|
+
|
|
281
|
+
| Export | Kind | Description |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| `inaiAuthMiddleware` | Function | Auth middleware with token refresh |
|
|
284
|
+
| `requireAuth` | Function | RBAC route guard |
|
|
285
|
+
|
|
286
|
+
### `@inai-dev/hono/api-routes`
|
|
287
|
+
|
|
288
|
+
| Export | Kind | Description |
|
|
289
|
+
|---|---|---|
|
|
290
|
+
| `createAuthRoutes` | Function | Create Hono sub-app with auth endpoints |
|
|
291
|
+
|
|
292
|
+
## Exported Types
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
import type {
|
|
296
|
+
InAIHonoMiddlewareConfig,
|
|
297
|
+
RequireAuthConfig,
|
|
298
|
+
} from "@inai-dev/hono";
|
|
299
|
+
|
|
300
|
+
import type {
|
|
301
|
+
AuthObject,
|
|
302
|
+
UserResource,
|
|
303
|
+
OrganizationResource,
|
|
304
|
+
} from "@inai-dev/hono";
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Questions & Support
|
|
308
|
+
|
|
309
|
+
Visit [https://inai.dev](https://inai.dev) for documentation, guides, and support.
|
|
310
|
+
|
|
311
|
+
## License
|
|
312
|
+
|
|
313
|
+
[MIT](../../LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inai-dev/hono",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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.
|
|
39
|
-
"@inai-dev/shared": "^1.
|
|
40
|
-
"@inai-dev/backend": "^1.
|
|
38
|
+
"@inai-dev/types": "^1.2.0",
|
|
39
|
+
"@inai-dev/shared": "^1.2.0",
|
|
40
|
+
"@inai-dev/backend": "^1.3.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"hono": ">=4.0.0"
|