@inai-dev/astro 0.1.1 → 0.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 +173 -23
- package/dist/api-routes.d.ts +24 -0
- package/dist/api-routes.js +178 -0
- package/dist/api-routes.js.map +1 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.js +211 -24
- package/dist/index.js.map +1 -1
- package/dist/middleware.js +24 -1
- package/dist/middleware.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +59 -8
- package/dist/server.js.map +1 -1
- package/package.json +10 -6
- package/src/components/signed-in.astro +0 -6
- package/src/components/signed-out.astro +0 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @inai-dev/astro
|
|
2
2
|
|
|
3
|
-
Astro integration for InAI Auth.
|
|
3
|
+
Full Astro integration for InAI Auth. Includes middleware with automatic token refresh, API route handlers, and server-side helpers.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,6 +8,13 @@ Astro integration for InAI Auth. Provides middleware, server-side helpers, and A
|
|
|
8
8
|
npm install @inai-dev/astro
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Environment Variables
|
|
12
|
+
|
|
13
|
+
```env
|
|
14
|
+
# Required — your publishable key
|
|
15
|
+
INAI_PUBLISHABLE_KEY=pk_live_...
|
|
16
|
+
```
|
|
17
|
+
|
|
11
18
|
## Setup
|
|
12
19
|
|
|
13
20
|
### 1. Add Integration
|
|
@@ -15,46 +22,189 @@ npm install @inai-dev/astro
|
|
|
15
22
|
```ts
|
|
16
23
|
// astro.config.mjs
|
|
17
24
|
import { defineConfig } from "astro/config";
|
|
18
|
-
import inaiAuth from "@inai-dev/astro";
|
|
25
|
+
import { inaiAuth } from "@inai-dev/astro";
|
|
19
26
|
|
|
20
27
|
export default defineConfig({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
output: "server", // Required — SSR mode for auth
|
|
29
|
+
integrations: [inaiAuth()],
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> **Note:** Auth requires `output: "server"` (or `"hybrid"`) since middleware and API routes run on the server.
|
|
34
|
+
|
|
35
|
+
### 2. Middleware
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// src/middleware.ts
|
|
39
|
+
import { inaiAstroMiddleware } from "@inai-dev/astro/middleware";
|
|
40
|
+
|
|
41
|
+
export const onRequest = inaiAstroMiddleware({
|
|
42
|
+
publicRoutes: ["/", "/about", "/login"],
|
|
43
|
+
signInUrl: "/login",
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The middleware automatically:
|
|
48
|
+
- Skips public routes, `/api/*`, and `/_*` paths
|
|
49
|
+
- Validates the auth token from cookies
|
|
50
|
+
- Refreshes expired tokens via `/api/auth/refresh` when a refresh token exists
|
|
51
|
+
- Sets `Astro.locals.auth` with the `AuthObject` for authenticated requests
|
|
52
|
+
- Redirects to sign-in for unauthenticated requests on protected routes
|
|
53
|
+
|
|
54
|
+
### 3. API Routes
|
|
55
|
+
|
|
56
|
+
Auth uses standard Astro API routes (not Astro Actions). This is intentional: the middleware refreshes tokens via self-fetch to `/api/auth/refresh`, which requires a real HTTP endpoint. API routes also allow direct cookie manipulation and standard REST semantics for auth flows.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// src/pages/api/auth/[path].ts
|
|
60
|
+
import { createAuthRoutes } from "@inai-dev/astro/api-routes";
|
|
61
|
+
|
|
62
|
+
const routes = createAuthRoutes({
|
|
63
|
+
publishableKey: process.env.INAI_PUBLISHABLE_KEY,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const ALL = routes.ALL;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Handles the following endpoints automatically:
|
|
70
|
+
- `POST /api/auth/login` — User login (returns `{ user }` or `{ mfa_required, mfa_token }`)
|
|
71
|
+
- `POST /api/auth/register` — User registration
|
|
72
|
+
- `POST /api/auth/mfa-challenge` — MFA verification
|
|
73
|
+
- `POST /api/auth/refresh` — Token refresh (also called automatically by middleware)
|
|
74
|
+
- `POST /api/auth/logout` — User logout
|
|
75
|
+
|
|
76
|
+
#### Calling from the client
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// Login
|
|
80
|
+
const res = await fetch("/api/auth/login", {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
83
|
+
body: JSON.stringify({ email, password }),
|
|
84
|
+
});
|
|
85
|
+
const { user, mfa_required, mfa_token } = await res.json();
|
|
86
|
+
|
|
87
|
+
// MFA (if required)
|
|
88
|
+
const res = await fetch("/api/auth/mfa-challenge", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { "Content-Type": "application/json" },
|
|
91
|
+
body: JSON.stringify({ mfa_token, code }),
|
|
26
92
|
});
|
|
93
|
+
|
|
94
|
+
// Logout
|
|
95
|
+
await fetch("/api/auth/logout", { method: "POST" });
|
|
27
96
|
```
|
|
28
97
|
|
|
29
|
-
###
|
|
98
|
+
### 4. Server-Side Auth
|
|
99
|
+
|
|
100
|
+
#### `auth()`
|
|
101
|
+
|
|
102
|
+
Returns the `AuthObject` from the current request context (populated by middleware).
|
|
30
103
|
|
|
31
104
|
```astro
|
|
32
105
|
---
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
106
|
+
import { auth } from "@inai-dev/astro/server";
|
|
107
|
+
|
|
108
|
+
const authObj = auth(Astro);
|
|
109
|
+
if (!authObj?.userId) {
|
|
110
|
+
return Astro.redirect("/login");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check roles/permissions
|
|
114
|
+
if (authObj.has({ role: "admin" })) {
|
|
115
|
+
// admin-only logic
|
|
116
|
+
}
|
|
36
117
|
---
|
|
37
|
-
<p>
|
|
118
|
+
<p>User: {authObj.userId}</p>
|
|
38
119
|
```
|
|
39
120
|
|
|
40
|
-
|
|
121
|
+
**`AuthObject`:**
|
|
122
|
+
|
|
123
|
+
| Property | Type | Description |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `userId` | `string \| null` | Current user ID |
|
|
126
|
+
| `tenantId` | `string \| null` | Tenant ID |
|
|
127
|
+
| `appId` | `string \| null` | Application ID |
|
|
128
|
+
| `envId` | `string \| null` | Environment ID |
|
|
129
|
+
| `orgId` | `string \| null` | Active organization ID |
|
|
130
|
+
| `orgRole` | `string \| null` | Role in active organization |
|
|
131
|
+
| `sessionId` | `string \| null` | Session ID |
|
|
132
|
+
| `getToken()` | `() => Promise<string \| null>` | Get the access token |
|
|
133
|
+
| `has(params)` | `({ role?, permission? }) => boolean` | Check role or permission |
|
|
134
|
+
|
|
135
|
+
#### `currentUser()`
|
|
136
|
+
|
|
137
|
+
Fetches the full `UserResource` from the API.
|
|
138
|
+
|
|
139
|
+
```astro
|
|
140
|
+
---
|
|
141
|
+
import { currentUser } from "@inai-dev/astro/server";
|
|
142
|
+
|
|
143
|
+
const user = await currentUser(Astro);
|
|
144
|
+
if (!user) return Astro.redirect("/login");
|
|
145
|
+
---
|
|
146
|
+
<p>{user.email}</p>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### Cookie Helpers
|
|
150
|
+
|
|
151
|
+
For advanced use cases (custom auth flows, manual token management):
|
|
41
152
|
|
|
42
153
|
```ts
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
154
|
+
import { setAuthCookies, clearAuthCookies } from "@inai-dev/astro/server";
|
|
155
|
+
|
|
156
|
+
// Set auth cookies after manual authentication
|
|
157
|
+
setAuthCookies(Astro.cookies, tokens, user);
|
|
158
|
+
|
|
159
|
+
// Clear all auth cookies (manual logout)
|
|
160
|
+
clearAuthCookies(Astro.cookies);
|
|
46
161
|
```
|
|
47
162
|
|
|
48
|
-
## Exports
|
|
163
|
+
## Exports Reference
|
|
164
|
+
|
|
165
|
+
### `@inai-dev/astro`
|
|
166
|
+
|
|
167
|
+
| Export | Kind | Description |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `inaiAuth` | Function | Astro integration |
|
|
170
|
+
| `inaiAstroMiddleware` | Function | Auth middleware |
|
|
171
|
+
| `auth` | Function | Get `AuthObject` from context |
|
|
172
|
+
| `currentUser` | Function | Get current user from API |
|
|
173
|
+
| `setAuthCookies` | Function | Set auth cookies |
|
|
174
|
+
| `clearAuthCookies` | Function | Clear auth cookies |
|
|
175
|
+
| `createAuthRoutes` | Function | API route handlers |
|
|
176
|
+
|
|
177
|
+
### `@inai-dev/astro/middleware`
|
|
49
178
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
179
|
+
| Export | Kind | Description |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `inaiAstroMiddleware` | Function | Auth middleware with token refresh |
|
|
53
182
|
|
|
54
|
-
|
|
183
|
+
### `@inai-dev/astro/server`
|
|
55
184
|
|
|
56
|
-
|
|
57
|
-
|
|
185
|
+
| Export | Kind | Description |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `auth` | Function | Get `AuthObject` from context |
|
|
188
|
+
| `currentUser` | Function | Get current user |
|
|
189
|
+
| `setAuthCookies` | Function | Set auth cookies |
|
|
190
|
+
| `clearAuthCookies` | Function | Clear auth cookies |
|
|
191
|
+
|
|
192
|
+
### `@inai-dev/astro/api-routes`
|
|
193
|
+
|
|
194
|
+
| Export | Kind | Description |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `createAuthRoutes` | Function | Create API route handlers |
|
|
197
|
+
| `setAuthCookies` | Function | Set auth cookies |
|
|
198
|
+
| `clearAuthCookies` | Function | Clear auth cookies |
|
|
199
|
+
|
|
200
|
+
## Exported Types
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import type { InAIAstroConfig } from "@inai-dev/astro";
|
|
204
|
+
import type { InAIAstroMiddlewareConfig } from "@inai-dev/astro/middleware";
|
|
205
|
+
import type { AstroCookies, AstroAPIContext } from "@inai-dev/astro/api-routes";
|
|
206
|
+
import type { AuthObject, UserResource, OrganizationResource } from "@inai-dev/astro";
|
|
207
|
+
```
|
|
58
208
|
|
|
59
209
|
## License
|
|
60
210
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { InAIAuthConfig, TokenPair, UserResource } from '@inai-dev/types';
|
|
2
|
+
|
|
3
|
+
interface AstroCookies {
|
|
4
|
+
get(name: string): {
|
|
5
|
+
value: string;
|
|
6
|
+
} | undefined;
|
|
7
|
+
set(name: string, value: string, options?: Record<string, unknown>): void;
|
|
8
|
+
delete(name: string, options?: Record<string, unknown>): void;
|
|
9
|
+
}
|
|
10
|
+
interface AstroAPIContext {
|
|
11
|
+
request: Request;
|
|
12
|
+
cookies: AstroCookies;
|
|
13
|
+
params: Record<string, string | undefined>;
|
|
14
|
+
url: URL;
|
|
15
|
+
}
|
|
16
|
+
declare function setAuthCookies(cookies: AstroCookies, tokens: TokenPair, user: UserResource): void;
|
|
17
|
+
declare function clearAuthCookies(cookies: AstroCookies): void;
|
|
18
|
+
declare function createAuthRoutes(config?: InAIAuthConfig): {
|
|
19
|
+
ALL: (context: AstroAPIContext) => Promise<Response>;
|
|
20
|
+
POST: (context: AstroAPIContext) => Promise<Response>;
|
|
21
|
+
GET: (context: AstroAPIContext) => Promise<Response>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { type AstroAPIContext, type AstroCookies, clearAuthCookies, createAuthRoutes, setAuthCookies };
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// src/api-routes.ts
|
|
2
|
+
import { InAIAuthClient } from "@inai-dev/backend";
|
|
3
|
+
import {
|
|
4
|
+
COOKIE_AUTH_TOKEN,
|
|
5
|
+
COOKIE_REFRESH_TOKEN,
|
|
6
|
+
COOKIE_AUTH_SESSION,
|
|
7
|
+
decodeJWTPayload
|
|
8
|
+
} from "@inai-dev/shared";
|
|
9
|
+
function setAuthCookies(cookies, tokens, user) {
|
|
10
|
+
const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
11
|
+
const claims = decodeJWTPayload(tokens.access_token);
|
|
12
|
+
const expiresAt = claims ? new Date(claims.exp * 1e3).toISOString() : new Date(Date.now() + tokens.expires_in * 1e3).toISOString();
|
|
13
|
+
cookies.set(COOKIE_AUTH_TOKEN, tokens.access_token, {
|
|
14
|
+
httpOnly: true,
|
|
15
|
+
secure: isProduction,
|
|
16
|
+
sameSite: "lax",
|
|
17
|
+
path: "/",
|
|
18
|
+
maxAge: tokens.expires_in
|
|
19
|
+
});
|
|
20
|
+
cookies.set(COOKIE_REFRESH_TOKEN, tokens.refresh_token, {
|
|
21
|
+
httpOnly: true,
|
|
22
|
+
secure: isProduction,
|
|
23
|
+
sameSite: "strict",
|
|
24
|
+
path: "/api/auth",
|
|
25
|
+
maxAge: 7 * 24 * 60 * 60
|
|
26
|
+
});
|
|
27
|
+
cookies.set(COOKIE_AUTH_SESSION, JSON.stringify({
|
|
28
|
+
user,
|
|
29
|
+
expiresAt,
|
|
30
|
+
permissions: claims?.permissions ?? [],
|
|
31
|
+
orgId: claims?.org_id,
|
|
32
|
+
orgRole: claims?.org_role,
|
|
33
|
+
appId: claims?.app_id,
|
|
34
|
+
envId: claims?.env_id
|
|
35
|
+
}), {
|
|
36
|
+
httpOnly: false,
|
|
37
|
+
secure: isProduction,
|
|
38
|
+
sameSite: "lax",
|
|
39
|
+
path: "/",
|
|
40
|
+
maxAge: tokens.expires_in
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function clearAuthCookies(cookies) {
|
|
44
|
+
cookies.delete(COOKIE_AUTH_TOKEN, { path: "/" });
|
|
45
|
+
cookies.delete(COOKIE_REFRESH_TOKEN, { path: "/api/auth" });
|
|
46
|
+
cookies.delete(COOKIE_AUTH_SESSION, { path: "/" });
|
|
47
|
+
}
|
|
48
|
+
function jsonResponse(data, status = 200) {
|
|
49
|
+
return new Response(JSON.stringify(data), {
|
|
50
|
+
status,
|
|
51
|
+
headers: { "Content-Type": "application/json" }
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function createAuthRoutes(config = {}) {
|
|
55
|
+
const client = new InAIAuthClient(config);
|
|
56
|
+
async function handleLogin(context) {
|
|
57
|
+
try {
|
|
58
|
+
const body = await context.request.json();
|
|
59
|
+
const result = await client.login({
|
|
60
|
+
email: body.email,
|
|
61
|
+
password: body.password
|
|
62
|
+
});
|
|
63
|
+
if (result.mfa_required) {
|
|
64
|
+
return jsonResponse({
|
|
65
|
+
mfa_required: true,
|
|
66
|
+
mfa_token: result.mfa_token
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const tokens = result;
|
|
70
|
+
const loginUser = result.user;
|
|
71
|
+
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
72
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
73
|
+
return jsonResponse({ user });
|
|
74
|
+
} catch (err) {
|
|
75
|
+
const message = err instanceof Error ? err.message : "Login failed";
|
|
76
|
+
return jsonResponse({ error: message }, 401);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function handleRegister(context) {
|
|
80
|
+
try {
|
|
81
|
+
const body = await context.request.json();
|
|
82
|
+
const result = await client.register({
|
|
83
|
+
email: body.email,
|
|
84
|
+
password: body.password,
|
|
85
|
+
firstName: body.firstName,
|
|
86
|
+
lastName: body.lastName
|
|
87
|
+
});
|
|
88
|
+
if (!result.access_token) {
|
|
89
|
+
return jsonResponse({
|
|
90
|
+
needs_email_verification: true,
|
|
91
|
+
user: result.user
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const tokens = result;
|
|
95
|
+
const loginUser = result.user;
|
|
96
|
+
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
97
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
98
|
+
return jsonResponse({ user });
|
|
99
|
+
} catch (err) {
|
|
100
|
+
const message = err instanceof Error ? err.message : "Registration failed";
|
|
101
|
+
return jsonResponse({ error: message }, 400);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function handleMFAChallenge(context) {
|
|
105
|
+
try {
|
|
106
|
+
const body = await context.request.json();
|
|
107
|
+
const tokens = await client.mfaChallenge({
|
|
108
|
+
mfa_token: body.mfa_token,
|
|
109
|
+
code: body.code
|
|
110
|
+
});
|
|
111
|
+
const { data: user } = await client.getMe(tokens.access_token);
|
|
112
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
113
|
+
return jsonResponse({ user });
|
|
114
|
+
} catch (err) {
|
|
115
|
+
const message = err instanceof Error ? err.message : "MFA verification failed";
|
|
116
|
+
return jsonResponse({ error: message }, 401);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function handleRefresh(context) {
|
|
120
|
+
try {
|
|
121
|
+
const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;
|
|
122
|
+
if (!refreshToken) {
|
|
123
|
+
clearAuthCookies(context.cookies);
|
|
124
|
+
return jsonResponse({ error: "No refresh token" }, 401);
|
|
125
|
+
}
|
|
126
|
+
const tokens = await client.refresh(refreshToken);
|
|
127
|
+
const { data: user } = await client.getMe(tokens.access_token);
|
|
128
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
129
|
+
return jsonResponse({ user });
|
|
130
|
+
} catch {
|
|
131
|
+
clearAuthCookies(context.cookies);
|
|
132
|
+
return jsonResponse({ error: "Refresh failed" }, 401);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function handleLogout(context) {
|
|
136
|
+
try {
|
|
137
|
+
const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;
|
|
138
|
+
if (refreshToken) {
|
|
139
|
+
await client.logout(refreshToken).catch(() => {
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
clearAuthCookies(context.cookies);
|
|
143
|
+
return jsonResponse({ success: true });
|
|
144
|
+
} catch {
|
|
145
|
+
clearAuthCookies(context.cookies);
|
|
146
|
+
return jsonResponse({ success: true });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function handler(context) {
|
|
150
|
+
const path = context.params.path ?? "";
|
|
151
|
+
if (context.request.method === "POST") {
|
|
152
|
+
switch (path) {
|
|
153
|
+
case "login":
|
|
154
|
+
return handleLogin(context);
|
|
155
|
+
case "register":
|
|
156
|
+
return handleRegister(context);
|
|
157
|
+
case "mfa-challenge":
|
|
158
|
+
return handleMFAChallenge(context);
|
|
159
|
+
case "refresh":
|
|
160
|
+
return handleRefresh(context);
|
|
161
|
+
case "logout":
|
|
162
|
+
return handleLogout(context);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return jsonResponse({ error: "Not found" }, 404);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
ALL: handler,
|
|
169
|
+
POST: handler,
|
|
170
|
+
GET: handler
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
clearAuthCookies,
|
|
175
|
+
createAuthRoutes,
|
|
176
|
+
setAuthCookies
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=api-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-routes.ts"],"sourcesContent":["import type { InAIAuthConfig, TokenPair, UserResource, LoginResult } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\ninterface AstroCookies {\n get(name: string): { value: string } | undefined;\n set(name: string, value: string, options?: Record<string, unknown>): void;\n delete(name: string, options?: Record<string, unknown>): void;\n}\n\ninterface AstroAPIContext {\n request: Request;\n cookies: AstroCookies;\n params: Record<string, string | undefined>;\n url: URL;\n}\n\nfunction setAuthCookies(\n cookies: AstroCookies,\n tokens: TokenPair,\n user: UserResource,\n): void {\n const isProduction = 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 cookies.set(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 cookies.set(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 cookies.set(COOKIE_AUTH_SESSION, 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 httpOnly: false,\n secure: isProduction,\n sameSite: \"lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n}\n\nfunction clearAuthCookies(cookies: AstroCookies): void {\n cookies.delete(COOKIE_AUTH_TOKEN, { path: \"/\" });\n cookies.delete(COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n cookies.delete(COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n\nfunction jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nexport function createAuthRoutes(config: InAIAuthConfig = {}) {\n const client = new InAIAuthClient(config);\n\n async function handleLogin(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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 jsonResponse({\n mfa_required: true,\n mfa_token: result.mfa_token,\n });\n }\n\n const tokens = result as unknown as TokenPair;\n const loginUser = result.user;\n const user = loginUser ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Login failed\";\n return jsonResponse({ error: message }, 401);\n }\n }\n\n async function handleRegister(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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 jsonResponse({\n needs_email_verification: true,\n user: result.user,\n });\n }\n\n const tokens = result as unknown as TokenPair;\n const loginUser = result.user;\n const user = loginUser ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Registration failed\";\n return jsonResponse({ error: message }, 400);\n }\n }\n\n async function handleMFAChallenge(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"MFA verification failed\";\n return jsonResponse({ error: message }, 401);\n }\n }\n\n async function handleRefresh(context: AstroAPIContext): Promise<Response> {\n try {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n\n if (!refreshToken) {\n clearAuthCookies(context.cookies);\n return jsonResponse({ 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(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch {\n clearAuthCookies(context.cookies);\n return jsonResponse({ error: \"Refresh failed\" }, 401);\n }\n }\n\n async function handleLogout(context: AstroAPIContext): Promise<Response> {\n try {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n if (refreshToken) {\n await client.logout(refreshToken).catch(() => {});\n }\n clearAuthCookies(context.cookies);\n return jsonResponse({ success: true });\n } catch {\n clearAuthCookies(context.cookies);\n return jsonResponse({ success: true });\n }\n }\n\n async function handler(context: AstroAPIContext): Promise<Response> {\n const path = context.params.path ?? \"\";\n\n if (context.request.method === \"POST\") {\n switch (path) {\n case \"login\":\n return handleLogin(context);\n case \"register\":\n return handleRegister(context);\n case \"mfa-challenge\":\n return handleMFAChallenge(context);\n case \"refresh\":\n return handleRefresh(context);\n case \"logout\":\n return handleLogout(context);\n }\n }\n\n return jsonResponse({ error: \"Not found\" }, 404);\n }\n\n return {\n ALL: handler,\n POST: handler,\n GET: handler,\n };\n}\n\nexport { setAuthCookies, clearAuthCookies };\nexport type { AstroCookies, AstroAPIContext };\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { AstroIntegration } from 'astro';
|
|
2
2
|
export { InAIAstroMiddlewareConfig, inaiAstroMiddleware } from './middleware.js';
|
|
3
3
|
export { auth, currentUser } from './server.js';
|
|
4
|
+
export { AstroAPIContext, AstroCookies, clearAuthCookies, createAuthRoutes, setAuthCookies } from './api-routes.js';
|
|
4
5
|
export { AuthObject, OrganizationResource, UserResource } from '@inai-dev/types';
|
|
5
6
|
|
|
6
7
|
interface InAIAstroConfig {
|
|
7
|
-
apiUrl: string;
|
|
8
|
-
publishableKey?: string;
|
|
9
8
|
}
|
|
10
|
-
declare function inaiAuth(
|
|
9
|
+
declare function inaiAuth(_config?: InAIAstroConfig): AstroIntegration;
|
|
11
10
|
|
|
12
11
|
export { type InAIAstroConfig, inaiAuth };
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,15 @@
|
|
|
1
1
|
// src/integration.ts
|
|
2
|
-
function inaiAuth(
|
|
2
|
+
function inaiAuth(_config = {}) {
|
|
3
3
|
return {
|
|
4
4
|
name: "@inai-dev/astro",
|
|
5
|
-
hooks: {
|
|
6
|
-
"astro:config:setup": ({ updateConfig }) => {
|
|
7
|
-
updateConfig({
|
|
8
|
-
vite: {
|
|
9
|
-
define: {
|
|
10
|
-
"import.meta.env.INAI_API_URL": JSON.stringify(config.apiUrl),
|
|
11
|
-
"import.meta.env.INAI_PUBLISHABLE_KEY": JSON.stringify(
|
|
12
|
-
config.publishableKey ?? ""
|
|
13
|
-
)
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
}
|
|
5
|
+
hooks: {}
|
|
19
6
|
};
|
|
20
7
|
}
|
|
21
8
|
|
|
22
9
|
// src/middleware.ts
|
|
23
10
|
import {
|
|
24
11
|
COOKIE_AUTH_TOKEN,
|
|
12
|
+
COOKIE_REFRESH_TOKEN,
|
|
25
13
|
getClaimsFromToken,
|
|
26
14
|
isTokenExpired
|
|
27
15
|
} from "@inai-dev/shared";
|
|
@@ -38,8 +26,30 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
38
26
|
if (isPublic) {
|
|
39
27
|
return next();
|
|
40
28
|
}
|
|
41
|
-
|
|
29
|
+
let token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;
|
|
42
30
|
if (!token || isTokenExpired(token)) {
|
|
31
|
+
const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;
|
|
32
|
+
if (refreshToken) {
|
|
33
|
+
try {
|
|
34
|
+
const refreshUrl = new URL("/api/auth/refresh", context.url.origin);
|
|
35
|
+
const refreshRes = await fetch(refreshUrl.toString(), {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
Cookie: context.request.headers.get("cookie") ?? ""
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (refreshRes.ok) {
|
|
43
|
+
const setCookies = refreshRes.headers.getSetCookie?.() ?? [];
|
|
44
|
+
const response = await next();
|
|
45
|
+
for (const cookie of setCookies) {
|
|
46
|
+
response.headers.append("Set-Cookie", cookie);
|
|
47
|
+
}
|
|
48
|
+
return response;
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
43
53
|
return context.redirect(
|
|
44
54
|
`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`
|
|
45
55
|
);
|
|
@@ -72,16 +82,191 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
72
82
|
}
|
|
73
83
|
|
|
74
84
|
// src/server.ts
|
|
75
|
-
import { InAIAuthClient } from "@inai-dev/backend";
|
|
85
|
+
import { InAIAuthClient as InAIAuthClient2 } from "@inai-dev/backend";
|
|
76
86
|
import {
|
|
77
|
-
COOKIE_AUTH_TOKEN as
|
|
87
|
+
COOKIE_AUTH_TOKEN as COOKIE_AUTH_TOKEN3,
|
|
78
88
|
getClaimsFromToken as getClaimsFromToken2,
|
|
79
89
|
isTokenExpired as isTokenExpired2
|
|
80
90
|
} from "@inai-dev/shared";
|
|
91
|
+
|
|
92
|
+
// src/api-routes.ts
|
|
93
|
+
import { InAIAuthClient } from "@inai-dev/backend";
|
|
94
|
+
import {
|
|
95
|
+
COOKIE_AUTH_TOKEN as COOKIE_AUTH_TOKEN2,
|
|
96
|
+
COOKIE_REFRESH_TOKEN as COOKIE_REFRESH_TOKEN2,
|
|
97
|
+
COOKIE_AUTH_SESSION,
|
|
98
|
+
decodeJWTPayload
|
|
99
|
+
} from "@inai-dev/shared";
|
|
100
|
+
function setAuthCookies(cookies, tokens, user) {
|
|
101
|
+
const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
102
|
+
const claims = decodeJWTPayload(tokens.access_token);
|
|
103
|
+
const expiresAt = claims ? new Date(claims.exp * 1e3).toISOString() : new Date(Date.now() + tokens.expires_in * 1e3).toISOString();
|
|
104
|
+
cookies.set(COOKIE_AUTH_TOKEN2, tokens.access_token, {
|
|
105
|
+
httpOnly: true,
|
|
106
|
+
secure: isProduction,
|
|
107
|
+
sameSite: "lax",
|
|
108
|
+
path: "/",
|
|
109
|
+
maxAge: tokens.expires_in
|
|
110
|
+
});
|
|
111
|
+
cookies.set(COOKIE_REFRESH_TOKEN2, tokens.refresh_token, {
|
|
112
|
+
httpOnly: true,
|
|
113
|
+
secure: isProduction,
|
|
114
|
+
sameSite: "strict",
|
|
115
|
+
path: "/api/auth",
|
|
116
|
+
maxAge: 7 * 24 * 60 * 60
|
|
117
|
+
});
|
|
118
|
+
cookies.set(COOKIE_AUTH_SESSION, JSON.stringify({
|
|
119
|
+
user,
|
|
120
|
+
expiresAt,
|
|
121
|
+
permissions: claims?.permissions ?? [],
|
|
122
|
+
orgId: claims?.org_id,
|
|
123
|
+
orgRole: claims?.org_role,
|
|
124
|
+
appId: claims?.app_id,
|
|
125
|
+
envId: claims?.env_id
|
|
126
|
+
}), {
|
|
127
|
+
httpOnly: false,
|
|
128
|
+
secure: isProduction,
|
|
129
|
+
sameSite: "lax",
|
|
130
|
+
path: "/",
|
|
131
|
+
maxAge: tokens.expires_in
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function clearAuthCookies(cookies) {
|
|
135
|
+
cookies.delete(COOKIE_AUTH_TOKEN2, { path: "/" });
|
|
136
|
+
cookies.delete(COOKIE_REFRESH_TOKEN2, { path: "/api/auth" });
|
|
137
|
+
cookies.delete(COOKIE_AUTH_SESSION, { path: "/" });
|
|
138
|
+
}
|
|
139
|
+
function jsonResponse(data, status = 200) {
|
|
140
|
+
return new Response(JSON.stringify(data), {
|
|
141
|
+
status,
|
|
142
|
+
headers: { "Content-Type": "application/json" }
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function createAuthRoutes(config = {}) {
|
|
146
|
+
const client = new InAIAuthClient(config);
|
|
147
|
+
async function handleLogin(context) {
|
|
148
|
+
try {
|
|
149
|
+
const body = await context.request.json();
|
|
150
|
+
const result = await client.login({
|
|
151
|
+
email: body.email,
|
|
152
|
+
password: body.password
|
|
153
|
+
});
|
|
154
|
+
if (result.mfa_required) {
|
|
155
|
+
return jsonResponse({
|
|
156
|
+
mfa_required: true,
|
|
157
|
+
mfa_token: result.mfa_token
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const tokens = result;
|
|
161
|
+
const loginUser = result.user;
|
|
162
|
+
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
163
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
164
|
+
return jsonResponse({ user });
|
|
165
|
+
} catch (err) {
|
|
166
|
+
const message = err instanceof Error ? err.message : "Login failed";
|
|
167
|
+
return jsonResponse({ error: message }, 401);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function handleRegister(context) {
|
|
171
|
+
try {
|
|
172
|
+
const body = await context.request.json();
|
|
173
|
+
const result = await client.register({
|
|
174
|
+
email: body.email,
|
|
175
|
+
password: body.password,
|
|
176
|
+
firstName: body.firstName,
|
|
177
|
+
lastName: body.lastName
|
|
178
|
+
});
|
|
179
|
+
if (!result.access_token) {
|
|
180
|
+
return jsonResponse({
|
|
181
|
+
needs_email_verification: true,
|
|
182
|
+
user: result.user
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const tokens = result;
|
|
186
|
+
const loginUser = result.user;
|
|
187
|
+
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
188
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
189
|
+
return jsonResponse({ user });
|
|
190
|
+
} catch (err) {
|
|
191
|
+
const message = err instanceof Error ? err.message : "Registration failed";
|
|
192
|
+
return jsonResponse({ error: message }, 400);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function handleMFAChallenge(context) {
|
|
196
|
+
try {
|
|
197
|
+
const body = await context.request.json();
|
|
198
|
+
const tokens = await client.mfaChallenge({
|
|
199
|
+
mfa_token: body.mfa_token,
|
|
200
|
+
code: body.code
|
|
201
|
+
});
|
|
202
|
+
const { data: user } = await client.getMe(tokens.access_token);
|
|
203
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
204
|
+
return jsonResponse({ user });
|
|
205
|
+
} catch (err) {
|
|
206
|
+
const message = err instanceof Error ? err.message : "MFA verification failed";
|
|
207
|
+
return jsonResponse({ error: message }, 401);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function handleRefresh(context) {
|
|
211
|
+
try {
|
|
212
|
+
const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN2)?.value;
|
|
213
|
+
if (!refreshToken) {
|
|
214
|
+
clearAuthCookies(context.cookies);
|
|
215
|
+
return jsonResponse({ error: "No refresh token" }, 401);
|
|
216
|
+
}
|
|
217
|
+
const tokens = await client.refresh(refreshToken);
|
|
218
|
+
const { data: user } = await client.getMe(tokens.access_token);
|
|
219
|
+
setAuthCookies(context.cookies, tokens, user);
|
|
220
|
+
return jsonResponse({ user });
|
|
221
|
+
} catch {
|
|
222
|
+
clearAuthCookies(context.cookies);
|
|
223
|
+
return jsonResponse({ error: "Refresh failed" }, 401);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function handleLogout(context) {
|
|
227
|
+
try {
|
|
228
|
+
const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN2)?.value;
|
|
229
|
+
if (refreshToken) {
|
|
230
|
+
await client.logout(refreshToken).catch(() => {
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
clearAuthCookies(context.cookies);
|
|
234
|
+
return jsonResponse({ success: true });
|
|
235
|
+
} catch {
|
|
236
|
+
clearAuthCookies(context.cookies);
|
|
237
|
+
return jsonResponse({ success: true });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function handler(context) {
|
|
241
|
+
const path = context.params.path ?? "";
|
|
242
|
+
if (context.request.method === "POST") {
|
|
243
|
+
switch (path) {
|
|
244
|
+
case "login":
|
|
245
|
+
return handleLogin(context);
|
|
246
|
+
case "register":
|
|
247
|
+
return handleRegister(context);
|
|
248
|
+
case "mfa-challenge":
|
|
249
|
+
return handleMFAChallenge(context);
|
|
250
|
+
case "refresh":
|
|
251
|
+
return handleRefresh(context);
|
|
252
|
+
case "logout":
|
|
253
|
+
return handleLogout(context);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return jsonResponse({ error: "Not found" }, 404);
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
ALL: handler,
|
|
260
|
+
POST: handler,
|
|
261
|
+
GET: handler
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/server.ts
|
|
81
266
|
function auth(context) {
|
|
82
267
|
const existing = context.locals.auth;
|
|
83
268
|
if (existing) return existing;
|
|
84
|
-
const token = context.cookies.get(
|
|
269
|
+
const token = context.cookies.get(COOKIE_AUTH_TOKEN3)?.value;
|
|
85
270
|
if (!token || isTokenExpired2(token)) return null;
|
|
86
271
|
const claims = getClaimsFromToken2(token);
|
|
87
272
|
if (!claims) return null;
|
|
@@ -105,11 +290,10 @@ function auth(context) {
|
|
|
105
290
|
};
|
|
106
291
|
}
|
|
107
292
|
async function currentUser(context, config) {
|
|
108
|
-
const token = context.cookies.get(
|
|
293
|
+
const token = context.cookies.get(COOKIE_AUTH_TOKEN3)?.value;
|
|
109
294
|
if (!token || isTokenExpired2(token)) return null;
|
|
110
|
-
const client = new
|
|
111
|
-
|
|
112
|
-
publishableKey: config.publishableKey
|
|
295
|
+
const client = new InAIAuthClient2({
|
|
296
|
+
publishableKey: config?.publishableKey
|
|
113
297
|
});
|
|
114
298
|
try {
|
|
115
299
|
const { data } = await client.getMe(token);
|
|
@@ -120,8 +304,11 @@ async function currentUser(context, config) {
|
|
|
120
304
|
}
|
|
121
305
|
export {
|
|
122
306
|
auth,
|
|
307
|
+
clearAuthCookies,
|
|
308
|
+
createAuthRoutes,
|
|
123
309
|
currentUser,
|
|
124
310
|
inaiAstroMiddleware,
|
|
125
|
-
inaiAuth
|
|
311
|
+
inaiAuth,
|
|
312
|
+
setAuthCookies
|
|
126
313
|
};
|
|
127
314
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/integration.ts","../src/middleware.ts","../src/server.ts"],"sourcesContent":["import type { AstroIntegration } from \"astro\";\n\nexport interface InAIAstroConfig {\n apiUrl: string;\n publishableKey?: string;\n}\n\nexport function inaiAuth(config: InAIAstroConfig): AstroIntegration {\n return {\n name: \"@inai-dev/astro\",\n hooks: {\n \"astro:config:setup\": ({ updateConfig }) => {\n updateConfig({\n vite: {\n define: {\n \"import.meta.env.INAI_API_URL\": JSON.stringify(config.apiUrl),\n \"import.meta.env.INAI_PUBLISHABLE_KEY\": JSON.stringify(\n config.publishableKey ?? \"\",\n ),\n },\n },\n });\n },\n },\n };\n}\n","import type { MiddlewareHandler } from \"astro\";\nimport type { AuthObject } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\nexport interface InAIAstroMiddlewareConfig {\n publicRoutes?: string[];\n signInUrl?: string;\n}\n\nexport function inaiAstroMiddleware(\n config: InAIAstroMiddlewareConfig = {},\n): MiddlewareHandler {\n const { publicRoutes = [], signInUrl = \"/login\" } = config;\n\n return async (context, next) => {\n const { pathname } = context.url;\n\n const isPublic =\n publicRoutes.some((route) => {\n if (route.endsWith(\"*\")) {\n return pathname.startsWith(route.slice(0, -1));\n }\n return pathname === route;\n }) ||\n pathname === signInUrl ||\n pathname.startsWith(\"/_\") ||\n pathname.startsWith(\"/api/\");\n\n if (isPublic) {\n return next();\n }\n\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n\n if (!token || isTokenExpired(token)) {\n return context.redirect(\n `${signInUrl}?returnTo=${encodeURIComponent(pathname)}`,\n );\n }\n\n const claims = getClaimsFromToken(token);\n if (!claims) {\n return context.redirect(signInUrl);\n }\n\n const roles = claims.roles ?? [];\n const permissions = claims.permissions ?? [];\n\n const authObject: AuthObject = {\n userId: claims.sub,\n tenantId: claims.tenant_id,\n appId: claims.app_id ?? null,\n envId: claims.env_id ?? null,\n orgId: claims.org_id ?? null,\n orgRole: claims.org_role ?? null,\n sessionId: null,\n getToken: async () => token,\n has: (params: { role?: string; permission?: string }) => {\n if (params.role && roles.includes(params.role)) return true;\n if (params.permission && permissions.includes(params.permission))\n return true;\n return false;\n },\n };\n\n (context.locals as Record<string, unknown>).auth = authObject;\n\n return next();\n };\n}\n","import type { AuthObject, UserResource } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n COOKIE_AUTH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\ninterface AstroContext {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n locals: Record<string, unknown>;\n}\n\nexport function auth(context: AstroContext): AuthObject | null {\n const existing = (context.locals as Record<string, unknown>).auth as AuthObject | undefined;\n if (existing) return existing;\n\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const claims = getClaimsFromToken(token);\n if (!claims) return null;\n\n const roles = claims.roles ?? [];\n const permissions = claims.permissions ?? [];\n\n return {\n userId: claims.sub,\n tenantId: claims.tenant_id,\n appId: claims.app_id ?? null,\n envId: claims.env_id ?? null,\n orgId: claims.org_id ?? null,\n orgRole: claims.org_role ?? null,\n sessionId: null,\n getToken: async () => token,\n has: (params: { role?: string; permission?: string }) => {\n if (params.role && roles.includes(params.role)) return true;\n if (params.permission && permissions.includes(params.permission))\n return true;\n return false;\n },\n };\n}\n\nexport async function currentUser(\n context: AstroContext,\n config: { apiUrl: string; publishableKey?: string },\n): Promise<UserResource | null> {\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const client = new InAIAuthClient({\n apiUrl: config.apiUrl,\n publishableKey: config.publishableKey,\n });\n\n try {\n const { data } = await client.getMe(token);\n return data;\n } catch {\n return null;\n }\n}\n"],"mappings":"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","names":["COOKIE_AUTH_TOKEN","getClaimsFromToken","isTokenExpired"]}
|
|
1
|
+
{"version":3,"sources":["../src/integration.ts","../src/middleware.ts","../src/server.ts","../src/api-routes.ts"],"sourcesContent":["import type { AstroIntegration } from \"astro\";\n\nexport interface InAIAstroConfig {}\n\nexport function inaiAuth(_config: InAIAstroConfig = {}): AstroIntegration {\n return {\n name: \"@inai-dev/astro\",\n hooks: {},\n };\n}\n","import type { MiddlewareHandler } from \"astro\";\nimport type { AuthObject } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\nexport interface InAIAstroMiddlewareConfig {\n publicRoutes?: string[];\n signInUrl?: string;\n}\n\nexport function inaiAstroMiddleware(\n config: InAIAstroMiddlewareConfig = {},\n): MiddlewareHandler {\n const { publicRoutes = [], signInUrl = \"/login\" } = config;\n\n return async (context, next) => {\n const { pathname } = context.url;\n\n const isPublic =\n publicRoutes.some((route) => {\n if (route.endsWith(\"*\")) {\n return pathname.startsWith(route.slice(0, -1));\n }\n return pathname === route;\n }) ||\n pathname === signInUrl ||\n pathname.startsWith(\"/_\") ||\n pathname.startsWith(\"/api/\");\n\n if (isPublic) {\n return next();\n }\n\n let token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n if (refreshToken) {\n try {\n const refreshUrl = new URL(\"/api/auth/refresh\", context.url.origin);\n const refreshRes = await fetch(refreshUrl.toString(), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Cookie: context.request.headers.get(\"cookie\") ?? \"\",\n },\n });\n if (refreshRes.ok) {\n const setCookies = refreshRes.headers.getSetCookie?.() ?? [];\n const response = await next();\n for (const cookie of setCookies) {\n response.headers.append(\"Set-Cookie\", cookie);\n }\n return response;\n }\n } catch {\n // Refresh failed, redirect to sign-in\n }\n }\n\n return context.redirect(\n `${signInUrl}?returnTo=${encodeURIComponent(pathname)}`,\n );\n }\n\n const claims = getClaimsFromToken(token);\n if (!claims) {\n return context.redirect(signInUrl);\n }\n\n const roles = claims.roles ?? [];\n const permissions = claims.permissions ?? [];\n\n const authObject: AuthObject = {\n userId: claims.sub,\n tenantId: claims.tenant_id,\n appId: claims.app_id ?? null,\n envId: claims.env_id ?? null,\n orgId: claims.org_id ?? null,\n orgRole: claims.org_role ?? null,\n sessionId: null,\n getToken: async () => token,\n has: (params: { role?: string; permission?: string }) => {\n if (params.role && roles.includes(params.role)) return true;\n if (params.permission && permissions.includes(params.permission))\n return true;\n return false;\n },\n };\n\n (context.locals as Record<string, unknown>).auth = authObject;\n\n return next();\n };\n}\n","import type { AuthObject, UserResource } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n COOKIE_AUTH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\ninterface AstroContext {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n locals: Record<string, unknown>;\n}\n\nexport function auth(context: AstroContext): AuthObject | null {\n const existing = (context.locals as Record<string, unknown>).auth as AuthObject | undefined;\n if (existing) return existing;\n\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const claims = getClaimsFromToken(token);\n if (!claims) return null;\n\n const roles = claims.roles ?? [];\n const permissions = claims.permissions ?? [];\n\n return {\n userId: claims.sub,\n tenantId: claims.tenant_id,\n appId: claims.app_id ?? null,\n envId: claims.env_id ?? null,\n orgId: claims.org_id ?? null,\n orgRole: claims.org_role ?? null,\n sessionId: null,\n getToken: async () => token,\n has: (params: { role?: string; permission?: string }) => {\n if (params.role && roles.includes(params.role)) return true;\n if (params.permission && permissions.includes(params.permission))\n return true;\n return false;\n },\n };\n}\n\nexport async function currentUser(\n context: AstroContext,\n config?: { publishableKey?: string },\n): Promise<UserResource | null> {\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const client = new InAIAuthClient({\n publishableKey: config?.publishableKey,\n });\n\n try {\n const { data } = await client.getMe(token);\n return data;\n } catch {\n return null;\n }\n}\n\nexport { setAuthCookies, clearAuthCookies } from \"./api-routes\";\nexport type { AstroCookies } from \"./api-routes\";\n","import type { InAIAuthConfig, TokenPair, UserResource, LoginResult } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\ninterface AstroCookies {\n get(name: string): { value: string } | undefined;\n set(name: string, value: string, options?: Record<string, unknown>): void;\n delete(name: string, options?: Record<string, unknown>): void;\n}\n\ninterface AstroAPIContext {\n request: Request;\n cookies: AstroCookies;\n params: Record<string, string | undefined>;\n url: URL;\n}\n\nfunction setAuthCookies(\n cookies: AstroCookies,\n tokens: TokenPair,\n user: UserResource,\n): void {\n const isProduction = 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 cookies.set(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 cookies.set(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 cookies.set(COOKIE_AUTH_SESSION, 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 httpOnly: false,\n secure: isProduction,\n sameSite: \"lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n}\n\nfunction clearAuthCookies(cookies: AstroCookies): void {\n cookies.delete(COOKIE_AUTH_TOKEN, { path: \"/\" });\n cookies.delete(COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n cookies.delete(COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n\nfunction jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nexport function createAuthRoutes(config: InAIAuthConfig = {}) {\n const client = new InAIAuthClient(config);\n\n async function handleLogin(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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 jsonResponse({\n mfa_required: true,\n mfa_token: result.mfa_token,\n });\n }\n\n const tokens = result as unknown as TokenPair;\n const loginUser = result.user;\n const user = loginUser ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Login failed\";\n return jsonResponse({ error: message }, 401);\n }\n }\n\n async function handleRegister(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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 jsonResponse({\n needs_email_verification: true,\n user: result.user,\n });\n }\n\n const tokens = result as unknown as TokenPair;\n const loginUser = result.user;\n const user = loginUser ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Registration failed\";\n return jsonResponse({ error: message }, 400);\n }\n }\n\n async function handleMFAChallenge(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"MFA verification failed\";\n return jsonResponse({ error: message }, 401);\n }\n }\n\n async function handleRefresh(context: AstroAPIContext): Promise<Response> {\n try {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n\n if (!refreshToken) {\n clearAuthCookies(context.cookies);\n return jsonResponse({ 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(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch {\n clearAuthCookies(context.cookies);\n return jsonResponse({ error: \"Refresh failed\" }, 401);\n }\n }\n\n async function handleLogout(context: AstroAPIContext): Promise<Response> {\n try {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n if (refreshToken) {\n await client.logout(refreshToken).catch(() => {});\n }\n clearAuthCookies(context.cookies);\n return jsonResponse({ success: true });\n } catch {\n clearAuthCookies(context.cookies);\n return jsonResponse({ success: true });\n }\n }\n\n async function handler(context: AstroAPIContext): Promise<Response> {\n const path = context.params.path ?? \"\";\n\n if (context.request.method === \"POST\") {\n switch (path) {\n case \"login\":\n return handleLogin(context);\n case \"register\":\n return handleRegister(context);\n case \"mfa-challenge\":\n return handleMFAChallenge(context);\n case \"refresh\":\n return handleRefresh(context);\n case \"logout\":\n return handleLogout(context);\n }\n }\n\n return jsonResponse({ error: \"Not found\" }, 404);\n }\n\n return {\n ALL: handler,\n POST: handler,\n GET: handler,\n };\n}\n\nexport { setAuthCookies, clearAuthCookies };\nexport type { AstroCookies, AstroAPIContext };\n"],"mappings":"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","names":["InAIAuthClient","COOKIE_AUTH_TOKEN","getClaimsFromToken","isTokenExpired","COOKIE_AUTH_TOKEN","COOKIE_REFRESH_TOKEN","COOKIE_AUTH_TOKEN","isTokenExpired","getClaimsFromToken","InAIAuthClient"]}
|
package/dist/middleware.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/middleware.ts
|
|
2
2
|
import {
|
|
3
3
|
COOKIE_AUTH_TOKEN,
|
|
4
|
+
COOKIE_REFRESH_TOKEN,
|
|
4
5
|
getClaimsFromToken,
|
|
5
6
|
isTokenExpired
|
|
6
7
|
} from "@inai-dev/shared";
|
|
@@ -17,8 +18,30 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
17
18
|
if (isPublic) {
|
|
18
19
|
return next();
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
let token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;
|
|
21
22
|
if (!token || isTokenExpired(token)) {
|
|
23
|
+
const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;
|
|
24
|
+
if (refreshToken) {
|
|
25
|
+
try {
|
|
26
|
+
const refreshUrl = new URL("/api/auth/refresh", context.url.origin);
|
|
27
|
+
const refreshRes = await fetch(refreshUrl.toString(), {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
Cookie: context.request.headers.get("cookie") ?? ""
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
if (refreshRes.ok) {
|
|
35
|
+
const setCookies = refreshRes.headers.getSetCookie?.() ?? [];
|
|
36
|
+
const response = await next();
|
|
37
|
+
for (const cookie of setCookies) {
|
|
38
|
+
response.headers.append("Set-Cookie", cookie);
|
|
39
|
+
}
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
22
45
|
return context.redirect(
|
|
23
46
|
`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`
|
|
24
47
|
);
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/middleware.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"astro\";\nimport type { AuthObject } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\nexport interface InAIAstroMiddlewareConfig {\n publicRoutes?: string[];\n signInUrl?: string;\n}\n\nexport function inaiAstroMiddleware(\n config: InAIAstroMiddlewareConfig = {},\n): MiddlewareHandler {\n const { publicRoutes = [], signInUrl = \"/login\" } = config;\n\n return async (context, next) => {\n const { pathname } = context.url;\n\n const isPublic =\n publicRoutes.some((route) => {\n if (route.endsWith(\"*\")) {\n return pathname.startsWith(route.slice(0, -1));\n }\n return pathname === route;\n }) ||\n pathname === signInUrl ||\n pathname.startsWith(\"/_\") ||\n pathname.startsWith(\"/api/\");\n\n if (isPublic) {\n return next();\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../src/middleware.ts"],"sourcesContent":["import type { MiddlewareHandler } from \"astro\";\nimport type { AuthObject } from \"@inai-dev/types\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\nexport interface InAIAstroMiddlewareConfig {\n publicRoutes?: string[];\n signInUrl?: string;\n}\n\nexport function inaiAstroMiddleware(\n config: InAIAstroMiddlewareConfig = {},\n): MiddlewareHandler {\n const { publicRoutes = [], signInUrl = \"/login\" } = config;\n\n return async (context, next) => {\n const { pathname } = context.url;\n\n const isPublic =\n publicRoutes.some((route) => {\n if (route.endsWith(\"*\")) {\n return pathname.startsWith(route.slice(0, -1));\n }\n return pathname === route;\n }) ||\n pathname === signInUrl ||\n pathname.startsWith(\"/_\") ||\n pathname.startsWith(\"/api/\");\n\n if (isPublic) {\n return next();\n }\n\n let token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n\n if (!token || isTokenExpired(token)) {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n if (refreshToken) {\n try {\n const refreshUrl = new URL(\"/api/auth/refresh\", context.url.origin);\n const refreshRes = await fetch(refreshUrl.toString(), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Cookie: context.request.headers.get(\"cookie\") ?? \"\",\n },\n });\n if (refreshRes.ok) {\n const setCookies = refreshRes.headers.getSetCookie?.() ?? [];\n const response = await next();\n for (const cookie of setCookies) {\n response.headers.append(\"Set-Cookie\", cookie);\n }\n return response;\n }\n } catch {\n // Refresh failed, redirect to sign-in\n }\n }\n\n return context.redirect(\n `${signInUrl}?returnTo=${encodeURIComponent(pathname)}`,\n );\n }\n\n const claims = getClaimsFromToken(token);\n if (!claims) {\n return context.redirect(signInUrl);\n }\n\n const roles = claims.roles ?? [];\n const permissions = claims.permissions ?? [];\n\n const authObject: AuthObject = {\n userId: claims.sub,\n tenantId: claims.tenant_id,\n appId: claims.app_id ?? null,\n envId: claims.env_id ?? null,\n orgId: claims.org_id ?? null,\n orgRole: claims.org_role ?? null,\n sessionId: null,\n getToken: async () => token,\n has: (params: { role?: string; permission?: string }) => {\n if (params.role && roles.includes(params.role)) return true;\n if (params.permission && permissions.includes(params.permission))\n return true;\n return false;\n },\n };\n\n (context.locals as Record<string, unknown>).auth = authObject;\n\n return next();\n };\n}\n"],"mappings":";AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAOA,SAAS,oBACd,SAAoC,CAAC,GAClB;AACnB,QAAM,EAAE,eAAe,CAAC,GAAG,YAAY,SAAS,IAAI;AAEpD,SAAO,OAAO,SAAS,SAAS;AAC9B,UAAM,EAAE,SAAS,IAAI,QAAQ;AAE7B,UAAM,WACJ,aAAa,KAAK,CAAC,UAAU;AAC3B,UAAI,MAAM,SAAS,GAAG,GAAG;AACvB,eAAO,SAAS,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MAC/C;AACA,aAAO,aAAa;AAAA,IACtB,CAAC,KACD,aAAa,aACb,SAAS,WAAW,IAAI,KACxB,SAAS,WAAW,OAAO;AAE7B,QAAI,UAAU;AACZ,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,QAAQ,QAAQ,QAAQ,IAAI,iBAAiB,GAAG;AAEpD,QAAI,CAAC,SAAS,eAAe,KAAK,GAAG;AACnC,YAAM,eAAe,QAAQ,QAAQ,IAAI,oBAAoB,GAAG;AAChE,UAAI,cAAc;AAChB,YAAI;AACF,gBAAM,aAAa,IAAI,IAAI,qBAAqB,QAAQ,IAAI,MAAM;AAClE,gBAAM,aAAa,MAAM,MAAM,WAAW,SAAS,GAAG;AAAA,YACpD,QAAQ;AAAA,YACR,SAAS;AAAA,cACP,gBAAgB;AAAA,cAChB,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AAAA,YACnD;AAAA,UACF,CAAC;AACD,cAAI,WAAW,IAAI;AACjB,kBAAM,aAAa,WAAW,QAAQ,eAAe,KAAK,CAAC;AAC3D,kBAAM,WAAW,MAAM,KAAK;AAC5B,uBAAW,UAAU,YAAY;AAC/B,uBAAS,QAAQ,OAAO,cAAc,MAAM;AAAA,YAC9C;AACA,mBAAO;AAAA,UACT;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,aAAO,QAAQ;AAAA,QACb,GAAG,SAAS,aAAa,mBAAmB,QAAQ,CAAC;AAAA,MACvD;AAAA,IACF;AAEA,UAAM,SAAS,mBAAmB,KAAK;AACvC,QAAI,CAAC,QAAQ;AACX,aAAO,QAAQ,SAAS,SAAS;AAAA,IACnC;AAEA,UAAM,QAAQ,OAAO,SAAS,CAAC;AAC/B,UAAM,cAAc,OAAO,eAAe,CAAC;AAE3C,UAAM,aAAyB;AAAA,MAC7B,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO,UAAU;AAAA,MACxB,OAAO,OAAO,UAAU;AAAA,MACxB,OAAO,OAAO,UAAU;AAAA,MACxB,SAAS,OAAO,YAAY;AAAA,MAC5B,WAAW;AAAA,MACX,UAAU,YAAY;AAAA,MACtB,KAAK,CAAC,WAAmD;AACvD,YAAI,OAAO,QAAQ,MAAM,SAAS,OAAO,IAAI,EAAG,QAAO;AACvD,YAAI,OAAO,cAAc,YAAY,SAAS,OAAO,UAAU;AAC7D,iBAAO;AACT,eAAO;AAAA,MACT;AAAA,IACF;AAEA,IAAC,QAAQ,OAAmC,OAAO;AAEnD,WAAO,KAAK;AAAA,EACd;AACF;","names":[]}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthObject, UserResource } from '@inai-dev/types';
|
|
2
|
+
export { AstroCookies, clearAuthCookies, setAuthCookies } from './api-routes.js';
|
|
2
3
|
|
|
3
4
|
interface AstroContext {
|
|
4
5
|
cookies: {
|
|
@@ -9,8 +10,7 @@ interface AstroContext {
|
|
|
9
10
|
locals: Record<string, unknown>;
|
|
10
11
|
}
|
|
11
12
|
declare function auth(context: AstroContext): AuthObject | null;
|
|
12
|
-
declare function currentUser(context: AstroContext, config
|
|
13
|
-
apiUrl: string;
|
|
13
|
+
declare function currentUser(context: AstroContext, config?: {
|
|
14
14
|
publishableKey?: string;
|
|
15
15
|
}): Promise<UserResource | null>;
|
|
16
16
|
|
package/dist/server.js
CHANGED
|
@@ -1,14 +1,64 @@
|
|
|
1
1
|
// src/server.ts
|
|
2
|
-
import { InAIAuthClient } from "@inai-dev/backend";
|
|
2
|
+
import { InAIAuthClient as InAIAuthClient2 } from "@inai-dev/backend";
|
|
3
3
|
import {
|
|
4
|
-
COOKIE_AUTH_TOKEN,
|
|
4
|
+
COOKIE_AUTH_TOKEN as COOKIE_AUTH_TOKEN2,
|
|
5
5
|
getClaimsFromToken,
|
|
6
6
|
isTokenExpired
|
|
7
7
|
} from "@inai-dev/shared";
|
|
8
|
+
|
|
9
|
+
// src/api-routes.ts
|
|
10
|
+
import { InAIAuthClient } from "@inai-dev/backend";
|
|
11
|
+
import {
|
|
12
|
+
COOKIE_AUTH_TOKEN,
|
|
13
|
+
COOKIE_REFRESH_TOKEN,
|
|
14
|
+
COOKIE_AUTH_SESSION,
|
|
15
|
+
decodeJWTPayload
|
|
16
|
+
} from "@inai-dev/shared";
|
|
17
|
+
function setAuthCookies(cookies, tokens, user) {
|
|
18
|
+
const isProduction = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
19
|
+
const claims = decodeJWTPayload(tokens.access_token);
|
|
20
|
+
const expiresAt = claims ? new Date(claims.exp * 1e3).toISOString() : new Date(Date.now() + tokens.expires_in * 1e3).toISOString();
|
|
21
|
+
cookies.set(COOKIE_AUTH_TOKEN, tokens.access_token, {
|
|
22
|
+
httpOnly: true,
|
|
23
|
+
secure: isProduction,
|
|
24
|
+
sameSite: "lax",
|
|
25
|
+
path: "/",
|
|
26
|
+
maxAge: tokens.expires_in
|
|
27
|
+
});
|
|
28
|
+
cookies.set(COOKIE_REFRESH_TOKEN, tokens.refresh_token, {
|
|
29
|
+
httpOnly: true,
|
|
30
|
+
secure: isProduction,
|
|
31
|
+
sameSite: "strict",
|
|
32
|
+
path: "/api/auth",
|
|
33
|
+
maxAge: 7 * 24 * 60 * 60
|
|
34
|
+
});
|
|
35
|
+
cookies.set(COOKIE_AUTH_SESSION, JSON.stringify({
|
|
36
|
+
user,
|
|
37
|
+
expiresAt,
|
|
38
|
+
permissions: claims?.permissions ?? [],
|
|
39
|
+
orgId: claims?.org_id,
|
|
40
|
+
orgRole: claims?.org_role,
|
|
41
|
+
appId: claims?.app_id,
|
|
42
|
+
envId: claims?.env_id
|
|
43
|
+
}), {
|
|
44
|
+
httpOnly: false,
|
|
45
|
+
secure: isProduction,
|
|
46
|
+
sameSite: "lax",
|
|
47
|
+
path: "/",
|
|
48
|
+
maxAge: tokens.expires_in
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function clearAuthCookies(cookies) {
|
|
52
|
+
cookies.delete(COOKIE_AUTH_TOKEN, { path: "/" });
|
|
53
|
+
cookies.delete(COOKIE_REFRESH_TOKEN, { path: "/api/auth" });
|
|
54
|
+
cookies.delete(COOKIE_AUTH_SESSION, { path: "/" });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/server.ts
|
|
8
58
|
function auth(context) {
|
|
9
59
|
const existing = context.locals.auth;
|
|
10
60
|
if (existing) return existing;
|
|
11
|
-
const token = context.cookies.get(
|
|
61
|
+
const token = context.cookies.get(COOKIE_AUTH_TOKEN2)?.value;
|
|
12
62
|
if (!token || isTokenExpired(token)) return null;
|
|
13
63
|
const claims = getClaimsFromToken(token);
|
|
14
64
|
if (!claims) return null;
|
|
@@ -32,11 +82,10 @@ function auth(context) {
|
|
|
32
82
|
};
|
|
33
83
|
}
|
|
34
84
|
async function currentUser(context, config) {
|
|
35
|
-
const token = context.cookies.get(
|
|
85
|
+
const token = context.cookies.get(COOKIE_AUTH_TOKEN2)?.value;
|
|
36
86
|
if (!token || isTokenExpired(token)) return null;
|
|
37
|
-
const client = new
|
|
38
|
-
|
|
39
|
-
publishableKey: config.publishableKey
|
|
87
|
+
const client = new InAIAuthClient2({
|
|
88
|
+
publishableKey: config?.publishableKey
|
|
40
89
|
});
|
|
41
90
|
try {
|
|
42
91
|
const { data } = await client.getMe(token);
|
|
@@ -47,6 +96,8 @@ async function currentUser(context, config) {
|
|
|
47
96
|
}
|
|
48
97
|
export {
|
|
49
98
|
auth,
|
|
50
|
-
|
|
99
|
+
clearAuthCookies,
|
|
100
|
+
currentUser,
|
|
101
|
+
setAuthCookies
|
|
51
102
|
};
|
|
52
103
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import type { AuthObject, UserResource } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n COOKIE_AUTH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\ninterface AstroContext {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n locals: Record<string, unknown>;\n}\n\nexport function auth(context: AstroContext): AuthObject | null {\n const existing = (context.locals as Record<string, unknown>).auth as AuthObject | undefined;\n if (existing) return existing;\n\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const claims = getClaimsFromToken(token);\n if (!claims) return null;\n\n const roles = claims.roles ?? [];\n const permissions = claims.permissions ?? [];\n\n return {\n userId: claims.sub,\n tenantId: claims.tenant_id,\n appId: claims.app_id ?? null,\n envId: claims.env_id ?? null,\n orgId: claims.org_id ?? null,\n orgRole: claims.org_role ?? null,\n sessionId: null,\n getToken: async () => token,\n has: (params: { role?: string; permission?: string }) => {\n if (params.role && roles.includes(params.role)) return true;\n if (params.permission && permissions.includes(params.permission))\n return true;\n return false;\n },\n };\n}\n\nexport async function currentUser(\n context: AstroContext,\n config: { apiUrl: string; publishableKey?: string },\n): Promise<UserResource | null> {\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const client = new InAIAuthClient({\n apiUrl: config.apiUrl,\n publishableKey: config.publishableKey,\n });\n\n try {\n const { data } = await client.getMe(token);\n return data;\n } catch {\n return null;\n }\n}\n"],"mappings":";AACA,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASA,SAAS,KAAK,SAA0C;AAC7D,QAAM,WAAY,QAAQ,OAAmC;AAC7D,MAAI,SAAU,QAAO;AAErB,QAAM,QAAQ,QAAQ,QAAQ,IAAI,iBAAiB,GAAG;AACtD,MAAI,CAAC,SAAS,eAAe,KAAK,EAAG,QAAO;AAE5C,QAAM,SAAS,mBAAmB,KAAK;AACvC,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,QAAQ,OAAO,SAAS,CAAC;AAC/B,QAAM,cAAc,OAAO,eAAe,CAAC;AAE3C,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO,UAAU;AAAA,IACxB,OAAO,OAAO,UAAU;AAAA,IACxB,OAAO,OAAO,UAAU;AAAA,IACxB,SAAS,OAAO,YAAY;AAAA,IAC5B,WAAW;AAAA,IACX,UAAU,YAAY;AAAA,IACtB,KAAK,CAAC,WAAmD;AACvD,UAAI,OAAO,QAAQ,MAAM,SAAS,OAAO,IAAI,EAAG,QAAO;AACvD,UAAI,OAAO,cAAc,YAAY,SAAS,OAAO,UAAU;AAC7D,eAAO;AACT,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,eAAsB,YACpB,SACA,QAC8B;AAC9B,QAAM,QAAQ,QAAQ,QAAQ,IAAI,iBAAiB,GAAG;AACtD,MAAI,CAAC,SAAS,eAAe,KAAK,EAAG,QAAO;AAE5C,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,QAAQ,OAAO;AAAA,IACf,gBAAgB,OAAO;AAAA,EACzB,CAAC;AAED,MAAI;AACF,UAAM,EAAE,KAAK,IAAI,MAAM,OAAO,MAAM,KAAK;AACzC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/api-routes.ts"],"sourcesContent":["import type { AuthObject, UserResource } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n COOKIE_AUTH_TOKEN,\n getClaimsFromToken,\n isTokenExpired,\n} from \"@inai-dev/shared\";\n\ninterface AstroContext {\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n locals: Record<string, unknown>;\n}\n\nexport function auth(context: AstroContext): AuthObject | null {\n const existing = (context.locals as Record<string, unknown>).auth as AuthObject | undefined;\n if (existing) return existing;\n\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const claims = getClaimsFromToken(token);\n if (!claims) return null;\n\n const roles = claims.roles ?? [];\n const permissions = claims.permissions ?? [];\n\n return {\n userId: claims.sub,\n tenantId: claims.tenant_id,\n appId: claims.app_id ?? null,\n envId: claims.env_id ?? null,\n orgId: claims.org_id ?? null,\n orgRole: claims.org_role ?? null,\n sessionId: null,\n getToken: async () => token,\n has: (params: { role?: string; permission?: string }) => {\n if (params.role && roles.includes(params.role)) return true;\n if (params.permission && permissions.includes(params.permission))\n return true;\n return false;\n },\n };\n}\n\nexport async function currentUser(\n context: AstroContext,\n config?: { publishableKey?: string },\n): Promise<UserResource | null> {\n const token = context.cookies.get(COOKIE_AUTH_TOKEN)?.value;\n if (!token || isTokenExpired(token)) return null;\n\n const client = new InAIAuthClient({\n publishableKey: config?.publishableKey,\n });\n\n try {\n const { data } = await client.getMe(token);\n return data;\n } catch {\n return null;\n }\n}\n\nexport { setAuthCookies, clearAuthCookies } from \"./api-routes\";\nexport type { AstroCookies } from \"./api-routes\";\n","import type { InAIAuthConfig, TokenPair, UserResource, LoginResult } from \"@inai-dev/types\";\nimport { InAIAuthClient } from \"@inai-dev/backend\";\nimport {\n COOKIE_AUTH_TOKEN,\n COOKIE_REFRESH_TOKEN,\n COOKIE_AUTH_SESSION,\n decodeJWTPayload,\n} from \"@inai-dev/shared\";\n\ninterface AstroCookies {\n get(name: string): { value: string } | undefined;\n set(name: string, value: string, options?: Record<string, unknown>): void;\n delete(name: string, options?: Record<string, unknown>): void;\n}\n\ninterface AstroAPIContext {\n request: Request;\n cookies: AstroCookies;\n params: Record<string, string | undefined>;\n url: URL;\n}\n\nfunction setAuthCookies(\n cookies: AstroCookies,\n tokens: TokenPair,\n user: UserResource,\n): void {\n const isProduction = 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 cookies.set(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 cookies.set(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 cookies.set(COOKIE_AUTH_SESSION, 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 httpOnly: false,\n secure: isProduction,\n sameSite: \"lax\",\n path: \"/\",\n maxAge: tokens.expires_in,\n });\n}\n\nfunction clearAuthCookies(cookies: AstroCookies): void {\n cookies.delete(COOKIE_AUTH_TOKEN, { path: \"/\" });\n cookies.delete(COOKIE_REFRESH_TOKEN, { path: \"/api/auth\" });\n cookies.delete(COOKIE_AUTH_SESSION, { path: \"/\" });\n}\n\nfunction jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n\nexport function createAuthRoutes(config: InAIAuthConfig = {}) {\n const client = new InAIAuthClient(config);\n\n async function handleLogin(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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 jsonResponse({\n mfa_required: true,\n mfa_token: result.mfa_token,\n });\n }\n\n const tokens = result as unknown as TokenPair;\n const loginUser = result.user;\n const user = loginUser ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Login failed\";\n return jsonResponse({ error: message }, 401);\n }\n }\n\n async function handleRegister(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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 jsonResponse({\n needs_email_verification: true,\n user: result.user,\n });\n }\n\n const tokens = result as unknown as TokenPair;\n const loginUser = result.user;\n const user = loginUser ?? (await client.getMe(tokens.access_token)).data;\n setAuthCookies(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Registration failed\";\n return jsonResponse({ error: message }, 400);\n }\n }\n\n async function handleMFAChallenge(context: AstroAPIContext): Promise<Response> {\n try {\n const body = await context.request.json() as 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(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"MFA verification failed\";\n return jsonResponse({ error: message }, 401);\n }\n }\n\n async function handleRefresh(context: AstroAPIContext): Promise<Response> {\n try {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n\n if (!refreshToken) {\n clearAuthCookies(context.cookies);\n return jsonResponse({ 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(context.cookies, tokens, user);\n\n return jsonResponse({ user });\n } catch {\n clearAuthCookies(context.cookies);\n return jsonResponse({ error: \"Refresh failed\" }, 401);\n }\n }\n\n async function handleLogout(context: AstroAPIContext): Promise<Response> {\n try {\n const refreshToken = context.cookies.get(COOKIE_REFRESH_TOKEN)?.value;\n if (refreshToken) {\n await client.logout(refreshToken).catch(() => {});\n }\n clearAuthCookies(context.cookies);\n return jsonResponse({ success: true });\n } catch {\n clearAuthCookies(context.cookies);\n return jsonResponse({ success: true });\n }\n }\n\n async function handler(context: AstroAPIContext): Promise<Response> {\n const path = context.params.path ?? \"\";\n\n if (context.request.method === \"POST\") {\n switch (path) {\n case \"login\":\n return handleLogin(context);\n case \"register\":\n return handleRegister(context);\n case \"mfa-challenge\":\n return handleMFAChallenge(context);\n case \"refresh\":\n return handleRefresh(context);\n case \"logout\":\n return handleLogout(context);\n }\n }\n\n return jsonResponse({ error: \"Not found\" }, 404);\n }\n\n return {\n ALL: handler,\n POST: handler,\n GET: handler,\n };\n}\n\nexport { setAuthCookies, clearAuthCookies };\nexport type { AstroCookies, AstroAPIContext };\n"],"mappings":"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","names":["InAIAuthClient","COOKIE_AUTH_TOKEN","COOKIE_AUTH_TOKEN","InAIAuthClient"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inai-dev/astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Astro integration for InAI Auth SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,11 +17,15 @@
|
|
|
17
17
|
"./server": {
|
|
18
18
|
"types": "./dist/server.d.ts",
|
|
19
19
|
"import": "./dist/server.js"
|
|
20
|
+
},
|
|
21
|
+
"./api-routes": {
|
|
22
|
+
"types": "./dist/api-routes.d.ts",
|
|
23
|
+
"import": "./dist/api-routes.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
26
|
+
"sideEffects": false,
|
|
22
27
|
"files": [
|
|
23
|
-
"dist"
|
|
24
|
-
"src/components"
|
|
28
|
+
"dist"
|
|
25
29
|
],
|
|
26
30
|
"scripts": {
|
|
27
31
|
"build": "tsup",
|
|
@@ -31,9 +35,9 @@
|
|
|
31
35
|
"prepublishOnly": "npm run build"
|
|
32
36
|
},
|
|
33
37
|
"dependencies": {
|
|
34
|
-
"@inai-dev/types": "^1.
|
|
35
|
-
"@inai-dev/shared": "^1.
|
|
36
|
-
"@inai-dev/backend": "^1.
|
|
38
|
+
"@inai-dev/types": "^1.1.0",
|
|
39
|
+
"@inai-dev/shared": "^1.1.0",
|
|
40
|
+
"@inai-dev/backend": "^1.2.0"
|
|
37
41
|
},
|
|
38
42
|
"peerDependencies": {
|
|
39
43
|
"astro": ">=4.0.0"
|