@qelos/integrator-next 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -164
- package/dist/context.d.ts +5 -11
- package/dist/context.js +63 -43
- package/dist/context.js.map +1 -1
- package/dist/cookies.d.ts +19 -0
- package/dist/cookies.js +39 -0
- package/dist/cookies.js.map +1 -0
- package/dist/env-config.d.ts +3 -3
- package/dist/env-config.js +6 -7
- package/dist/env-config.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/me-client.d.ts +15 -0
- package/dist/me-client.js +35 -0
- package/dist/me-client.js.map +1 -0
- package/dist/middleware.d.ts +10 -34
- package/dist/middleware.js +75 -80
- package/dist/middleware.js.map +1 -1
- package/dist/pages.d.ts +5 -9
- package/dist/pages.js +61 -98
- package/dist/pages.js.map +1 -1
- package/dist/proxy-target.d.ts +17 -0
- package/dist/proxy-target.js +26 -0
- package/dist/proxy-target.js.map +1 -0
- package/dist/runtime/api-proxy.d.ts +16 -0
- package/dist/runtime/api-proxy.js +90 -0
- package/dist/runtime/api-proxy.js.map +1 -0
- package/dist/sdk-factory.d.ts +13 -16
- package/dist/sdk-factory.js +29 -79
- package/dist/sdk-factory.js.map +1 -1
- package/dist/types.d.ts +28 -16
- package/dist/types.js.map +1 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
# @qelos/integrator-next
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
every
|
|
3
|
+
Next.js integrator for [Qelos](https://qelos.io). It turns your Next.js host into
|
|
4
|
+
a same-origin BFF for a managed Qelos app: Edge middleware resolves the current
|
|
5
|
+
user via `GET /api/me` with inbound cookies forwarded verbatim, a Node catch-all
|
|
6
|
+
route proxies `/api/**` to Qelos with `Set-Cookie` domain rewriting, and the
|
|
7
|
+
SDK reads live `Cookie` / `Authorization` headers on every call — no
|
|
8
|
+
integrator-managed access or refresh tokens.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Supports the **App Router** and the **Pages Router** on Next.js **14** and
|
|
11
|
+
**15**.
|
|
12
|
+
|
|
13
|
+
> Requires **Node 18+** — the middleware and proxy use
|
|
14
|
+
> [`Response.headers.getSetCookie()`](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie)
|
|
15
|
+
> to forward individual upstream `Set-Cookie` headers.
|
|
12
16
|
|
|
13
17
|
## Install
|
|
14
18
|
|
|
@@ -16,13 +20,11 @@ shape (`{ user, workspace, workspaces, sdk, tokens }`).
|
|
|
16
20
|
pnpm add @qelos/integrator-next @qelos/sdk
|
|
17
21
|
```
|
|
18
22
|
|
|
19
|
-
`next` (>=13.4) and `react` are peer dependencies.
|
|
20
|
-
and 15.x on both the App Router and the Pages Router.
|
|
23
|
+
`next` (>=13.4) and `react` are peer dependencies.
|
|
21
24
|
|
|
22
25
|
## Quick start
|
|
23
26
|
|
|
24
|
-
Set `QELOS_APP_URL
|
|
25
|
-
re-export the pre-built middleware and use the no-arg context loader:
|
|
27
|
+
Set `QELOS_APP_URL`, then wire Edge middleware and a catch-all API proxy route.
|
|
26
28
|
|
|
27
29
|
```ts
|
|
28
30
|
// middleware.ts
|
|
@@ -33,6 +35,16 @@ export const config = {
|
|
|
33
35
|
};
|
|
34
36
|
```
|
|
35
37
|
|
|
38
|
+
```ts
|
|
39
|
+
// app/api/[...qelos]/route.ts
|
|
40
|
+
import { createQelosApiProxyHandlers } from '@qelos/integrator-next/runtime/api-proxy';
|
|
41
|
+
|
|
42
|
+
const { runtime, GET, POST, PUT, PATCH, DELETE, OPTIONS } =
|
|
43
|
+
createQelosApiProxyHandlers({ appUrl: process.env.QELOS_APP_URL! });
|
|
44
|
+
|
|
45
|
+
export { runtime, GET, POST, PUT, PATCH, DELETE, OPTIONS };
|
|
46
|
+
```
|
|
47
|
+
|
|
36
48
|
```tsx
|
|
37
49
|
// app/dashboard/page.tsx
|
|
38
50
|
import { getQelosContext } from '@qelos/integrator-next/context';
|
|
@@ -41,86 +53,86 @@ export default async function Dashboard() {
|
|
|
41
53
|
const { user, sdk } = await getQelosContext();
|
|
42
54
|
if (!user) return <p>Please sign in.</p>;
|
|
43
55
|
const products = await sdk.entities('products').getList();
|
|
44
|
-
return
|
|
56
|
+
return (
|
|
57
|
+
<div>
|
|
58
|
+
Welcome {user.fullName}, you have {products.length} products
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
45
61
|
}
|
|
46
62
|
```
|
|
47
63
|
|
|
48
64
|
### Environment variables
|
|
49
65
|
|
|
50
66
|
| Variable | Default | Description |
|
|
51
|
-
|
|
52
|
-
| `QELOS_APP_URL` | — (required) | Qelos
|
|
53
|
-
| `QELOS_API_TOKEN` | — | Service-to-service token (skips
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `QELOS_SKIP_PATHS` | — | Comma-separated path prefixes to bypass entirely |
|
|
67
|
+
|----------|---------|-------------|
|
|
68
|
+
| `QELOS_APP_URL` | — (required for default exports) | Qelos managed-app base URL |
|
|
69
|
+
| `QELOS_API_TOKEN` | — | Service-to-service token (skips cookie identity for anonymous resolution) |
|
|
70
|
+
| `QELOS_REQUIRE_AUTH` | `false` | `true` / `1` → anonymous requests get `401` from middleware |
|
|
71
|
+
| `QELOS_SKIP_PATHS` | — | Comma-separated path prefixes to bypass middleware |
|
|
72
|
+
| `QELOS_DISABLE_PROXY` | `false` | `true` when you are **not** using the catch-all `/api/**` proxy — skips auto `/api/` in middleware `skipPaths` |
|
|
58
73
|
|
|
59
|
-
|
|
74
|
+
Dev-time proxy target overrides (same resolution order as the proxy):
|
|
60
75
|
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
1. `NEXT_QELOS_PROXY_TARGET`
|
|
77
|
+
2. `QELOS_IP`
|
|
78
|
+
3. `QELOS_API_IP`
|
|
79
|
+
4. `appUrl` from config / `QELOS_APP_URL`
|
|
63
80
|
|
|
64
|
-
|
|
81
|
+
## BFF proxy (`/api/**`)
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
Register a catch-all App Router handler (see above) that exports the handlers
|
|
84
|
+
from `createQelosApiProxyHandlers`. Every request your app does not handle
|
|
85
|
+
itself is forwarded to the configured Qelos origin; the response body streams
|
|
86
|
+
back and upstream `Set-Cookie` headers are rewritten so `Domain=` matches the
|
|
87
|
+
inbound `Host` (port stripped).
|
|
69
88
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
appUrl: process.env.QELOS_APP_URL!,
|
|
73
|
-
skipPaths: ['/_next', '/favicon.ico'],
|
|
74
|
-
requireAuth: false,
|
|
75
|
-
},
|
|
76
|
-
resolveWorkspace: ({ req, workspaces }) => {
|
|
77
|
-
const slug = req.nextUrl.searchParams.get('workspace');
|
|
78
|
-
return workspaces.find((w) => (w as any).slug === slug) ?? workspaces[0] ?? null;
|
|
79
|
-
},
|
|
80
|
-
});
|
|
89
|
+
User-defined routes under `app/api/...` still take precedence over the catch-all
|
|
90
|
+
segment — the proxy only runs for paths nothing else matched.
|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
84
|
-
};
|
|
85
|
-
```
|
|
92
|
+
### Opting out (`disableProxy`)
|
|
86
93
|
|
|
87
|
-
|
|
94
|
+
Set `disableProxy: true` on `QelosNextConfig` when you do not use the catch-all
|
|
95
|
+
proxy. Middleware will then **not** auto-prepend `/api/` to `skipPaths`, so you
|
|
96
|
+
can run your own `/api/*` handlers without the integrator skipping them for the
|
|
97
|
+
proxy.
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
`Authorization` header.
|
|
91
|
-
- Calls the Qelos SDK to resolve the user and the active workspace.
|
|
92
|
-
- Forwards the resolved ids to downstream handlers via the
|
|
93
|
-
`x-qelos-user-id` and `x-qelos-workspace-id` request headers.
|
|
94
|
-
- Refreshes the access/refresh token pair when needed and writes the new
|
|
95
|
-
cookies onto the outbound response.
|
|
96
|
-
- Returns `401` early when `config.requireAuth: true` and the user cannot be
|
|
97
|
-
resolved.
|
|
99
|
+
WebSocket upgrades are not proxied.
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
## Edge middleware
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
Next.js 14 (sync `cookies()` / `headers()`) and Next.js 15 (async). Wrap it
|
|
103
|
-
with React's `cache()` so re-renders within the same request reuse the result:
|
|
103
|
+
On each matched request, middleware:
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
1. Resolves the upstream origin (`NEXT_QELOS_PROXY_TARGET` → `QELOS_IP` →
|
|
106
|
+
`QELOS_API_IP` → `appUrl`). If neither that nor `apiToken` is configured and
|
|
107
|
+
`requireAuth` is false, identity headers are cleared and the request
|
|
108
|
+
continues anonymously.
|
|
109
|
+
2. When an upstream origin exists, issues `fetch('${origin}/api/me', { redirect:
|
|
110
|
+
'manual' })` with the inbound `Cookie` and `Authorization` headers forwarded.
|
|
111
|
+
3. Appends every upstream `Set-Cookie` to the outgoing response after rewriting
|
|
112
|
+
`Domain=` to the inbound `Host`.
|
|
113
|
+
4. On `200` JSON, uses that body as `IUser`, loads workspaces via the SDK, and
|
|
114
|
+
sets the active workspace from `user.workspace` (or your `resolveWorkspace`
|
|
115
|
+
callback) — **not** `workspaces[0]`.
|
|
116
|
+
5. Forwards `x-qelos-user-id` and `x-qelos-workspace-id` on the rewritten request
|
|
117
|
+
headers for downstream App Router code.
|
|
109
118
|
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
When the proxy is enabled (`disableProxy !== true`), `/api/` is prepended to
|
|
120
|
+
`skipPaths` so middleware does not run the `/api/me` probe on proxied API
|
|
121
|
+
traffic (avoids double upstream calls).
|
|
112
122
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
> Edge middleware runs in the Edge runtime. The `/api/me` round-trip uses the
|
|
124
|
+
> standard `fetch` API with `redirect: 'manual'`. In local dev over plain HTTP,
|
|
125
|
+
> browsers may drop upstream `Secure` cookies — use HTTPS locally or configure
|
|
126
|
+
> Qelos for non-`Secure` cookies in development.
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
## App Router server code
|
|
129
|
+
|
|
130
|
+
Wrap `getQelosContext()` with React `cache()` if you want one resolution per
|
|
131
|
+
request. It performs the same `/api/me` identification as middleware (with live
|
|
132
|
+
headers) and attempts to apply upstream `Set-Cookie` values through
|
|
133
|
+
`next/headers` `cookies().set` when the runtime allows it.
|
|
122
134
|
|
|
123
|
-
###
|
|
135
|
+
### Route handlers
|
|
124
136
|
|
|
125
137
|
```ts
|
|
126
138
|
// app/api/me/route.ts
|
|
@@ -133,26 +145,8 @@ export const GET = withQelosRoute((_req, _ctx, qelos) => {
|
|
|
133
145
|
});
|
|
134
146
|
```
|
|
135
147
|
|
|
136
|
-
Anywhere downstream — including modules outside the React tree — can read the
|
|
137
|
-
active context from AsyncLocalStorage via `getStoredQelosContext()`, provided
|
|
138
|
-
execution went through `withQelosRoute`, `withQelosContext`, or one of the
|
|
139
|
-
Pages Router wrappers.
|
|
140
|
-
|
|
141
|
-
```ts
|
|
142
|
-
// services/products.ts
|
|
143
|
-
import { getStoredQelosContext } from '@qelos/integrator-next/context';
|
|
144
|
-
|
|
145
|
-
export async function listProducts() {
|
|
146
|
-
const qelos = getStoredQelosContext();
|
|
147
|
-
if (!qelos?.sdk) throw new Error('no qelos context');
|
|
148
|
-
return qelos.sdk.entities('products').getList();
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
148
|
## Pages Router
|
|
153
149
|
|
|
154
|
-
### API routes
|
|
155
|
-
|
|
156
150
|
```ts
|
|
157
151
|
// pages/api/me.ts
|
|
158
152
|
import { withQelosApi } from '@qelos/integrator-next/pages';
|
|
@@ -165,92 +159,43 @@ export default withQelosApi(
|
|
|
165
159
|
);
|
|
166
160
|
```
|
|
167
161
|
|
|
168
|
-
### `getServerSideProps`
|
|
169
|
-
|
|
170
162
|
```ts
|
|
171
163
|
// pages/dashboard.tsx
|
|
172
164
|
import { withQelosSSR } from '@qelos/integrator-next/pages';
|
|
173
165
|
|
|
174
166
|
export const getServerSideProps = withQelosSSR(
|
|
175
|
-
async (_ctx, qelos) => ({
|
|
167
|
+
async (_ctx, qelos) => ({
|
|
168
|
+
props: { user: qelos.user, workspace: qelos.workspace },
|
|
169
|
+
}),
|
|
176
170
|
{ config: { appUrl: process.env.QELOS_APP_URL!, requireAuth: true } },
|
|
177
171
|
);
|
|
178
172
|
```
|
|
179
173
|
|
|
180
|
-
##
|
|
181
|
-
|
|
182
|
-
When a request arrives with an expired access token, the SDK's failed-auth
|
|
183
|
-
path triggers a refresh, in order:
|
|
184
|
-
|
|
185
|
-
1. If a refresh token is present, calls
|
|
186
|
-
`sdk.authentication.refreshToken()` (`POST /api/token/refresh`) — issues a
|
|
187
|
-
new access + refresh pair.
|
|
188
|
-
2. Otherwise, calls `sdk.authentication.refreshCookieToken()`
|
|
189
|
-
(`POST /api/cookie/refresh`) — used for cookie-only sessions that do not
|
|
190
|
-
carry a separate refresh token (e.g. social-auth flows).
|
|
191
|
-
|
|
192
|
-
The new pair is then written back to cookies using the configured
|
|
193
|
-
`accessTokenCookie` / `refreshTokenCookie` names — automatically in App
|
|
194
|
-
Router middleware (`response.cookies.set`), Pages Router API routes
|
|
195
|
-
(`Set-Cookie` header), and `getServerSideProps` responses.
|
|
196
|
-
|
|
197
|
-
App Router server components and route handlers cannot mutate cookies after
|
|
198
|
-
rendering starts, so the default refresh hook there is a no-op — the new
|
|
199
|
-
tokens are still applied to the in-memory pair so subsequent SDK calls in the
|
|
200
|
-
same request use the fresh access token. To persist the refreshed pair, hand
|
|
201
|
-
the request off to a route handler that calls `cookies().set(...)`.
|
|
202
|
-
|
|
203
|
-
Override the behaviour by passing `onTokenRefresh` to any of the wrappers.
|
|
204
|
-
The hook receives:
|
|
205
|
-
|
|
206
|
-
```ts
|
|
207
|
-
interface TokenRefreshContext<T> {
|
|
208
|
-
target: T; // NextResponse | NextApiResponse | ServerResponse | null
|
|
209
|
-
oldTokens: { accessToken?: string; refreshToken?: string };
|
|
210
|
-
newTokens: { accessToken: string; refreshToken?: string };
|
|
211
|
-
sdk: QelosSDK;
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Manual cookie refresh
|
|
216
|
-
|
|
217
|
-
Long-lived integrator-hosted sessions can also call the SDK directly to
|
|
218
|
-
proactively refresh the cookie token. From a route handler that owns the
|
|
219
|
-
response (so it can mutate cookies):
|
|
174
|
+
## Configuration (`QelosNextConfig`)
|
|
220
175
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return NextResponse.json({ user: result.payload.user });
|
|
230
|
-
});
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
## Configuration
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
interface QelosNextConfig {
|
|
237
|
-
appUrl: string; // Qelos backend base URL
|
|
238
|
-
apiToken?: string; // service-to-service token (skips refresh logic)
|
|
239
|
-
accessTokenCookie?: string; // default 'q_access_token'
|
|
240
|
-
refreshTokenCookie?: string; // default 'q_refresh_token'
|
|
241
|
-
requireAuth?: boolean; // 401/redirect on anonymous (default false)
|
|
242
|
-
skipPaths?: string[]; // path prefixes to bypass entirely
|
|
243
|
-
sdkOptions?: Partial<QelosSDKOptions>;
|
|
244
|
-
}
|
|
245
|
-
```
|
|
176
|
+
| Field | Description |
|
|
177
|
+
|-------|-------------|
|
|
178
|
+
| `appUrl` | Qelos managed-app base URL |
|
|
179
|
+
| `apiToken` | Static token — no `/api/me` cookie identity |
|
|
180
|
+
| `requireAuth` | Reject anonymous requests (`401` / redirect) |
|
|
181
|
+
| `skipPaths` | Path prefixes to bypass middleware (and `/api/` is auto-prepended when the proxy is enabled) |
|
|
182
|
+
| `disableProxy` | Opt out of auto `/api/` skip for middleware |
|
|
183
|
+
| `sdkOptions` | Extra `QelosSDK` constructor options |
|
|
246
184
|
|
|
247
185
|
## Subpath exports
|
|
248
186
|
|
|
249
187
|
| Import | Purpose |
|
|
250
|
-
|
|
251
|
-
| `@qelos/integrator-next` |
|
|
252
|
-
| `@qelos/integrator-next/middleware` | Edge middleware
|
|
253
|
-
| `@qelos/integrator-next/context` |
|
|
254
|
-
| `@qelos/integrator-next/route` |
|
|
255
|
-
| `@qelos/integrator-next/pages` | Pages Router wrappers
|
|
256
|
-
| `@qelos/integrator-next/
|
|
188
|
+
|--------|---------|
|
|
189
|
+
| `@qelos/integrator-next` | Barrel |
|
|
190
|
+
| `@qelos/integrator-next/middleware` | Edge middleware |
|
|
191
|
+
| `@qelos/integrator-next/context` | `getQelosContext`, AsyncLocalStorage helpers |
|
|
192
|
+
| `@qelos/integrator-next/route` | `withQelosRoute` |
|
|
193
|
+
| `@qelos/integrator-next/pages` | Pages Router wrappers |
|
|
194
|
+
| `@qelos/integrator-next/runtime/api-proxy` | Catch-all proxy handlers (`runtime = 'nodejs'`) |
|
|
195
|
+
| `@qelos/integrator-next/types` | Types and header name constants |
|
|
196
|
+
|
|
197
|
+
## Social auth
|
|
198
|
+
|
|
199
|
+
`completeSocialAuthCallback` and the re-exported SDK helpers forward Qelos
|
|
200
|
+
session cookies from `socialCallback` onto your Node response — use them from
|
|
201
|
+
a route handler that owns the response object.
|
package/dist/context.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IUser } from '@qelos/sdk/dist/authentication';
|
|
2
2
|
import type { IWorkspace } from '@qelos/sdk/workspaces';
|
|
3
|
-
import type { QelosNextConfig, QelosRequestContext
|
|
3
|
+
import type { QelosNextConfig, QelosRequestContext } from './types';
|
|
4
4
|
/**
|
|
5
5
|
* Run `fn` with `ctx` as the active Qelos request context. Code anywhere in
|
|
6
6
|
* the resulting async stack can call `getStoredQelosContext()` to retrieve it
|
|
@@ -15,15 +15,9 @@ export declare function getStoredQelosContext(): QelosRequestContext | null;
|
|
|
15
15
|
export interface GetQelosContextOptions {
|
|
16
16
|
config: QelosNextConfig;
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* route handler can override this to call `cookies().set(...)`.
|
|
22
|
-
*/
|
|
23
|
-
onTokenRefresh?: TokenRefreshHook<null>;
|
|
24
|
-
/**
|
|
25
|
-
* Resolve the active workspace for a request. Defaults to picking the first
|
|
26
|
-
* workspace returned from `sdk.workspaces.getList()`.
|
|
18
|
+
* Resolve the active workspace for a request. Defaults to whatever
|
|
19
|
+
* `/api/me` reports on `user.workspace` (the user's currently activated
|
|
20
|
+
* workspace, or `null` when none is active).
|
|
27
21
|
*/
|
|
28
22
|
resolveWorkspace?: (params: {
|
|
29
23
|
user: IUser;
|
|
@@ -33,7 +27,7 @@ export interface GetQelosContextOptions {
|
|
|
33
27
|
/**
|
|
34
28
|
* Identify the user / workspace for the current App Router request and
|
|
35
29
|
* return the Qelos context. Safe to call multiple times within the same
|
|
36
|
-
* request — each call performs its own
|
|
30
|
+
* request — each call performs its own `/api/me` round-trip, so wrap it with
|
|
37
31
|
* React's `cache()` if you want a single resolution per render.
|
|
38
32
|
*
|
|
39
33
|
* Calling without arguments uses the env-derived config (see
|
package/dist/context.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { rewriteSetCookieDomain } from './cookies';
|
|
2
3
|
import { getDefaultQelosConfig } from './env-config';
|
|
4
|
+
import { fetchMeIdentity } from './me-client';
|
|
5
|
+
import { resolveQelosProxyTarget } from './proxy-target';
|
|
3
6
|
import { createRequestSdk } from './sdk-factory';
|
|
4
|
-
const DEFAULT_ACCESS_COOKIE = 'q_access_token';
|
|
5
|
-
const DEFAULT_REFRESH_COOKIE = 'q_refresh_token';
|
|
6
7
|
const storage = new AsyncLocalStorage();
|
|
7
8
|
/**
|
|
8
9
|
* Run `fn` with `ctx` as the active Qelos request context. Code anywhere in
|
|
@@ -29,27 +30,46 @@ function loadNextHeaders() {
|
|
|
29
30
|
}
|
|
30
31
|
return nextHeadersModulePromise;
|
|
31
32
|
}
|
|
32
|
-
async function
|
|
33
|
-
const {
|
|
34
|
-
const
|
|
35
|
-
const refreshCookie = config.refreshTokenCookie || DEFAULT_REFRESH_COOKIE;
|
|
36
|
-
const jar = await cookies();
|
|
37
|
-
const hdr = await headers();
|
|
38
|
-
const cookieAccess = jar.get(accessCookie)?.value;
|
|
39
|
-
const cookieRefresh = jar.get(refreshCookie)?.value;
|
|
40
|
-
const authHeader = hdr.get('authorization');
|
|
41
|
-
const headerAccess = authHeader && authHeader.toLowerCase().startsWith('bearer ')
|
|
42
|
-
? authHeader.slice(7).trim()
|
|
43
|
-
: undefined;
|
|
33
|
+
async function getInboundAuthHeaders() {
|
|
34
|
+
const { headers } = await loadNextHeaders();
|
|
35
|
+
const hdr = await Promise.resolve(headers());
|
|
44
36
|
return {
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
cookie: hdr.get('cookie') ?? undefined,
|
|
38
|
+
authorization: hdr.get('authorization') ?? undefined,
|
|
39
|
+
host: hdr.get('host'),
|
|
47
40
|
};
|
|
48
41
|
}
|
|
42
|
+
async function forwardSetCookieThroughNextCookies(rawLines, originalHost) {
|
|
43
|
+
const rewritten = rawLines.map((v) => rewriteSetCookieDomain(v, originalHost ?? undefined));
|
|
44
|
+
try {
|
|
45
|
+
const { cookies } = await import('next/headers');
|
|
46
|
+
const jar = await Promise.resolve(cookies());
|
|
47
|
+
for (const line of rewritten) {
|
|
48
|
+
const semi = line.indexOf(';');
|
|
49
|
+
const nv = (semi === -1 ? line : line.slice(0, semi)).trim();
|
|
50
|
+
const eq = nv.indexOf('=');
|
|
51
|
+
if (eq <= 0)
|
|
52
|
+
continue;
|
|
53
|
+
const name = nv.slice(0, eq).trim();
|
|
54
|
+
const value = nv.slice(eq + 1).trim();
|
|
55
|
+
if (!name)
|
|
56
|
+
continue;
|
|
57
|
+
try {
|
|
58
|
+
jar.set(name, value, { path: '/' });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// not writable in this context
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
//
|
|
67
|
+
}
|
|
68
|
+
}
|
|
49
69
|
/**
|
|
50
70
|
* Identify the user / workspace for the current App Router request and
|
|
51
71
|
* return the Qelos context. Safe to call multiple times within the same
|
|
52
|
-
* request — each call performs its own
|
|
72
|
+
* request — each call performs its own `/api/me` round-trip, so wrap it with
|
|
53
73
|
* React's `cache()` if you want a single resolution per render.
|
|
54
74
|
*
|
|
55
75
|
* Calling without arguments uses the env-derived config (see
|
|
@@ -59,49 +79,49 @@ async function readTokensFromHeaders(config) {
|
|
|
59
79
|
export async function getQelosContext(options) {
|
|
60
80
|
const resolved = options ?? { config: getDefaultQelosConfig() };
|
|
61
81
|
const { config, resolveWorkspace } = resolved;
|
|
62
|
-
const
|
|
63
|
-
const sdk = createRequestSdk({
|
|
64
|
-
config,
|
|
65
|
-
tokens,
|
|
66
|
-
refreshTarget: null,
|
|
67
|
-
onTokenRefresh: resolved.onTokenRefresh,
|
|
68
|
-
});
|
|
82
|
+
const getHeaders = () => getInboundAuthHeaders();
|
|
83
|
+
const sdk = createRequestSdk({ config, getHeaders });
|
|
69
84
|
const ctx = {
|
|
70
85
|
user: null,
|
|
71
86
|
workspace: null,
|
|
72
87
|
workspaces: [],
|
|
73
88
|
sdk,
|
|
74
|
-
tokens,
|
|
75
89
|
};
|
|
76
|
-
const
|
|
77
|
-
if (!
|
|
90
|
+
const base = resolveQelosProxyTarget(config);
|
|
91
|
+
if (!base && !config.apiToken) {
|
|
78
92
|
return ctx;
|
|
79
93
|
}
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
if (base) {
|
|
95
|
+
const h = await getInboundAuthHeaders();
|
|
96
|
+
const { response, user } = await fetchMeIdentity({
|
|
97
|
+
base,
|
|
98
|
+
cookie: h.cookie,
|
|
99
|
+
authorization: h.authorization,
|
|
100
|
+
});
|
|
101
|
+
const setCookieValues = response.headers.getSetCookie?.() ?? [];
|
|
102
|
+
await forwardSetCookieThroughNextCookies(setCookieValues, h.host);
|
|
103
|
+
ctx.user = user;
|
|
82
104
|
}
|
|
83
|
-
|
|
105
|
+
if (!ctx.user) {
|
|
84
106
|
return ctx;
|
|
85
107
|
}
|
|
86
|
-
if (!ctx.user)
|
|
87
|
-
return ctx;
|
|
88
108
|
try {
|
|
89
109
|
ctx.workspaces = await sdk.workspaces.getList();
|
|
90
110
|
}
|
|
91
111
|
catch {
|
|
92
112
|
ctx.workspaces = [];
|
|
93
113
|
}
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
ctx.workspace
|
|
104
|
-
|
|
114
|
+
if (resolveWorkspace) {
|
|
115
|
+
ctx.workspace =
|
|
116
|
+
(await resolveWorkspace({
|
|
117
|
+
user: ctx.user,
|
|
118
|
+
workspaces: ctx.workspaces,
|
|
119
|
+
})) || null;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
ctx.workspace =
|
|
123
|
+
ctx.user.workspace ||
|
|
124
|
+
null;
|
|
105
125
|
}
|
|
106
126
|
return ctx;
|
|
107
127
|
}
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrite the `Domain=` attribute of a single `Set-Cookie` header value.
|
|
3
|
+
*
|
|
4
|
+
* - If the cookie contains a `Domain=<value>` attribute (case-insensitive), its
|
|
5
|
+
* value is replaced with `newDomain`, with any trailing `:port` suffix
|
|
6
|
+
* stripped first (cookie `Domain` attributes do not include a port).
|
|
7
|
+
* - If `newDomain` is `undefined` (or empty), the entire `; Domain=<value>`
|
|
8
|
+
* segment is removed so the browser scopes the cookie to the current host.
|
|
9
|
+
* - If no `Domain=` attribute is present, the input is returned unchanged.
|
|
10
|
+
*
|
|
11
|
+
* All other attributes, their casing/order and the original cookie name/value
|
|
12
|
+
* bytes are preserved.
|
|
13
|
+
*/
|
|
14
|
+
export declare function rewriteSetCookieDomain(setCookie: string, newDomain: string | undefined): string;
|
|
15
|
+
/**
|
|
16
|
+
* Apply {@link rewriteSetCookieDomain} to each entry of a `Set-Cookie` array
|
|
17
|
+
* (e.g. the result of `Headers.getSetCookie()`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function rewriteSetCookieDomains(values: string[], newDomain: string | undefined): string[];
|
package/dist/cookies.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrite the `Domain=` attribute of a single `Set-Cookie` header value.
|
|
3
|
+
*
|
|
4
|
+
* - If the cookie contains a `Domain=<value>` attribute (case-insensitive), its
|
|
5
|
+
* value is replaced with `newDomain`, with any trailing `:port` suffix
|
|
6
|
+
* stripped first (cookie `Domain` attributes do not include a port).
|
|
7
|
+
* - If `newDomain` is `undefined` (or empty), the entire `; Domain=<value>`
|
|
8
|
+
* segment is removed so the browser scopes the cookie to the current host.
|
|
9
|
+
* - If no `Domain=` attribute is present, the input is returned unchanged.
|
|
10
|
+
*
|
|
11
|
+
* All other attributes, their casing/order and the original cookie name/value
|
|
12
|
+
* bytes are preserved.
|
|
13
|
+
*/
|
|
14
|
+
export function rewriteSetCookieDomain(setCookie, newDomain) {
|
|
15
|
+
const domainSegment = /;\s*Domain\s*=\s*[^;]+/i;
|
|
16
|
+
if (!domainSegment.test(setCookie))
|
|
17
|
+
return setCookie;
|
|
18
|
+
const stripped = newDomain ? stripPort(newDomain) : '';
|
|
19
|
+
if (!stripped) {
|
|
20
|
+
return setCookie.replace(domainSegment, '');
|
|
21
|
+
}
|
|
22
|
+
return setCookie.replace(/(;\s*Domain\s*=\s*)[^;]+/i, `$1${stripped}`);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Apply {@link rewriteSetCookieDomain} to each entry of a `Set-Cookie` array
|
|
26
|
+
* (e.g. the result of `Headers.getSetCookie()`).
|
|
27
|
+
*/
|
|
28
|
+
export function rewriteSetCookieDomains(values, newDomain) {
|
|
29
|
+
return values.map((value) => rewriteSetCookieDomain(value, newDomain));
|
|
30
|
+
}
|
|
31
|
+
function stripPort(host) {
|
|
32
|
+
if (host.startsWith('[')) {
|
|
33
|
+
const end = host.indexOf(']');
|
|
34
|
+
return end === -1 ? host : host.slice(1, end);
|
|
35
|
+
}
|
|
36
|
+
const colon = host.indexOf(':');
|
|
37
|
+
return colon === -1 ? host : host.slice(0, colon);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.js","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAiB,EACjB,SAA6B;IAE7B,MAAM,aAAa,GAAG,yBAAyB,CAAC;IAChD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAErD,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,SAAS,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,SAAS,CAAC,OAAO,CACtB,2BAA2B,EAC3B,KAAK,QAAQ,EAAE,CAChB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAgB,EAChB,SAA6B;IAE7B,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,sBAAsB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACpD,CAAC"}
|
package/dist/env-config.d.ts
CHANGED
|
@@ -8,11 +8,11 @@ import type { QelosNextConfig } from './types';
|
|
|
8
8
|
* Recognized variables:
|
|
9
9
|
*
|
|
10
10
|
* - `QELOS_APP_URL` (required) — Qelos backend base URL.
|
|
11
|
-
* - `QELOS_API_TOKEN` — service-to-service token (skips
|
|
12
|
-
* - `QELOS_ACCESS_TOKEN_COOKIE` — defaults to `q_access_token`.
|
|
13
|
-
* - `QELOS_REFRESH_TOKEN_COOKIE` — defaults to `q_refresh_token`.
|
|
11
|
+
* - `QELOS_API_TOKEN` — service-to-service token (skips cookie identity).
|
|
14
12
|
* - `QELOS_REQUIRE_AUTH` — `"true"` to reject anonymous requests.
|
|
15
13
|
* - `QELOS_SKIP_PATHS` — comma-separated path prefixes to bypass.
|
|
14
|
+
* - `QELOS_DISABLE_PROXY` — `"true"` to disable auto `/api/` skip in middleware
|
|
15
|
+
* (when you are not using the BFF catch-all proxy).
|
|
16
16
|
*/
|
|
17
17
|
export declare function loadQelosConfigFromEnv(env?: NodeJS.ProcessEnv): QelosNextConfig;
|
|
18
18
|
/**
|