@zitadel/sdk-next 0.1.0-alpha.8 → 0.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 +207 -37
- package/dist/auth.d.ts +66 -13
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +162 -7
- package/dist/client.d.ts +27 -2
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +29 -9
- package/dist/components.d.ts +16 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +15 -0
- package/dist/context.d.ts +21 -10
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +24 -8
- package/dist/index.d.ts +16 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -24
- package/dist/jsx.d.ts +5 -0
- package/dist/lib/jwt.d.ts +9 -0
- package/dist/lib/jwt.d.ts.map +1 -0
- package/dist/lib/jwt.js +7 -0
- package/dist/middleware.d.ts +19 -11
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +304 -9
- package/dist/provider.d.ts +52 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +75 -0
- package/dist/react.d.ts +26 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +24 -0
- package/dist/server.d.ts +13 -2
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +10 -7
- package/dist/session.d.ts +77 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +156 -0
- package/dist/types.d.ts +8 -3
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -1
- package/dist/useAuth.d.ts +17 -5
- package/dist/useAuth.d.ts.map +1 -0
- package/dist/useAuth.js +18 -7
- package/package.json +46 -31
- package/dist/chunk-2BFQLJQE.js +0 -27
- package/dist/chunk-4KENHIG4.js +0 -222
- package/dist/chunk-6F4PWJZI.js +0 -0
- package/dist/chunk-B7S6XMT3.js +0 -41
- package/dist/chunk-OCZMYSFX.js +0 -13
- package/dist/chunk-XTCHTAIQ.js +0 -12
|
@@ -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"}
|
package/dist/provider.js
ADDED
|
@@ -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
|
+
}
|
package/dist/react.d.ts
ADDED
|
@@ -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,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
+
export type { AuthResult, AuthState, UnauthState, NextgenSession, ClientAuthResult, NextgenMiddlewareOptions, } from "./types.js";
|
|
13
|
+
//# 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;AAChD,YAAY,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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";
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
* The project this app signs its users in to. A session of another project
|
|
12
|
+
* reads as signed out. Defaults to the project from `configureZitadel()`
|
|
13
|
+
* when it has run on this page, else `NEXT_PUBLIC_ZITADEL_PROJECT_ID`. With
|
|
14
|
+
* none, nobody reads as signed in, as on the server.
|
|
15
|
+
*/
|
|
16
|
+
projectId?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Reads the current session state from the browser — the supported way for
|
|
20
|
+
* an app's own UI (header navigation, account menus) to know whether a user
|
|
21
|
+
* is signed in and who they are.
|
|
22
|
+
*
|
|
23
|
+
* Fetches the same-origin `{proxyPath}/sessions/me?project_id=…` with
|
|
24
|
+
* credentials and `cache: "no-store"` — the same read the `<zitadel-session>`
|
|
25
|
+
* card performs — so the answer is the server's, not a client-side guess.
|
|
26
|
+
* Works on any page: unlike server-side `auth()`, it does not require the
|
|
27
|
+
* route to be covered by the middleware `matcher` (only the proxy path itself
|
|
28
|
+
* must be matched, which the scaffolded request boundary always does).
|
|
29
|
+
*
|
|
30
|
+
* A session counts only in the app's own project: `options.projectId`, else
|
|
31
|
+
* the project of `configureZitadel()`, else `NEXT_PUBLIC_ZITADEL_PROJECT_ID`.
|
|
32
|
+
* With none of them, it reads as signed out without asking the server.
|
|
33
|
+
*
|
|
34
|
+
* - `200` with an authenticated user → `{ isAuthenticated: true, session }`
|
|
35
|
+
* with the client-safe identity (`userId`, `email`, `name` — no token).
|
|
36
|
+
* - `200` for an anonymous session, `401` with `auth.unauthorized`, `403`
|
|
37
|
+
* with `sess.project_mismatch`, or `404` with `sess.not_found` → signed out.
|
|
38
|
+
* The `403` answers a session of another project on the same server, and a
|
|
39
|
+
* `200` naming another project (from a server that predates the parameter)
|
|
40
|
+
* reads as signed out too.
|
|
41
|
+
* - Any other response throws — including a framework's HTML 404 page from
|
|
42
|
+
* a misrouted proxy: a failing proxy is a misconfiguration and must not
|
|
43
|
+
* silently render as "signed out". Treat a rejection as "unknown" —
|
|
44
|
+
* distinct from signed-out — as the example does.
|
|
45
|
+
*
|
|
46
|
+
* ```tsx
|
|
47
|
+
* "use client";
|
|
48
|
+
* import { getSession, type ClientAuthResult } from "@zitadel/sdk-next/session";
|
|
49
|
+
*
|
|
50
|
+
* export function HeaderNav() {
|
|
51
|
+
* // undefined = not yet known — render neutral chrome, not "Sign in".
|
|
52
|
+
* const [auth, setAuth] = useState<ClientAuthResult>();
|
|
53
|
+
* const [failed, setFailed] = useState(false);
|
|
54
|
+
* useEffect(() => {
|
|
55
|
+
* getSession({ projectId: process.env.NEXT_PUBLIC_ZITADEL_PROJECT_ID }).then(
|
|
56
|
+
* setAuth,
|
|
57
|
+
* () => setFailed(true),
|
|
58
|
+
* );
|
|
59
|
+
* }, []);
|
|
60
|
+
* if (failed) return <span role="alert">Session unavailable</span>;
|
|
61
|
+
* if (!auth) return null;
|
|
62
|
+
* return auth.isAuthenticated
|
|
63
|
+
* ? <a href="/profile">{auth.session.display ?? auth.session.identifier ?? "Account"}</a>
|
|
64
|
+
* : <a href="/login">Sign in</a>;
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* The transitions need no extra wiring in the scaffolded posture: sign-in
|
|
69
|
+
* (`post-sign-in-url`) and sign-out (`post-sign-out-url`) both navigate, so
|
|
70
|
+
* chrome re-reads on the next page load. To react in place instead, listen
|
|
71
|
+
* for the widgets' `zitadel-signout` / `zitadel-flow-complete` events.
|
|
72
|
+
*
|
|
73
|
+
* @returns The current {@link ClientAuthResult}.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getSession(options?: GetSessionOptions): Promise<ClientAuthResult>;
|
|
76
|
+
export type { ClientAuthResult, ClientAuthState, ClientSession } from "./types.js";
|
|
77
|
+
//# 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;IAEnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA8BF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqE3F;AAED,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
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
|
+
* `NEXT_PUBLIC_ZITADEL_PROJECT_ID`, which Next.js inlines into client bundles
|
|
6
|
+
* at build time. Read defensively: where it was not inlined, `process` may not
|
|
7
|
+
* exist in the browser.
|
|
8
|
+
*/
|
|
9
|
+
function publicProjectId() {
|
|
10
|
+
try {
|
|
11
|
+
return process.env.NEXT_PUBLIC_ZITADEL_PROJECT_ID || undefined;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
let warnedMissingProject = false;
|
|
18
|
+
/** Says once per page why `getSession()` signs no one in: no project is known. */
|
|
19
|
+
function warnMissingProject() {
|
|
20
|
+
if (warnedMissingProject) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
warnedMissingProject = true;
|
|
24
|
+
console.warn("[nextgen] getSession(): no project id is known, so no session counts as signed in. " +
|
|
25
|
+
"Pass `projectId` (e.g. process.env.NEXT_PUBLIC_ZITADEL_PROJECT_ID) or call " +
|
|
26
|
+
"configureZitadel() first.");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reads the current session state from the browser — the supported way for
|
|
30
|
+
* an app's own UI (header navigation, account menus) to know whether a user
|
|
31
|
+
* is signed in and who they are.
|
|
32
|
+
*
|
|
33
|
+
* Fetches the same-origin `{proxyPath}/sessions/me?project_id=…` with
|
|
34
|
+
* credentials and `cache: "no-store"` — the same read the `<zitadel-session>`
|
|
35
|
+
* card performs — so the answer is the server's, not a client-side guess.
|
|
36
|
+
* Works on any page: unlike server-side `auth()`, it does not require the
|
|
37
|
+
* route to be covered by the middleware `matcher` (only the proxy path itself
|
|
38
|
+
* must be matched, which the scaffolded request boundary always does).
|
|
39
|
+
*
|
|
40
|
+
* A session counts only in the app's own project: `options.projectId`, else
|
|
41
|
+
* the project of `configureZitadel()`, else `NEXT_PUBLIC_ZITADEL_PROJECT_ID`.
|
|
42
|
+
* With none of them, it reads as signed out without asking the server.
|
|
43
|
+
*
|
|
44
|
+
* - `200` with an authenticated user → `{ isAuthenticated: true, session }`
|
|
45
|
+
* with the client-safe identity (`userId`, `email`, `name` — no token).
|
|
46
|
+
* - `200` for an anonymous session, `401` with `auth.unauthorized`, `403`
|
|
47
|
+
* with `sess.project_mismatch`, or `404` with `sess.not_found` → signed out.
|
|
48
|
+
* The `403` answers a session of another project on the same server, and a
|
|
49
|
+
* `200` naming another project (from a server that predates the parameter)
|
|
50
|
+
* reads as signed out too.
|
|
51
|
+
* - Any other response throws — including a framework's HTML 404 page from
|
|
52
|
+
* a misrouted proxy: a failing proxy is a misconfiguration and must not
|
|
53
|
+
* silently render as "signed out". Treat a rejection as "unknown" —
|
|
54
|
+
* distinct from signed-out — as the example does.
|
|
55
|
+
*
|
|
56
|
+
* ```tsx
|
|
57
|
+
* "use client";
|
|
58
|
+
* import { getSession, type ClientAuthResult } from "@zitadel/sdk-next/session";
|
|
59
|
+
*
|
|
60
|
+
* export function HeaderNav() {
|
|
61
|
+
* // undefined = not yet known — render neutral chrome, not "Sign in".
|
|
62
|
+
* const [auth, setAuth] = useState<ClientAuthResult>();
|
|
63
|
+
* const [failed, setFailed] = useState(false);
|
|
64
|
+
* useEffect(() => {
|
|
65
|
+
* getSession({ projectId: process.env.NEXT_PUBLIC_ZITADEL_PROJECT_ID }).then(
|
|
66
|
+
* setAuth,
|
|
67
|
+
* () => setFailed(true),
|
|
68
|
+
* );
|
|
69
|
+
* }, []);
|
|
70
|
+
* if (failed) return <span role="alert">Session unavailable</span>;
|
|
71
|
+
* if (!auth) return null;
|
|
72
|
+
* return auth.isAuthenticated
|
|
73
|
+
* ? <a href="/profile">{auth.session.display ?? auth.session.identifier ?? "Account"}</a>
|
|
74
|
+
* : <a href="/login">Sign in</a>;
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* The transitions need no extra wiring in the scaffolded posture: sign-in
|
|
79
|
+
* (`post-sign-in-url`) and sign-out (`post-sign-out-url`) both navigate, so
|
|
80
|
+
* chrome re-reads on the next page load. To react in place instead, listen
|
|
81
|
+
* for the widgets' `zitadel-signout` / `zitadel-flow-complete` events.
|
|
82
|
+
*
|
|
83
|
+
* @returns The current {@link ClientAuthResult}.
|
|
84
|
+
*/
|
|
85
|
+
export async function getSession(options = {}) {
|
|
86
|
+
if (typeof window === "undefined") {
|
|
87
|
+
throw new Error("[nextgen] getSession() reads the session from the browser. " +
|
|
88
|
+
"In Server Components and Route Handlers use auth() from @zitadel/sdk-next/server " +
|
|
89
|
+
"(requires the route to be covered by the middleware matcher).");
|
|
90
|
+
}
|
|
91
|
+
// Strip trailing slashes so "/__nextgen/" doesn't produce a double-slash
|
|
92
|
+
// URL that misses the request-boundary matcher — same normalization the
|
|
93
|
+
// typed API client applies to its base URL.
|
|
94
|
+
let proxyPath = options.proxyPath ?? getZitadelConfig()?.proxyPath ?? DEFAULT_PROXY_PATH;
|
|
95
|
+
while (proxyPath.endsWith("/")) {
|
|
96
|
+
proxyPath = proxyPath.slice(0, -1);
|
|
97
|
+
}
|
|
98
|
+
const projectId = options.projectId || getZitadelConfig()?.projectId || publicProjectId();
|
|
99
|
+
if (!projectId) {
|
|
100
|
+
warnMissingProject();
|
|
101
|
+
return { isAuthenticated: false, session: null };
|
|
102
|
+
}
|
|
103
|
+
const response = await fetch(`${proxyPath}/sessions/me?project_id=${encodeURIComponent(projectId)}`, {
|
|
104
|
+
cache: "no-store",
|
|
105
|
+
credentials: "include",
|
|
106
|
+
headers: { accept: "application/json" },
|
|
107
|
+
});
|
|
108
|
+
// Only the backend's canonical error envelope is definitive signed-out.
|
|
109
|
+
// A framework route, gateway, or WAF can also return 401/404; accepting the
|
|
110
|
+
// status or content type alone would turn a broken proxy into signed-out.
|
|
111
|
+
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
|
112
|
+
const error = await readErrorEnvelope(response);
|
|
113
|
+
const isSignedOut = (response.status === 401 && error?.code === "auth.unauthorized") ||
|
|
114
|
+
(response.status === 403 && error?.code === "sess.project_mismatch") ||
|
|
115
|
+
(response.status === 404 && error?.code === "sess.not_found");
|
|
116
|
+
if (isSignedOut) {
|
|
117
|
+
return { isAuthenticated: false, session: null };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(`[nextgen] Session read failed: HTTP ${response.status} from ${proxyPath}/sessions/me`);
|
|
122
|
+
}
|
|
123
|
+
const session = (await response.json());
|
|
124
|
+
// An anonymous session (no verified user factor yet) has no user_id, and a
|
|
125
|
+
// backend that predates `project_id` answers for any project: for app
|
|
126
|
+
// chrome, either is "not signed in".
|
|
127
|
+
if (!session.user_id || session.project_id !== projectId) {
|
|
128
|
+
return { isAuthenticated: false, session: null };
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
isAuthenticated: true,
|
|
132
|
+
session: {
|
|
133
|
+
userId: session.user_id,
|
|
134
|
+
identifier: session.user?.identifier ?? null,
|
|
135
|
+
identifierProperty: session.user?.identifier_property ?? null,
|
|
136
|
+
display: session.user?.display ?? null,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function readErrorEnvelope(response) {
|
|
141
|
+
try {
|
|
142
|
+
const body = (await response.json());
|
|
143
|
+
if (typeof body === "object" &&
|
|
144
|
+
body !== null &&
|
|
145
|
+
"code" in body &&
|
|
146
|
+
typeof body.code === "string" &&
|
|
147
|
+
"message" in body &&
|
|
148
|
+
typeof body.message === "string") {
|
|
149
|
+
return { code: body.code, message: body.message };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// The caller reports the HTTP failure below; parsing details are untrusted.
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
1
|
+
export {};
|
package/dist/useAuth.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
3
|
+
"version": "0.1.0",
|
|
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,12 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"type": "module",
|
|
19
|
+
"sideEffects": [
|
|
20
|
+
"./dist/client.js",
|
|
21
|
+
"./dist/components.js",
|
|
22
|
+
"./dist/auth.js",
|
|
23
|
+
"./dist/provider.js"
|
|
24
|
+
],
|
|
19
25
|
"exports": {
|
|
20
26
|
".": {
|
|
21
27
|
"types": "./dist/index.d.ts",
|
|
@@ -29,54 +35,63 @@
|
|
|
29
35
|
"types": "./dist/client.d.ts",
|
|
30
36
|
"import": "./dist/client.js"
|
|
31
37
|
},
|
|
38
|
+
"./components": {
|
|
39
|
+
"types": "./dist/components.d.ts",
|
|
40
|
+
"import": "./dist/components.js"
|
|
41
|
+
},
|
|
42
|
+
"./react": {
|
|
43
|
+
"types": "./dist/react.d.ts",
|
|
44
|
+
"import": "./dist/react.js"
|
|
45
|
+
},
|
|
32
46
|
"./middleware": {
|
|
33
47
|
"types": "./dist/middleware.d.ts",
|
|
34
48
|
"import": "./dist/middleware.js"
|
|
49
|
+
},
|
|
50
|
+
"./jsx": {
|
|
51
|
+
"types": "./dist/jsx.d.ts"
|
|
52
|
+
},
|
|
53
|
+
"./session": {
|
|
54
|
+
"types": "./dist/session.d.ts",
|
|
55
|
+
"import": "./dist/session.js"
|
|
35
56
|
}
|
|
36
57
|
},
|
|
37
58
|
"publishConfig": {
|
|
38
59
|
"access": "public"
|
|
39
60
|
},
|
|
40
61
|
"dependencies": {
|
|
62
|
+
"@zitadel/api": "0.1.0",
|
|
63
|
+
"@zitadel/components": "0.1.0",
|
|
64
|
+
"@zitadel/sdk-core": "0.1.0",
|
|
65
|
+
"@zitadel/sdk-react": "0.1.0",
|
|
41
66
|
"server-only": "^0.0.1",
|
|
42
|
-
"
|
|
43
|
-
"@zitadel/sdk-core": "0.1.0-alpha.8",
|
|
44
|
-
"@zitadel/components": "0.1.0-alpha.8"
|
|
67
|
+
"ufo": "^1.6.4"
|
|
45
68
|
},
|
|
46
69
|
"peerDependencies": {
|
|
47
|
-
"next": ">=
|
|
70
|
+
"next": ">=15.2.3",
|
|
48
71
|
"react": ">=18",
|
|
49
72
|
"react-dom": ">=18"
|
|
50
73
|
},
|
|
51
74
|
"devDependencies": {
|
|
52
|
-
"@
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"@
|
|
56
|
-
"@eslint/markdown": "^6.0.0",
|
|
57
|
-
"@testing-library/react": "^16.0.0",
|
|
58
|
-
"@testing-library/user-event": "^14.0.0",
|
|
75
|
+
"@edge-runtime/vm": "^5.0.0",
|
|
76
|
+
"@testing-library/react": "^16.3.2",
|
|
77
|
+
"@testing-library/user-event": "^14.6.1",
|
|
78
|
+
"@types/node": "^24.19.1",
|
|
59
79
|
"@types/react": "^19.2.14",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"eslint-plugin-react-hooks": "^5.0.0",
|
|
69
|
-
"eslint-plugin-testing-library": "^7.0.0",
|
|
70
|
-
"jsdom": "^26.0.0",
|
|
71
|
-
"next": "^16.2.4",
|
|
72
|
-
"prettier": "^3.0.0",
|
|
73
|
-
"typescript-eslint": "^8.0.0",
|
|
74
|
-
"vitest": "^3.0.0"
|
|
80
|
+
"@types/react-dom": "^19.2.3",
|
|
81
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
82
|
+
"jsdom": "^29.0.2",
|
|
83
|
+
"next": "^16.3.8",
|
|
84
|
+
"react": "^19.2.8",
|
|
85
|
+
"react-dom": "^19.2.8",
|
|
86
|
+
"typescript": "^6.0.3",
|
|
87
|
+
"vitest": "^4.1.11"
|
|
75
88
|
},
|
|
76
89
|
"scripts": {
|
|
77
|
-
"build": "
|
|
78
|
-
"typecheck": "tsc --noEmit",
|
|
79
|
-
"test": "vitest run
|
|
80
|
-
"
|
|
90
|
+
"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')\"",
|
|
91
|
+
"typecheck": "tsc --noEmit -p tsconfig.lib.json && tsc --noEmit -p tsconfig.spec.json",
|
|
92
|
+
"test": "vitest run",
|
|
93
|
+
"test:all": "pnpm run test",
|
|
94
|
+
"lint": "biome check .",
|
|
95
|
+
"build-release": "node ../../scripts/release-clean.mjs local-dist && pnpm run build"
|
|
81
96
|
}
|
|
82
97
|
}
|