@poly-x/next 0.1.0-alpha.0 → 0.1.0-alpha.2

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/dist/server.cjs CHANGED
@@ -11,7 +11,8 @@ function resolveNextConfig(overrides = {}) {
11
11
  return {
12
12
  publishableKey,
13
13
  secret,
14
- baseUrl: overrides.baseUrl ?? process.env.POLYX_BASE_URL
14
+ baseUrl: overrides.baseUrl ?? process.env.POLYX_BASE_URL,
15
+ signInUrl: overrides.signInUrl ?? process.env.NEXT_PUBLIC_POLYX_SIGN_IN_URL
15
16
  };
16
17
  }
17
18
  function buildServerAuthClient(config) {
@@ -19,6 +20,7 @@ function buildServerAuthClient(config) {
19
20
  config: (0, _poly_x_core.resolveConfig)({
20
21
  key: config.publishableKey,
21
22
  baseUrl: config.baseUrl,
23
+ signInUrl: config.signInUrl,
22
24
  context: "server"
23
25
  }),
24
26
  transport: new _poly_x_core.FetchTransport()
@@ -80,13 +82,58 @@ function createAuthHandlers(handlersConfig = {}) {
80
82
  path: "/",
81
83
  maxAge: VERIFIER_MAX_AGE
82
84
  });
83
- const url = buildServerAuthClient(config).buildAuthorizeUrl({
85
+ const url = buildServerAuthClient(config).buildSignInUrl({
84
86
  redirectUri,
85
87
  state,
86
88
  challenge
87
89
  });
88
90
  return Response.redirect(url, 302);
89
91
  },
92
+ async authenticate(request) {
93
+ const config = resolveNextConfig(handlersConfig);
94
+ const redirectUri = `${new URL(request.url).origin}${callbackPath}`;
95
+ let payload;
96
+ try {
97
+ payload = await request.json();
98
+ } catch {
99
+ return Response.json({ status: "invalid_request" }, { status: 400 });
100
+ }
101
+ const email = typeof payload.email === "string" ? payload.email : "";
102
+ const password = typeof payload.password === "string" ? payload.password : "";
103
+ if (!email || !password) return Response.json({ status: "invalid_request" }, { status: 400 });
104
+ const organizationCode = typeof payload.organizationCode === "string" ? payload.organizationCode : void 0;
105
+ const tenantId = typeof payload.tenantId === "string" ? payload.tenantId : void 0;
106
+ const { verifier, challenge } = await (0, _poly_x_core.generatePkce)();
107
+ const state = (0, _poly_x_core.randomState)();
108
+ const outcome = await buildServerAuthClient(config).authorizeWithPassword({
109
+ email,
110
+ password,
111
+ redirectUri,
112
+ state,
113
+ challenge,
114
+ organizationCode,
115
+ tenantId
116
+ });
117
+ switch (outcome.type) {
118
+ case "authorized":
119
+ await newSession(adapt(await (0, next_headers.cookies)()), config).establishFromCode(outcome.code, verifier, redirectUri);
120
+ return Response.json({
121
+ status: "success",
122
+ redirectTo: afterSignInPath
123
+ });
124
+ case "select_tenant": return Response.json({
125
+ status: "select_tenant",
126
+ tenants: outcome.tenants,
127
+ state: outcome.state
128
+ });
129
+ case "password_change_required": return Response.json({
130
+ status: "password_change_required",
131
+ userId: outcome.userId
132
+ }, { status: 403 });
133
+ case "no_access": return Response.json({ status: "no_access" }, { status: 403 });
134
+ default: return Response.json({ status: "invalid_credentials" }, { status: 401 });
135
+ }
136
+ },
90
137
  async callback(request) {
91
138
  const config = resolveNextConfig(handlersConfig);
92
139
  const url = new URL(request.url);
package/dist/server.d.cts CHANGED
@@ -6,6 +6,8 @@ interface NextServerConfig {
6
6
  /** Secret for encrypting the session cookie (AES-GCM key derivation). */
7
7
  secret: string;
8
8
  baseUrl?: string;
9
+ /** Hosted sign-in page URL the signin route redirects to. */
10
+ signInUrl?: string;
9
11
  }
10
12
  declare function resolveNextConfig(overrides?: Partial<NextServerConfig>): NextServerConfig;
11
13
  //#endregion
@@ -71,10 +73,35 @@ interface AuthHandlersConfig extends Partial<NextServerConfig> {
71
73
  }
72
74
  interface AuthHandlers {
73
75
  signin(request: Request): Promise<Response>;
76
+ /**
77
+ * The embedded `<SignIn/>` BFF endpoint: reads `{email, password}` (+ optional
78
+ * `organizationCode`/`tenantId`), runs the password authorize entirely
79
+ * server-side, and — on success — seals the httpOnly session before returning.
80
+ * The browser never sees the code, the tokens, or the credentials after POST.
81
+ */
82
+ authenticate(request: Request): Promise<Response>;
74
83
  callback(request: Request): Promise<Response>;
75
84
  refresh(request: Request): Promise<Response>;
76
85
  signout(request: Request): Promise<Response>;
77
86
  }
87
+ /** The JSON the `authenticate` handler returns — the `<SignIn/>` form branches on `status`. */
88
+ type AuthenticateResult = {
89
+ status: "success";
90
+ redirectTo: string;
91
+ } | {
92
+ status: "select_tenant";
93
+ tenants: unknown[];
94
+ state: string;
95
+ } | {
96
+ status: "password_change_required";
97
+ userId: string;
98
+ } | {
99
+ status: "no_access";
100
+ } | {
101
+ status: "invalid_credentials";
102
+ } | {
103
+ status: "invalid_request";
104
+ };
78
105
  declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
79
106
  //#endregion
80
- export { type AuthContext, AuthHandlers, AuthHandlersConfig, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
107
+ export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
package/dist/server.d.mts CHANGED
@@ -6,6 +6,8 @@ interface NextServerConfig {
6
6
  /** Secret for encrypting the session cookie (AES-GCM key derivation). */
7
7
  secret: string;
8
8
  baseUrl?: string;
9
+ /** Hosted sign-in page URL the signin route redirects to. */
10
+ signInUrl?: string;
9
11
  }
10
12
  declare function resolveNextConfig(overrides?: Partial<NextServerConfig>): NextServerConfig;
11
13
  //#endregion
@@ -71,10 +73,35 @@ interface AuthHandlersConfig extends Partial<NextServerConfig> {
71
73
  }
72
74
  interface AuthHandlers {
73
75
  signin(request: Request): Promise<Response>;
76
+ /**
77
+ * The embedded `<SignIn/>` BFF endpoint: reads `{email, password}` (+ optional
78
+ * `organizationCode`/`tenantId`), runs the password authorize entirely
79
+ * server-side, and — on success — seals the httpOnly session before returning.
80
+ * The browser never sees the code, the tokens, or the credentials after POST.
81
+ */
82
+ authenticate(request: Request): Promise<Response>;
74
83
  callback(request: Request): Promise<Response>;
75
84
  refresh(request: Request): Promise<Response>;
76
85
  signout(request: Request): Promise<Response>;
77
86
  }
87
+ /** The JSON the `authenticate` handler returns — the `<SignIn/>` form branches on `status`. */
88
+ type AuthenticateResult = {
89
+ status: "success";
90
+ redirectTo: string;
91
+ } | {
92
+ status: "select_tenant";
93
+ tenants: unknown[];
94
+ state: string;
95
+ } | {
96
+ status: "password_change_required";
97
+ userId: string;
98
+ } | {
99
+ status: "no_access";
100
+ } | {
101
+ status: "invalid_credentials";
102
+ } | {
103
+ status: "invalid_request";
104
+ };
78
105
  declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
79
106
  //#endregion
80
- export { type AuthContext, AuthHandlers, AuthHandlersConfig, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
107
+ export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
package/dist/server.mjs CHANGED
@@ -10,7 +10,8 @@ function resolveNextConfig(overrides = {}) {
10
10
  return {
11
11
  publishableKey,
12
12
  secret,
13
- baseUrl: overrides.baseUrl ?? process.env.POLYX_BASE_URL
13
+ baseUrl: overrides.baseUrl ?? process.env.POLYX_BASE_URL,
14
+ signInUrl: overrides.signInUrl ?? process.env.NEXT_PUBLIC_POLYX_SIGN_IN_URL
14
15
  };
15
16
  }
16
17
  function buildServerAuthClient(config) {
@@ -18,6 +19,7 @@ function buildServerAuthClient(config) {
18
19
  config: resolveConfig({
19
20
  key: config.publishableKey,
20
21
  baseUrl: config.baseUrl,
22
+ signInUrl: config.signInUrl,
21
23
  context: "server"
22
24
  }),
23
25
  transport: new FetchTransport()
@@ -79,13 +81,58 @@ function createAuthHandlers(handlersConfig = {}) {
79
81
  path: "/",
80
82
  maxAge: VERIFIER_MAX_AGE
81
83
  });
82
- const url = buildServerAuthClient(config).buildAuthorizeUrl({
84
+ const url = buildServerAuthClient(config).buildSignInUrl({
83
85
  redirectUri,
84
86
  state,
85
87
  challenge
86
88
  });
87
89
  return Response.redirect(url, 302);
88
90
  },
91
+ async authenticate(request) {
92
+ const config = resolveNextConfig(handlersConfig);
93
+ const redirectUri = `${new URL(request.url).origin}${callbackPath}`;
94
+ let payload;
95
+ try {
96
+ payload = await request.json();
97
+ } catch {
98
+ return Response.json({ status: "invalid_request" }, { status: 400 });
99
+ }
100
+ const email = typeof payload.email === "string" ? payload.email : "";
101
+ const password = typeof payload.password === "string" ? payload.password : "";
102
+ if (!email || !password) return Response.json({ status: "invalid_request" }, { status: 400 });
103
+ const organizationCode = typeof payload.organizationCode === "string" ? payload.organizationCode : void 0;
104
+ const tenantId = typeof payload.tenantId === "string" ? payload.tenantId : void 0;
105
+ const { verifier, challenge } = await generatePkce();
106
+ const state = randomState();
107
+ const outcome = await buildServerAuthClient(config).authorizeWithPassword({
108
+ email,
109
+ password,
110
+ redirectUri,
111
+ state,
112
+ challenge,
113
+ organizationCode,
114
+ tenantId
115
+ });
116
+ switch (outcome.type) {
117
+ case "authorized":
118
+ await newSession(adapt(await cookies()), config).establishFromCode(outcome.code, verifier, redirectUri);
119
+ return Response.json({
120
+ status: "success",
121
+ redirectTo: afterSignInPath
122
+ });
123
+ case "select_tenant": return Response.json({
124
+ status: "select_tenant",
125
+ tenants: outcome.tenants,
126
+ state: outcome.state
127
+ });
128
+ case "password_change_required": return Response.json({
129
+ status: "password_change_required",
130
+ userId: outcome.userId
131
+ }, { status: 403 });
132
+ case "no_access": return Response.json({ status: "no_access" }, { status: 403 });
133
+ default: return Response.json({ status: "invalid_credentials" }, { status: 401 });
134
+ }
135
+ },
89
136
  async callback(request) {
90
137
  const config = resolveNextConfig(handlersConfig);
91
138
  const url = new URL(request.url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poly-x/next",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "PolyX SDK for Next.js App Router - components plus server helpers",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,8 +44,8 @@
44
44
  "module": "./dist/index.mjs",
45
45
  "types": "./dist/index.d.cts",
46
46
  "dependencies": {
47
- "@poly-x/react": "0.1.0-alpha.0",
48
- "@poly-x/core": "0.1.0-alpha.0"
47
+ "@poly-x/core": "0.1.0-alpha.2",
48
+ "@poly-x/react": "0.1.0-alpha.2"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "next": ">=14",