@poly-x/react 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/index.cjs +148 -11
- package/dist/index.d.cts +35 -9
- package/dist/index.d.mts +35 -9
- package/dist/index.mjs +150 -13
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -109,7 +109,7 @@ function createLoginController(deps) {
|
|
|
109
109
|
redirectUri,
|
|
110
110
|
createdAt: Date.now()
|
|
111
111
|
});
|
|
112
|
-
const url = authClient.
|
|
112
|
+
const url = authClient.buildSignInUrl({
|
|
113
113
|
redirectUri,
|
|
114
114
|
state,
|
|
115
115
|
challenge,
|
|
@@ -257,12 +257,13 @@ var WebLocksLock = class {
|
|
|
257
257
|
};
|
|
258
258
|
//#endregion
|
|
259
259
|
//#region src/provider.tsx
|
|
260
|
-
function PolyXProvider({ publishableKey, baseUrl, children }) {
|
|
260
|
+
function PolyXProvider({ publishableKey, baseUrl, signInUrl, children }) {
|
|
261
261
|
const value = (0, react.useMemo)(() => {
|
|
262
262
|
const authClient = new _poly_x_core.AuthClient({
|
|
263
263
|
config: (0, _poly_x_core.resolveConfig)({
|
|
264
264
|
key: publishableKey,
|
|
265
265
|
baseUrl,
|
|
266
|
+
signInUrl,
|
|
266
267
|
context: "browser"
|
|
267
268
|
}),
|
|
268
269
|
transport: new _poly_x_core.FetchTransport()
|
|
@@ -284,7 +285,11 @@ function PolyXProvider({ publishableKey, baseUrl, children }) {
|
|
|
284
285
|
channel: new BroadcastAuthChannel()
|
|
285
286
|
})
|
|
286
287
|
};
|
|
287
|
-
}, [
|
|
288
|
+
}, [
|
|
289
|
+
publishableKey,
|
|
290
|
+
baseUrl,
|
|
291
|
+
signInUrl
|
|
292
|
+
]);
|
|
288
293
|
(0, react.useEffect)(() => {
|
|
289
294
|
value.engine.hydrate();
|
|
290
295
|
return () => value.engine.dispose();
|
|
@@ -347,14 +352,146 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
347
352
|
}
|
|
348
353
|
//#endregion
|
|
349
354
|
//#region src/components/sign-in.tsx
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
const DEFAULT_LABELS = {
|
|
356
|
+
emailLabel: "Email",
|
|
357
|
+
passwordLabel: "Password",
|
|
358
|
+
submit: "Sign in",
|
|
359
|
+
submitting: "Signing in…",
|
|
360
|
+
chooseWorkspace: "Choose a workspace",
|
|
361
|
+
invalidCredentials: "Invalid email or password.",
|
|
362
|
+
noAccess: "You don't have access to this application.",
|
|
363
|
+
passwordChangeRequired: "You must reset your password before you can sign in.",
|
|
364
|
+
genericError: "Something went wrong. Please try again."
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
|
|
368
|
+
* consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
|
|
369
|
+
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
370
|
+
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
371
|
+
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
372
|
+
*/
|
|
373
|
+
function SignIn(props) {
|
|
374
|
+
const action = props.action ?? "/api/polyx/authenticate";
|
|
375
|
+
const labels = {
|
|
376
|
+
...DEFAULT_LABELS,
|
|
377
|
+
...props.labels
|
|
378
|
+
};
|
|
379
|
+
const [email, setEmail] = (0, react.useState)("");
|
|
380
|
+
const [password, setPassword] = (0, react.useState)("");
|
|
381
|
+
const [tenants, setTenants] = (0, react.useState)(null);
|
|
382
|
+
const [submitting, setSubmitting] = (0, react.useState)(false);
|
|
383
|
+
const [error, setError] = (0, react.useState)(null);
|
|
384
|
+
async function authenticate(tenantId) {
|
|
385
|
+
setSubmitting(true);
|
|
386
|
+
setError(null);
|
|
387
|
+
try {
|
|
388
|
+
const body = await (await fetch(action, {
|
|
389
|
+
method: "POST",
|
|
390
|
+
headers: { "content-type": "application/json" },
|
|
391
|
+
body: JSON.stringify({
|
|
392
|
+
email,
|
|
393
|
+
password,
|
|
394
|
+
organizationCode: props.organizationCode,
|
|
395
|
+
tenantId
|
|
396
|
+
})
|
|
397
|
+
})).json().catch(() => ({}));
|
|
398
|
+
switch (body.status) {
|
|
399
|
+
case "success":
|
|
400
|
+
window.location.assign(props.afterSignInPath ?? body.redirectTo ?? "/");
|
|
401
|
+
return;
|
|
402
|
+
case "select_tenant":
|
|
403
|
+
setTenants(body.tenants ?? []);
|
|
404
|
+
return;
|
|
405
|
+
case "invalid_credentials":
|
|
406
|
+
setError(labels.invalidCredentials);
|
|
407
|
+
return;
|
|
408
|
+
case "no_access":
|
|
409
|
+
setError(labels.noAccess);
|
|
410
|
+
return;
|
|
411
|
+
case "password_change_required":
|
|
412
|
+
setError(labels.passwordChangeRequired);
|
|
413
|
+
if (body.userId) props.onPasswordChangeRequired?.(body.userId);
|
|
414
|
+
return;
|
|
415
|
+
default:
|
|
416
|
+
setError(labels.genericError);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
} catch {
|
|
420
|
+
setError(labels.genericError);
|
|
421
|
+
} finally {
|
|
422
|
+
setSubmitting(false);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function onSubmit(event) {
|
|
426
|
+
event.preventDefault();
|
|
427
|
+
authenticate();
|
|
428
|
+
}
|
|
429
|
+
if (tenants) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
430
|
+
className: props.className,
|
|
431
|
+
"data-polyx-signin": "select-tenant",
|
|
432
|
+
children: [
|
|
433
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
434
|
+
className: "polyx-signin__heading",
|
|
435
|
+
children: labels.chooseWorkspace
|
|
436
|
+
}),
|
|
437
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
438
|
+
className: "polyx-signin__tenants",
|
|
439
|
+
children: tenants.map((tenant) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
440
|
+
type: "button",
|
|
441
|
+
className: "polyx-signin__tenant",
|
|
442
|
+
disabled: submitting,
|
|
443
|
+
onClick: () => void authenticate(tenant.tenantId),
|
|
444
|
+
children: tenant.name ?? tenant.slug ?? tenant.organizationCode ?? tenant.tenantId
|
|
445
|
+
}) }, tenant.tenantId))
|
|
446
|
+
}),
|
|
447
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
448
|
+
role: "alert",
|
|
449
|
+
className: "polyx-signin__error",
|
|
450
|
+
children: error
|
|
451
|
+
}) : null
|
|
452
|
+
]
|
|
453
|
+
});
|
|
454
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
455
|
+
className: props.className,
|
|
456
|
+
"data-polyx-signin": "credentials",
|
|
457
|
+
onSubmit,
|
|
458
|
+
children: [
|
|
459
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
460
|
+
className: "polyx-signin__field",
|
|
461
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: labels.emailLabel }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
462
|
+
type: "email",
|
|
463
|
+
name: "email",
|
|
464
|
+
autoComplete: "username",
|
|
465
|
+
required: true,
|
|
466
|
+
value: email,
|
|
467
|
+
disabled: submitting,
|
|
468
|
+
onChange: (event) => setEmail(event.target.value)
|
|
469
|
+
})]
|
|
470
|
+
}),
|
|
471
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
472
|
+
className: "polyx-signin__field",
|
|
473
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: labels.passwordLabel }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
474
|
+
type: "password",
|
|
475
|
+
name: "password",
|
|
476
|
+
autoComplete: "current-password",
|
|
477
|
+
required: true,
|
|
478
|
+
value: password,
|
|
479
|
+
disabled: submitting,
|
|
480
|
+
onChange: (event) => setPassword(event.target.value)
|
|
481
|
+
})]
|
|
482
|
+
}),
|
|
483
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
484
|
+
role: "alert",
|
|
485
|
+
className: "polyx-signin__error",
|
|
486
|
+
children: error
|
|
487
|
+
}) : null,
|
|
488
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
489
|
+
type: "submit",
|
|
490
|
+
className: "polyx-signin__submit",
|
|
491
|
+
disabled: submitting,
|
|
492
|
+
children: submitting ? labels.submitting : labels.submit
|
|
493
|
+
})
|
|
494
|
+
]
|
|
358
495
|
});
|
|
359
496
|
}
|
|
360
497
|
//#endregion
|
package/dist/index.d.cts
CHANGED
|
@@ -7,11 +7,14 @@ interface PolyXProviderProps {
|
|
|
7
7
|
publishableKey: string;
|
|
8
8
|
/** Override the key-embedded deployment host (proxies, container-internal). */
|
|
9
9
|
baseUrl?: string;
|
|
10
|
+
/** Hosted sign-in page URL (the account portal) the login popup/redirect opens. */
|
|
11
|
+
signInUrl?: string;
|
|
10
12
|
children: ReactNode;
|
|
11
13
|
}
|
|
12
14
|
declare function PolyXProvider({
|
|
13
15
|
publishableKey,
|
|
14
16
|
baseUrl,
|
|
17
|
+
signInUrl,
|
|
15
18
|
children
|
|
16
19
|
}: PolyXProviderProps): ReactNode;
|
|
17
20
|
//#endregion
|
|
@@ -188,16 +191,39 @@ declare function Protected({
|
|
|
188
191
|
}: ProtectedProps): ReactNode;
|
|
189
192
|
//#endregion
|
|
190
193
|
//#region src/components/sign-in.d.ts
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
195
|
+
interface SignInLabels {
|
|
196
|
+
emailLabel: string;
|
|
197
|
+
passwordLabel: string;
|
|
198
|
+
submit: string;
|
|
199
|
+
submitting: string;
|
|
200
|
+
chooseWorkspace: string;
|
|
201
|
+
invalidCredentials: string;
|
|
202
|
+
noAccess: string;
|
|
203
|
+
passwordChangeRequired: string;
|
|
204
|
+
genericError: string;
|
|
205
|
+
}
|
|
206
|
+
interface SignInProps {
|
|
207
|
+
/** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
|
|
208
|
+
action?: string;
|
|
209
|
+
/** Where to send the browser after success. Overrides the server's default redirect. */
|
|
210
|
+
afterSignInPath?: string;
|
|
211
|
+
/** Pre-scope the login to a single organization (skips org discovery). */
|
|
212
|
+
organizationCode?: string;
|
|
213
|
+
/** Applied to the root `<form>` / picker container — style via your own CSS. */
|
|
193
214
|
className?: string;
|
|
215
|
+
labels?: Partial<SignInLabels>;
|
|
216
|
+
/** Fired when the backend requires the user to set a new password before continuing. */
|
|
217
|
+
onPasswordChangeRequired?: (userId: string) => void;
|
|
194
218
|
}
|
|
195
|
-
/**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
219
|
+
/**
|
|
220
|
+
* The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
|
|
221
|
+
* consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
|
|
222
|
+
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
223
|
+
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
224
|
+
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
225
|
+
*/
|
|
226
|
+
declare function SignIn(props: SignInProps): ReactNode;
|
|
201
227
|
//#endregion
|
|
202
228
|
//#region src/components/auth-callback.d.ts
|
|
203
229
|
interface AuthCallbackProps {
|
|
@@ -217,4 +243,4 @@ declare function AuthCallback({
|
|
|
217
243
|
declare const PACKAGE_NAME = "@poly-x/react";
|
|
218
244
|
declare const version = "0.0.0";
|
|
219
245
|
//#endregion
|
|
220
|
-
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInOptions, type SignInProps, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
|
246
|
+
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
package/dist/index.d.mts
CHANGED
|
@@ -7,11 +7,14 @@ interface PolyXProviderProps {
|
|
|
7
7
|
publishableKey: string;
|
|
8
8
|
/** Override the key-embedded deployment host (proxies, container-internal). */
|
|
9
9
|
baseUrl?: string;
|
|
10
|
+
/** Hosted sign-in page URL (the account portal) the login popup/redirect opens. */
|
|
11
|
+
signInUrl?: string;
|
|
10
12
|
children: ReactNode;
|
|
11
13
|
}
|
|
12
14
|
declare function PolyXProvider({
|
|
13
15
|
publishableKey,
|
|
14
16
|
baseUrl,
|
|
17
|
+
signInUrl,
|
|
15
18
|
children
|
|
16
19
|
}: PolyXProviderProps): ReactNode;
|
|
17
20
|
//#endregion
|
|
@@ -188,16 +191,39 @@ declare function Protected({
|
|
|
188
191
|
}: ProtectedProps): ReactNode;
|
|
189
192
|
//#endregion
|
|
190
193
|
//#region src/components/sign-in.d.ts
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
/** Every user-facing string, overridable for i18n / white-labelling. */
|
|
195
|
+
interface SignInLabels {
|
|
196
|
+
emailLabel: string;
|
|
197
|
+
passwordLabel: string;
|
|
198
|
+
submit: string;
|
|
199
|
+
submitting: string;
|
|
200
|
+
chooseWorkspace: string;
|
|
201
|
+
invalidCredentials: string;
|
|
202
|
+
noAccess: string;
|
|
203
|
+
passwordChangeRequired: string;
|
|
204
|
+
genericError: string;
|
|
205
|
+
}
|
|
206
|
+
interface SignInProps {
|
|
207
|
+
/** BFF endpoint backed by `createAuthHandlers().authenticate`. Default `/api/polyx/authenticate`. */
|
|
208
|
+
action?: string;
|
|
209
|
+
/** Where to send the browser after success. Overrides the server's default redirect. */
|
|
210
|
+
afterSignInPath?: string;
|
|
211
|
+
/** Pre-scope the login to a single organization (skips org discovery). */
|
|
212
|
+
organizationCode?: string;
|
|
213
|
+
/** Applied to the root `<form>` / picker container — style via your own CSS. */
|
|
193
214
|
className?: string;
|
|
215
|
+
labels?: Partial<SignInLabels>;
|
|
216
|
+
/** Fired when the backend requires the user to set a new password before continuing. */
|
|
217
|
+
onPasswordChangeRequired?: (userId: string) => void;
|
|
194
218
|
}
|
|
195
|
-
/**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
219
|
+
/**
|
|
220
|
+
* The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
|
|
221
|
+
* consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
|
|
222
|
+
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
223
|
+
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
224
|
+
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
225
|
+
*/
|
|
226
|
+
declare function SignIn(props: SignInProps): ReactNode;
|
|
201
227
|
//#endregion
|
|
202
228
|
//#region src/components/auth-callback.d.ts
|
|
203
229
|
interface AuthCallbackProps {
|
|
@@ -217,4 +243,4 @@ declare function AuthCallback({
|
|
|
217
243
|
declare const PACKAGE_NAME = "@poly-x/react";
|
|
218
244
|
declare const version = "0.0.0";
|
|
219
245
|
//#endregion
|
|
220
|
-
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInOptions, type SignInProps, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
|
246
|
+
export { AuthCallback, type AuthCallbackMessage, type AuthCallbackProps, type AuthChannel, BroadcastAuthChannel, BroadcastChannelSessionChannel, type BrowserBridge, type CallbackParams, type LoginController, type LoginControllerDeps, PACKAGE_NAME, PolyXContext, type PolyXContextValue, PolyXProvider, type PolyXProviderProps, PopupCancelledError, type PopupHandle, Protected, type ProtectedProps, SessionStoragePkceStore, SignIn, type SignInLabels, type SignInOptions, type SignInProps, type UseAuth, WebLocksLock, WindowBrowserBridge, createLoginController, useAuth, useAuthCallback, useSession, useUser, version };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { AuthClient, FetchTransport, InMemoryLock, InMemoryPkceStore, InMemorySessionChannel, InMemorySessionStore, createSessionEngine, generatePkce, randomState, resolveConfig } from "@poly-x/core";
|
|
3
|
-
import { createContext, useCallback, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
|
|
4
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/auth-channel.ts
|
|
6
6
|
var BroadcastAuthChannel = class {
|
|
7
7
|
supported;
|
|
@@ -108,7 +108,7 @@ function createLoginController(deps) {
|
|
|
108
108
|
redirectUri,
|
|
109
109
|
createdAt: Date.now()
|
|
110
110
|
});
|
|
111
|
-
const url = authClient.
|
|
111
|
+
const url = authClient.buildSignInUrl({
|
|
112
112
|
redirectUri,
|
|
113
113
|
state,
|
|
114
114
|
challenge,
|
|
@@ -256,12 +256,13 @@ var WebLocksLock = class {
|
|
|
256
256
|
};
|
|
257
257
|
//#endregion
|
|
258
258
|
//#region src/provider.tsx
|
|
259
|
-
function PolyXProvider({ publishableKey, baseUrl, children }) {
|
|
259
|
+
function PolyXProvider({ publishableKey, baseUrl, signInUrl, children }) {
|
|
260
260
|
const value = useMemo(() => {
|
|
261
261
|
const authClient = new AuthClient({
|
|
262
262
|
config: resolveConfig({
|
|
263
263
|
key: publishableKey,
|
|
264
264
|
baseUrl,
|
|
265
|
+
signInUrl,
|
|
265
266
|
context: "browser"
|
|
266
267
|
}),
|
|
267
268
|
transport: new FetchTransport()
|
|
@@ -283,7 +284,11 @@ function PolyXProvider({ publishableKey, baseUrl, children }) {
|
|
|
283
284
|
channel: new BroadcastAuthChannel()
|
|
284
285
|
})
|
|
285
286
|
};
|
|
286
|
-
}, [
|
|
287
|
+
}, [
|
|
288
|
+
publishableKey,
|
|
289
|
+
baseUrl,
|
|
290
|
+
signInUrl
|
|
291
|
+
]);
|
|
287
292
|
useEffect(() => {
|
|
288
293
|
value.engine.hydrate();
|
|
289
294
|
return () => value.engine.dispose();
|
|
@@ -346,14 +351,146 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
346
351
|
}
|
|
347
352
|
//#endregion
|
|
348
353
|
//#region src/components/sign-in.tsx
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
354
|
+
const DEFAULT_LABELS = {
|
|
355
|
+
emailLabel: "Email",
|
|
356
|
+
passwordLabel: "Password",
|
|
357
|
+
submit: "Sign in",
|
|
358
|
+
submitting: "Signing in…",
|
|
359
|
+
chooseWorkspace: "Choose a workspace",
|
|
360
|
+
invalidCredentials: "Invalid email or password.",
|
|
361
|
+
noAccess: "You don't have access to this application.",
|
|
362
|
+
passwordChangeRequired: "You must reset your password before you can sign in.",
|
|
363
|
+
genericError: "Something went wrong. Please try again."
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* The embedded, Clerk-style sign-in form (D2b). Renders email/password inside the
|
|
367
|
+
* consuming app and POSTs to the SDK's BFF (`@poly-x/next` `authenticate`), which
|
|
368
|
+
* runs the credential authorize server-side and seals an httpOnly session — no
|
|
369
|
+
* redirect to a hosted PolyX login page, no tokens or credentials in the browser
|
|
370
|
+
* after submit. Handles the workspace picker (select_tenant) inline.
|
|
371
|
+
*/
|
|
372
|
+
function SignIn(props) {
|
|
373
|
+
const action = props.action ?? "/api/polyx/authenticate";
|
|
374
|
+
const labels = {
|
|
375
|
+
...DEFAULT_LABELS,
|
|
376
|
+
...props.labels
|
|
377
|
+
};
|
|
378
|
+
const [email, setEmail] = useState("");
|
|
379
|
+
const [password, setPassword] = useState("");
|
|
380
|
+
const [tenants, setTenants] = useState(null);
|
|
381
|
+
const [submitting, setSubmitting] = useState(false);
|
|
382
|
+
const [error, setError] = useState(null);
|
|
383
|
+
async function authenticate(tenantId) {
|
|
384
|
+
setSubmitting(true);
|
|
385
|
+
setError(null);
|
|
386
|
+
try {
|
|
387
|
+
const body = await (await fetch(action, {
|
|
388
|
+
method: "POST",
|
|
389
|
+
headers: { "content-type": "application/json" },
|
|
390
|
+
body: JSON.stringify({
|
|
391
|
+
email,
|
|
392
|
+
password,
|
|
393
|
+
organizationCode: props.organizationCode,
|
|
394
|
+
tenantId
|
|
395
|
+
})
|
|
396
|
+
})).json().catch(() => ({}));
|
|
397
|
+
switch (body.status) {
|
|
398
|
+
case "success":
|
|
399
|
+
window.location.assign(props.afterSignInPath ?? body.redirectTo ?? "/");
|
|
400
|
+
return;
|
|
401
|
+
case "select_tenant":
|
|
402
|
+
setTenants(body.tenants ?? []);
|
|
403
|
+
return;
|
|
404
|
+
case "invalid_credentials":
|
|
405
|
+
setError(labels.invalidCredentials);
|
|
406
|
+
return;
|
|
407
|
+
case "no_access":
|
|
408
|
+
setError(labels.noAccess);
|
|
409
|
+
return;
|
|
410
|
+
case "password_change_required":
|
|
411
|
+
setError(labels.passwordChangeRequired);
|
|
412
|
+
if (body.userId) props.onPasswordChangeRequired?.(body.userId);
|
|
413
|
+
return;
|
|
414
|
+
default:
|
|
415
|
+
setError(labels.genericError);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
} catch {
|
|
419
|
+
setError(labels.genericError);
|
|
420
|
+
} finally {
|
|
421
|
+
setSubmitting(false);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function onSubmit(event) {
|
|
425
|
+
event.preventDefault();
|
|
426
|
+
authenticate();
|
|
427
|
+
}
|
|
428
|
+
if (tenants) return /* @__PURE__ */ jsxs("div", {
|
|
429
|
+
className: props.className,
|
|
430
|
+
"data-polyx-signin": "select-tenant",
|
|
431
|
+
children: [
|
|
432
|
+
/* @__PURE__ */ jsx("p", {
|
|
433
|
+
className: "polyx-signin__heading",
|
|
434
|
+
children: labels.chooseWorkspace
|
|
435
|
+
}),
|
|
436
|
+
/* @__PURE__ */ jsx("ul", {
|
|
437
|
+
className: "polyx-signin__tenants",
|
|
438
|
+
children: tenants.map((tenant) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
439
|
+
type: "button",
|
|
440
|
+
className: "polyx-signin__tenant",
|
|
441
|
+
disabled: submitting,
|
|
442
|
+
onClick: () => void authenticate(tenant.tenantId),
|
|
443
|
+
children: tenant.name ?? tenant.slug ?? tenant.organizationCode ?? tenant.tenantId
|
|
444
|
+
}) }, tenant.tenantId))
|
|
445
|
+
}),
|
|
446
|
+
error ? /* @__PURE__ */ jsx("p", {
|
|
447
|
+
role: "alert",
|
|
448
|
+
className: "polyx-signin__error",
|
|
449
|
+
children: error
|
|
450
|
+
}) : null
|
|
451
|
+
]
|
|
452
|
+
});
|
|
453
|
+
return /* @__PURE__ */ jsxs("form", {
|
|
454
|
+
className: props.className,
|
|
455
|
+
"data-polyx-signin": "credentials",
|
|
456
|
+
onSubmit,
|
|
457
|
+
children: [
|
|
458
|
+
/* @__PURE__ */ jsxs("label", {
|
|
459
|
+
className: "polyx-signin__field",
|
|
460
|
+
children: [/* @__PURE__ */ jsx("span", { children: labels.emailLabel }), /* @__PURE__ */ jsx("input", {
|
|
461
|
+
type: "email",
|
|
462
|
+
name: "email",
|
|
463
|
+
autoComplete: "username",
|
|
464
|
+
required: true,
|
|
465
|
+
value: email,
|
|
466
|
+
disabled: submitting,
|
|
467
|
+
onChange: (event) => setEmail(event.target.value)
|
|
468
|
+
})]
|
|
469
|
+
}),
|
|
470
|
+
/* @__PURE__ */ jsxs("label", {
|
|
471
|
+
className: "polyx-signin__field",
|
|
472
|
+
children: [/* @__PURE__ */ jsx("span", { children: labels.passwordLabel }), /* @__PURE__ */ jsx("input", {
|
|
473
|
+
type: "password",
|
|
474
|
+
name: "password",
|
|
475
|
+
autoComplete: "current-password",
|
|
476
|
+
required: true,
|
|
477
|
+
value: password,
|
|
478
|
+
disabled: submitting,
|
|
479
|
+
onChange: (event) => setPassword(event.target.value)
|
|
480
|
+
})]
|
|
481
|
+
}),
|
|
482
|
+
error ? /* @__PURE__ */ jsx("p", {
|
|
483
|
+
role: "alert",
|
|
484
|
+
className: "polyx-signin__error",
|
|
485
|
+
children: error
|
|
486
|
+
}) : null,
|
|
487
|
+
/* @__PURE__ */ jsx("button", {
|
|
488
|
+
type: "submit",
|
|
489
|
+
className: "polyx-signin__submit",
|
|
490
|
+
disabled: submitting,
|
|
491
|
+
children: submitting ? labels.submitting : labels.submit
|
|
492
|
+
})
|
|
493
|
+
]
|
|
357
494
|
});
|
|
358
495
|
}
|
|
359
496
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poly-x/react",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"description": "PolyX SDK for React SPAs - provider, hooks, drop-in auth UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"module": "./dist/index.mjs",
|
|
25
25
|
"types": "./dist/index.d.cts",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@poly-x/core": "0.1.0-alpha.
|
|
27
|
+
"@poly-x/core": "0.1.0-alpha.2"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": ">=18",
|