@poly-x/next 0.1.0-alpha.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 +60 -0
- package/dist/index.cjs +17 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +7 -0
- package/dist/proxy.cjs +24 -0
- package/dist/proxy.d.cts +13 -0
- package/dist/proxy.d.mts +13 -0
- package/dist/proxy.mjs +23 -0
- package/dist/server-session-B3zJ7CS8.mjs +130 -0
- package/dist/server-session-YbnXBt3c.cjs +159 -0
- package/dist/server.cjs +127 -0
- package/dist/server.d.cts +80 -0
- package/dist/server.d.mts +80 -0
- package/dist/server.mjs +120 -0
- package/package.json +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @poly-x/next
|
|
2
|
+
|
|
3
|
+
PolyX authentication for the Next.js App Router. Everything in `@poly-x/react`,
|
|
4
|
+
plus a server side: tokens live **AES-GCM-encrypted in an httpOnly cookie on your
|
|
5
|
+
own domain** — browser JavaScript never sees them.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @poly-x/next
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Set two env vars: `NEXT_PUBLIC_POLYX_PUBLISHABLE_KEY` (the `pk_…` key) and
|
|
14
|
+
`POLYX_SESSION_SECRET` (used to encrypt the session cookie).
|
|
15
|
+
|
|
16
|
+
## Client
|
|
17
|
+
|
|
18
|
+
The client entry re-exports `@poly-x/react` — `PolyXProvider`, hooks, `<SignIn>`,
|
|
19
|
+
`<Protected>`, and friends:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { PolyXProvider } from "@poly-x/next";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Server
|
|
26
|
+
|
|
27
|
+
Read the session in a Server Component or route handler (read-only — Server
|
|
28
|
+
Components can't set cookies):
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { auth } from "@poly-x/next/server";
|
|
32
|
+
|
|
33
|
+
export default async function Page() {
|
|
34
|
+
const { isSignedIn, userId } = await auth();
|
|
35
|
+
// …
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Mount the route handlers that own every cookie write (login / callback / refresh
|
|
40
|
+
/ signout):
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// app/api/polyx/[...action]/route.ts
|
|
44
|
+
import { createAuthHandlers } from "@poly-x/next/server";
|
|
45
|
+
export const { signin: GET } = createAuthHandlers();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Gate routes with the proxy (Next 16 `proxy.ts`; `middleware.ts` on ≤15):
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { proxy } from "@poly-x/next/proxy";
|
|
52
|
+
export const proxy = proxyHandler; // from proxy({ protectedRoutes: ["/dashboard"] })
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Exports
|
|
56
|
+
|
|
57
|
+
`@poly-x/next` (client) · `@poly-x/next/server` (`auth`, `createAuthHandlers`,
|
|
58
|
+
`ServerSession`) · `@poly-x/next/proxy` (`proxy`).
|
|
59
|
+
|
|
60
|
+
MIT licensed.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
const PACKAGE_NAME = "@poly-x/next";
|
|
5
|
+
const version = "0.0.0";
|
|
6
|
+
//#endregion
|
|
7
|
+
exports.PACKAGE_NAME = PACKAGE_NAME;
|
|
8
|
+
exports.version = version;
|
|
9
|
+
var _poly_x_react = require("@poly-x/react");
|
|
10
|
+
Object.keys(_poly_x_react).forEach(function(k) {
|
|
11
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function() {
|
|
14
|
+
return _poly_x_react[k];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
});
|
package/dist/index.d.cts
ADDED
package/dist/index.d.mts
ADDED
package/dist/index.mjs
ADDED
package/dist/proxy.cjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("./server-session-YbnXBt3c.cjs");
|
|
3
|
+
let next_server = require("next/server");
|
|
4
|
+
//#region src/proxy.ts
|
|
5
|
+
/**
|
|
6
|
+
* @poly-x/next/proxy — route gating (F007). In Next 16 this is wired via
|
|
7
|
+
* `proxy.ts` (`export { proxy as proxy }`); on ≤15 it's `middleware.ts`. It
|
|
8
|
+
* gates on the session cookie's *presence* (a fast edge check) and redirects
|
|
9
|
+
* signed-out users to sign-in; `auth()` does the real decrypt server-side.
|
|
10
|
+
*/
|
|
11
|
+
function isProtected(pathname, routes) {
|
|
12
|
+
return routes.some((route) => typeof route === "string" ? pathname.startsWith(route) : route.test(pathname));
|
|
13
|
+
}
|
|
14
|
+
function proxy(config) {
|
|
15
|
+
const cookieName = config.cookieName ?? "polyx_session";
|
|
16
|
+
const signInPath = config.signInPath ?? "/api/polyx/signin";
|
|
17
|
+
return (request) => {
|
|
18
|
+
if (!isProtected(request.nextUrl.pathname, config.protectedRoutes)) return next_server.NextResponse.next();
|
|
19
|
+
if (request.cookies.get(cookieName)?.value) return next_server.NextResponse.next();
|
|
20
|
+
return next_server.NextResponse.redirect(new URL(signInPath, request.url));
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.proxy = proxy;
|
package/dist/proxy.d.cts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
|
|
3
|
+
//#region src/proxy.d.ts
|
|
4
|
+
interface ProxyConfig {
|
|
5
|
+
/** Path prefixes or patterns that require a session. */
|
|
6
|
+
protectedRoutes: Array<string | RegExp>;
|
|
7
|
+
/** Where to send signed-out users. Default `/api/polyx/signin`. */
|
|
8
|
+
signInPath?: string;
|
|
9
|
+
cookieName?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function proxy(config: ProxyConfig): (request: NextRequest) => NextResponse;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ProxyConfig, proxy };
|
package/dist/proxy.d.mts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
|
|
3
|
+
//#region src/proxy.d.ts
|
|
4
|
+
interface ProxyConfig {
|
|
5
|
+
/** Path prefixes or patterns that require a session. */
|
|
6
|
+
protectedRoutes: Array<string | RegExp>;
|
|
7
|
+
/** Where to send signed-out users. Default `/api/polyx/signin`. */
|
|
8
|
+
signInPath?: string;
|
|
9
|
+
cookieName?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function proxy(config: ProxyConfig): (request: NextRequest) => NextResponse;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ProxyConfig, proxy };
|
package/dist/proxy.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "./server-session-B3zJ7CS8.mjs";
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
//#region src/proxy.ts
|
|
4
|
+
/**
|
|
5
|
+
* @poly-x/next/proxy — route gating (F007). In Next 16 this is wired via
|
|
6
|
+
* `proxy.ts` (`export { proxy as proxy }`); on ≤15 it's `middleware.ts`. It
|
|
7
|
+
* gates on the session cookie's *presence* (a fast edge check) and redirects
|
|
8
|
+
* signed-out users to sign-in; `auth()` does the real decrypt server-side.
|
|
9
|
+
*/
|
|
10
|
+
function isProtected(pathname, routes) {
|
|
11
|
+
return routes.some((route) => typeof route === "string" ? pathname.startsWith(route) : route.test(pathname));
|
|
12
|
+
}
|
|
13
|
+
function proxy(config) {
|
|
14
|
+
const cookieName = config.cookieName ?? "polyx_session";
|
|
15
|
+
const signInPath = config.signInPath ?? "/api/polyx/signin";
|
|
16
|
+
return (request) => {
|
|
17
|
+
if (!isProtected(request.nextUrl.pathname, config.protectedRoutes)) return NextResponse.next();
|
|
18
|
+
if (request.cookies.get(cookieName)?.value) return NextResponse.next();
|
|
19
|
+
return NextResponse.redirect(new URL(signInPath, request.url));
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { proxy };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { SessionExpiredError, SessionRevokedError } from "@poly-x/core";
|
|
2
|
+
//#region src/cookie-adapter.ts
|
|
3
|
+
/** Default attributes for the session cookie (ADR-0004 secure custody). */
|
|
4
|
+
const SESSION_COOKIE_OPTIONS = {
|
|
5
|
+
httpOnly: true,
|
|
6
|
+
secure: true,
|
|
7
|
+
sameSite: "lax",
|
|
8
|
+
path: "/"
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/cookie-codec.ts
|
|
12
|
+
const IV_BYTES = 12;
|
|
13
|
+
function base64UrlEncode(bytes) {
|
|
14
|
+
let binary = "";
|
|
15
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
16
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
17
|
+
}
|
|
18
|
+
function base64UrlDecode(text) {
|
|
19
|
+
const base64 = text.replace(/-/g, "+").replace(/_/g, "/");
|
|
20
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
21
|
+
const binary = atob(padded);
|
|
22
|
+
const bytes = new Uint8Array(binary.length);
|
|
23
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
24
|
+
return bytes;
|
|
25
|
+
}
|
|
26
|
+
async function deriveKey(secret) {
|
|
27
|
+
const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(secret));
|
|
28
|
+
return crypto.subtle.importKey("raw", hash, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
29
|
+
}
|
|
30
|
+
async function sealSession(session, secret) {
|
|
31
|
+
const key = await deriveKey(secret);
|
|
32
|
+
const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
|
|
33
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(session));
|
|
34
|
+
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({
|
|
35
|
+
name: "AES-GCM",
|
|
36
|
+
iv
|
|
37
|
+
}, key, plaintext));
|
|
38
|
+
const packed = new Uint8Array(iv.length + ciphertext.length);
|
|
39
|
+
packed.set(iv);
|
|
40
|
+
packed.set(ciphertext, iv.length);
|
|
41
|
+
return base64UrlEncode(packed);
|
|
42
|
+
}
|
|
43
|
+
async function openSession(token, secret) {
|
|
44
|
+
try {
|
|
45
|
+
const packed = base64UrlDecode(token);
|
|
46
|
+
if (packed.length <= IV_BYTES) return null;
|
|
47
|
+
const iv = packed.slice(0, IV_BYTES);
|
|
48
|
+
const ciphertext = packed.slice(IV_BYTES);
|
|
49
|
+
const key = await deriveKey(secret);
|
|
50
|
+
const plaintext = await crypto.subtle.decrypt({
|
|
51
|
+
name: "AES-GCM",
|
|
52
|
+
iv
|
|
53
|
+
}, key, ciphertext);
|
|
54
|
+
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/server-session.ts
|
|
61
|
+
/**
|
|
62
|
+
* The framework-agnostic BFF session controller (F007): tokens sealed in an
|
|
63
|
+
* httpOnly cookie on the consumer's domain, exchanged/refreshed server-side.
|
|
64
|
+
* Composes core's AuthClient with a CookieAdapter seam; fully unit-testable.
|
|
65
|
+
*/
|
|
66
|
+
const DEFAULT_SESSION_COOKIE = "polyx_session";
|
|
67
|
+
var ServerSession = class {
|
|
68
|
+
authClient;
|
|
69
|
+
secret;
|
|
70
|
+
cookies;
|
|
71
|
+
cookieName;
|
|
72
|
+
constructor(options) {
|
|
73
|
+
this.authClient = options.authClient;
|
|
74
|
+
this.secret = options.secret;
|
|
75
|
+
this.cookies = options.cookies;
|
|
76
|
+
this.cookieName = options.cookieName ?? "polyx_session";
|
|
77
|
+
}
|
|
78
|
+
async establishFromCode(code, codeVerifier, redirectUri) {
|
|
79
|
+
const stored = await this.authClient.exchangeCode({
|
|
80
|
+
code,
|
|
81
|
+
codeVerifier,
|
|
82
|
+
redirectUri
|
|
83
|
+
});
|
|
84
|
+
await this.write(stored);
|
|
85
|
+
return stored;
|
|
86
|
+
}
|
|
87
|
+
async read() {
|
|
88
|
+
const raw = this.cookies.get(this.cookieName);
|
|
89
|
+
if (!raw) return null;
|
|
90
|
+
return openSession(raw, this.secret);
|
|
91
|
+
}
|
|
92
|
+
/** Rotate the tokens; clears the cookie (returns null) on a terminal refresh failure. */
|
|
93
|
+
async refresh() {
|
|
94
|
+
const current = await this.read();
|
|
95
|
+
if (!current) return null;
|
|
96
|
+
try {
|
|
97
|
+
const next = await this.authClient.createRefresher()(current);
|
|
98
|
+
await this.write(next);
|
|
99
|
+
return next;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error instanceof SessionRevokedError || error instanceof SessionExpiredError) {
|
|
102
|
+
this.clear();
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
clear() {
|
|
109
|
+
this.cookies.delete(this.cookieName);
|
|
110
|
+
}
|
|
111
|
+
async write(session) {
|
|
112
|
+
const sealed = await sealSession(session, this.secret);
|
|
113
|
+
this.cookies.set(this.cookieName, sealed, SESSION_COOKIE_OPTIONS);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
function toAuthContext(session) {
|
|
117
|
+
if (!session) return {
|
|
118
|
+
isSignedIn: false,
|
|
119
|
+
userId: null,
|
|
120
|
+
claims: null
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
isSignedIn: true,
|
|
124
|
+
userId: session.session.claims.userId,
|
|
125
|
+
organizationId: session.session.claims.organizationId,
|
|
126
|
+
claims: session.session.claims
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { sealSession as a, openSession as i, ServerSession as n, toAuthContext as r, DEFAULT_SESSION_COOKIE as t };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
let _poly_x_core = require("@poly-x/core");
|
|
2
|
+
//#region src/cookie-adapter.ts
|
|
3
|
+
/** Default attributes for the session cookie (ADR-0004 secure custody). */
|
|
4
|
+
const SESSION_COOKIE_OPTIONS = {
|
|
5
|
+
httpOnly: true,
|
|
6
|
+
secure: true,
|
|
7
|
+
sameSite: "lax",
|
|
8
|
+
path: "/"
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/cookie-codec.ts
|
|
12
|
+
const IV_BYTES = 12;
|
|
13
|
+
function base64UrlEncode(bytes) {
|
|
14
|
+
let binary = "";
|
|
15
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
16
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
17
|
+
}
|
|
18
|
+
function base64UrlDecode(text) {
|
|
19
|
+
const base64 = text.replace(/-/g, "+").replace(/_/g, "/");
|
|
20
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
21
|
+
const binary = atob(padded);
|
|
22
|
+
const bytes = new Uint8Array(binary.length);
|
|
23
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
|
|
24
|
+
return bytes;
|
|
25
|
+
}
|
|
26
|
+
async function deriveKey(secret) {
|
|
27
|
+
const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(secret));
|
|
28
|
+
return crypto.subtle.importKey("raw", hash, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
29
|
+
}
|
|
30
|
+
async function sealSession(session, secret) {
|
|
31
|
+
const key = await deriveKey(secret);
|
|
32
|
+
const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
|
|
33
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(session));
|
|
34
|
+
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({
|
|
35
|
+
name: "AES-GCM",
|
|
36
|
+
iv
|
|
37
|
+
}, key, plaintext));
|
|
38
|
+
const packed = new Uint8Array(iv.length + ciphertext.length);
|
|
39
|
+
packed.set(iv);
|
|
40
|
+
packed.set(ciphertext, iv.length);
|
|
41
|
+
return base64UrlEncode(packed);
|
|
42
|
+
}
|
|
43
|
+
async function openSession(token, secret) {
|
|
44
|
+
try {
|
|
45
|
+
const packed = base64UrlDecode(token);
|
|
46
|
+
if (packed.length <= IV_BYTES) return null;
|
|
47
|
+
const iv = packed.slice(0, IV_BYTES);
|
|
48
|
+
const ciphertext = packed.slice(IV_BYTES);
|
|
49
|
+
const key = await deriveKey(secret);
|
|
50
|
+
const plaintext = await crypto.subtle.decrypt({
|
|
51
|
+
name: "AES-GCM",
|
|
52
|
+
iv
|
|
53
|
+
}, key, ciphertext);
|
|
54
|
+
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/server-session.ts
|
|
61
|
+
/**
|
|
62
|
+
* The framework-agnostic BFF session controller (F007): tokens sealed in an
|
|
63
|
+
* httpOnly cookie on the consumer's domain, exchanged/refreshed server-side.
|
|
64
|
+
* Composes core's AuthClient with a CookieAdapter seam; fully unit-testable.
|
|
65
|
+
*/
|
|
66
|
+
const DEFAULT_SESSION_COOKIE = "polyx_session";
|
|
67
|
+
var ServerSession = class {
|
|
68
|
+
authClient;
|
|
69
|
+
secret;
|
|
70
|
+
cookies;
|
|
71
|
+
cookieName;
|
|
72
|
+
constructor(options) {
|
|
73
|
+
this.authClient = options.authClient;
|
|
74
|
+
this.secret = options.secret;
|
|
75
|
+
this.cookies = options.cookies;
|
|
76
|
+
this.cookieName = options.cookieName ?? "polyx_session";
|
|
77
|
+
}
|
|
78
|
+
async establishFromCode(code, codeVerifier, redirectUri) {
|
|
79
|
+
const stored = await this.authClient.exchangeCode({
|
|
80
|
+
code,
|
|
81
|
+
codeVerifier,
|
|
82
|
+
redirectUri
|
|
83
|
+
});
|
|
84
|
+
await this.write(stored);
|
|
85
|
+
return stored;
|
|
86
|
+
}
|
|
87
|
+
async read() {
|
|
88
|
+
const raw = this.cookies.get(this.cookieName);
|
|
89
|
+
if (!raw) return null;
|
|
90
|
+
return openSession(raw, this.secret);
|
|
91
|
+
}
|
|
92
|
+
/** Rotate the tokens; clears the cookie (returns null) on a terminal refresh failure. */
|
|
93
|
+
async refresh() {
|
|
94
|
+
const current = await this.read();
|
|
95
|
+
if (!current) return null;
|
|
96
|
+
try {
|
|
97
|
+
const next = await this.authClient.createRefresher()(current);
|
|
98
|
+
await this.write(next);
|
|
99
|
+
return next;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error instanceof _poly_x_core.SessionRevokedError || error instanceof _poly_x_core.SessionExpiredError) {
|
|
102
|
+
this.clear();
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
clear() {
|
|
109
|
+
this.cookies.delete(this.cookieName);
|
|
110
|
+
}
|
|
111
|
+
async write(session) {
|
|
112
|
+
const sealed = await sealSession(session, this.secret);
|
|
113
|
+
this.cookies.set(this.cookieName, sealed, SESSION_COOKIE_OPTIONS);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
function toAuthContext(session) {
|
|
117
|
+
if (!session) return {
|
|
118
|
+
isSignedIn: false,
|
|
119
|
+
userId: null,
|
|
120
|
+
claims: null
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
isSignedIn: true,
|
|
124
|
+
userId: session.session.claims.userId,
|
|
125
|
+
organizationId: session.session.claims.organizationId,
|
|
126
|
+
claims: session.session.claims
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
Object.defineProperty(exports, "DEFAULT_SESSION_COOKIE", {
|
|
131
|
+
enumerable: true,
|
|
132
|
+
get: function() {
|
|
133
|
+
return DEFAULT_SESSION_COOKIE;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
Object.defineProperty(exports, "ServerSession", {
|
|
137
|
+
enumerable: true,
|
|
138
|
+
get: function() {
|
|
139
|
+
return ServerSession;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
Object.defineProperty(exports, "openSession", {
|
|
143
|
+
enumerable: true,
|
|
144
|
+
get: function() {
|
|
145
|
+
return openSession;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
Object.defineProperty(exports, "sealSession", {
|
|
149
|
+
enumerable: true,
|
|
150
|
+
get: function() {
|
|
151
|
+
return sealSession;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
Object.defineProperty(exports, "toAuthContext", {
|
|
155
|
+
enumerable: true,
|
|
156
|
+
get: function() {
|
|
157
|
+
return toAuthContext;
|
|
158
|
+
}
|
|
159
|
+
});
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_server_session = require("./server-session-YbnXBt3c.cjs");
|
|
3
|
+
let _poly_x_core = require("@poly-x/core");
|
|
4
|
+
let next_headers = require("next/headers");
|
|
5
|
+
//#region src/config.ts
|
|
6
|
+
function resolveNextConfig(overrides = {}) {
|
|
7
|
+
const publishableKey = overrides.publishableKey ?? process.env.NEXT_PUBLIC_POLYX_PUBLISHABLE_KEY;
|
|
8
|
+
const secret = overrides.secret ?? process.env.POLYX_SESSION_SECRET;
|
|
9
|
+
if (!publishableKey) throw new Error("PolyX: missing publishable key (set NEXT_PUBLIC_POLYX_PUBLISHABLE_KEY or pass config.publishableKey).");
|
|
10
|
+
if (!secret) throw new Error("PolyX: missing POLYX_SESSION_SECRET (needed to encrypt the session cookie).");
|
|
11
|
+
return {
|
|
12
|
+
publishableKey,
|
|
13
|
+
secret,
|
|
14
|
+
baseUrl: overrides.baseUrl ?? process.env.POLYX_BASE_URL
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function buildServerAuthClient(config) {
|
|
18
|
+
return new _poly_x_core.AuthClient({
|
|
19
|
+
config: (0, _poly_x_core.resolveConfig)({
|
|
20
|
+
key: config.publishableKey,
|
|
21
|
+
baseUrl: config.baseUrl,
|
|
22
|
+
context: "server"
|
|
23
|
+
}),
|
|
24
|
+
transport: new _poly_x_core.FetchTransport()
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/server.ts
|
|
29
|
+
/**
|
|
30
|
+
* @poly-x/next/server — App Router server helpers (F007). `auth()` reads the
|
|
31
|
+
* httpOnly session cookie (usable in RSC — read-only); `createAuthHandlers()`
|
|
32
|
+
* returns the route handlers that do every cookie write (login/callback/refresh/
|
|
33
|
+
* signout), since Server Components can't set cookies.
|
|
34
|
+
*/
|
|
35
|
+
const VERIFIER_COOKIE = "polyx_pkce";
|
|
36
|
+
const VERIFIER_MAX_AGE = 600;
|
|
37
|
+
function adapt(store) {
|
|
38
|
+
return {
|
|
39
|
+
get: (name) => store.get(name)?.value,
|
|
40
|
+
set: (name, value, options) => store.set(name, value, options),
|
|
41
|
+
delete: (name) => store.delete(name)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function requestCookies() {
|
|
45
|
+
return adapt(await (0, next_headers.cookies)());
|
|
46
|
+
}
|
|
47
|
+
/** Read the current session — safe in Server Components (no cookie write). */
|
|
48
|
+
async function auth(overrides) {
|
|
49
|
+
const config = resolveNextConfig(overrides);
|
|
50
|
+
return require_server_session.toAuthContext(await new require_server_session.ServerSession({
|
|
51
|
+
authClient: buildServerAuthClient(config),
|
|
52
|
+
secret: config.secret,
|
|
53
|
+
cookies: await requestCookies()
|
|
54
|
+
}).read());
|
|
55
|
+
}
|
|
56
|
+
function createAuthHandlers(handlersConfig = {}) {
|
|
57
|
+
const callbackPath = handlersConfig.callbackPath ?? "/api/polyx/callback";
|
|
58
|
+
const afterSignInPath = handlersConfig.afterSignInPath ?? "/";
|
|
59
|
+
function newSession(store, config) {
|
|
60
|
+
return new require_server_session.ServerSession({
|
|
61
|
+
authClient: buildServerAuthClient(config),
|
|
62
|
+
secret: config.secret,
|
|
63
|
+
cookies: store
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
async signin(request) {
|
|
68
|
+
const config = resolveNextConfig(handlersConfig);
|
|
69
|
+
const redirectUri = `${new URL(request.url).origin}${callbackPath}`;
|
|
70
|
+
const { verifier, challenge } = await (0, _poly_x_core.generatePkce)();
|
|
71
|
+
const state = (0, _poly_x_core.randomState)();
|
|
72
|
+
(await (0, next_headers.cookies)()).set(VERIFIER_COOKIE, JSON.stringify({
|
|
73
|
+
verifier,
|
|
74
|
+
state,
|
|
75
|
+
redirectUri
|
|
76
|
+
}), {
|
|
77
|
+
httpOnly: true,
|
|
78
|
+
secure: true,
|
|
79
|
+
sameSite: "lax",
|
|
80
|
+
path: "/",
|
|
81
|
+
maxAge: VERIFIER_MAX_AGE
|
|
82
|
+
});
|
|
83
|
+
const url = buildServerAuthClient(config).buildAuthorizeUrl({
|
|
84
|
+
redirectUri,
|
|
85
|
+
state,
|
|
86
|
+
challenge
|
|
87
|
+
});
|
|
88
|
+
return Response.redirect(url, 302);
|
|
89
|
+
},
|
|
90
|
+
async callback(request) {
|
|
91
|
+
const config = resolveNextConfig(handlersConfig);
|
|
92
|
+
const url = new URL(request.url);
|
|
93
|
+
const code = url.searchParams.get("code");
|
|
94
|
+
const state = url.searchParams.get("state");
|
|
95
|
+
const store = await (0, next_headers.cookies)();
|
|
96
|
+
const raw = store.get(VERIFIER_COOKIE)?.value;
|
|
97
|
+
store.delete(VERIFIER_COOKIE);
|
|
98
|
+
const home = `${url.origin}${afterSignInPath}`;
|
|
99
|
+
if (!code || !state || !raw) return Response.redirect(home, 302);
|
|
100
|
+
const tx = JSON.parse(raw);
|
|
101
|
+
if (tx.state !== state) return Response.redirect(home, 302);
|
|
102
|
+
await newSession(adapt(store), config).establishFromCode(code, tx.verifier, tx.redirectUri);
|
|
103
|
+
return Response.redirect(home, 302);
|
|
104
|
+
},
|
|
105
|
+
async refresh() {
|
|
106
|
+
const config = resolveNextConfig(handlersConfig);
|
|
107
|
+
const session = await newSession(await requestCookies(), config).refresh();
|
|
108
|
+
return Response.json({ isSignedIn: session !== null }, { status: session ? 200 : 401 });
|
|
109
|
+
},
|
|
110
|
+
async signout(request) {
|
|
111
|
+
const config = resolveNextConfig(handlersConfig);
|
|
112
|
+
newSession(adapt(await (0, next_headers.cookies)()), config).clear();
|
|
113
|
+
try {
|
|
114
|
+
await buildServerAuthClient(config).logout("device");
|
|
115
|
+
} catch {}
|
|
116
|
+
return Response.redirect(new URL(request.url).origin + "/", 302);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
exports.DEFAULT_SESSION_COOKIE = require_server_session.DEFAULT_SESSION_COOKIE;
|
|
122
|
+
exports.ServerSession = require_server_session.ServerSession;
|
|
123
|
+
exports.auth = auth;
|
|
124
|
+
exports.createAuthHandlers = createAuthHandlers;
|
|
125
|
+
exports.openSession = require_server_session.openSession;
|
|
126
|
+
exports.resolveNextConfig = resolveNextConfig;
|
|
127
|
+
exports.sealSession = require_server_session.sealSession;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { AuthClient, StoredSession } from "@poly-x/core";
|
|
2
|
+
|
|
3
|
+
//#region src/config.d.ts
|
|
4
|
+
interface NextServerConfig {
|
|
5
|
+
publishableKey: string;
|
|
6
|
+
/** Secret for encrypting the session cookie (AES-GCM key derivation). */
|
|
7
|
+
secret: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
declare function resolveNextConfig(overrides?: Partial<NextServerConfig>): NextServerConfig;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/cookie-adapter.d.ts
|
|
13
|
+
/**
|
|
14
|
+
* Cookie access behind a seam so the server-session core is testable without a
|
|
15
|
+
* Next runtime. The Next adapter (built from an awaited `cookies()` store) lives
|
|
16
|
+
* in server.ts / proxy.ts; a fake backs the unit tests.
|
|
17
|
+
*/
|
|
18
|
+
interface CookieSetOptions {
|
|
19
|
+
httpOnly?: boolean;
|
|
20
|
+
secure?: boolean;
|
|
21
|
+
sameSite?: "lax" | "strict" | "none";
|
|
22
|
+
path?: string;
|
|
23
|
+
maxAge?: number;
|
|
24
|
+
}
|
|
25
|
+
interface CookieAdapter {
|
|
26
|
+
get(name: string): string | undefined;
|
|
27
|
+
set(name: string, value: string, options?: CookieSetOptions): void;
|
|
28
|
+
delete(name: string): void;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/server-session.d.ts
|
|
32
|
+
declare const DEFAULT_SESSION_COOKIE = "polyx_session";
|
|
33
|
+
interface ServerSessionOptions {
|
|
34
|
+
authClient: AuthClient;
|
|
35
|
+
secret: string;
|
|
36
|
+
cookies: CookieAdapter;
|
|
37
|
+
cookieName?: string;
|
|
38
|
+
}
|
|
39
|
+
declare class ServerSession {
|
|
40
|
+
private readonly authClient;
|
|
41
|
+
private readonly secret;
|
|
42
|
+
private readonly cookies;
|
|
43
|
+
private readonly cookieName;
|
|
44
|
+
constructor(options: ServerSessionOptions);
|
|
45
|
+
establishFromCode(code: string, codeVerifier: string, redirectUri: string): Promise<StoredSession>;
|
|
46
|
+
read(): Promise<StoredSession | null>;
|
|
47
|
+
/** Rotate the tokens; clears the cookie (returns null) on a terminal refresh failure. */
|
|
48
|
+
refresh(): Promise<StoredSession | null>;
|
|
49
|
+
clear(): void;
|
|
50
|
+
private write;
|
|
51
|
+
}
|
|
52
|
+
interface AuthContext {
|
|
53
|
+
isSignedIn: boolean;
|
|
54
|
+
userId: string | null;
|
|
55
|
+
organizationId?: string;
|
|
56
|
+
claims: StoredSession["session"]["claims"] | null;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/cookie-codec.d.ts
|
|
60
|
+
declare function sealSession(session: StoredSession, secret: string): Promise<string>;
|
|
61
|
+
declare function openSession(token: string, secret: string): Promise<StoredSession | null>;
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/server.d.ts
|
|
64
|
+
/** Read the current session — safe in Server Components (no cookie write). */
|
|
65
|
+
declare function auth(overrides?: Partial<NextServerConfig>): Promise<AuthContext>;
|
|
66
|
+
interface AuthHandlersConfig extends Partial<NextServerConfig> {
|
|
67
|
+
/** Where to send the user after a successful sign-in. Default `/`. */
|
|
68
|
+
afterSignInPath?: string;
|
|
69
|
+
/** The callback route path this app mounts. Default `/api/polyx/callback`. */
|
|
70
|
+
callbackPath?: string;
|
|
71
|
+
}
|
|
72
|
+
interface AuthHandlers {
|
|
73
|
+
signin(request: Request): Promise<Response>;
|
|
74
|
+
callback(request: Request): Promise<Response>;
|
|
75
|
+
refresh(request: Request): Promise<Response>;
|
|
76
|
+
signout(request: Request): Promise<Response>;
|
|
77
|
+
}
|
|
78
|
+
declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
|
|
79
|
+
//#endregion
|
|
80
|
+
export { type AuthContext, AuthHandlers, AuthHandlersConfig, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { AuthClient, StoredSession } from "@poly-x/core";
|
|
2
|
+
|
|
3
|
+
//#region src/config.d.ts
|
|
4
|
+
interface NextServerConfig {
|
|
5
|
+
publishableKey: string;
|
|
6
|
+
/** Secret for encrypting the session cookie (AES-GCM key derivation). */
|
|
7
|
+
secret: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
declare function resolveNextConfig(overrides?: Partial<NextServerConfig>): NextServerConfig;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/cookie-adapter.d.ts
|
|
13
|
+
/**
|
|
14
|
+
* Cookie access behind a seam so the server-session core is testable without a
|
|
15
|
+
* Next runtime. The Next adapter (built from an awaited `cookies()` store) lives
|
|
16
|
+
* in server.ts / proxy.ts; a fake backs the unit tests.
|
|
17
|
+
*/
|
|
18
|
+
interface CookieSetOptions {
|
|
19
|
+
httpOnly?: boolean;
|
|
20
|
+
secure?: boolean;
|
|
21
|
+
sameSite?: "lax" | "strict" | "none";
|
|
22
|
+
path?: string;
|
|
23
|
+
maxAge?: number;
|
|
24
|
+
}
|
|
25
|
+
interface CookieAdapter {
|
|
26
|
+
get(name: string): string | undefined;
|
|
27
|
+
set(name: string, value: string, options?: CookieSetOptions): void;
|
|
28
|
+
delete(name: string): void;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/server-session.d.ts
|
|
32
|
+
declare const DEFAULT_SESSION_COOKIE = "polyx_session";
|
|
33
|
+
interface ServerSessionOptions {
|
|
34
|
+
authClient: AuthClient;
|
|
35
|
+
secret: string;
|
|
36
|
+
cookies: CookieAdapter;
|
|
37
|
+
cookieName?: string;
|
|
38
|
+
}
|
|
39
|
+
declare class ServerSession {
|
|
40
|
+
private readonly authClient;
|
|
41
|
+
private readonly secret;
|
|
42
|
+
private readonly cookies;
|
|
43
|
+
private readonly cookieName;
|
|
44
|
+
constructor(options: ServerSessionOptions);
|
|
45
|
+
establishFromCode(code: string, codeVerifier: string, redirectUri: string): Promise<StoredSession>;
|
|
46
|
+
read(): Promise<StoredSession | null>;
|
|
47
|
+
/** Rotate the tokens; clears the cookie (returns null) on a terminal refresh failure. */
|
|
48
|
+
refresh(): Promise<StoredSession | null>;
|
|
49
|
+
clear(): void;
|
|
50
|
+
private write;
|
|
51
|
+
}
|
|
52
|
+
interface AuthContext {
|
|
53
|
+
isSignedIn: boolean;
|
|
54
|
+
userId: string | null;
|
|
55
|
+
organizationId?: string;
|
|
56
|
+
claims: StoredSession["session"]["claims"] | null;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/cookie-codec.d.ts
|
|
60
|
+
declare function sealSession(session: StoredSession, secret: string): Promise<string>;
|
|
61
|
+
declare function openSession(token: string, secret: string): Promise<StoredSession | null>;
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/server.d.ts
|
|
64
|
+
/** Read the current session — safe in Server Components (no cookie write). */
|
|
65
|
+
declare function auth(overrides?: Partial<NextServerConfig>): Promise<AuthContext>;
|
|
66
|
+
interface AuthHandlersConfig extends Partial<NextServerConfig> {
|
|
67
|
+
/** Where to send the user after a successful sign-in. Default `/`. */
|
|
68
|
+
afterSignInPath?: string;
|
|
69
|
+
/** The callback route path this app mounts. Default `/api/polyx/callback`. */
|
|
70
|
+
callbackPath?: string;
|
|
71
|
+
}
|
|
72
|
+
interface AuthHandlers {
|
|
73
|
+
signin(request: Request): Promise<Response>;
|
|
74
|
+
callback(request: Request): Promise<Response>;
|
|
75
|
+
refresh(request: Request): Promise<Response>;
|
|
76
|
+
signout(request: Request): Promise<Response>;
|
|
77
|
+
}
|
|
78
|
+
declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
|
|
79
|
+
//#endregion
|
|
80
|
+
export { type AuthContext, AuthHandlers, AuthHandlersConfig, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
|
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { a as sealSession, i as openSession, n as ServerSession, r as toAuthContext, t as DEFAULT_SESSION_COOKIE } from "./server-session-B3zJ7CS8.mjs";
|
|
2
|
+
import { AuthClient, FetchTransport, generatePkce, randomState, resolveConfig } from "@poly-x/core";
|
|
3
|
+
import { cookies } from "next/headers";
|
|
4
|
+
//#region src/config.ts
|
|
5
|
+
function resolveNextConfig(overrides = {}) {
|
|
6
|
+
const publishableKey = overrides.publishableKey ?? process.env.NEXT_PUBLIC_POLYX_PUBLISHABLE_KEY;
|
|
7
|
+
const secret = overrides.secret ?? process.env.POLYX_SESSION_SECRET;
|
|
8
|
+
if (!publishableKey) throw new Error("PolyX: missing publishable key (set NEXT_PUBLIC_POLYX_PUBLISHABLE_KEY or pass config.publishableKey).");
|
|
9
|
+
if (!secret) throw new Error("PolyX: missing POLYX_SESSION_SECRET (needed to encrypt the session cookie).");
|
|
10
|
+
return {
|
|
11
|
+
publishableKey,
|
|
12
|
+
secret,
|
|
13
|
+
baseUrl: overrides.baseUrl ?? process.env.POLYX_BASE_URL
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function buildServerAuthClient(config) {
|
|
17
|
+
return new AuthClient({
|
|
18
|
+
config: resolveConfig({
|
|
19
|
+
key: config.publishableKey,
|
|
20
|
+
baseUrl: config.baseUrl,
|
|
21
|
+
context: "server"
|
|
22
|
+
}),
|
|
23
|
+
transport: new FetchTransport()
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/server.ts
|
|
28
|
+
/**
|
|
29
|
+
* @poly-x/next/server — App Router server helpers (F007). `auth()` reads the
|
|
30
|
+
* httpOnly session cookie (usable in RSC — read-only); `createAuthHandlers()`
|
|
31
|
+
* returns the route handlers that do every cookie write (login/callback/refresh/
|
|
32
|
+
* signout), since Server Components can't set cookies.
|
|
33
|
+
*/
|
|
34
|
+
const VERIFIER_COOKIE = "polyx_pkce";
|
|
35
|
+
const VERIFIER_MAX_AGE = 600;
|
|
36
|
+
function adapt(store) {
|
|
37
|
+
return {
|
|
38
|
+
get: (name) => store.get(name)?.value,
|
|
39
|
+
set: (name, value, options) => store.set(name, value, options),
|
|
40
|
+
delete: (name) => store.delete(name)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function requestCookies() {
|
|
44
|
+
return adapt(await cookies());
|
|
45
|
+
}
|
|
46
|
+
/** Read the current session — safe in Server Components (no cookie write). */
|
|
47
|
+
async function auth(overrides) {
|
|
48
|
+
const config = resolveNextConfig(overrides);
|
|
49
|
+
return toAuthContext(await new ServerSession({
|
|
50
|
+
authClient: buildServerAuthClient(config),
|
|
51
|
+
secret: config.secret,
|
|
52
|
+
cookies: await requestCookies()
|
|
53
|
+
}).read());
|
|
54
|
+
}
|
|
55
|
+
function createAuthHandlers(handlersConfig = {}) {
|
|
56
|
+
const callbackPath = handlersConfig.callbackPath ?? "/api/polyx/callback";
|
|
57
|
+
const afterSignInPath = handlersConfig.afterSignInPath ?? "/";
|
|
58
|
+
function newSession(store, config) {
|
|
59
|
+
return new ServerSession({
|
|
60
|
+
authClient: buildServerAuthClient(config),
|
|
61
|
+
secret: config.secret,
|
|
62
|
+
cookies: store
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
async signin(request) {
|
|
67
|
+
const config = resolveNextConfig(handlersConfig);
|
|
68
|
+
const redirectUri = `${new URL(request.url).origin}${callbackPath}`;
|
|
69
|
+
const { verifier, challenge } = await generatePkce();
|
|
70
|
+
const state = randomState();
|
|
71
|
+
(await cookies()).set(VERIFIER_COOKIE, JSON.stringify({
|
|
72
|
+
verifier,
|
|
73
|
+
state,
|
|
74
|
+
redirectUri
|
|
75
|
+
}), {
|
|
76
|
+
httpOnly: true,
|
|
77
|
+
secure: true,
|
|
78
|
+
sameSite: "lax",
|
|
79
|
+
path: "/",
|
|
80
|
+
maxAge: VERIFIER_MAX_AGE
|
|
81
|
+
});
|
|
82
|
+
const url = buildServerAuthClient(config).buildAuthorizeUrl({
|
|
83
|
+
redirectUri,
|
|
84
|
+
state,
|
|
85
|
+
challenge
|
|
86
|
+
});
|
|
87
|
+
return Response.redirect(url, 302);
|
|
88
|
+
},
|
|
89
|
+
async callback(request) {
|
|
90
|
+
const config = resolveNextConfig(handlersConfig);
|
|
91
|
+
const url = new URL(request.url);
|
|
92
|
+
const code = url.searchParams.get("code");
|
|
93
|
+
const state = url.searchParams.get("state");
|
|
94
|
+
const store = await cookies();
|
|
95
|
+
const raw = store.get(VERIFIER_COOKIE)?.value;
|
|
96
|
+
store.delete(VERIFIER_COOKIE);
|
|
97
|
+
const home = `${url.origin}${afterSignInPath}`;
|
|
98
|
+
if (!code || !state || !raw) return Response.redirect(home, 302);
|
|
99
|
+
const tx = JSON.parse(raw);
|
|
100
|
+
if (tx.state !== state) return Response.redirect(home, 302);
|
|
101
|
+
await newSession(adapt(store), config).establishFromCode(code, tx.verifier, tx.redirectUri);
|
|
102
|
+
return Response.redirect(home, 302);
|
|
103
|
+
},
|
|
104
|
+
async refresh() {
|
|
105
|
+
const config = resolveNextConfig(handlersConfig);
|
|
106
|
+
const session = await newSession(await requestCookies(), config).refresh();
|
|
107
|
+
return Response.json({ isSignedIn: session !== null }, { status: session ? 200 : 401 });
|
|
108
|
+
},
|
|
109
|
+
async signout(request) {
|
|
110
|
+
const config = resolveNextConfig(handlersConfig);
|
|
111
|
+
newSession(adapt(await cookies()), config).clear();
|
|
112
|
+
try {
|
|
113
|
+
await buildServerAuthClient(config).logout("device");
|
|
114
|
+
} catch {}
|
|
115
|
+
return Response.redirect(new URL(request.url).origin + "/", 302);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { DEFAULT_SESSION_COOKIE, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@poly-x/next",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "PolyX SDK for Next.js App Router - components plus server helpers",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.mts",
|
|
15
|
+
"default": "./dist/index.mjs"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
|
+
"default": "./dist/index.cjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"./server": {
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./dist/server.d.mts",
|
|
25
|
+
"default": "./dist/server.mjs"
|
|
26
|
+
},
|
|
27
|
+
"require": {
|
|
28
|
+
"types": "./dist/server.d.cts",
|
|
29
|
+
"default": "./dist/server.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"./proxy": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/proxy.d.mts",
|
|
35
|
+
"default": "./dist/proxy.mjs"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/proxy.d.cts",
|
|
39
|
+
"default": "./dist/proxy.cjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"main": "./dist/index.cjs",
|
|
44
|
+
"module": "./dist/index.mjs",
|
|
45
|
+
"types": "./dist/index.d.cts",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@poly-x/react": "0.1.0-alpha.0",
|
|
48
|
+
"@poly-x/core": "0.1.0-alpha.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"next": ">=14",
|
|
52
|
+
"react": ">=18"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "https://dev.azure.com/waiindustries/wAI%20Engineering/_git/poly-x-sdk"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=20"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/react": "^19.2.17",
|
|
66
|
+
"next": "^16.2.10",
|
|
67
|
+
"react": "^19.2.7",
|
|
68
|
+
"react-dom": "^19.2.7"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "tsdown",
|
|
72
|
+
"test": "vitest run",
|
|
73
|
+
"lint": "eslint src"
|
|
74
|
+
}
|
|
75
|
+
}
|