@poly-x/next 0.1.1 → 0.3.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 +166 -23
- package/dist/index.cjs +2 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/proxy.cjs +24 -4
- package/dist/proxy.d.cts +2 -1
- package/dist/proxy.d.mts +2 -1
- package/dist/proxy.mjs +23 -5
- package/dist/server.cjs +1085 -22
- package/dist/server.d.cts +294 -3
- package/dist/server.d.mts +294 -3
- package/dist/server.mjs +1066 -12
- package/dist/session-cookie-AEnY4PVW.d.cts +21 -0
- package/dist/session-cookie-AEnY4PVW.d.mts +21 -0
- package/dist/session-cookie-BQD10B3D.cjs +34 -0
- package/dist/session-cookie-C23WspJZ.mjs +23 -0
- package/dist/styles.css +151 -0
- package/package.json +3 -3
- package/dist/server-session-Df9HXl_5.cjs +0 -254
- package/dist/server-session-pY-nzh5x.mjs +0 -207
package/README.md
CHANGED
|
@@ -4,57 +4,200 @@ PolyX authentication for the Next.js App Router. Everything in `@poly-x/react`,
|
|
|
4
4
|
plus a server side: tokens live **AES-GCM-encrypted in an httpOnly cookie on your
|
|
5
5
|
own domain** — browser JavaScript never sees them.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Adopt it in three files
|
|
8
|
+
|
|
9
|
+
Install, set two environment values, add three files. None of them contains
|
|
10
|
+
security logic.
|
|
8
11
|
|
|
9
12
|
```bash
|
|
10
13
|
npm install @poly-x/next
|
|
11
14
|
```
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
```bash
|
|
17
|
+
NEXT_PUBLIC_POLYX_PUBLISHABLE_KEY=pk_test_… # your app's publishable key
|
|
18
|
+
POLYX_SESSION_SECRET=… # encrypts the session cookie
|
|
19
|
+
```
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
**1. Mount the routes.** One declaration mounts every action:
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
```ts
|
|
24
|
+
// app/api/polyx/[...polyx]/route.ts
|
|
25
|
+
import { createPolyXHandler } from "@poly-x/next/server";
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
export const { GET, POST } = createPolyXHandler();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**2. Gate your routes** (Next 16 `proxy.ts`; `middleware.ts` on ≤15):
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// proxy.ts
|
|
34
|
+
import { proxy } from "@poly-x/next/proxy";
|
|
35
|
+
|
|
36
|
+
export default proxy({ protectedRoutes: ["/dashboard"] });
|
|
23
37
|
```
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
**3. Declare each backend you call.** Because the browser holds no token, calls to
|
|
40
|
+
your own API are proxied server-side — you declare the upstream, the SDK attaches
|
|
41
|
+
the credential, refreshes it on expiry and retries once:
|
|
26
42
|
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
```ts
|
|
44
|
+
// app/api/billing/proxy/[...path]/route.ts
|
|
45
|
+
import { createDataProxy } from "@poly-x/next/server";
|
|
46
|
+
|
|
47
|
+
export const { GET, POST, PUT, PATCH, DELETE } = createDataProxy({
|
|
48
|
+
upstream: process.env.BILLING_URL!,
|
|
49
|
+
label: "Billing",
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Point your existing client at `/api/billing/proxy` instead of the upstream. No
|
|
54
|
+
call sites change beyond the base URL.
|
|
55
|
+
|
|
56
|
+
## Reading the session
|
|
57
|
+
|
|
58
|
+
**Server** — `auth()` is read-only, so it is safe in a Server Component:
|
|
29
59
|
|
|
30
60
|
```ts
|
|
31
61
|
import { auth } from "@poly-x/next/server";
|
|
32
62
|
|
|
33
|
-
export
|
|
63
|
+
export async function currentUserId(): Promise<string | null> {
|
|
34
64
|
const { isSignedIn, userId } = await auth();
|
|
35
|
-
|
|
65
|
+
return isSignedIn ? userId : null;
|
|
36
66
|
}
|
|
37
67
|
```
|
|
38
68
|
|
|
39
|
-
|
|
40
|
-
|
|
69
|
+
For the full profile from the platform, use `getSessionContext()`. It distinguishes
|
|
70
|
+
"the directory is briefly unavailable" from "signed out", so a transient outage
|
|
71
|
+
never signs your users out:
|
|
41
72
|
|
|
42
73
|
```ts
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export
|
|
74
|
+
import { getSessionContext } from "@poly-x/next/server";
|
|
75
|
+
|
|
76
|
+
export async function loadProfile() {
|
|
77
|
+
const ctx = await getSessionContext();
|
|
78
|
+
if (ctx.isSignedIn && ctx.profileStatus === "unavailable") {
|
|
79
|
+
// still signed in — the profile just could not be loaded right now
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return ctx.user;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Client** — the hooks work with no provider under this custody model:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
"use client";
|
|
90
|
+
import { useAuth, useUser } from "@poly-x/next";
|
|
91
|
+
|
|
92
|
+
export function Profile() {
|
|
93
|
+
const { isLoaded, isSignedIn, signOut, refresh } = useAuth();
|
|
94
|
+
const user = useUser();
|
|
95
|
+
if (!isLoaded) return null;
|
|
96
|
+
if (!isSignedIn) return <a href="/sign-in">Sign in</a>;
|
|
97
|
+
return (
|
|
98
|
+
<div>
|
|
99
|
+
<span>{user?.email}</span>
|
|
100
|
+
<button onClick={() => void refresh()}>Re-check permissions</button>
|
|
101
|
+
<button onClick={() => void signOut({ redirectTo: "/sign-in" })}>Sign out</button>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
46
105
|
```
|
|
47
106
|
|
|
48
|
-
|
|
107
|
+
`signOut()` performs the full-page navigation this custody model requires — a
|
|
108
|
+
client-side redirect would leave the httpOnly cookie in place and your route gate
|
|
109
|
+
would bounce the user straight back in. Do not hand-roll it.
|
|
110
|
+
|
|
111
|
+
`refresh()` re-reads authorization and resolves `true` only when the claims
|
|
112
|
+
actually changed; pair it with `useLiveAuthz` to re-guard a tab without a reload.
|
|
113
|
+
|
|
114
|
+
`consumeAuthEvent()` returns `"signed-in" | "signed-out" | null` once after the
|
|
115
|
+
navigation, for a toast or an analytics call that would otherwise be destroyed
|
|
116
|
+
when the page unloads.
|
|
117
|
+
|
|
118
|
+
## Styles
|
|
119
|
+
|
|
120
|
+
If you render `<SignIn>` or `<UserButton>`, import the stylesheet **before** your
|
|
121
|
+
Tailwind entry. Cascade-layer order is fixed at first declaration, so importing it
|
|
122
|
+
later silently loses to your utilities:
|
|
49
123
|
|
|
50
124
|
```ts
|
|
51
|
-
|
|
52
|
-
|
|
125
|
+
// app/layout.tsx
|
|
126
|
+
import "@poly-x/next/styles.css";
|
|
127
|
+
import "./globals.css";
|
|
53
128
|
```
|
|
54
129
|
|
|
130
|
+
## When something goes wrong
|
|
131
|
+
|
|
132
|
+
| Symptom | Cause |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `401` from a proxied call, no upstream request made | No session, or the cookie would not unseal (rotated `POLYX_SESSION_SECRET`). |
|
|
135
|
+
| `401` from a proxied call to a path you did not declare | Only declared upstreams receive a credential. |
|
|
136
|
+
| `{ status: "configuration_error", code: "REDIRECT_URI_NOT_ALLOWED" }` | The redirect URI the SDK sent is not registered. Behind a proxy set `POLYX_PUBLIC_ORIGIN`, or enable `POLYX_TRUST_FORWARDED_ORIGIN` — `request.url` is the bind address. The server log names the URI actually used. |
|
|
137
|
+
| `{ status: "session_too_large" }` | The sealed session exceeds the ~4096-byte browser cookie limit. Usually a tenant configured to embed full permissions in the JWT. |
|
|
138
|
+
| Sign-in returns 200 but the user is never signed in | Upgrade to ≥ 0.1.1 — earlier versions wrote an oversized cookie the browser silently discarded. |
|
|
139
|
+
|
|
140
|
+
## Profile self-service
|
|
141
|
+
|
|
142
|
+
`createPolyXHandler()` also mounts `/api/polyx/profile`, which backs
|
|
143
|
+
`<UserProfile>` — nothing extra to wire. It reads and updates **only** the
|
|
144
|
+
signed-in person's own profile: the identity comes from the sealed session, never
|
|
145
|
+
from the request, so it cannot be aimed at anyone else.
|
|
146
|
+
|
|
147
|
+
The submitted body is rebuilt from an allow-list rather than forwarded. The
|
|
148
|
+
platform's update applies whatever recognised fields it receives — including
|
|
149
|
+
`email`, `password` and `role` — so relaying a browser's multipart verbatim would
|
|
150
|
+
turn a profile form into a way to change a credential or grant a role. Only the
|
|
151
|
+
six profile fields ever leave the server.
|
|
152
|
+
|
|
153
|
+
## Cross-application sign-on and switching workspace
|
|
154
|
+
|
|
155
|
+
`createPolyXHandler()` also mounts `/api/polyx/hop` and `/api/polyx/hop-complete`. You do not
|
|
156
|
+
call these directly — `@poly-x/react`'s `useWarmHop()` and `switchWorkspace()` drive them.
|
|
157
|
+
|
|
158
|
+
**The attempt is made by the browser, and it has to be.** The platform recognises a returning
|
|
159
|
+
person from an ecosystem cookie set on *its own* domain. Your server does not have that cookie,
|
|
160
|
+
so a server-side attempt is told "sign-in required" every single time — correctly, and
|
|
161
|
+
indistinguishably from someone who genuinely is signed out. The feature would look implemented
|
|
162
|
+
and never once work.
|
|
163
|
+
|
|
164
|
+
So the routes bracket a browser-made request rather than making one:
|
|
165
|
+
|
|
166
|
+
- **`hop`** mints the PKCE pair, keeps the verifier in an httpOnly cookie, and returns only a
|
|
167
|
+
URL. It makes no request of its own.
|
|
168
|
+
- **`hop-complete`** takes the `code` the browser received and exchanges it here. The code is
|
|
169
|
+
safe for a page to hold precisely because the verifier that redeems it never left your server.
|
|
170
|
+
|
|
171
|
+
Because of that, calling `completeAuthorize()` from server code **throws**
|
|
172
|
+
(`WARM_HOP_ON_SERVER`) rather than returning the plausible answer.
|
|
173
|
+
|
|
174
|
+
Register your `/api/polyx/callback` on the App even though nothing redirects to it during a
|
|
175
|
+
hop — the platform validates `redirect_uri` on both the authorize and the exchange.
|
|
176
|
+
|
|
177
|
+
> **Warm hop needs https end to end** (or a `localhost` dev setup). The platform issues its
|
|
178
|
+
> ecosystem cookie with `Secure`, and a browser silently discards a `Secure` cookie delivered
|
|
179
|
+
> over http. Nothing reports this: sign-in still succeeds, the `Set-Cookie` header is still
|
|
180
|
+
> there, and every hop afterwards reports "sign-in required" — indistinguishable from someone
|
|
181
|
+
> who is genuinely signed out. `localhost` is the one host browsers exempt.
|
|
182
|
+
|
|
183
|
+
### The boundary, stated plainly
|
|
184
|
+
|
|
185
|
+
**This works between applications that share the ecosystem cookie's domain.** Browsers block
|
|
186
|
+
third-party cookies by default, so an application on an unrelated domain cannot see that cookie
|
|
187
|
+
and will fall back to ordinary sign-in. That is a browser policy, not a limitation we can
|
|
188
|
+
configure away — solving it needs a different mechanism entirely. Nothing here claims otherwise.
|
|
189
|
+
|
|
190
|
+
Switching workspace is unaffected by that boundary: it happens inside one application.
|
|
191
|
+
|
|
55
192
|
## Exports
|
|
56
193
|
|
|
57
|
-
`@poly-x/next`
|
|
58
|
-
`
|
|
194
|
+
- `@poly-x/next` — the client surface (re-exports `@poly-x/react`): `PolyXProvider`,
|
|
195
|
+
`useAuth`, `useUser`, `useSession`, `useLiveAuthz`, `consumeAuthEvent`, `<SignIn>`,
|
|
196
|
+
`<UserButton>`, `<UserProfile>`, `<Protected>`.
|
|
197
|
+
- `@poly-x/next/server` — `createPolyXHandler`, `createDataProxy`, `auth`,
|
|
198
|
+
`getSessionContext`, `refreshSession`, `createAuthHandlers`, `ServerSession`,
|
|
199
|
+
`sealSession` / `openSession`, `SESSION_COOKIE_MAX_BYTES`, `resolvePublicOrigin`.
|
|
200
|
+
- `@poly-x/next/proxy` — `proxy`, `DEFAULT_SESSION_COOKIE`, `readSessionCookie`
|
|
201
|
+
(edge-safe: no server-only imports).
|
|
59
202
|
|
|
60
203
|
MIT licensed.
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
//#region src/index.ts
|
|
4
4
|
const PACKAGE_NAME = "@poly-x/next";
|
|
5
|
-
|
|
5
|
+
/** The published version of this package, injected at build time. */
|
|
6
|
+
const version = "0.3.0";
|
|
6
7
|
//#endregion
|
|
7
8
|
exports.PACKAGE_NAME = PACKAGE_NAME;
|
|
8
9
|
exports.version = version;
|
package/dist/index.d.cts
CHANGED
|
@@ -2,6 +2,7 @@ export * from "@poly-x/react";
|
|
|
2
2
|
|
|
3
3
|
//#region src/index.d.ts
|
|
4
4
|
declare const PACKAGE_NAME = "@poly-x/next";
|
|
5
|
-
|
|
5
|
+
/** The published version of this package, injected at build time. */
|
|
6
|
+
declare const version: string;
|
|
6
7
|
//#endregion
|
|
7
8
|
export { PACKAGE_NAME, version };
|
package/dist/index.d.mts
CHANGED
|
@@ -2,6 +2,7 @@ export * from "@poly-x/react";
|
|
|
2
2
|
|
|
3
3
|
//#region src/index.d.ts
|
|
4
4
|
declare const PACKAGE_NAME = "@poly-x/next";
|
|
5
|
-
|
|
5
|
+
/** The published version of this package, injected at build time. */
|
|
6
|
+
declare const version: string;
|
|
6
7
|
//#endregion
|
|
7
8
|
export { PACKAGE_NAME, version };
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export * from "@poly-x/react";
|
|
3
3
|
//#region src/index.ts
|
|
4
4
|
const PACKAGE_NAME = "@poly-x/next";
|
|
5
|
-
|
|
5
|
+
/** The published version of this package, injected at build time. */
|
|
6
|
+
const version = "0.3.0";
|
|
6
7
|
//#endregion
|
|
7
8
|
export { PACKAGE_NAME, version };
|
package/dist/proxy.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./
|
|
2
|
+
const require_session_cookie = require("./session-cookie-BQD10B3D.cjs");
|
|
3
3
|
let next_server = require("next/server");
|
|
4
4
|
//#region src/proxy.ts
|
|
5
5
|
/**
|
|
@@ -8,17 +8,37 @@ let next_server = require("next/server");
|
|
|
8
8
|
* gates on the session cookie's *presence* (a fast edge check) and redirects
|
|
9
9
|
* signed-out users to sign-in; `auth()` does the real decrypt server-side.
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Make a matcher that gives the same answer every time it is called.
|
|
13
|
+
*
|
|
14
|
+
* `RegExp.prototype.test` advances `lastIndex` when the pattern carries `g` or `y`, so testing the
|
|
15
|
+
* SAME regex object repeatedly alternates true/false/true. This middleware holds the consumer's
|
|
16
|
+
* patterns for the lifetime of the process and tests them once per request — so a consumer writing
|
|
17
|
+
* `/^\/admin/g`, an easy habit, would have their protected routes gated on every OTHER request.
|
|
18
|
+
* That fails **open**, intermittently, and is close to undebuggable from the outside.
|
|
19
|
+
*
|
|
20
|
+
* The stateful flags are stripped into a fresh regex here, once at construction: no per-request
|
|
21
|
+
* allocation, and the consumer's own object is left untouched (they may be using it elsewhere, and
|
|
22
|
+
* resetting its `lastIndex` behind their back would be its own surprise).
|
|
23
|
+
*/
|
|
24
|
+
function toMatcher(route) {
|
|
25
|
+
if (typeof route === "string") return (pathname) => pathname.startsWith(route);
|
|
26
|
+
if (!route.global && !route.sticky) return (pathname) => route.test(pathname);
|
|
27
|
+
const stateless = new RegExp(route.source, route.flags.replace(/[gy]/g, ""));
|
|
28
|
+
return (pathname) => stateless.test(pathname);
|
|
13
29
|
}
|
|
14
30
|
function proxy(config) {
|
|
15
31
|
const cookieName = config.cookieName ?? "polyx_session";
|
|
16
32
|
const signInPath = config.signInPath ?? "/api/polyx/signin";
|
|
33
|
+
const matchers = config.protectedRoutes.map(toMatcher);
|
|
34
|
+
const isProtected = (pathname) => matchers.some((match) => match(pathname));
|
|
17
35
|
return (request) => {
|
|
18
|
-
if (!isProtected(request.nextUrl.pathname
|
|
36
|
+
if (!isProtected(request.nextUrl.pathname)) return next_server.NextResponse.next();
|
|
19
37
|
if (request.cookies.get(cookieName)?.value) return next_server.NextResponse.next();
|
|
20
38
|
return next_server.NextResponse.redirect(new URL(signInPath, request.url));
|
|
21
39
|
};
|
|
22
40
|
}
|
|
23
41
|
//#endregion
|
|
42
|
+
exports.DEFAULT_SESSION_COOKIE = require_session_cookie.DEFAULT_SESSION_COOKIE;
|
|
24
43
|
exports.proxy = proxy;
|
|
44
|
+
exports.readSessionCookie = require_session_cookie.readSessionCookie;
|
package/dist/proxy.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as readSessionCookie, t as DEFAULT_SESSION_COOKIE } from "./session-cookie-AEnY4PVW.cjs";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
|
|
3
4
|
//#region src/proxy.d.ts
|
|
@@ -10,4 +11,4 @@ interface ProxyConfig {
|
|
|
10
11
|
}
|
|
11
12
|
declare function proxy(config: ProxyConfig): (request: NextRequest) => NextResponse;
|
|
12
13
|
//#endregion
|
|
13
|
-
export { ProxyConfig, proxy };
|
|
14
|
+
export { DEFAULT_SESSION_COOKIE, ProxyConfig, proxy, readSessionCookie };
|
package/dist/proxy.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as readSessionCookie, t as DEFAULT_SESSION_COOKIE } from "./session-cookie-AEnY4PVW.mjs";
|
|
1
2
|
import { NextRequest, NextResponse } from "next/server";
|
|
2
3
|
|
|
3
4
|
//#region src/proxy.d.ts
|
|
@@ -10,4 +11,4 @@ interface ProxyConfig {
|
|
|
10
11
|
}
|
|
11
12
|
declare function proxy(config: ProxyConfig): (request: NextRequest) => NextResponse;
|
|
12
13
|
//#endregion
|
|
13
|
-
export { ProxyConfig, proxy };
|
|
14
|
+
export { DEFAULT_SESSION_COOKIE, ProxyConfig, proxy, readSessionCookie };
|
package/dist/proxy.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./
|
|
1
|
+
import { n as readSessionCookie, t as DEFAULT_SESSION_COOKIE } from "./session-cookie-C23WspJZ.mjs";
|
|
2
2
|
import { NextResponse } from "next/server";
|
|
3
3
|
//#region src/proxy.ts
|
|
4
4
|
/**
|
|
@@ -7,17 +7,35 @@ import { NextResponse } from "next/server";
|
|
|
7
7
|
* gates on the session cookie's *presence* (a fast edge check) and redirects
|
|
8
8
|
* signed-out users to sign-in; `auth()` does the real decrypt server-side.
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Make a matcher that gives the same answer every time it is called.
|
|
12
|
+
*
|
|
13
|
+
* `RegExp.prototype.test` advances `lastIndex` when the pattern carries `g` or `y`, so testing the
|
|
14
|
+
* SAME regex object repeatedly alternates true/false/true. This middleware holds the consumer's
|
|
15
|
+
* patterns for the lifetime of the process and tests them once per request — so a consumer writing
|
|
16
|
+
* `/^\/admin/g`, an easy habit, would have their protected routes gated on every OTHER request.
|
|
17
|
+
* That fails **open**, intermittently, and is close to undebuggable from the outside.
|
|
18
|
+
*
|
|
19
|
+
* The stateful flags are stripped into a fresh regex here, once at construction: no per-request
|
|
20
|
+
* allocation, and the consumer's own object is left untouched (they may be using it elsewhere, and
|
|
21
|
+
* resetting its `lastIndex` behind their back would be its own surprise).
|
|
22
|
+
*/
|
|
23
|
+
function toMatcher(route) {
|
|
24
|
+
if (typeof route === "string") return (pathname) => pathname.startsWith(route);
|
|
25
|
+
if (!route.global && !route.sticky) return (pathname) => route.test(pathname);
|
|
26
|
+
const stateless = new RegExp(route.source, route.flags.replace(/[gy]/g, ""));
|
|
27
|
+
return (pathname) => stateless.test(pathname);
|
|
12
28
|
}
|
|
13
29
|
function proxy(config) {
|
|
14
30
|
const cookieName = config.cookieName ?? "polyx_session";
|
|
15
31
|
const signInPath = config.signInPath ?? "/api/polyx/signin";
|
|
32
|
+
const matchers = config.protectedRoutes.map(toMatcher);
|
|
33
|
+
const isProtected = (pathname) => matchers.some((match) => match(pathname));
|
|
16
34
|
return (request) => {
|
|
17
|
-
if (!isProtected(request.nextUrl.pathname
|
|
35
|
+
if (!isProtected(request.nextUrl.pathname)) return NextResponse.next();
|
|
18
36
|
if (request.cookies.get(cookieName)?.value) return NextResponse.next();
|
|
19
37
|
return NextResponse.redirect(new URL(signInPath, request.url));
|
|
20
38
|
};
|
|
21
39
|
}
|
|
22
40
|
//#endregion
|
|
23
|
-
export { proxy };
|
|
41
|
+
export { DEFAULT_SESSION_COOKIE, proxy, readSessionCookie };
|