@inai-dev/astro 0.4.0 → 0.6.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 +2 -0
- package/dist/api-routes.js +2 -2
- package/dist/api-routes.js.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/middleware.js +2 -0
- 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
|
@@ -132,6 +132,8 @@ if (authObj.has({ role: "admin" })) {
|
|
|
132
132
|
| `orgId` | `string \| null` | Active organization ID |
|
|
133
133
|
| `orgRole` | `string \| null` | Role in active organization |
|
|
134
134
|
| `sessionId` | `string \| null` | Session ID |
|
|
135
|
+
| `roles` | `string[]` | User's global roles |
|
|
136
|
+
| `permissions` | `string[]` | User's global permissions |
|
|
135
137
|
| `getToken()` | `() => Promise<string \| null>` | Get the access token |
|
|
136
138
|
| `has(params)` | `({ role?, permission? }) => boolean` | Check role or permission |
|
|
137
139
|
|
package/dist/api-routes.js
CHANGED
|
@@ -66,7 +66,7 @@ function createAuthRoutes(config = {}) {
|
|
|
66
66
|
mfa_token: result.mfa_token
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
const tokens = result;
|
|
69
|
+
const tokens = { access_token: result.access_token, refresh_token: result.refresh_token, token_type: result.token_type, expires_in: result.expires_in };
|
|
70
70
|
const loginUser = result.user;
|
|
71
71
|
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
72
72
|
setAuthCookies(context.cookies, tokens, user);
|
|
@@ -91,7 +91,7 @@ function createAuthRoutes(config = {}) {
|
|
|
91
91
|
user: result.user
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
-
const tokens = result;
|
|
94
|
+
const tokens = { access_token: result.access_token, refresh_token: result.refresh_token, token_type: result.token_type, expires_in: result.expires_in };
|
|
95
95
|
const loginUser = result.user;
|
|
96
96
|
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
97
97
|
setAuthCookies(context.cookies, tokens, user);
|
package/dist/api-routes.js.map
CHANGED
|
@@ -1 +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":[]}
|
|
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 });\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 = { access_token: result.access_token!, refresh_token: result.refresh_token!, token_type: result.token_type!, expires_in: result.expires_in! };\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 = { access_token: result.access_token!, refresh_token: result.refresh_token!, token_type: result.token_type!, expires_in: result.expires_in! };\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.js
CHANGED
|
@@ -92,6 +92,8 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
92
92
|
orgId: claims.org_id ?? null,
|
|
93
93
|
orgRole: claims.org_role ?? null,
|
|
94
94
|
sessionId: null,
|
|
95
|
+
roles,
|
|
96
|
+
permissions,
|
|
95
97
|
getToken: async () => token,
|
|
96
98
|
has: (params) => {
|
|
97
99
|
if (params.role && roles.includes(params.role)) return true;
|
|
@@ -181,7 +183,7 @@ function createAuthRoutes(config = {}) {
|
|
|
181
183
|
mfa_token: result.mfa_token
|
|
182
184
|
});
|
|
183
185
|
}
|
|
184
|
-
const tokens = result;
|
|
186
|
+
const tokens = { access_token: result.access_token, refresh_token: result.refresh_token, token_type: result.token_type, expires_in: result.expires_in };
|
|
185
187
|
const loginUser = result.user;
|
|
186
188
|
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
187
189
|
setAuthCookies(context.cookies, tokens, user);
|
|
@@ -206,7 +208,7 @@ function createAuthRoutes(config = {}) {
|
|
|
206
208
|
user: result.user
|
|
207
209
|
});
|
|
208
210
|
}
|
|
209
|
-
const tokens = result;
|
|
211
|
+
const tokens = { access_token: result.access_token, refresh_token: result.refresh_token, token_type: result.token_type, expires_in: result.expires_in };
|
|
210
212
|
const loginUser = result.user;
|
|
211
213
|
const user = loginUser ?? (await client.getMe(tokens.access_token)).data;
|
|
212
214
|
setAuthCookies(context.cookies, tokens, user);
|
|
@@ -304,6 +306,8 @@ function auth(context) {
|
|
|
304
306
|
orgId: claims.org_id ?? null,
|
|
305
307
|
orgRole: claims.org_role ?? null,
|
|
306
308
|
sessionId: null,
|
|
309
|
+
roles,
|
|
310
|
+
permissions,
|
|
307
311
|
getToken: async () => token,
|
|
308
312
|
has: (params) => {
|
|
309
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 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 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","isTokenExpired","COOKIE_AUTH_TOKEN","COOKIE_REFRESH_TOKEN","COOKIE_AUTH_TOKEN","isTokenExpired","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 });\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 = { access_token: result.access_token!, refresh_token: result.refresh_token!, token_type: result.token_type!, expires_in: result.expires_in! };\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 = { access_token: result.access_token!, refresh_token: result.refresh_token!, token_type: result.token_type!, expires_in: result.expires_in! };\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.js
CHANGED
|
@@ -84,6 +84,8 @@ function inaiAstroMiddleware(config = {}) {
|
|
|
84
84
|
orgId: claims.org_id ?? null,
|
|
85
85
|
orgRole: claims.org_role ?? null,
|
|
86
86
|
sessionId: null,
|
|
87
|
+
roles,
|
|
88
|
+
permissions,
|
|
87
89
|
getToken: async () => token,
|
|
88
90
|
has: (params) => {
|
|
89
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 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 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":[]}
|
|
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 });\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 = { access_token: result.access_token!, refresh_token: result.refresh_token!, token_type: result.token_type!, expires_in: result.expires_in! };\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 = { access_token: result.access_token!, refresh_token: result.refresh_token!, token_type: result.token_type!, expires_in: result.expires_in! };\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.6.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.5.0",
|
|
39
|
+
"@inai-dev/shared": "^1.6.0",
|
|
40
|
+
"@inai-dev/backend": "^1.6.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"astro": ">=4.0.0"
|