@zitadel/sdk-nuxt 0.0.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/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/chunk-J2P7YNGG.js +199 -0
- package/dist/chunk-ZNXAFGLP.js +0 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +21 -0
- package/dist/module.js +40 -0
- package/dist/runtime/composables/useAuth.js +9 -0
- package/dist/runtime/plugin.js +35 -0
- package/dist/runtime/server/handler.d.ts +11 -0
- package/dist/runtime/server/handler.js +14 -0
- package/dist/runtime/server/middleware.d.ts +49 -0
- package/dist/runtime/server/middleware.js +8 -0
- package/dist/runtime/types.d.ts +37 -0
- package/dist/runtime/types.js +0 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +9 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ZITADEL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @zitadel/sdk-nuxt
|
|
2
|
+
|
|
3
|
+
Nuxt middleware and helpers for Nextgen Auth.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @zitadel/sdk-nuxt
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
### 1. Server middleware
|
|
14
|
+
|
|
15
|
+
Create `server/middleware/auth.ts`:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createNextgenMiddleware } from '@zitadel/sdk-nuxt/server';
|
|
19
|
+
|
|
20
|
+
const { nextgen } = useRuntimeConfig();
|
|
21
|
+
|
|
22
|
+
export default createNextgenMiddleware({
|
|
23
|
+
url: nextgen.url,
|
|
24
|
+
protectedRoutes: ['/admin', '/dashboard*'],
|
|
25
|
+
loginPath: '/login',
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The middleware runs on every request and does three things in one pass:
|
|
30
|
+
|
|
31
|
+
1. **Proxies** `/__nextgen/*` requests to the auth backend
|
|
32
|
+
2. **Verifies** the session JWT via JWKS using the Web Crypto API
|
|
33
|
+
3. **Redirects** unauthenticated requests to `loginPath` for protected routes
|
|
34
|
+
|
|
35
|
+
Add the issuer URL to `nuxt.config.ts`:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
export default defineNuxtConfig({
|
|
39
|
+
runtimeConfig: {
|
|
40
|
+
nextgen: {
|
|
41
|
+
url: process.env.ZITADEL_URL ?? 'http://localhost:4000',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Plugin
|
|
48
|
+
|
|
49
|
+
Create `plugins/auth.server.ts` to make auth state available in pages:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { defineNuxtPlugin, useRequestEvent, useState } from '#imports';
|
|
53
|
+
|
|
54
|
+
export default defineNuxtPlugin(() => {
|
|
55
|
+
const event = useRequestEvent();
|
|
56
|
+
const auth = event?.context.nextgenAuth ?? {
|
|
57
|
+
isAuthenticated: false as const,
|
|
58
|
+
session: null,
|
|
59
|
+
};
|
|
60
|
+
useState('nextgen-auth', () => auth);
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. Reading auth in a page
|
|
65
|
+
|
|
66
|
+
```vue
|
|
67
|
+
<script setup lang="ts">
|
|
68
|
+
const auth = useState('nextgen-auth');
|
|
69
|
+
if (auth.value?.isAuthenticated) {
|
|
70
|
+
await navigateTo('/admin');
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<p>{{ auth.isAuthenticated ? auth.session.email : 'Not signed in' }}</p>
|
|
76
|
+
</template>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 4. Register components (client only)
|
|
80
|
+
|
|
81
|
+
Create `plugins/zitadel-components.client.ts` — do **not** import
|
|
82
|
+
`@zitadel/components` from page `<script setup>` (that runs during SSR):
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import "@zitadel/components";
|
|
86
|
+
|
|
87
|
+
export default defineNuxtPlugin(() => {});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Set `body { margin: 0; font-family: sans-serif; }` in `app.vue` (see
|
|
91
|
+
[`apps/demo-nuxt`](../../apps/demo-nuxt/README.md)). Arimo loads from
|
|
92
|
+
`branding.font_url` inside `<zitadel-login>` when the mock/API supplies it.
|
|
93
|
+
|
|
94
|
+
### 5. Login page
|
|
95
|
+
|
|
96
|
+
Render `<zitadel-login>` inside `<ClientOnly>`:
|
|
97
|
+
|
|
98
|
+
```vue
|
|
99
|
+
<template>
|
|
100
|
+
<main>
|
|
101
|
+
<ClientOnly>
|
|
102
|
+
<zitadel-login api-base="/__nextgen" project-id="demo" post-sign-in-url="/admin" />
|
|
103
|
+
</ClientOnly>
|
|
104
|
+
</main>
|
|
105
|
+
</template>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 6. Reading auth in a server route
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { getAuth } from '@zitadel/sdk-nuxt/server';
|
|
112
|
+
|
|
113
|
+
export default defineEventHandler((event) => {
|
|
114
|
+
const auth = getAuth(event);
|
|
115
|
+
if (!auth.isAuthenticated) throw createError({ statusCode: 401 });
|
|
116
|
+
return { userId: auth.session.userId };
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Middleware options
|
|
121
|
+
|
|
122
|
+
| Option | Type | Default | Description |
|
|
123
|
+
| ------------------- | -------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
|
|
124
|
+
| `url` | `string` | `ZITADEL_URL` env | Full URL of the Zitadel auth backend |
|
|
125
|
+
| `proxyPath` | `string` | `"/__nextgen"` | Path prefix proxied to the auth backend |
|
|
126
|
+
| `protectedRoutes` | `string[]` | `[]` | Paths requiring a valid session. Trailing `*` matches sub-paths |
|
|
127
|
+
| `ignoredRoutes` | `string[]` | `[]` | Paths skipped entirely — no JWT check, no tunnelling. Useful for webhooks or health checks. Trailing `*` matches sub-paths |
|
|
128
|
+
| `loginPath` | `string` | `"/login"` | Where to redirect unauthenticated users |
|
|
129
|
+
| `allowedAlgorithms` | `string[]` | `["RS256", "ES256"]` | JWT `alg` values to accept. Tokens with any other algorithm are rejected before JWKS is fetched |
|
|
130
|
+
| `allowedTokenTypes` | `string[]` | `["JWT", "at+JWT"]` | Accepted `typ` header values (case-insensitive). Set to `[]` to disable this check |
|
|
131
|
+
| `clockSkewMs` | `number` | `5000` | Clock skew tolerance in ms for `exp`, `nbf`, `iat` |
|
|
132
|
+
| `jwksTimeoutMs` | `number` | `5000` | Timeout in ms for JWKS endpoint requests. Token is rejected if the fetch exceeds this window |
|
|
133
|
+
| `audience` | `string \| string[]` | not validated | Expected `aud` claim value(s). When omitted, audience is not checked |
|
|
134
|
+
|
|
135
|
+
## How JWT verification works
|
|
136
|
+
|
|
137
|
+
1. Bearer token from `Authorization` header is checked first; `__nextgen_session` cookie is the fallback
|
|
138
|
+
2. The JWT header is decoded to extract `kid` and `alg`
|
|
139
|
+
3. Tokens with an `alg` not in `allowedAlgorithms` (`RS256`, `ES256` by default) are rejected immediately — no JWKS fetch
|
|
140
|
+
4. Tokens with a `typ` not in `allowedTokenTypes` are rejected immediately
|
|
141
|
+
5. The public key is fetched from `{url}/auth/keys` (JWKS) using the Web Crypto API, with a 5 s timeout, and cached for 5 minutes per `kid`
|
|
142
|
+
6. The signature is verified **before** any claim checks
|
|
143
|
+
7. `iss` must be present and must equal `url` — tokens without an issuer are rejected
|
|
144
|
+
8. `exp` must be present and must be in the future (with `clockSkewMs` tolerance) — tokens without an expiry are rejected
|
|
145
|
+
9. `nbf` and `iat` are validated with `clockSkewMs` tolerance when present
|
|
146
|
+
10. The `x-nextgen-auth-token` header is stripped from all proxied requests to prevent internal state leakage
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// src/runtime/server/middleware.ts
|
|
2
|
+
import {
|
|
3
|
+
HOP_BY_HOP,
|
|
4
|
+
INTERNAL_HEADERS,
|
|
5
|
+
filterResponseHeaders,
|
|
6
|
+
matchesRoutes
|
|
7
|
+
} from "@zitadel/sdk-core/middleware";
|
|
8
|
+
import {
|
|
9
|
+
defineEventHandler,
|
|
10
|
+
getCookie,
|
|
11
|
+
parseCookies,
|
|
12
|
+
deleteCookie,
|
|
13
|
+
sendRedirect,
|
|
14
|
+
getRequestURL,
|
|
15
|
+
getRequestHeader,
|
|
16
|
+
readRawBody
|
|
17
|
+
} from "h3";
|
|
18
|
+
|
|
19
|
+
// src/runtime/lib/jwt.ts
|
|
20
|
+
import {
|
|
21
|
+
JWKS_TTL_MS,
|
|
22
|
+
base64UrlDecode,
|
|
23
|
+
decodeJwt,
|
|
24
|
+
verifyJwt
|
|
25
|
+
} from "@zitadel/sdk-core/jwt";
|
|
26
|
+
|
|
27
|
+
// src/runtime/server/middleware.ts
|
|
28
|
+
function buildUpstreamHeaders(event) {
|
|
29
|
+
const headers = new Headers();
|
|
30
|
+
for (const [key, value] of Object.entries(event.node.req.headers)) {
|
|
31
|
+
const lower = key.toLowerCase();
|
|
32
|
+
if (!value || HOP_BY_HOP.has(lower) || INTERNAL_HEADERS.has(lower)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
headers.set(key, Array.isArray(value) ? value.join(", ") : value);
|
|
36
|
+
}
|
|
37
|
+
const url = getRequestURL(event);
|
|
38
|
+
const socketIp = event.node.req.socket?.remoteAddress;
|
|
39
|
+
if (socketIp) {
|
|
40
|
+
const existingXff = headers.get("x-forwarded-for");
|
|
41
|
+
headers.set(
|
|
42
|
+
"x-forwarded-for",
|
|
43
|
+
existingXff ? `${existingXff}, ${socketIp}` : socketIp
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (!headers.has("x-forwarded-host")) {
|
|
47
|
+
headers.set("x-forwarded-host", url.host);
|
|
48
|
+
}
|
|
49
|
+
if (!headers.has("x-forwarded-proto")) {
|
|
50
|
+
headers.set("x-forwarded-proto", url.protocol.replace(":", ""));
|
|
51
|
+
}
|
|
52
|
+
return headers;
|
|
53
|
+
}
|
|
54
|
+
function createNextgenMiddleware(options = {}) {
|
|
55
|
+
const {
|
|
56
|
+
url = process.env.ZITADEL_URL ?? "http://localhost:8080",
|
|
57
|
+
proxyPath = "/__nextgen",
|
|
58
|
+
protectedRoutes = [],
|
|
59
|
+
ignoredRoutes = [],
|
|
60
|
+
loginPath = "/login",
|
|
61
|
+
allowedAlgorithms = ["RS256", "ES256"],
|
|
62
|
+
clockSkewMs = 5e3,
|
|
63
|
+
audience,
|
|
64
|
+
allowedTokenTypes = ["JWT", "at+JWT"],
|
|
65
|
+
jwksTimeoutMs,
|
|
66
|
+
proxyTimeoutMs = 5e3,
|
|
67
|
+
onExchangeResponse
|
|
68
|
+
} = options;
|
|
69
|
+
if (!loginPath.startsWith("/") || loginPath.startsWith("//")) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`[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.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return defineEventHandler(async (event) => {
|
|
75
|
+
const urlObj = getRequestURL(event);
|
|
76
|
+
const { pathname } = urlObj;
|
|
77
|
+
if (matchesRoutes(pathname, ignoredRoutes)) {
|
|
78
|
+
delete event.node.req.headers["x-nextgen-auth-token"];
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (pathname === proxyPath || pathname.startsWith(`${proxyPath}/`)) {
|
|
82
|
+
return proxyRequest(
|
|
83
|
+
event,
|
|
84
|
+
url,
|
|
85
|
+
proxyPath,
|
|
86
|
+
urlObj,
|
|
87
|
+
proxyTimeoutMs,
|
|
88
|
+
onExchangeResponse
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return handleAuth(event, {
|
|
92
|
+
url,
|
|
93
|
+
protectedRoutes,
|
|
94
|
+
loginPath,
|
|
95
|
+
allowedAlgorithms,
|
|
96
|
+
clockSkewMs,
|
|
97
|
+
audience,
|
|
98
|
+
allowedTokenTypes,
|
|
99
|
+
jwksTimeoutMs,
|
|
100
|
+
pathname
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async function proxyRequest(event, authUrl, proxyPath, url, proxyTimeoutMs, onExchangeResponse) {
|
|
105
|
+
const suffix = url.pathname.slice(proxyPath.length);
|
|
106
|
+
const target = `${authUrl}${suffix}${url.search}`;
|
|
107
|
+
const method = event.node.req.method ?? "GET";
|
|
108
|
+
const hasBody = !["GET", "HEAD"].includes(method);
|
|
109
|
+
const rawBody = hasBody ? await readRawBody(event, false) : void 0;
|
|
110
|
+
const body = rawBody != null ? new Uint8Array(rawBody) : void 0;
|
|
111
|
+
const upstream = await fetch(target, {
|
|
112
|
+
method,
|
|
113
|
+
headers: buildUpstreamHeaders(event),
|
|
114
|
+
body,
|
|
115
|
+
redirect: "manual",
|
|
116
|
+
signal: AbortSignal.timeout(proxyTimeoutMs)
|
|
117
|
+
});
|
|
118
|
+
const responseHeaders = filterResponseHeaders(upstream.headers);
|
|
119
|
+
const setCookieHeaders = upstream.headers.getSetCookie?.() ?? [];
|
|
120
|
+
for (const cookie of setCookieHeaders) {
|
|
121
|
+
responseHeaders.append("set-cookie", cookie);
|
|
122
|
+
}
|
|
123
|
+
let response = new Response(upstream.body, {
|
|
124
|
+
status: upstream.status,
|
|
125
|
+
headers: responseHeaders
|
|
126
|
+
});
|
|
127
|
+
const isExchange = method === "POST" && suffix.startsWith("/sessions/exchange");
|
|
128
|
+
if (isExchange && onExchangeResponse) {
|
|
129
|
+
response = await onExchangeResponse(response);
|
|
130
|
+
}
|
|
131
|
+
event.node.res.statusCode = response.status;
|
|
132
|
+
for (const [key, value] of response.headers.entries()) {
|
|
133
|
+
if (key.toLowerCase() === "set-cookie") continue;
|
|
134
|
+
event.node.res.setHeader(key, value);
|
|
135
|
+
}
|
|
136
|
+
const finalCookies = response.headers.getSetCookie?.() ?? [];
|
|
137
|
+
for (const cookie of finalCookies) {
|
|
138
|
+
event.node.res.appendHeader("set-cookie", cookie);
|
|
139
|
+
}
|
|
140
|
+
return response.body;
|
|
141
|
+
}
|
|
142
|
+
async function handleAuth(event, opts) {
|
|
143
|
+
const {
|
|
144
|
+
url,
|
|
145
|
+
protectedRoutes,
|
|
146
|
+
loginPath,
|
|
147
|
+
allowedAlgorithms,
|
|
148
|
+
clockSkewMs,
|
|
149
|
+
audience,
|
|
150
|
+
allowedTokenTypes,
|
|
151
|
+
jwksTimeoutMs,
|
|
152
|
+
pathname
|
|
153
|
+
} = opts;
|
|
154
|
+
delete event.node.req.headers["x-nextgen-auth-token"];
|
|
155
|
+
const authHeader = getRequestHeader(event, "authorization");
|
|
156
|
+
const bearerToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
|
|
157
|
+
const cookieToken = getCookie(event, "__nextgen_session") ?? null;
|
|
158
|
+
const token = bearerToken ?? cookieToken;
|
|
159
|
+
const payload = token ? await verifyJwt(token, {
|
|
160
|
+
issuerUrl: url,
|
|
161
|
+
allowedAlgorithms,
|
|
162
|
+
clockSkewMs,
|
|
163
|
+
audience,
|
|
164
|
+
allowedTokenTypes,
|
|
165
|
+
jwksTimeoutMs
|
|
166
|
+
}) : null;
|
|
167
|
+
if (payload && token && payload.sub) {
|
|
168
|
+
event.context.nextgenAuth = {
|
|
169
|
+
isAuthenticated: true,
|
|
170
|
+
session: {
|
|
171
|
+
userId: payload.sub,
|
|
172
|
+
email: payload.email ?? null,
|
|
173
|
+
name: payload.name ?? null,
|
|
174
|
+
token
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
event.context.nextgenAuth = { isAuthenticated: false, session: null };
|
|
180
|
+
for (const name of Object.keys(parseCookies(event))) {
|
|
181
|
+
if (name.startsWith("__nextgen")) {
|
|
182
|
+
deleteCookie(event, name);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (matchesRoutes(pathname, protectedRoutes)) {
|
|
186
|
+
const loginUrl = new URL(loginPath, getRequestURL(event));
|
|
187
|
+
loginUrl.searchParams.set("next", pathname);
|
|
188
|
+
await sendRedirect(event, loginUrl.toString(), 302);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function getAuth(event) {
|
|
193
|
+
return event.context.nextgenAuth ?? { isAuthenticated: false, session: null };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export {
|
|
197
|
+
createNextgenMiddleware,
|
|
198
|
+
getAuth
|
|
199
|
+
};
|
|
File without changes
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export { ZitadelLogin, ZitadelLogout } from '@zitadel/components';
|
|
2
|
+
export { createNextgenMiddleware, getAuth } from './runtime/server/middleware.js';
|
|
3
|
+
import { ZitadelProject } from '@zitadel/api/config';
|
|
4
|
+
export { ClientAuthResult } from './runtime/types.js';
|
|
5
|
+
export { AuthResult, NextgenMiddlewareOptions } from '@zitadel/sdk-core/types';
|
|
6
|
+
import 'h3';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns the current Zitadel project handle.
|
|
10
|
+
*
|
|
11
|
+
* Reads from the global config set by `configureZitadel()` in the
|
|
12
|
+
* Nextgen Nuxt plugin. Pass the returned handle to web components
|
|
13
|
+
* via the `:project` prop for explicit data flow.
|
|
14
|
+
*
|
|
15
|
+
* ```vue
|
|
16
|
+
* <script setup lang="ts">
|
|
17
|
+
* import { useZitadelProject } from '@zitadel/sdk-nuxt';
|
|
18
|
+
*
|
|
19
|
+
* const project = useZitadelProject();
|
|
20
|
+
* </script>
|
|
21
|
+
*
|
|
22
|
+
* <template>
|
|
23
|
+
* <zitadel-login :project="project" post-sign-in-url="/admin" />
|
|
24
|
+
* </template>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @returns The current {@link ZitadelProject}, or `null` if the plugin
|
|
28
|
+
* has not been configured (e.g. missing `projectId`).
|
|
29
|
+
*/
|
|
30
|
+
declare function useZitadelProject(): ZitadelProject | null;
|
|
31
|
+
|
|
32
|
+
export { useZitadelProject };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "./chunk-ZNXAFGLP.js";
|
|
2
|
+
import {
|
|
3
|
+
createNextgenMiddleware,
|
|
4
|
+
getAuth
|
|
5
|
+
} from "./chunk-J2P7YNGG.js";
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
import { ZitadelLogin, ZitadelLogout } from "@zitadel/components";
|
|
9
|
+
|
|
10
|
+
// src/runtime/composables/useZitadelProject.ts
|
|
11
|
+
import { getZitadelConfig } from "@zitadel/api/config";
|
|
12
|
+
function useZitadelProject() {
|
|
13
|
+
return getZitadelConfig();
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
ZitadelLogin,
|
|
17
|
+
ZitadelLogout,
|
|
18
|
+
createNextgenMiddleware,
|
|
19
|
+
getAuth,
|
|
20
|
+
useZitadelProject
|
|
21
|
+
};
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/module.ts
|
|
2
|
+
import {
|
|
3
|
+
defineNuxtModule,
|
|
4
|
+
addPlugin,
|
|
5
|
+
addServerHandler,
|
|
6
|
+
addImportsDir,
|
|
7
|
+
createResolver
|
|
8
|
+
} from "@nuxt/kit";
|
|
9
|
+
var module_default = defineNuxtModule({
|
|
10
|
+
meta: {
|
|
11
|
+
name: "@zitadel/sdk-nuxt",
|
|
12
|
+
configKey: "nextgen"
|
|
13
|
+
},
|
|
14
|
+
defaults: {
|
|
15
|
+
url: process.env.ZITADEL_URL ?? "http://localhost:8080",
|
|
16
|
+
proxyPath: "/__nextgen",
|
|
17
|
+
protectedRoutes: [],
|
|
18
|
+
loginPath: "/login"
|
|
19
|
+
},
|
|
20
|
+
setup(options, nuxt) {
|
|
21
|
+
const { resolve } = createResolver(import.meta.url);
|
|
22
|
+
nuxt.options.runtimeConfig.nextgen = {
|
|
23
|
+
url: options.url ?? "http://localhost:4000",
|
|
24
|
+
loginPath: options.loginPath ?? "/login",
|
|
25
|
+
protectedRoutes: options.protectedRoutes ?? [],
|
|
26
|
+
jwtKey: options.jwtKey
|
|
27
|
+
};
|
|
28
|
+
nuxt.options.runtimeConfig.public.zitadelProxyPath = options.proxyPath ?? "/__nextgen";
|
|
29
|
+
nuxt.options.runtimeConfig.public.nextgenProxyPath = options.proxyPath ?? "/__nextgen";
|
|
30
|
+
addPlugin(resolve("./runtime/plugin"));
|
|
31
|
+
addServerHandler({
|
|
32
|
+
middleware: true,
|
|
33
|
+
handler: resolve("./runtime/server/handler")
|
|
34
|
+
});
|
|
35
|
+
addImportsDir(resolve("./runtime/composables"));
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
export {
|
|
39
|
+
module_default as default
|
|
40
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/runtime/plugin.ts
|
|
2
|
+
import {
|
|
3
|
+
defineNuxtPlugin,
|
|
4
|
+
useRequestEvent,
|
|
5
|
+
useRuntimeConfig,
|
|
6
|
+
useState
|
|
7
|
+
} from "#imports";
|
|
8
|
+
import { configureZitadel } from "@zitadel/api/config";
|
|
9
|
+
var plugin_default = defineNuxtPlugin(() => {
|
|
10
|
+
const event = useRequestEvent();
|
|
11
|
+
const auth = event?.context.nextgenAuth ?? {
|
|
12
|
+
isAuthenticated: false,
|
|
13
|
+
session: null
|
|
14
|
+
};
|
|
15
|
+
const clientAuth = auth.isAuthenticated ? {
|
|
16
|
+
isAuthenticated: true,
|
|
17
|
+
session: {
|
|
18
|
+
userId: auth.session.userId,
|
|
19
|
+
email: auth.session.email,
|
|
20
|
+
name: auth.session.name
|
|
21
|
+
}
|
|
22
|
+
} : { isAuthenticated: false, session: null };
|
|
23
|
+
useState("nextgen-auth", () => clientAuth);
|
|
24
|
+
const runtimeConfig = useRuntimeConfig();
|
|
25
|
+
const publicConfig = runtimeConfig.public;
|
|
26
|
+
const proxyPath = publicConfig.zitadelProxyPath ?? publicConfig.nextgenProxyPath ?? publicConfig.nextgenApiBase ?? "/__nextgen";
|
|
27
|
+
const projectId = publicConfig.zitadelProjectId ?? "";
|
|
28
|
+
const url = runtimeConfig.nextgen?.url ?? runtimeConfig.nextgen?.issuerUrl ?? "http://localhost:4000";
|
|
29
|
+
if (proxyPath && projectId) {
|
|
30
|
+
configureZitadel({ proxyPath, projectId, url });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
export {
|
|
34
|
+
plugin_default as default
|
|
35
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as h3 from 'h3';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default Nitro/H3 event handler registered by the `@zitadel/sdk-nuxt` module
|
|
5
|
+
* via {@link addServerHandler}. Reads middleware options from Nuxt runtime
|
|
6
|
+
* config (`nuxt.config.ts → nextgen`) so consumers do not need to create
|
|
7
|
+
* their own `server/middleware/auth.ts` when using the module.
|
|
8
|
+
*/
|
|
9
|
+
declare const _default: h3.EventHandler<h3.EventHandlerRequest, any>;
|
|
10
|
+
|
|
11
|
+
export { _default as default };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createNextgenMiddleware
|
|
3
|
+
} from "../../chunk-J2P7YNGG.js";
|
|
4
|
+
|
|
5
|
+
// src/runtime/server/handler.ts
|
|
6
|
+
var config = useRuntimeConfig();
|
|
7
|
+
var handler_default = createNextgenMiddleware({
|
|
8
|
+
url: config.nextgen?.url ?? config.nextgen?.issuerUrl ?? "http://localhost:4000",
|
|
9
|
+
loginPath: config.nextgen?.loginPath ?? "/login",
|
|
10
|
+
protectedRoutes: config.nextgen?.protectedRoutes ?? []
|
|
11
|
+
});
|
|
12
|
+
export {
|
|
13
|
+
handler_default as default
|
|
14
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AuthResult, NextgenMiddlewareOptions } from '@zitadel/sdk-core/types';
|
|
2
|
+
import { EventHandler, H3Event } from 'h3';
|
|
3
|
+
|
|
4
|
+
declare module 'h3' {
|
|
5
|
+
interface H3EventContext {
|
|
6
|
+
nextgenAuth: AuthResult;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Creates an H3 event handler that handles proxying, JWT verification, and
|
|
11
|
+
* route protection in a single pass.
|
|
12
|
+
*
|
|
13
|
+
* Register it as a Nitro server middleware in `server/middleware/auth.ts`:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { createNextgenMiddleware } from "@zitadel/sdk-nuxt/server";
|
|
17
|
+
*
|
|
18
|
+
* export default createNextgenMiddleware({
|
|
19
|
+
* url: process.env.ZITADEL_URL,
|
|
20
|
+
* protectedRoutes: ["/admin*", "/dashboard*"],
|
|
21
|
+
* loginPath: "/login",
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param options - Middleware configuration options.
|
|
26
|
+
* @returns An H3 event handler suitable for use as a global server middleware.
|
|
27
|
+
*/
|
|
28
|
+
declare function createNextgenMiddleware(options?: NextgenMiddlewareOptions): EventHandler;
|
|
29
|
+
/**
|
|
30
|
+
* Reads the auth state from a Nitro/H3 event context.
|
|
31
|
+
*
|
|
32
|
+
* Call this inside any server route or API handler after the middleware has run:
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { getAuth } from "@zitadel/sdk-nuxt/server";
|
|
36
|
+
*
|
|
37
|
+
* export default defineEventHandler((event) => {
|
|
38
|
+
* const auth = getAuth(event);
|
|
39
|
+
* if (!auth.isAuthenticated) throw createError({ statusCode: 401 });
|
|
40
|
+
* return { userId: auth.session.userId };
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param event - The current H3 event.
|
|
45
|
+
* @returns The current {@link AuthResult}.
|
|
46
|
+
*/
|
|
47
|
+
declare function getAuth(event: H3Event): AuthResult;
|
|
48
|
+
|
|
49
|
+
export { createNextgenMiddleware, getAuth };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { UnauthState } from '@zitadel/sdk-core/types';
|
|
2
|
+
export { AuthResult, AuthState, NextgenMiddlewareOptions, NextgenSession, UnauthState } from '@zitadel/sdk-core/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Re-exports shared SDK types from `@zitadel/sdk-core`.
|
|
6
|
+
*
|
|
7
|
+
* These types are defined once in sdk-core and shared by both sdk-next
|
|
8
|
+
* and sdk-nuxt.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The client-safe session exposed to Vue components via {@link useAuth}.
|
|
13
|
+
* Identical to {@link NextgenSession} but omits `token` — the raw JWT must
|
|
14
|
+
* not be serialised into the Nuxt SSR payload where third-party scripts can
|
|
15
|
+
* read it.
|
|
16
|
+
*/
|
|
17
|
+
type ClientSession = {
|
|
18
|
+
/** The user's unique identifier (`sub` claim). */
|
|
19
|
+
userId: string;
|
|
20
|
+
/** The user's email address, or `null` if not present in the token. */
|
|
21
|
+
email: string | null;
|
|
22
|
+
/** The user's display name, or `null` if not present in the token. */
|
|
23
|
+
name: string | null;
|
|
24
|
+
};
|
|
25
|
+
/** Client-safe auth state when the user is signed in. */
|
|
26
|
+
type ClientAuthState = {
|
|
27
|
+
isAuthenticated: true;
|
|
28
|
+
session: ClientSession;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Union of all possible auth states returned by {@link useAuth}.
|
|
32
|
+
* Token is intentionally absent — use {@link getAuth} server-side when the
|
|
33
|
+
* raw JWT is needed.
|
|
34
|
+
*/
|
|
35
|
+
type ClientAuthResult = ClientAuthState | UnauthState;
|
|
36
|
+
|
|
37
|
+
export type { ClientAuthResult, ClientAuthState, ClientSession };
|
|
File without changes
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zitadel/sdk-nuxt",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Nuxt proxy middleware and helpers for Nextgen Auth",
|
|
5
|
+
"homepage": "https://github.com/zitadel/nextgen/tree/main/packages/sdk-nuxt#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/zitadel/nextgen/issues"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/zitadel/nextgen.git",
|
|
13
|
+
"directory": "packages/sdk-nuxt"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./module": {
|
|
22
|
+
"import": "./dist/module.js"
|
|
23
|
+
},
|
|
24
|
+
"./server": {
|
|
25
|
+
"types": "./dist/server.d.ts",
|
|
26
|
+
"import": "./dist/server.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@zitadel/api": "0.0.0",
|
|
37
|
+
"@zitadel/components": "0.0.0",
|
|
38
|
+
"@zitadel/sdk-core": "0.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"h3": ">=1",
|
|
42
|
+
"nuxt": ">=4"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^9.0.0",
|
|
46
|
+
"@eslint/json": "^0.11.0",
|
|
47
|
+
"@eslint/markdown": "^6.0.0",
|
|
48
|
+
"@nuxt/eslint-plugin": "^0.7.0",
|
|
49
|
+
"eslint": "^9.0.0",
|
|
50
|
+
"eslint-config-prettier": "^10.0.0",
|
|
51
|
+
"eslint-import-resolver-typescript": "^3.7.0",
|
|
52
|
+
"eslint-plugin-import": "^2.31.0",
|
|
53
|
+
"eslint-plugin-perfectionist": "^4.0.0",
|
|
54
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
55
|
+
"h3": "^1.15.3",
|
|
56
|
+
"prettier": "^3.0.0",
|
|
57
|
+
"tsup": "^8.3.5",
|
|
58
|
+
"typescript": "^5.7.3",
|
|
59
|
+
"typescript-eslint": "^8.0.0",
|
|
60
|
+
"vitest": "^3.0.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsup src/index.ts src/server.ts src/runtime/types.ts src/runtime/server/middleware.ts src/runtime/server/handler.ts --format esm --dts --clean && tsup src/runtime/plugin.ts src/runtime/composables/useAuth.ts src/module.ts --format esm --no-clean --external @nuxt/kit --external '#imports'",
|
|
64
|
+
"dev": "tsup src/index.ts src/server.ts --format esm --dts --watch",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"test": "vitest run --passWithNoTests",
|
|
67
|
+
"lint": "eslint ."
|
|
68
|
+
}
|
|
69
|
+
}
|