@zitadel/sdk-next 0.1.0-alpha.9 → 1.0.0-alpha.21

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.
Files changed (45) hide show
  1. package/README.md +134 -26
  2. package/dist/auth.d.ts +54 -13
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +154 -7
  5. package/dist/client.d.ts +23 -2
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/client.js +21 -9
  8. package/dist/context.d.ts +21 -10
  9. package/dist/context.d.ts.map +1 -0
  10. package/dist/context.js +24 -8
  11. package/dist/index.d.ts +16 -9
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +13 -24
  14. package/dist/jsx.d.ts +5 -0
  15. package/dist/lib/jwt.d.ts +9 -0
  16. package/dist/lib/jwt.d.ts.map +1 -0
  17. package/dist/lib/jwt.js +7 -0
  18. package/dist/middleware.d.ts +9 -10
  19. package/dist/middleware.d.ts.map +1 -0
  20. package/dist/middleware.js +294 -9
  21. package/dist/provider.d.ts +52 -0
  22. package/dist/provider.d.ts.map +1 -0
  23. package/dist/provider.js +75 -0
  24. package/dist/react.d.ts +26 -0
  25. package/dist/react.d.ts.map +1 -0
  26. package/dist/react.js +24 -0
  27. package/dist/server.d.ts +12 -2
  28. package/dist/server.d.ts.map +1 -0
  29. package/dist/server.js +10 -7
  30. package/dist/session.d.ts +61 -0
  31. package/dist/session.d.ts.map +1 -0
  32. package/dist/session.js +116 -0
  33. package/dist/types.d.ts +8 -3
  34. package/dist/types.d.ts.map +1 -0
  35. package/dist/types.js +1 -1
  36. package/dist/useAuth.d.ts +17 -5
  37. package/dist/useAuth.d.ts.map +1 -0
  38. package/dist/useAuth.js +18 -7
  39. package/package.json +28 -13
  40. package/dist/chunk-2BFQLJQE.js +0 -27
  41. package/dist/chunk-4KENHIG4.js +0 -222
  42. package/dist/chunk-6F4PWJZI.js +0 -0
  43. package/dist/chunk-B7S6XMT3.js +0 -41
  44. package/dist/chunk-OCZMYSFX.js +0 -13
  45. package/dist/chunk-XTCHTAIQ.js +0 -12
package/dist/index.js CHANGED
@@ -1,24 +1,13 @@
1
- import {
2
- createProxy,
3
- nextgenMiddleware
4
- } from "./chunk-4KENHIG4.js";
5
- import {
6
- auth
7
- } from "./chunk-B7S6XMT3.js";
8
- import "./chunk-OCZMYSFX.js";
9
- import "./chunk-6F4PWJZI.js";
10
- import {
11
- useAuth
12
- } from "./chunk-XTCHTAIQ.js";
13
- import {
14
- NextgenProvider,
15
- useAuthContext
16
- } from "./chunk-2BFQLJQE.js";
17
- export {
18
- NextgenProvider,
19
- auth,
20
- createProxy,
21
- nextgenMiddleware,
22
- useAuth,
23
- useAuthContext
24
- };
1
+ /**
2
+ * Package root — for **Server Components and server modules only**: it
3
+ * includes the server-only `auth()`, so importing it from a `"use client"`
4
+ * module fails at build time. Client components import from
5
+ * `@zitadel/sdk-next/react` (provider + hooks) or
6
+ * `@zitadel/sdk-next/session` (`getSession()`) instead.
7
+ */
8
+ export * from "./types.js";
9
+ export { nextgenMiddleware, createProxy } from "./middleware.js";
10
+ export { auth } from "./auth.js";
11
+ export { NextgenProvider } from "./provider.js";
12
+ export { AuthContextProvider, useAuthContext } from "./context.js";
13
+ export { useAuth } from "./useAuth.js";
package/dist/jsx.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ // Types-only shim: scaffolded apps depend on @zitadel/sdk-next, not
2
+ // @zitadel/components, so the React JSX declarations for the <zitadel-*>
3
+ // elements are re-exposed here as `@zitadel/sdk-next/jsx`. Copied verbatim
4
+ // into dist/ by the build script.
5
+ /// <reference types="@zitadel/components/jsx" />
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Re-exports shared JWT verification from `@zitadel/sdk-core`.
3
+ *
4
+ * The JWT module is runtime-agnostic (uses `atob` which is available in both
5
+ * Edge and Node.js 16+) and is defined once in sdk-core.
6
+ */
7
+ export { JWKS_TTL_MS, base64UrlDecode, decodeJwt, isJwtShaped, verifyJwt, } from "@zitadel/sdk-core/jwt";
8
+ export type { JwtPayload, JwtHeader, DecodedJwt, VerifyJwtOptions } from "@zitadel/sdk-core/jwt";
9
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/lib/jwt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,WAAW,EACX,eAAe,EACf,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Re-exports shared JWT verification from `@zitadel/sdk-core`.
3
+ *
4
+ * The JWT module is runtime-agnostic (uses `atob` which is available in both
5
+ * Edge and Node.js 16+) and is defined once in sdk-core.
6
+ */
7
+ export { JWKS_TTL_MS, base64UrlDecode, decodeJwt, isJwtShaped, verifyJwt, } from "@zitadel/sdk-core/jwt";
@@ -1,7 +1,7 @@
1
- import { ZitadelProject } from '@zitadel/api/config';
2
- import { NextgenMiddlewareOptions } from '@zitadel/sdk-core/types';
3
- import { NextRequest, NextResponse } from 'next/server';
4
-
1
+ import type { ZitadelProject } from "@zitadel/api/config";
2
+ import type { NextgenMiddlewareOptions } from "@zitadel/sdk-core/middleware";
3
+ import type { NextRequest } from "next/server";
4
+ import { NextResponse } from "next/server";
5
5
  /**
6
6
  * Next.js Edge middleware that handles proxying, JWT verification, and route
7
7
  * protection in a single pass.
@@ -29,7 +29,7 @@ import { NextRequest, NextResponse } from 'next/server';
29
29
  * @param options - Middleware configuration options.
30
30
  * @returns A `NextResponse` or `Response` to continue, redirect, or proxy.
31
31
  */
32
- declare function nextgenMiddleware(req: NextRequest, options?: NextgenMiddlewareOptions): Promise<NextResponse | Response>;
32
+ export declare function nextgenMiddleware(req: NextRequest, options?: NextgenMiddlewareOptions): Promise<NextResponse | Response>;
33
33
  /**
34
34
  * Options for {@link createProxy} that are separate from the shared
35
35
  * {@link ZitadelConfig}. These configure route protection, login
@@ -38,11 +38,11 @@ declare function nextgenMiddleware(req: NextRequest, options?: NextgenMiddleware
38
38
  * `proxyPath` and `url` are omitted because they come from the
39
39
  * {@link ZitadelConfig} passed as the first argument.
40
40
  */
41
- type ProxyOptions = Omit<NextgenMiddlewareOptions, 'proxyPath' | 'url'>;
41
+ export type ProxyOptions = Omit<NextgenMiddlewareOptions, "proxyPath" | "url">;
42
42
  /**
43
43
  * A pre-configured middleware handler returned by {@link createProxy}.
44
44
  */
45
- type ProxyHandler = (req: NextRequest) => Promise<NextResponse | Response>;
45
+ export type ProxyHandler = (req: NextRequest) => Promise<NextResponse | Response>;
46
46
  /**
47
47
  * Creates a pre-configured middleware handler from the SDK config
48
48
  * returned by `configureZitadel()`. This is the derived-service
@@ -75,6 +75,5 @@ type ProxyHandler = (req: NextRequest) => Promise<NextResponse | Response>;
75
75
  * @param options - Route protection and JWT options.
76
76
  * @returns A middleware handler function.
77
77
  */
78
- declare function createProxy(config: ZitadelProject, options?: ProxyOptions): ProxyHandler;
79
-
80
- export { type ProxyHandler, type ProxyOptions, createProxy, nextgenMiddleware };
78
+ export declare function createProxy(config: ZitadelProject, options?: ProxyOptions): ProxyHandler;
79
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmC3C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,WAAW,EAChB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAqDlC;AAqPD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,wBAAwB,EAAE,WAAW,GAAG,KAAK,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,YAAiB,GAAG,YAAY,CAO5F"}
@@ -1,9 +1,294 @@
1
- import {
2
- createProxy,
3
- nextgenMiddleware
4
- } from "./chunk-4KENHIG4.js";
5
- import "./chunk-OCZMYSFX.js";
6
- export {
7
- createProxy,
8
- nextgenMiddleware
9
- };
1
+ import { HOP_BY_HOP, INTERNAL_HEADERS, filterResponseHeaders, matchesRoutes, } from "@zitadel/sdk-core/middleware";
2
+ import { NextResponse } from "next/server";
3
+ import { verifyJwt, isJwtShaped } from "./lib/jwt.js";
4
+ /**
5
+ * Clones the incoming request headers, injects `extra` key/value pairs,
6
+ * and registers them with the Next.js header-tunnelling mechanism so that
7
+ * server components can read them via `headers()`.
8
+ *
9
+ * @param req - The incoming edge request.
10
+ * @param extra - Additional headers to inject (e.g. `x-nextgen-auth-token`).
11
+ * @returns A new `Headers` instance with the injected values registered.
12
+ */
13
+ function tunnelHeaders(req, extra) {
14
+ const headers = new Headers(req.headers);
15
+ // Security: strip any x-middleware-override-headers the client may have sent.
16
+ // Preserving the client value would allow an attacker to inject arbitrary
17
+ // header names into the Next.js override list, potentially bypassing the
18
+ // internal header-tunnelling safety checks. We always build this header from
19
+ // scratch using only the names we inject ourselves.
20
+ headers.delete("x-middleware-override-headers");
21
+ const injectedNames = [];
22
+ for (const [name, value] of Object.entries(extra)) {
23
+ headers.set(name, value);
24
+ headers.set(`x-middleware-request-${name}`, value);
25
+ injectedNames.push(name);
26
+ }
27
+ headers.set("x-middleware-override-headers", injectedNames.join(","));
28
+ return headers;
29
+ }
30
+ /**
31
+ * Next.js Edge middleware that handles proxying, JWT verification, and route
32
+ * protection in a single pass.
33
+ *
34
+ * Place this in your `middleware.ts` file:
35
+ *
36
+ * ```ts
37
+ * import { nextgenMiddleware } from "@zitadel/sdk-next/middleware";
38
+ * import type { NextRequest } from "next/server";
39
+ *
40
+ * export function middleware(req: NextRequest) {
41
+ * return nextgenMiddleware(req, {
42
+ * url: process.env.ZITADEL_URL,
43
+ * protectedRoutes: ["/profile"],
44
+ * loginPath: "/login",
45
+ * });
46
+ * }
47
+ *
48
+ * export const config = {
49
+ * matcher: ["/__nextgen/:path*", "/profile/:path*"],
50
+ * };
51
+ * ```
52
+ *
53
+ * @param req - The incoming Next.js edge request.
54
+ * @param options - Middleware configuration options.
55
+ * @returns A `NextResponse` or `Response` to continue, redirect, or proxy.
56
+ */
57
+ export async function nextgenMiddleware(req, options = {}) {
58
+ const { url = process.env.ZITADEL_URL ?? "http://localhost:8080", proxyPath = "/__nextgen", protectedRoutes = [], ignoredRoutes = [], loginPath = "/login", allowedAlgorithms = ["RS256", "ES256"], clockSkewMs = 5000, audience, allowedTokenTypes = ["JWT", "at+JWT"], jwksTimeoutMs, proxyTimeoutMs = 5000, opaqueTokenTimeoutMs = 5000, } = options;
59
+ // Guard against open-redirect: loginPath must be a relative path. An absolute
60
+ // URL (e.g. "https://evil.com/phish") or a protocol-relative URL
61
+ // (e.g. "//evil.com") would redirect the browser to an external host.
62
+ if (!loginPath.startsWith("/") || loginPath.startsWith("//")) {
63
+ throw new Error(`[nextgen] loginPath must be a relative path starting with a single "/". ` +
64
+ `Received: "${loginPath}". Using an absolute or protocol-relative URL ` +
65
+ `would allow open-redirect attacks.`);
66
+ }
67
+ const { pathname } = new URL(req.url);
68
+ if (matchesRoutes(pathname, ignoredRoutes)) {
69
+ // Neutralise any client-supplied x-nextgen-auth-token on ignored routes.
70
+ // handleAuth is skipped for ignored routes, so we must strip the header
71
+ // here to prevent a forged value from reaching server components.
72
+ const headers = tunnelHeaders(req, { "x-nextgen-auth-token": "" });
73
+ return NextResponse.next({ request: { headers } });
74
+ }
75
+ if (pathname === proxyPath || pathname.startsWith(`${proxyPath}/`)) {
76
+ return proxyRequest(req, url, proxyPath, proxyTimeoutMs);
77
+ }
78
+ return handleAuth(req, {
79
+ url,
80
+ protectedRoutes,
81
+ loginPath,
82
+ allowedAlgorithms,
83
+ clockSkewMs,
84
+ audience,
85
+ allowedTokenTypes,
86
+ jwksTimeoutMs,
87
+ opaqueTokenTimeoutMs,
88
+ pathname,
89
+ });
90
+ }
91
+ /**
92
+ * Validates an opaque (non-JWT) session token by calling the backend's
93
+ * `/sessions/me` endpoint. Returns `true` when the backend confirms the
94
+ * session is live (HTTP 200), `false` otherwise.
95
+ *
96
+ * This is the fallback path for backends that issue encrypted opaque tokens
97
+ * rather than self-contained JWTs.
98
+ */
99
+ async function validateOpaqueSessionToken(token, issuerUrl, timeoutMs) {
100
+ try {
101
+ const res = await fetch(`${issuerUrl}/sessions/me`, {
102
+ method: "GET",
103
+ headers: { cookie: `__nextgen_session=${token}` },
104
+ signal: AbortSignal.timeout(timeoutMs),
105
+ });
106
+ return res.ok;
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ /**
113
+ * Forwards a `/__nextgen/*` request to the upstream auth backend and streams
114
+ * the response back verbatim, stripping hop-by-hop headers in both directions.
115
+ *
116
+ * All non-hop-by-hop headers from the incoming request are forwarded, including
117
+ * any `X-Forwarded-For` chain already set by an upstream CDN or load balancer.
118
+ * When `X-Forwarded-For` is absent (direct connection with no upstream proxy),
119
+ * it is initialised from the runtime-provided client IP so the auth server
120
+ * always receives origin information. `X-Forwarded-Host` and `X-Forwarded-Proto`
121
+ * are set only when absent, preserving values injected by an upstream CDN.
122
+ *
123
+ * @param req - The incoming edge request.
124
+ * @param authUrl - Base URL of the auth backend.
125
+ * @param proxyPath - The path prefix being proxied (e.g. `"/__nextgen"`).
126
+ * @returns The proxied upstream `Response`.
127
+ */
128
+ async function proxyRequest(req, authUrl, proxyPath, proxyTimeoutMs) {
129
+ const url = new URL(req.url);
130
+ const suffix = url.pathname.slice(proxyPath.length);
131
+ const target = `${authUrl}${suffix}${url.search}`;
132
+ const upstreamHeaders = new Headers();
133
+ for (const [key, value] of req.headers.entries()) {
134
+ const lower = key.toLowerCase();
135
+ if (!HOP_BY_HOP.has(lower) && !INTERNAL_HEADERS.has(lower)) {
136
+ upstreamHeaders.set(key, value);
137
+ }
138
+ }
139
+ // Always append the direct client IP to the X-Forwarded-For chain so the
140
+ // upstream auth server sees the full proxy path. We never skip this even when
141
+ // a chain is already present — a CDN or load balancer may have set XFF before
142
+ // the request reached this edge node, and our hop must still be recorded.
143
+ const directIp = req.ip ?? req.headers.get("x-real-ip");
144
+ if (directIp) {
145
+ const existingXff = upstreamHeaders.get("x-forwarded-for");
146
+ upstreamHeaders.set("x-forwarded-for", existingXff ? `${existingXff}, ${directIp}` : directIp);
147
+ }
148
+ if (!upstreamHeaders.has("x-forwarded-host")) {
149
+ upstreamHeaders.set("x-forwarded-host", url.host);
150
+ }
151
+ if (!upstreamHeaders.has("x-forwarded-proto")) {
152
+ upstreamHeaders.set("x-forwarded-proto", url.protocol.replace(":", ""));
153
+ }
154
+ const hasBody = !["GET", "HEAD"].includes(req.method);
155
+ // ADR 036: the current browser flow needs the confidential project secret
156
+ // only for the handoff exchange. Stamping it onto every proxied operation
157
+ // turns this public request boundary into an operator-capable open relay.
158
+ // Preserve an explicit caller credential so the exchange can use the future
159
+ // publishable-key plane without being overwritten.
160
+ if (requiresProjectSecret(req.method, suffix) && !upstreamHeaders.has("authorization")) {
161
+ const secret = process.env.ZITADEL_PROJECT_SECRET;
162
+ if (secret) {
163
+ upstreamHeaders.set("authorization", `Bearer ${secret}`);
164
+ }
165
+ }
166
+ // Read body eagerly so undici receives a concrete buffer rather than a
167
+ // WinterCG ReadableStream, which is incompatible with Node.js fetch body
168
+ // extraction.
169
+ const bodyBuffer = hasBody ? await req.arrayBuffer() : undefined;
170
+ const upstream = await fetch(target, {
171
+ method: req.method,
172
+ headers: upstreamHeaders,
173
+ body: bodyBuffer,
174
+ redirect: "manual",
175
+ signal: AbortSignal.timeout(proxyTimeoutMs),
176
+ });
177
+ const responseHeaders = filterResponseHeaders(upstream.headers);
178
+ const setCookies = upstream.headers.getSetCookie?.() ?? [];
179
+ for (const cookie of setCookies) {
180
+ responseHeaders.append("set-cookie", cookie);
181
+ }
182
+ return new Response(upstream.body, {
183
+ status: upstream.status,
184
+ headers: responseHeaders,
185
+ });
186
+ }
187
+ function requiresProjectSecret(method, pathname) {
188
+ return method.toUpperCase() === "POST" && pathname === "/sessions/exchange";
189
+ }
190
+ /**
191
+ * Verifies the session token and tunnels the result to server components via
192
+ * a request header. Redirects to the login page when the token is absent or
193
+ * invalid on a protected route.
194
+ *
195
+ * @param req - The incoming edge request.
196
+ * @param opts - Auth handler options.
197
+ * @returns A `NextResponse` to continue or redirect.
198
+ */
199
+ async function handleAuth(req, opts) {
200
+ const { url, protectedRoutes, loginPath, allowedAlgorithms, clockSkewMs, audience, allowedTokenTypes, jwksTimeoutMs, opaqueTokenTimeoutMs, pathname, } = opts;
201
+ const authHeader = req.headers.get("authorization");
202
+ const bearerToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
203
+ const cookieToken = req.cookies.get("__nextgen_session")?.value ?? null;
204
+ // Bearer token takes explicit precedence over the session cookie. API clients
205
+ // (e.g. mobile apps, CLIs) use Authorization headers while browsers use
206
+ // cookies; when both are present the caller clearly intended the Bearer token.
207
+ const token = bearerToken ?? cookieToken;
208
+ const payload = token
209
+ ? await verifyJwt(token, {
210
+ issuerUrl: url,
211
+ allowedAlgorithms,
212
+ clockSkewMs,
213
+ audience,
214
+ allowedTokenTypes,
215
+ jwksTimeoutMs,
216
+ })
217
+ : null;
218
+ if (payload && token && payload.sub) {
219
+ const tunnelled = tunnelHeaders(req, { "x-nextgen-auth-token": token });
220
+ return NextResponse.next({ request: { headers: tunnelled } });
221
+ }
222
+ // The backend issues opaque encrypted session tokens rather than JWTs.
223
+ // Only fall back to /sessions/me validation when the token is definitively
224
+ // not a JWT (non-JSON segments). A token with a valid JWT structure that
225
+ // failed verification (bad sig, wrong typ/alg) must be rejected — never
226
+ // accepted by a backend call that doesn't re-check the JWT claims.
227
+ if (!payload && cookieToken && !isJwtShaped(cookieToken)) {
228
+ const isValid = await validateOpaqueSessionToken(cookieToken, url, opaqueTokenTimeoutMs);
229
+ if (isValid) {
230
+ const tunnelled = tunnelHeaders(req, {
231
+ "x-nextgen-auth-token": cookieToken,
232
+ });
233
+ return NextResponse.next({ request: { headers: tunnelled } });
234
+ }
235
+ }
236
+ const tunnelled = tunnelHeaders(req, { "x-nextgen-auth-token": "" });
237
+ const staleNextgenCookies = req.cookies
238
+ .getAll()
239
+ .filter((c) => c.name.startsWith("__nextgen"));
240
+ if (matchesRoutes(pathname, protectedRoutes)) {
241
+ const loginUrl = new URL(loginPath, req.url);
242
+ loginUrl.searchParams.set("next", pathname);
243
+ const redirect = NextResponse.redirect(loginUrl, { status: 302 });
244
+ for (const cookie of staleNextgenCookies) {
245
+ redirect.cookies.delete(cookie.name);
246
+ }
247
+ return redirect;
248
+ }
249
+ const response = NextResponse.next({ request: { headers: tunnelled } });
250
+ for (const cookie of staleNextgenCookies) {
251
+ response.cookies.delete(cookie.name);
252
+ }
253
+ return response;
254
+ }
255
+ /**
256
+ * Creates a pre-configured middleware handler from the SDK config
257
+ * returned by `configureZitadel()`. This is the derived-service
258
+ * derived service pattern:
259
+ *
260
+ * ```ts
261
+ * // src/zitadel.ts
262
+ * import { configureZitadel } from "@zitadel/api/config";
263
+ * import { createProxy } from "@zitadel/sdk-next/middleware";
264
+ *
265
+ * const zitadel = configureZitadel({
266
+ * projectId: "demo",
267
+ * url: process.env.ZITADEL_URL,
268
+ * });
269
+ *
270
+ * export const proxy = createProxy(zitadel, {
271
+ * protectedRoutes: ["/admin*"],
272
+ * loginPath: "/login",
273
+ * });
274
+ * ```
275
+ *
276
+ * Then in middleware.ts:
277
+ *
278
+ * ```ts
279
+ * import { proxy } from "./zitadel";
280
+ * export const middleware = proxy;
281
+ * ```
282
+ *
283
+ * @param config - The SDK handle from `configureZitadel()`.
284
+ * @param options - Route protection and JWT options.
285
+ * @returns A middleware handler function.
286
+ */
287
+ export function createProxy(config, options = {}) {
288
+ const mergedOptions = {
289
+ ...options,
290
+ proxyPath: config.proxyPath,
291
+ url: config.url,
292
+ };
293
+ return (req) => nextgenMiddleware(req, mergedOptions);
294
+ }
@@ -0,0 +1,52 @@
1
+ import "server-only";
2
+ import type { ReactNode } from "react";
3
+ import type { AuthResult, ClientAuthResult, ClientSession, NextgenSession } from "./types.js";
4
+ /**
5
+ * Seeds client components (the `useAuth()` hook) with the server-known auth
6
+ * state. Render it in a Server Component — typically the root layout — and
7
+ * pass the `auth()` result directly:
8
+ *
9
+ * ```tsx
10
+ * import { auth, NextgenProvider } from "@zitadel/sdk-next/server";
11
+ *
12
+ * export default async function RootLayout({ children }) {
13
+ * const session = await auth();
14
+ * return (
15
+ * <html>
16
+ * <body>
17
+ * <NextgenProvider session={session}>{children}</NextgenProvider>
18
+ * </body>
19
+ * </html>
20
+ * );
21
+ * }
22
+ * ```
23
+ *
24
+ * Executed in a Server Component, it converts the input to the client-safe
25
+ * {@link ClientAuthResult} — dropping the raw session token — and only the
26
+ * stripped value is serialised across the server→client boundary. Passing the
27
+ * full `auth()` result straight into a `"use client"` component would embed
28
+ * the raw token in the RSC flight payload, readable by any script running on
29
+ * the page. (sdk-nuxt strips the token before seeding client state for the
30
+ * same reason.)
31
+ *
32
+ * This module is **server-only, on purpose**. The strip only protects the
33
+ * boundary when it runs on the server: if this component could be imported
34
+ * into a `"use client"` wrapper (the common `providers.tsx` pattern), the
35
+ * *wrapper* would become the boundary and its still-unstripped `session`
36
+ * prop — token included — would serialise into the flight payload before
37
+ * this code ever ran. The `server-only` import turns that wrapper into a
38
+ * build error instead of a silent leak. For a client-seeded tree (e.g. state
39
+ * from `getSession()`), use `AuthContextProvider` from
40
+ * `@zitadel/sdk-next/react`, which only accepts the token-less
41
+ * {@link ClientAuthResult}.
42
+ *
43
+ * Client components therefore only ever see `userId` / `email` / `name`. When
44
+ * the raw token is needed server-side (e.g. to call an upstream API), read it
45
+ * from `auth()` in a Server Component or Route Handler — never through this
46
+ * provider.
47
+ */
48
+ export declare function NextgenProvider({ session, children, }: {
49
+ session: AuthResult | ClientAuthResult | ClientSession | NextgenSession | null;
50
+ children: ReactNode;
51
+ }): import("react/jsx-runtime").JSX.Element;
52
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,cAAc,EAEf,MAAM,YAAY,CAAC;AAmBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,UAAU,GAAG,gBAAgB,GAAG,aAAa,GAAG,cAAc,GAAG,IAAI,CAAC;IAC/E,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAaA"}
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import "server-only";
3
+ import { AuthContextProvider } from "./context.js";
4
+ const signedOut = { isAuthenticated: false, session: null };
5
+ /**
6
+ * Explicit field pick — never a spread. The raw session token (and any future
7
+ * server-only field) must not survive the conversion to the client shape.
8
+ */
9
+ function toClientSession(session) {
10
+ return {
11
+ userId: session.userId,
12
+ identifier: session.identifier,
13
+ identifierProperty: session.identifierProperty,
14
+ display: session.display,
15
+ };
16
+ }
17
+ /**
18
+ * Seeds client components (the `useAuth()` hook) with the server-known auth
19
+ * state. Render it in a Server Component — typically the root layout — and
20
+ * pass the `auth()` result directly:
21
+ *
22
+ * ```tsx
23
+ * import { auth, NextgenProvider } from "@zitadel/sdk-next/server";
24
+ *
25
+ * export default async function RootLayout({ children }) {
26
+ * const session = await auth();
27
+ * return (
28
+ * <html>
29
+ * <body>
30
+ * <NextgenProvider session={session}>{children}</NextgenProvider>
31
+ * </body>
32
+ * </html>
33
+ * );
34
+ * }
35
+ * ```
36
+ *
37
+ * Executed in a Server Component, it converts the input to the client-safe
38
+ * {@link ClientAuthResult} — dropping the raw session token — and only the
39
+ * stripped value is serialised across the server→client boundary. Passing the
40
+ * full `auth()` result straight into a `"use client"` component would embed
41
+ * the raw token in the RSC flight payload, readable by any script running on
42
+ * the page. (sdk-nuxt strips the token before seeding client state for the
43
+ * same reason.)
44
+ *
45
+ * This module is **server-only, on purpose**. The strip only protects the
46
+ * boundary when it runs on the server: if this component could be imported
47
+ * into a `"use client"` wrapper (the common `providers.tsx` pattern), the
48
+ * *wrapper* would become the boundary and its still-unstripped `session`
49
+ * prop — token included — would serialise into the flight payload before
50
+ * this code ever ran. The `server-only` import turns that wrapper into a
51
+ * build error instead of a silent leak. For a client-seeded tree (e.g. state
52
+ * from `getSession()`), use `AuthContextProvider` from
53
+ * `@zitadel/sdk-next/react`, which only accepts the token-less
54
+ * {@link ClientAuthResult}.
55
+ *
56
+ * Client components therefore only ever see `userId` / `email` / `name`. When
57
+ * the raw token is needed server-side (e.g. to call an upstream API), read it
58
+ * from `auth()` in a Server Component or Route Handler — never through this
59
+ * provider.
60
+ */
61
+ export function NextgenProvider({ session, children, }) {
62
+ let value;
63
+ if (!session) {
64
+ value = signedOut;
65
+ }
66
+ else if ("isAuthenticated" in session) {
67
+ value = session.isAuthenticated
68
+ ? { isAuthenticated: true, session: toClientSession(session.session) }
69
+ : signedOut;
70
+ }
71
+ else {
72
+ value = { isAuthenticated: true, session: toClientSession(session) };
73
+ }
74
+ return _jsx(AuthContextProvider, { value: value, children: children });
75
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Client-safe React bindings for `@zitadel/sdk-next`.
3
+ *
4
+ * This is the entry point for `"use client"` components. It pulls in no
5
+ * server-only modules (`next/headers`, `auth()`, `NextgenProvider`), so it is
6
+ * safe to import anywhere.
7
+ *
8
+ * ```tsx
9
+ * "use client";
10
+ * import { useAuth } from "@zitadel/sdk-next/react";
11
+ * ```
12
+ *
13
+ * `NextgenProvider` is deliberately **not** exported here: it accepts the
14
+ * token-bearing `auth()` result, so it must execute in a Server Component —
15
+ * wrapping it in a `"use client"` file would serialise the raw token across
16
+ * the boundary before the provider strips it. Import it from
17
+ * `@zitadel/sdk-next/server` (or the package root) instead; the `server-only`
18
+ * guard turns a client-side import into a build error. To seed the context
19
+ * from client-side state (e.g. a `getSession()` read), render
20
+ * {@link AuthContextProvider}, which only accepts the token-less
21
+ * `ClientAuthResult`.
22
+ */
23
+ export { AuthContextProvider, useAuthContext } from "./context.js";
24
+ export { useAuth } from "./useAuth.js";
25
+ export type { AuthResult, AuthState, ClientAuthResult, ClientAuthState, ClientSession, NextgenSession, UnauthState, } from "./types.js";
26
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,cAAc,EACd,WAAW,GACZ,MAAM,YAAY,CAAC"}
package/dist/react.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Client-safe React bindings for `@zitadel/sdk-next`.
3
+ *
4
+ * This is the entry point for `"use client"` components. It pulls in no
5
+ * server-only modules (`next/headers`, `auth()`, `NextgenProvider`), so it is
6
+ * safe to import anywhere.
7
+ *
8
+ * ```tsx
9
+ * "use client";
10
+ * import { useAuth } from "@zitadel/sdk-next/react";
11
+ * ```
12
+ *
13
+ * `NextgenProvider` is deliberately **not** exported here: it accepts the
14
+ * token-bearing `auth()` result, so it must execute in a Server Component —
15
+ * wrapping it in a `"use client"` file would serialise the raw token across
16
+ * the boundary before the provider strips it. Import it from
17
+ * `@zitadel/sdk-next/server` (or the package root) instead; the `server-only`
18
+ * guard turns a client-side import into a build error. To seed the context
19
+ * from client-side state (e.g. a `getSession()` read), render
20
+ * {@link AuthContextProvider}, which only accepts the token-less
21
+ * `ClientAuthResult`.
22
+ */
23
+ export { AuthContextProvider, useAuthContext } from "./context.js";
24
+ export { useAuth } from "./useAuth.js";
package/dist/server.d.ts CHANGED
@@ -1,2 +1,12 @@
1
- export { auth } from './auth.js';
2
- import '@zitadel/sdk-core/types';
1
+ /**
2
+ * Server-side surface of `@zitadel/sdk-next`: everything here is guarded by
3
+ * `server-only` and fails the build if pulled into a `"use client"` graph.
4
+ *
5
+ * ```tsx
6
+ * import { auth, NextgenProvider } from "@zitadel/sdk-next/server";
7
+ * ```
8
+ */
9
+ export { auth } from "./auth.js";
10
+ export type { AuthOptions } from "./auth.js";
11
+ export { NextgenProvider } from "./provider.js";
12
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
package/dist/server.js CHANGED
@@ -1,7 +1,10 @@
1
- import {
2
- auth
3
- } from "./chunk-B7S6XMT3.js";
4
- import "./chunk-OCZMYSFX.js";
5
- export {
6
- auth
7
- };
1
+ /**
2
+ * Server-side surface of `@zitadel/sdk-next`: everything here is guarded by
3
+ * `server-only` and fails the build if pulled into a `"use client"` graph.
4
+ *
5
+ * ```tsx
6
+ * import { auth, NextgenProvider } from "@zitadel/sdk-next/server";
7
+ * ```
8
+ */
9
+ export { auth } from "./auth.js";
10
+ export { NextgenProvider } from "./provider.js";