@poly-x/next 0.1.0-alpha.5 → 0.1.0-alpha.7

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
@@ -160,10 +160,55 @@ function createAuthHandlers(handlersConfig = {}) {
160
160
  await client.setPassword({
161
161
  userId: proof.userId,
162
162
  newPassword,
163
- confirmPassword
163
+ confirmPassword,
164
+ ticket: proof.ticket
164
165
  });
165
166
  return attempt(newPassword);
166
167
  },
168
+ async forgotPassword(request) {
169
+ const config = resolveNextConfig(handlersConfig);
170
+ let payload;
171
+ try {
172
+ payload = await request.json();
173
+ } catch {
174
+ return Response.json({ status: "invalid_request" }, { status: 400 });
175
+ }
176
+ const email = typeof payload.email === "string" ? payload.email.trim() : "";
177
+ if (!email) return Response.json({ status: "invalid_request" }, { status: 400 });
178
+ try {
179
+ await buildServerAuthClient(config).requestPasswordReset({ email });
180
+ } catch {}
181
+ return Response.json({ status: "accepted" });
182
+ },
183
+ async resetPassword(request) {
184
+ const config = resolveNextConfig(handlersConfig);
185
+ let payload;
186
+ try {
187
+ payload = await request.json();
188
+ } catch {
189
+ return Response.json({ status: "invalid_request" }, { status: 400 });
190
+ }
191
+ const token = typeof payload.token === "string" ? payload.token : "";
192
+ const newPassword = typeof payload.newPassword === "string" ? payload.newPassword : "";
193
+ const confirmPassword = typeof payload.confirmPassword === "string" ? payload.confirmPassword : void 0;
194
+ if (!token) return Response.json({ status: "invalid_request" }, { status: 400 });
195
+ if (newPassword.length < MIN_PASSWORD_LENGTH) return Response.json({
196
+ status: "weak_password",
197
+ minLength: MIN_PASSWORD_LENGTH
198
+ }, { status: 400 });
199
+ if (confirmPassword !== void 0 && newPassword !== confirmPassword) return Response.json({ status: "password_mismatch" }, { status: 400 });
200
+ switch (await buildServerAuthClient(config).resetPassword({
201
+ token,
202
+ newPassword
203
+ })) {
204
+ case "ok": return Response.json({ status: "success" });
205
+ case "weak_password": return Response.json({
206
+ status: "weak_password",
207
+ minLength: MIN_PASSWORD_LENGTH
208
+ }, { status: 400 });
209
+ default: return Response.json({ status: "invalid_or_expired" }, { status: 400 });
210
+ }
211
+ },
167
212
  async callback(request) {
168
213
  const config = resolveNextConfig(handlersConfig);
169
214
  const url = new URL(request.url);
package/dist/server.d.cts CHANGED
@@ -80,10 +80,40 @@ interface AuthHandlers {
80
80
  * The browser never sees the code, the tokens, or the credentials after POST.
81
81
  */
82
82
  authenticate(request: Request): Promise<Response>;
83
+ /**
84
+ * The `<ForgotPassword/>` BFF endpoint: reads `{email}`, adds this app's publishable
85
+ * key, and asks poly-auth to email a reset link server-side. Always relays a uniform
86
+ * `accepted` — it never reveals whether the account exists.
87
+ */
88
+ forgotPassword(request: Request): Promise<Response>;
89
+ /**
90
+ * The `<ResetPassword/>` BFF endpoint: reads `{token, newPassword}` and redeems the
91
+ * opaque single-use token. `success` on completion; `invalid_or_expired` otherwise.
92
+ */
93
+ resetPassword(request: Request): Promise<Response>;
83
94
  callback(request: Request): Promise<Response>;
84
95
  refresh(request: Request): Promise<Response>;
85
96
  signout(request: Request): Promise<Response>;
86
97
  }
98
+ /** The JSON the `forgotPassword` handler returns — always uniform (no enumeration). */
99
+ type ForgotPasswordResult = {
100
+ status: "accepted";
101
+ } | {
102
+ status: "invalid_request";
103
+ };
104
+ /** The JSON the `resetPassword` handler returns — the `<ResetPassword/>` form branches on `status`. */
105
+ type ResetPasswordResult = {
106
+ status: "success";
107
+ } | {
108
+ status: "weak_password";
109
+ minLength: number;
110
+ } | {
111
+ status: "password_mismatch";
112
+ } | {
113
+ status: "invalid_or_expired";
114
+ } | {
115
+ status: "invalid_request";
116
+ };
87
117
  /** The JSON the `authenticate` handler returns — the `<SignIn/>` form branches on `status`. */
88
118
  type AuthenticateResult = {
89
119
  status: "success";
@@ -109,4 +139,4 @@ type AuthenticateResult = {
109
139
  };
110
140
  declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
111
141
  //#endregion
112
- export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
142
+ export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, ForgotPasswordResult, type NextServerConfig, ResetPasswordResult, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
package/dist/server.d.mts CHANGED
@@ -80,10 +80,40 @@ interface AuthHandlers {
80
80
  * The browser never sees the code, the tokens, or the credentials after POST.
81
81
  */
82
82
  authenticate(request: Request): Promise<Response>;
83
+ /**
84
+ * The `<ForgotPassword/>` BFF endpoint: reads `{email}`, adds this app's publishable
85
+ * key, and asks poly-auth to email a reset link server-side. Always relays a uniform
86
+ * `accepted` — it never reveals whether the account exists.
87
+ */
88
+ forgotPassword(request: Request): Promise<Response>;
89
+ /**
90
+ * The `<ResetPassword/>` BFF endpoint: reads `{token, newPassword}` and redeems the
91
+ * opaque single-use token. `success` on completion; `invalid_or_expired` otherwise.
92
+ */
93
+ resetPassword(request: Request): Promise<Response>;
83
94
  callback(request: Request): Promise<Response>;
84
95
  refresh(request: Request): Promise<Response>;
85
96
  signout(request: Request): Promise<Response>;
86
97
  }
98
+ /** The JSON the `forgotPassword` handler returns — always uniform (no enumeration). */
99
+ type ForgotPasswordResult = {
100
+ status: "accepted";
101
+ } | {
102
+ status: "invalid_request";
103
+ };
104
+ /** The JSON the `resetPassword` handler returns — the `<ResetPassword/>` form branches on `status`. */
105
+ type ResetPasswordResult = {
106
+ status: "success";
107
+ } | {
108
+ status: "weak_password";
109
+ minLength: number;
110
+ } | {
111
+ status: "password_mismatch";
112
+ } | {
113
+ status: "invalid_or_expired";
114
+ } | {
115
+ status: "invalid_request";
116
+ };
87
117
  /** The JSON the `authenticate` handler returns — the `<SignIn/>` form branches on `status`. */
88
118
  type AuthenticateResult = {
89
119
  status: "success";
@@ -109,4 +139,4 @@ type AuthenticateResult = {
109
139
  };
110
140
  declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
111
141
  //#endregion
112
- export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
142
+ export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, ForgotPasswordResult, type NextServerConfig, ResetPasswordResult, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
package/dist/server.mjs CHANGED
@@ -159,10 +159,55 @@ function createAuthHandlers(handlersConfig = {}) {
159
159
  await client.setPassword({
160
160
  userId: proof.userId,
161
161
  newPassword,
162
- confirmPassword
162
+ confirmPassword,
163
+ ticket: proof.ticket
163
164
  });
164
165
  return attempt(newPassword);
165
166
  },
167
+ async forgotPassword(request) {
168
+ const config = resolveNextConfig(handlersConfig);
169
+ let payload;
170
+ try {
171
+ payload = await request.json();
172
+ } catch {
173
+ return Response.json({ status: "invalid_request" }, { status: 400 });
174
+ }
175
+ const email = typeof payload.email === "string" ? payload.email.trim() : "";
176
+ if (!email) return Response.json({ status: "invalid_request" }, { status: 400 });
177
+ try {
178
+ await buildServerAuthClient(config).requestPasswordReset({ email });
179
+ } catch {}
180
+ return Response.json({ status: "accepted" });
181
+ },
182
+ async resetPassword(request) {
183
+ const config = resolveNextConfig(handlersConfig);
184
+ let payload;
185
+ try {
186
+ payload = await request.json();
187
+ } catch {
188
+ return Response.json({ status: "invalid_request" }, { status: 400 });
189
+ }
190
+ const token = typeof payload.token === "string" ? payload.token : "";
191
+ const newPassword = typeof payload.newPassword === "string" ? payload.newPassword : "";
192
+ const confirmPassword = typeof payload.confirmPassword === "string" ? payload.confirmPassword : void 0;
193
+ if (!token) return Response.json({ status: "invalid_request" }, { status: 400 });
194
+ if (newPassword.length < MIN_PASSWORD_LENGTH) return Response.json({
195
+ status: "weak_password",
196
+ minLength: MIN_PASSWORD_LENGTH
197
+ }, { status: 400 });
198
+ if (confirmPassword !== void 0 && newPassword !== confirmPassword) return Response.json({ status: "password_mismatch" }, { status: 400 });
199
+ switch (await buildServerAuthClient(config).resetPassword({
200
+ token,
201
+ newPassword
202
+ })) {
203
+ case "ok": return Response.json({ status: "success" });
204
+ case "weak_password": return Response.json({
205
+ status: "weak_password",
206
+ minLength: MIN_PASSWORD_LENGTH
207
+ }, { status: 400 });
208
+ default: return Response.json({ status: "invalid_or_expired" }, { status: 400 });
209
+ }
210
+ },
166
211
  async callback(request) {
167
212
  const config = resolveNextConfig(handlersConfig);
168
213
  const url = new URL(request.url);
package/dist/styles.css CHANGED
@@ -339,6 +339,77 @@
339
339
  opacity: 0.85;
340
340
  }
341
341
 
342
+ /* --- Recovery confirmation states (ForgotPassword / ResetPassword) ------- */
343
+
344
+ .polyx-signin__header--center {
345
+ align-items: center;
346
+ text-align: center;
347
+ }
348
+
349
+ .polyx-signin__status-icon {
350
+ display: inline-flex;
351
+ align-items: center;
352
+ justify-content: center;
353
+ width: 3rem;
354
+ height: 3rem;
355
+ margin-bottom: 0.25rem;
356
+ border-radius: 999px;
357
+ }
358
+
359
+ .polyx-signin__status-icon svg {
360
+ width: 1.5rem;
361
+ height: 1.5rem;
362
+ }
363
+
364
+ .polyx-signin__status-icon--brand {
365
+ color: var(--polyx-brand);
366
+ background: color-mix(in srgb, var(--polyx-brand) 12%, transparent);
367
+ }
368
+
369
+ .polyx-signin__status-icon--danger {
370
+ color: var(--polyx-danger);
371
+ background: var(--polyx-danger-bg);
372
+ }
373
+
374
+ /* The entered email, emphasized inside the confirmation copy. */
375
+ .polyx-signin__email {
376
+ color: var(--polyx-fg);
377
+ font-weight: 600;
378
+ word-break: break-all;
379
+ }
380
+
381
+ /* Secondary text-link actions under a confirmation screen — quieter than the CTA. */
382
+ .polyx-signin__actions {
383
+ display: flex;
384
+ flex-direction: column;
385
+ align-items: center;
386
+ gap: 0.75rem;
387
+ margin-top: 1.5rem;
388
+ }
389
+
390
+ .polyx-signin__link {
391
+ padding: 0;
392
+ font: inherit;
393
+ font-size: 0.875rem;
394
+ font-weight: 500;
395
+ color: var(--polyx-brand);
396
+ background: none;
397
+ border: none;
398
+ cursor: pointer;
399
+ text-decoration: none;
400
+ }
401
+
402
+ .polyx-signin__link:hover {
403
+ color: var(--polyx-brand-hover);
404
+ text-decoration: underline;
405
+ }
406
+
407
+ .polyx-signin__link:focus-visible {
408
+ outline: none;
409
+ border-radius: 2px;
410
+ box-shadow: 0 0 0 3px var(--polyx-ring);
411
+ }
412
+
342
413
  /* --- Workspace picker ---------------------------------------------------- */
343
414
 
344
415
  .polyx-signin__tenants {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poly-x/next",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.7",
4
4
  "description": "PolyX SDK for Next.js App Router - components plus server helpers",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,8 +47,8 @@
47
47
  "module": "./dist/index.mjs",
48
48
  "types": "./dist/index.d.cts",
49
49
  "dependencies": {
50
- "@poly-x/core": "0.1.0-alpha.5",
51
- "@poly-x/react": "0.1.0-alpha.5"
50
+ "@poly-x/core": "0.1.0-alpha.7",
51
+ "@poly-x/react": "0.1.0-alpha.7"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "next": ">=14",