@inai-dev/astro 0.3.0 → 0.5.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 +5 -0
- package/dist/index.js +34 -6
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +2 -0
- package/dist/middleware.js +30 -4
- package/dist/middleware.js.map +1 -1
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -41,9 +41,12 @@ import { inaiAstroMiddleware } from "@inai-dev/astro/middleware";
|
|
|
41
41
|
export const onRequest = inaiAstroMiddleware({
|
|
42
42
|
publicRoutes: ["/", "/about", "/login"],
|
|
43
43
|
signInUrl: "/login",
|
|
44
|
+
// jwksUrl: "https://apiauth.inai.dev/.well-known/jwks.json", // optional override
|
|
44
45
|
});
|
|
45
46
|
```
|
|
46
47
|
|
|
48
|
+
> All tokens are cryptographically verified using ES256 (ECDSA P-256). Public keys are fetched from the JWKS endpoint and cached for 5 minutes.
|
|
49
|
+
|
|
47
50
|
The middleware automatically:
|
|
48
51
|
- Skips public routes, `/api/*`, and `/_*` paths
|
|
49
52
|
- Validates the auth token from cookies
|
|
@@ -129,6 +132,8 @@ if (authObj.has({ role: "admin" })) {
|
|
|
129
132
|
| `orgId` | `string \| null` | Active organization ID |
|
|
130
133
|
| `orgRole` | `string \| null` | Role in active organization |
|
|
131
134
|
| `sessionId` | `string \| null` | Session ID |
|
|
135
|
+
| `roles` | `string[]` | User's global roles |
|
|
136
|
+
| `permissions` | `string[]` | User's global permissions |
|
|
132
137
|
| `getToken()` | `() => Promise<string \| null>` | Get the access token |
|
|
133
138
|
| `has(params)` | `({ role?, permission? }) => boolean` | Check role or permission |
|
|
134
139
|
|
package/dist/index.js
CHANGED
|
@@ -10,11 +10,16 @@ function inaiAuth(_config = {}) {
|
|
|
10
10
|
import {
|
|
11
11
|
COOKIE_AUTH_TOKEN,
|
|
12
12
|
COOKIE_REFRESH_TOKEN,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
decodeJWTHeader,
|
|
14
|
+
verifyES256,
|
|
15
|
+
isTokenExpired,
|
|
16
|
+
JWKSClient,
|
|
17
|
+
DEFAULT_API_URL
|
|
15
18
|
} from "@inai-dev/shared";
|
|
16
19
|
function inaiAstroMiddleware(config = {}) {
|
|
17
20
|
const { publicRoutes = [], signInUrl = "/login" } = config;
|
|
21
|
+
const jwksUrl = config.jwksUrl ?? `${config.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;
|
|
22
|
+
const jwksClient = new JWKSClient(jwksUrl);
|
|
18
23
|
return async (context, next) => {
|
|
19
24
|
const { pathname } = context.url;
|
|
20
25
|
const isPublic = publicRoutes.some((route) => {
|
|
@@ -54,9 +59,28 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
54
59
|
`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`
|
|
55
60
|
);
|
|
56
61
|
}
|
|
57
|
-
const
|
|
62
|
+
const header = decodeJWTHeader(token);
|
|
63
|
+
if (!header?.kid) {
|
|
64
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
65
|
+
}
|
|
66
|
+
let publicKey;
|
|
67
|
+
try {
|
|
68
|
+
publicKey = await jwksClient.getKey(header.kid);
|
|
69
|
+
} catch {
|
|
70
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
71
|
+
}
|
|
72
|
+
let claims = await verifyES256(token, publicKey);
|
|
58
73
|
if (!claims) {
|
|
59
|
-
|
|
74
|
+
jwksClient.invalidate();
|
|
75
|
+
try {
|
|
76
|
+
publicKey = await jwksClient.getKey(header.kid);
|
|
77
|
+
} catch {
|
|
78
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
79
|
+
}
|
|
80
|
+
claims = await verifyES256(token, publicKey);
|
|
81
|
+
if (!claims) {
|
|
82
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
83
|
+
}
|
|
60
84
|
}
|
|
61
85
|
const roles = claims.roles ?? [];
|
|
62
86
|
const permissions = claims.permissions ?? [];
|
|
@@ -68,6 +92,8 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
68
92
|
orgId: claims.org_id ?? null,
|
|
69
93
|
orgRole: claims.org_role ?? null,
|
|
70
94
|
sessionId: null,
|
|
95
|
+
roles,
|
|
96
|
+
permissions,
|
|
71
97
|
getToken: async () => token,
|
|
72
98
|
has: (params) => {
|
|
73
99
|
if (params.role && roles.includes(params.role)) return true;
|
|
@@ -85,7 +111,7 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
85
111
|
import { InAIAuthClient as InAIAuthClient2 } from "@inai-dev/backend";
|
|
86
112
|
import {
|
|
87
113
|
COOKIE_AUTH_TOKEN as COOKIE_AUTH_TOKEN3,
|
|
88
|
-
getClaimsFromToken
|
|
114
|
+
getClaimsFromToken,
|
|
89
115
|
isTokenExpired as isTokenExpired2
|
|
90
116
|
} from "@inai-dev/shared";
|
|
91
117
|
|
|
@@ -268,7 +294,7 @@ function auth(context) {
|
|
|
268
294
|
if (existing) return existing;
|
|
269
295
|
const token = context.cookies.get(COOKIE_AUTH_TOKEN3)?.value;
|
|
270
296
|
if (!token || isTokenExpired2(token)) return null;
|
|
271
|
-
const claims =
|
|
297
|
+
const claims = getClaimsFromToken(token);
|
|
272
298
|
if (!claims) return null;
|
|
273
299
|
const roles = claims.roles ?? [];
|
|
274
300
|
const permissions = claims.permissions ?? [];
|
|
@@ -280,6 +306,8 @@ function auth(context) {
|
|
|
280
306
|
orgId: claims.org_id ?? null,
|
|
281
307
|
orgRole: claims.org_role ?? null,
|
|
282
308
|
sessionId: null,
|
|
309
|
+
roles,
|
|
310
|
+
permissions,
|
|
283
311
|
getToken: async () => token,
|
|
284
312
|
has: (params) => {
|
|
285
313
|
if (params.role && roles.includes(params.role)) return true;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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 decodeJWTHeader,\n verifyES256,\n isTokenExpired,\n JWKSClient,\n DEFAULT_API_URL,\n} from \"@inai-dev/shared\";\n\nexport interface InAIAstroMiddlewareConfig {\n publicRoutes?: string[];\n signInUrl?: string;\n jwksUrl?: string;\n apiUrl?: string;\n}\n\nexport function inaiAstroMiddleware(\n config: InAIAstroMiddlewareConfig = {},\n): MiddlewareHandler {\n const { publicRoutes = [], signInUrl = \"/login\" } = config;\n\n const jwksUrl = config.jwksUrl\n ?? `${config.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;\n const jwksClient = new JWKSClient(jwksUrl);\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 // Verify token signature with JWKS\n const header = decodeJWTHeader(token);\n if (!header?.kid) {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\n\n let publicKey: CryptoKey;\n try {\n publicKey = await jwksClient.getKey(header.kid);\n } catch {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\n\n let claims = await verifyES256(token, publicKey);\n if (!claims) {\n // Signature failed with cached key — refetch once in case of key rotation\n jwksClient.invalidate();\n try {\n publicKey = await jwksClient.getKey(header.kid);\n } catch {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\n claims = await verifyES256(token, publicKey);\n if (!claims) {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\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 roles,\n permissions,\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 roles,\n permissions,\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","isTokenExpired","COOKIE_AUTH_TOKEN","COOKIE_REFRESH_TOKEN","COOKIE_AUTH_TOKEN","isTokenExpired","InAIAuthClient"]}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { MiddlewareHandler } from 'astro';
|
|
|
3
3
|
interface InAIAstroMiddlewareConfig {
|
|
4
4
|
publicRoutes?: string[];
|
|
5
5
|
signInUrl?: string;
|
|
6
|
+
jwksUrl?: string;
|
|
7
|
+
apiUrl?: string;
|
|
6
8
|
}
|
|
7
9
|
declare function inaiAstroMiddleware(config?: InAIAstroMiddlewareConfig): MiddlewareHandler;
|
|
8
10
|
|
package/dist/middleware.js
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
import {
|
|
3
3
|
COOKIE_AUTH_TOKEN,
|
|
4
4
|
COOKIE_REFRESH_TOKEN,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
decodeJWTHeader,
|
|
6
|
+
verifyES256,
|
|
7
|
+
isTokenExpired,
|
|
8
|
+
JWKSClient,
|
|
9
|
+
DEFAULT_API_URL
|
|
7
10
|
} from "@inai-dev/shared";
|
|
8
11
|
function inaiAstroMiddleware(config = {}) {
|
|
9
12
|
const { publicRoutes = [], signInUrl = "/login" } = config;
|
|
13
|
+
const jwksUrl = config.jwksUrl ?? `${config.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;
|
|
14
|
+
const jwksClient = new JWKSClient(jwksUrl);
|
|
10
15
|
return async (context, next) => {
|
|
11
16
|
const { pathname } = context.url;
|
|
12
17
|
const isPublic = publicRoutes.some((route) => {
|
|
@@ -46,9 +51,28 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
46
51
|
`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`
|
|
47
52
|
);
|
|
48
53
|
}
|
|
49
|
-
const
|
|
54
|
+
const header = decodeJWTHeader(token);
|
|
55
|
+
if (!header?.kid) {
|
|
56
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
57
|
+
}
|
|
58
|
+
let publicKey;
|
|
59
|
+
try {
|
|
60
|
+
publicKey = await jwksClient.getKey(header.kid);
|
|
61
|
+
} catch {
|
|
62
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
63
|
+
}
|
|
64
|
+
let claims = await verifyES256(token, publicKey);
|
|
50
65
|
if (!claims) {
|
|
51
|
-
|
|
66
|
+
jwksClient.invalidate();
|
|
67
|
+
try {
|
|
68
|
+
publicKey = await jwksClient.getKey(header.kid);
|
|
69
|
+
} catch {
|
|
70
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
71
|
+
}
|
|
72
|
+
claims = await verifyES256(token, publicKey);
|
|
73
|
+
if (!claims) {
|
|
74
|
+
return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);
|
|
75
|
+
}
|
|
52
76
|
}
|
|
53
77
|
const roles = claims.roles ?? [];
|
|
54
78
|
const permissions = claims.permissions ?? [];
|
|
@@ -60,6 +84,8 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
60
84
|
orgId: claims.org_id ?? null,
|
|
61
85
|
orgRole: claims.org_role ?? null,
|
|
62
86
|
sessionId: null,
|
|
87
|
+
roles,
|
|
88
|
+
permissions,
|
|
63
89
|
getToken: async () => token,
|
|
64
90
|
has: (params) => {
|
|
65
91
|
if (params.role && roles.includes(params.role)) return true;
|
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 COOKIE_REFRESH_TOKEN,\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 decodeJWTHeader,\n verifyES256,\n isTokenExpired,\n JWKSClient,\n DEFAULT_API_URL,\n} from \"@inai-dev/shared\";\n\nexport interface InAIAstroMiddlewareConfig {\n publicRoutes?: string[];\n signInUrl?: string;\n jwksUrl?: string;\n apiUrl?: string;\n}\n\nexport function inaiAstroMiddleware(\n config: InAIAstroMiddlewareConfig = {},\n): MiddlewareHandler {\n const { publicRoutes = [], signInUrl = \"/login\" } = config;\n\n const jwksUrl = config.jwksUrl\n ?? `${config.apiUrl ?? DEFAULT_API_URL}/.well-known/jwks.json`;\n const jwksClient = new JWKSClient(jwksUrl);\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 // Verify token signature with JWKS\n const header = decodeJWTHeader(token);\n if (!header?.kid) {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\n\n let publicKey: CryptoKey;\n try {\n publicKey = await jwksClient.getKey(header.kid);\n } catch {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\n\n let claims = await verifyES256(token, publicKey);\n if (!claims) {\n // Signature failed with cached key — refetch once in case of key rotation\n jwksClient.invalidate();\n try {\n publicKey = await jwksClient.getKey(header.kid);\n } catch {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\n claims = await verifyES256(token, publicKey);\n if (!claims) {\n return context.redirect(`${signInUrl}?returnTo=${encodeURIComponent(pathname)}`);\n }\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 roles,\n permissions,\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":"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","names":[]}
|
package/dist/server.js
CHANGED
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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 roles,\n permissions,\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.5.0",
|
|
4
4
|
"description": "Astro integration for InAI Auth SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -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.4.0",
|
|
39
|
+
"@inai-dev/shared": "^1.5.0",
|
|
40
|
+
"@inai-dev/backend": "^1.5.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"astro": ">=4.0.0"
|