@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
@@ -0,0 +1,61 @@
1
+ import type { ClientAuthResult } from "./types.js";
2
+ /** Options for {@link getSession}. */
3
+ export type GetSessionOptions = {
4
+ /**
5
+ * Proxy path the scaffolded request boundary forwards to the Zitadel
6
+ * backend. Defaults to the path from `configureZitadel()` when it has run
7
+ * on this page, else `"/__nextgen"` (the scaffold default).
8
+ */
9
+ proxyPath?: string;
10
+ };
11
+ /**
12
+ * Reads the current session state from the browser — the supported way for
13
+ * an app's own UI (header navigation, account menus) to know whether a user
14
+ * is signed in and who they are.
15
+ *
16
+ * Fetches the same-origin `{proxyPath}/sessions/me` with credentials and
17
+ * `cache: "no-store"` — the same read the `<zitadel-session>` card performs —
18
+ * so the answer is the server's, not a client-side guess. Works on any page:
19
+ * unlike server-side `auth()`, it does not require the route to be covered by
20
+ * the middleware `matcher` (only the proxy path itself must be matched, which
21
+ * the scaffolded request boundary always does), and it does not require
22
+ * `configureZitadel()` to have run.
23
+ *
24
+ * - `200` with an authenticated user → `{ isAuthenticated: true, session }`
25
+ * with the client-safe identity (`userId`, `email`, `name` — no token).
26
+ * - `200` for an anonymous session, `401` with `auth.unauthorized`, or `404`
27
+ * with `sess.not_found` → signed out.
28
+ * - Any other response throws — including a framework's HTML 404 page from
29
+ * a misrouted proxy: a failing proxy is a misconfiguration and must not
30
+ * silently render as "signed out". Treat a rejection as "unknown" —
31
+ * distinct from signed-out — as the example does.
32
+ *
33
+ * ```tsx
34
+ * "use client";
35
+ * import { getSession, type ClientAuthResult } from "@zitadel/sdk-next/session";
36
+ *
37
+ * export function HeaderNav() {
38
+ * // undefined = not yet known — render neutral chrome, not "Sign in".
39
+ * const [auth, setAuth] = useState<ClientAuthResult>();
40
+ * const [failed, setFailed] = useState(false);
41
+ * useEffect(() => {
42
+ * getSession().then(setAuth, () => setFailed(true));
43
+ * }, []);
44
+ * if (failed) return <span role="alert">Session unavailable</span>;
45
+ * if (!auth) return null;
46
+ * return auth.isAuthenticated
47
+ * ? <a href="/profile">{auth.session.display ?? auth.session.identifier ?? "Account"}</a>
48
+ * : <a href="/login">Sign in</a>;
49
+ * }
50
+ * ```
51
+ *
52
+ * The transitions need no extra wiring in the scaffolded posture: sign-in
53
+ * (`post-sign-in-url`) and sign-out (`post-sign-out-url`) both navigate, so
54
+ * chrome re-reads on the next page load. To react in place instead, listen
55
+ * for the widgets' `zitadel-signout` / `zitadel-flow-complete` events.
56
+ *
57
+ * @returns The current {@link ClientAuthResult}.
58
+ */
59
+ export declare function getSession(options?: GetSessionOptions): Promise<ClientAuthResult>;
60
+ export type { ClientAuthResult, ClientAuthState, ClientSession } from "./types.js";
61
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,sCAAsC;AACtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA2D3F;AAED,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { getZitadelConfig } from "@zitadel/api/config";
2
+ /** Matches the `configureZitadel()` default so zero-config apps agree with the scaffold. */
3
+ const DEFAULT_PROXY_PATH = "/__nextgen";
4
+ /**
5
+ * Reads the current session state from the browser — the supported way for
6
+ * an app's own UI (header navigation, account menus) to know whether a user
7
+ * is signed in and who they are.
8
+ *
9
+ * Fetches the same-origin `{proxyPath}/sessions/me` with credentials and
10
+ * `cache: "no-store"` — the same read the `<zitadel-session>` card performs —
11
+ * so the answer is the server's, not a client-side guess. Works on any page:
12
+ * unlike server-side `auth()`, it does not require the route to be covered by
13
+ * the middleware `matcher` (only the proxy path itself must be matched, which
14
+ * the scaffolded request boundary always does), and it does not require
15
+ * `configureZitadel()` to have run.
16
+ *
17
+ * - `200` with an authenticated user → `{ isAuthenticated: true, session }`
18
+ * with the client-safe identity (`userId`, `email`, `name` — no token).
19
+ * - `200` for an anonymous session, `401` with `auth.unauthorized`, or `404`
20
+ * with `sess.not_found` → signed out.
21
+ * - Any other response throws — including a framework's HTML 404 page from
22
+ * a misrouted proxy: a failing proxy is a misconfiguration and must not
23
+ * silently render as "signed out". Treat a rejection as "unknown" —
24
+ * distinct from signed-out — as the example does.
25
+ *
26
+ * ```tsx
27
+ * "use client";
28
+ * import { getSession, type ClientAuthResult } from "@zitadel/sdk-next/session";
29
+ *
30
+ * export function HeaderNav() {
31
+ * // undefined = not yet known — render neutral chrome, not "Sign in".
32
+ * const [auth, setAuth] = useState<ClientAuthResult>();
33
+ * const [failed, setFailed] = useState(false);
34
+ * useEffect(() => {
35
+ * getSession().then(setAuth, () => setFailed(true));
36
+ * }, []);
37
+ * if (failed) return <span role="alert">Session unavailable</span>;
38
+ * if (!auth) return null;
39
+ * return auth.isAuthenticated
40
+ * ? <a href="/profile">{auth.session.display ?? auth.session.identifier ?? "Account"}</a>
41
+ * : <a href="/login">Sign in</a>;
42
+ * }
43
+ * ```
44
+ *
45
+ * The transitions need no extra wiring in the scaffolded posture: sign-in
46
+ * (`post-sign-in-url`) and sign-out (`post-sign-out-url`) both navigate, so
47
+ * chrome re-reads on the next page load. To react in place instead, listen
48
+ * for the widgets' `zitadel-signout` / `zitadel-flow-complete` events.
49
+ *
50
+ * @returns The current {@link ClientAuthResult}.
51
+ */
52
+ export async function getSession(options = {}) {
53
+ if (typeof window === "undefined") {
54
+ throw new Error("[nextgen] getSession() reads the session from the browser. " +
55
+ "In Server Components and Route Handlers use auth() from @zitadel/sdk-next/server " +
56
+ "(requires the route to be covered by the middleware matcher).");
57
+ }
58
+ // Strip trailing slashes so "/__nextgen/" doesn't produce a double-slash
59
+ // URL that misses the request-boundary matcher — same normalization the
60
+ // typed API client applies to its base URL.
61
+ let proxyPath = options.proxyPath ?? getZitadelConfig()?.proxyPath ?? DEFAULT_PROXY_PATH;
62
+ while (proxyPath.endsWith("/")) {
63
+ proxyPath = proxyPath.slice(0, -1);
64
+ }
65
+ const response = await fetch(`${proxyPath}/sessions/me`, {
66
+ cache: "no-store",
67
+ credentials: "include",
68
+ headers: { accept: "application/json" },
69
+ });
70
+ // Only the backend's canonical error envelope is definitive signed-out.
71
+ // A framework route, gateway, or WAF can also return 401/404; accepting the
72
+ // status or content type alone would turn a broken proxy into signed-out.
73
+ if (response.status === 401 || response.status === 404) {
74
+ const error = await readErrorEnvelope(response);
75
+ const isSignedOut = (response.status === 401 && error?.code === "auth.unauthorized") ||
76
+ (response.status === 404 && error?.code === "sess.not_found");
77
+ if (isSignedOut) {
78
+ return { isAuthenticated: false, session: null };
79
+ }
80
+ }
81
+ if (!response.ok) {
82
+ throw new Error(`[nextgen] Session read failed: HTTP ${response.status} from ${proxyPath}/sessions/me`);
83
+ }
84
+ const session = (await response.json());
85
+ // An anonymous session (no verified user factor yet) has no user_id —
86
+ // for app chrome that is "not signed in".
87
+ if (!session.user_id) {
88
+ return { isAuthenticated: false, session: null };
89
+ }
90
+ return {
91
+ isAuthenticated: true,
92
+ session: {
93
+ userId: session.user_id,
94
+ identifier: session.user?.identifier ?? null,
95
+ identifierProperty: session.user?.identifier_property ?? null,
96
+ display: session.user?.display ?? null,
97
+ },
98
+ };
99
+ }
100
+ async function readErrorEnvelope(response) {
101
+ try {
102
+ const body = (await response.json());
103
+ if (typeof body === "object" &&
104
+ body !== null &&
105
+ "code" in body &&
106
+ typeof body.code === "string" &&
107
+ "message" in body &&
108
+ typeof body.message === "string") {
109
+ return { code: body.code, message: body.message };
110
+ }
111
+ }
112
+ catch {
113
+ // The caller reports the HTTP failure below; parsing details are untrusted.
114
+ }
115
+ return undefined;
116
+ }
package/dist/types.d.ts CHANGED
@@ -1,3 +1,8 @@
1
- export { AuthResult, AuthState, NextgenMiddlewareOptions, NextgenSession, UnauthState } from '@zitadel/sdk-core/types';
2
- import '@zitadel/components';
3
- import '@zitadel/api/config';
1
+ /**
2
+ * Re-exports shared SDK types from `@zitadel/sdk-core`.
3
+ *
4
+ * These types are defined once in sdk-core and shared by both sdk-next
5
+ * and sdk-nuxt.
6
+ */
7
+ export type { NextgenSession, AuthState, UnauthState, AuthResult, NextgenMiddlewareOptions, ClientSession, ClientAuthState, ClientAuthResult, } from "@zitadel/sdk-core/middleware";
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,YAAY,EACV,cAAc,EACd,SAAS,EACT,WAAW,EACX,UAAU,EACV,wBAAwB,EAGxB,aAAa,EACb,eAAe,EACf,gBAAgB,GACjB,MAAM,8BAA8B,CAAC"}
package/dist/types.js CHANGED
@@ -1 +1 @@
1
- import "./chunk-6F4PWJZI.js";
1
+ export {};
package/dist/useAuth.d.ts CHANGED
@@ -1,5 +1,17 @@
1
- import { AuthResult } from '@zitadel/sdk-core/types';
2
-
3
- declare function useAuth(): AuthResult;
4
-
5
- export { useAuth };
1
+ import type { ClientAuthResult } from "./types.js";
2
+ /**
3
+ * Reads the auth state seeded by {@link NextgenProvider} in a client
4
+ * component.
5
+ *
6
+ * Returns the client-safe {@link ClientAuthResult} (`userId` / `email` /
7
+ * `name`) — the raw session token is stripped server-side by the provider
8
+ * and is never available client-side. This is the same shape sdk-nuxt's
9
+ * `useAuth()` and `getSession()` return.
10
+ *
11
+ * The value reflects what the server knew when the page rendered. For chrome
12
+ * on pages outside the middleware `matcher` (where `auth()` reports signed
13
+ * out), read the session live with `getSession()` from
14
+ * `@zitadel/sdk-next/session` instead.
15
+ */
16
+ export declare function useAuth(): ClientAuthResult;
17
+ //# sourceMappingURL=useAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,IAAI,gBAAgB,CAE1C"}
package/dist/useAuth.js CHANGED
@@ -1,8 +1,19 @@
1
1
  "use client";
2
- import {
3
- useAuth
4
- } from "./chunk-XTCHTAIQ.js";
5
- import "./chunk-2BFQLJQE.js";
6
- export {
7
- useAuth
8
- };
2
+ import { useAuthContext } from "./context.js";
3
+ /**
4
+ * Reads the auth state seeded by {@link NextgenProvider} in a client
5
+ * component.
6
+ *
7
+ * Returns the client-safe {@link ClientAuthResult} (`userId` / `email` /
8
+ * `name`) — the raw session token is stripped server-side by the provider
9
+ * and is never available client-side. This is the same shape sdk-nuxt's
10
+ * `useAuth()` and `getSession()` return.
11
+ *
12
+ * The value reflects what the server knew when the page rendered. For chrome
13
+ * on pages outside the middleware `matcher` (where `auth()` reports signed
14
+ * out), read the session live with `getSession()` from
15
+ * `@zitadel/sdk-next/session` instead.
16
+ */
17
+ export function useAuth() {
18
+ return useAuthContext();
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zitadel/sdk-next",
3
- "version": "0.1.0-alpha.9",
3
+ "version": "1.0.0-alpha.21",
4
4
  "description": "Next.js helpers and mock auth UI for Zitadel",
5
5
  "homepage": "https://github.com/zitadel/nextgen/tree/main/packages/sdk-next#readme",
6
6
  "bugs": {
@@ -16,6 +16,11 @@
16
16
  "dist"
17
17
  ],
18
18
  "type": "module",
19
+ "sideEffects": [
20
+ "./dist/client.js",
21
+ "./dist/auth.js",
22
+ "./dist/provider.js"
23
+ ],
19
24
  "exports": {
20
25
  ".": {
21
26
  "types": "./dist/index.d.ts",
@@ -29,9 +34,20 @@
29
34
  "types": "./dist/client.d.ts",
30
35
  "import": "./dist/client.js"
31
36
  },
37
+ "./react": {
38
+ "types": "./dist/react.d.ts",
39
+ "import": "./dist/react.js"
40
+ },
32
41
  "./middleware": {
33
42
  "types": "./dist/middleware.d.ts",
34
43
  "import": "./dist/middleware.js"
44
+ },
45
+ "./jsx": {
46
+ "types": "./dist/jsx.d.ts"
47
+ },
48
+ "./session": {
49
+ "types": "./dist/session.d.ts",
50
+ "import": "./dist/session.js"
35
51
  }
36
52
  },
37
53
  "publishConfig": {
@@ -39,23 +55,21 @@
39
55
  },
40
56
  "dependencies": {
41
57
  "server-only": "^0.0.1",
42
- "@zitadel/api": "0.1.0-alpha.9",
43
- "@zitadel/components": "0.1.0-alpha.9",
44
- "@zitadel/sdk-core": "0.1.0-alpha.9"
58
+ "@zitadel/sdk-core": "1.0.0-alpha.21",
59
+ "@zitadel/api": "1.0.0-alpha.21",
60
+ "@zitadel/components": "1.0.0-alpha.21"
45
61
  },
46
62
  "peerDependencies": {
47
- "next": ">=14",
63
+ "next": ">=15",
48
64
  "react": ">=18",
49
65
  "react-dom": ">=18"
50
66
  },
51
67
  "devDependencies": {
52
68
  "@eslint/js": "^9.0.0",
53
- "tsup": "^8.3.5",
54
- "typescript": "^5.7.3",
55
69
  "@eslint/json": "^0.11.0",
56
70
  "@eslint/markdown": "^6.0.0",
57
- "@testing-library/react": "^16.0.0",
58
- "@testing-library/user-event": "^14.0.0",
71
+ "@testing-library/react": "^16.3.2",
72
+ "@testing-library/user-event": "^14.6.1",
59
73
  "@types/react": "^19.2.14",
60
74
  "eslint": "^9.0.0",
61
75
  "eslint-config-prettier": "^10.0.0",
@@ -67,15 +81,16 @@
67
81
  "eslint-plugin-react": "^7.37.0",
68
82
  "eslint-plugin-react-hooks": "^5.0.0",
69
83
  "eslint-plugin-testing-library": "^7.0.0",
70
- "jsdom": "^26.0.0",
71
- "next": "^16.2.4",
84
+ "jsdom": "^29.0.2",
85
+ "next": "^16.2.11",
72
86
  "prettier": "^3.0.0",
87
+ "typescript": "^5.7.3",
73
88
  "typescript-eslint": "^8.0.0",
74
89
  "vitest": "^3.0.0"
75
90
  },
76
91
  "scripts": {
77
- "build": "tsup src/middleware.ts src/auth.ts src/context.tsx src/useAuth.ts src/types.ts src/index.ts src/server.ts src/client.ts --format esm --dts --clean --tsconfig tsconfig.build.json",
78
- "typecheck": "tsc --noEmit",
92
+ "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json && node -e \"require('node:fs').copyFileSync('src/jsx.d.ts', 'dist/jsx.d.ts')\"",
93
+ "typecheck": "tsc --build tsconfig.json",
79
94
  "test": "vitest run --passWithNoTests",
80
95
  "lint": "eslint ."
81
96
  }
@@ -1,27 +0,0 @@
1
- // src/context.tsx
2
- import { createContext, useContext } from "react";
3
- import { jsx } from "react/jsx-runtime";
4
- var defaultValue = { isAuthenticated: false, session: null };
5
- var NextgenAuthContext = createContext(defaultValue);
6
- function NextgenProvider({
7
- session,
8
- children
9
- }) {
10
- let value;
11
- if (!session) {
12
- value = { isAuthenticated: false, session: null };
13
- } else if ("isAuthenticated" in session) {
14
- value = session;
15
- } else {
16
- value = { isAuthenticated: true, session };
17
- }
18
- return /* @__PURE__ */ jsx(NextgenAuthContext.Provider, { value, children });
19
- }
20
- function useAuthContext() {
21
- return useContext(NextgenAuthContext);
22
- }
23
-
24
- export {
25
- NextgenProvider,
26
- useAuthContext
27
- };
@@ -1,222 +0,0 @@
1
- import {
2
- base64UrlDecode,
3
- verifyJwt
4
- } from "./chunk-OCZMYSFX.js";
5
-
6
- // src/middleware.ts
7
- import {
8
- HOP_BY_HOP,
9
- INTERNAL_HEADERS,
10
- filterResponseHeaders,
11
- matchesRoutes
12
- } from "@zitadel/sdk-core/middleware";
13
- import { NextResponse } from "next/server";
14
- function tunnelHeaders(req, extra) {
15
- const headers = new Headers(req.headers);
16
- headers.delete("x-middleware-override-headers");
17
- const injectedNames = [];
18
- for (const [name, value] of Object.entries(extra)) {
19
- headers.set(name, value);
20
- headers.set(`x-middleware-request-${name}`, value);
21
- injectedNames.push(name);
22
- }
23
- headers.set("x-middleware-override-headers", injectedNames.join(","));
24
- return headers;
25
- }
26
- async function nextgenMiddleware(req, options = {}) {
27
- const {
28
- url = process.env.ZITADEL_URL ?? "http://localhost:8080",
29
- proxyPath = "/__nextgen",
30
- protectedRoutes = [],
31
- ignoredRoutes = [],
32
- loginPath = "/login",
33
- allowedAlgorithms = ["RS256", "ES256"],
34
- clockSkewMs = 5e3,
35
- audience,
36
- allowedTokenTypes = ["JWT", "at+JWT"],
37
- jwksTimeoutMs,
38
- proxyTimeoutMs = 5e3,
39
- onExchangeResponse
40
- } = options;
41
- if (!loginPath.startsWith("/") || loginPath.startsWith("//")) {
42
- throw new Error(
43
- `[nextgen] loginPath must be a relative path starting with a single "/". Received: "${loginPath}". Using an absolute or protocol-relative URL would allow open-redirect attacks.`
44
- );
45
- }
46
- const { pathname } = new URL(req.url);
47
- if (matchesRoutes(pathname, ignoredRoutes)) {
48
- const headers = tunnelHeaders(req, { "x-nextgen-auth-token": "" });
49
- return NextResponse.next({ request: { headers } });
50
- }
51
- if (pathname === proxyPath || pathname.startsWith(`${proxyPath}/`)) {
52
- return proxyRequest(
53
- req,
54
- url,
55
- proxyPath,
56
- proxyTimeoutMs,
57
- onExchangeResponse
58
- );
59
- }
60
- return handleAuth(req, {
61
- url,
62
- protectedRoutes,
63
- loginPath,
64
- allowedAlgorithms,
65
- clockSkewMs,
66
- audience,
67
- allowedTokenTypes,
68
- jwksTimeoutMs,
69
- pathname
70
- });
71
- }
72
- var DECODER = new TextDecoder();
73
- function isJwtShaped(token) {
74
- const parts = token.split(".");
75
- if (parts.length < 3 || !parts[0]) return false;
76
- try {
77
- const header = JSON.parse(
78
- DECODER.decode(base64UrlDecode(parts[0]))
79
- );
80
- return typeof header?.alg === "string" && !("enc" in header);
81
- } catch {
82
- return false;
83
- }
84
- }
85
- async function validateOpaqueSessionToken(token, issuerUrl, timeoutMs) {
86
- try {
87
- const res = await fetch(`${issuerUrl}/sessions/me`, {
88
- method: "GET",
89
- headers: { cookie: `__nextgen_session=${token}` },
90
- signal: AbortSignal.timeout(timeoutMs)
91
- });
92
- return res.ok;
93
- } catch {
94
- return false;
95
- }
96
- }
97
- async function proxyRequest(req, authUrl, proxyPath, proxyTimeoutMs, onExchangeResponse) {
98
- const url = new URL(req.url);
99
- const suffix = url.pathname.slice(proxyPath.length);
100
- const target = `${authUrl}${suffix}${url.search}`;
101
- const upstreamHeaders = new Headers();
102
- for (const [key, value] of req.headers.entries()) {
103
- const lower = key.toLowerCase();
104
- if (!HOP_BY_HOP.has(lower) && !INTERNAL_HEADERS.has(lower)) {
105
- upstreamHeaders.set(key, value);
106
- }
107
- }
108
- const directIp = req.ip ?? req.headers.get("x-real-ip");
109
- if (directIp) {
110
- const existingXff = upstreamHeaders.get("x-forwarded-for");
111
- upstreamHeaders.set(
112
- "x-forwarded-for",
113
- existingXff ? `${existingXff}, ${directIp}` : directIp
114
- );
115
- }
116
- if (!upstreamHeaders.has("x-forwarded-host")) {
117
- upstreamHeaders.set("x-forwarded-host", url.host);
118
- }
119
- if (!upstreamHeaders.has("x-forwarded-proto")) {
120
- upstreamHeaders.set("x-forwarded-proto", url.protocol.replace(":", ""));
121
- }
122
- const hasBody = !["GET", "HEAD"].includes(req.method);
123
- const isExchangeRequest = req.method === "POST" && suffix.startsWith("/sessions/exchange");
124
- if (isExchangeRequest && !upstreamHeaders.has("authorization")) {
125
- const projectId = url.searchParams.get("project_id");
126
- if (projectId) {
127
- upstreamHeaders.set("authorization", `Bearer sk_${projectId}`);
128
- }
129
- }
130
- const bodyBuffer = hasBody ? await req.arrayBuffer() : void 0;
131
- const upstream = await fetch(target, {
132
- method: req.method,
133
- headers: upstreamHeaders,
134
- body: bodyBuffer,
135
- redirect: "manual",
136
- signal: AbortSignal.timeout(proxyTimeoutMs)
137
- });
138
- const responseHeaders = filterResponseHeaders(upstream.headers);
139
- const setCookies = upstream.headers.getSetCookie?.() ?? [];
140
- for (const cookie of setCookies) {
141
- responseHeaders.append("set-cookie", cookie);
142
- }
143
- let response = new Response(upstream.body, {
144
- status: upstream.status,
145
- headers: responseHeaders
146
- });
147
- if (isExchangeRequest && onExchangeResponse) {
148
- response = await onExchangeResponse(response);
149
- }
150
- return response;
151
- }
152
- async function handleAuth(req, opts) {
153
- const {
154
- url,
155
- protectedRoutes,
156
- loginPath,
157
- allowedAlgorithms,
158
- clockSkewMs,
159
- audience,
160
- allowedTokenTypes,
161
- jwksTimeoutMs,
162
- pathname
163
- } = opts;
164
- const authHeader = req.headers.get("authorization");
165
- const bearerToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
166
- const cookieToken = req.cookies.get("__nextgen_session")?.value ?? null;
167
- const token = bearerToken ?? cookieToken;
168
- const payload = token ? await verifyJwt(token, {
169
- issuerUrl: url,
170
- allowedAlgorithms,
171
- clockSkewMs,
172
- audience,
173
- allowedTokenTypes,
174
- jwksTimeoutMs
175
- }) : null;
176
- if (payload && token && payload.sub) {
177
- const tunnelled2 = tunnelHeaders(req, { "x-nextgen-auth-token": token });
178
- return NextResponse.next({ request: { headers: tunnelled2 } });
179
- }
180
- if (!payload && cookieToken && !isJwtShaped(cookieToken)) {
181
- const isValid = await validateOpaqueSessionToken(
182
- cookieToken,
183
- url,
184
- jwksTimeoutMs ?? 5e3
185
- );
186
- if (isValid) {
187
- const tunnelled2 = tunnelHeaders(req, {
188
- "x-nextgen-auth-token": cookieToken
189
- });
190
- return NextResponse.next({ request: { headers: tunnelled2 } });
191
- }
192
- }
193
- const tunnelled = tunnelHeaders(req, { "x-nextgen-auth-token": "" });
194
- const staleNextgenCookies = req.cookies.getAll().filter((c) => c.name.startsWith("__nextgen"));
195
- if (matchesRoutes(pathname, protectedRoutes)) {
196
- const loginUrl = new URL(loginPath, req.url);
197
- loginUrl.searchParams.set("next", pathname);
198
- const redirect = NextResponse.redirect(loginUrl, { status: 302 });
199
- for (const cookie of staleNextgenCookies) {
200
- redirect.cookies.delete(cookie.name);
201
- }
202
- return redirect;
203
- }
204
- const response = NextResponse.next({ request: { headers: tunnelled } });
205
- for (const cookie of staleNextgenCookies) {
206
- response.cookies.delete(cookie.name);
207
- }
208
- return response;
209
- }
210
- function createProxy(config, options = {}) {
211
- const mergedOptions = {
212
- ...options,
213
- proxyPath: config.proxyPath,
214
- url: config.url
215
- };
216
- return (req) => nextgenMiddleware(req, mergedOptions);
217
- }
218
-
219
- export {
220
- nextgenMiddleware,
221
- createProxy
222
- };
File without changes
@@ -1,41 +0,0 @@
1
- import {
2
- decodeJwt
3
- } from "./chunk-OCZMYSFX.js";
4
-
5
- // src/auth.ts
6
- import { headers } from "next/headers";
7
- async function auth() {
8
- const headerStore = await headers();
9
- const token = headerStore.get("x-nextgen-auth-token");
10
- if (!token) {
11
- return { isAuthenticated: false, session: null };
12
- }
13
- try {
14
- const { payload } = decodeJwt(token);
15
- if (payload.sub) {
16
- return {
17
- isAuthenticated: true,
18
- session: {
19
- userId: payload.sub,
20
- email: payload.email ?? null,
21
- name: payload.name ?? null,
22
- token
23
- }
24
- };
25
- }
26
- } catch {
27
- }
28
- return {
29
- isAuthenticated: true,
30
- session: {
31
- userId: "unknown",
32
- email: null,
33
- name: null,
34
- token
35
- }
36
- };
37
- }
38
-
39
- export {
40
- auth
41
- };
@@ -1,13 +0,0 @@
1
- // src/lib/jwt.ts
2
- import {
3
- JWKS_TTL_MS,
4
- base64UrlDecode,
5
- decodeJwt,
6
- verifyJwt
7
- } from "@zitadel/sdk-core/jwt";
8
-
9
- export {
10
- base64UrlDecode,
11
- decodeJwt,
12
- verifyJwt
13
- };
@@ -1,12 +0,0 @@
1
- import {
2
- useAuthContext
3
- } from "./chunk-2BFQLJQE.js";
4
-
5
- // src/useAuth.ts
6
- function useAuth() {
7
- return useAuthContext();
8
- }
9
-
10
- export {
11
- useAuth
12
- };