@object-ui/auth 17.7.0 → 18.0.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/CHANGELOG.md +108 -0
- package/README.md +27 -4
- package/dist/AuthContext.d.ts +6 -1
- package/dist/AuthContext.d.ts.map +1 -1
- package/dist/AuthProvider.d.ts.map +1 -1
- package/dist/AuthProvider.js +97 -14
- package/dist/LoginForm.d.ts +26 -4
- package/dist/LoginForm.d.ts.map +1 -1
- package/dist/LoginForm.js +16 -3
- package/dist/SocialSignInButtons.d.ts +7 -1
- package/dist/SocialSignInButtons.d.ts.map +1 -1
- package/dist/SocialSignInButtons.js +2 -2
- package/dist/auth-gate-events.d.ts +6 -4
- package/dist/auth-gate-events.d.ts.map +1 -1
- package/dist/auth-gate-events.js +11 -1
- package/dist/authStyles.d.ts +8 -3
- package/dist/authStyles.d.ts.map +1 -1
- package/dist/authStyles.js +8 -3
- package/dist/createAuthClient.d.ts +15 -1
- package/dist/createAuthClient.d.ts.map +1 -1
- package/dist/createAuthClient.js +48 -1
- package/dist/createAuthenticatedFetch.d.ts +4 -3
- package/dist/createAuthenticatedFetch.d.ts.map +1 -1
- package/dist/createAuthenticatedFetch.js +1 -1
- package/dist/types.d.ts +39 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,113 @@
|
|
|
1
1
|
# @object-ui/auth
|
|
2
2
|
|
|
3
|
+
## 18.0.0
|
|
4
|
+
|
|
5
|
+
### Dependency floors
|
|
6
|
+
|
|
7
|
+
Computed from `.objectstack-sha` `06cab4849a8a`: the earliest published `@objectstack/spec` that contains it is `18.0.0`.
|
|
8
|
+
|
|
9
|
+
- `@objectstack/spec` (`dependencies`): `^18.0.0-next.1` → `^18.0.0`
|
|
10
|
+
|
|
11
|
+
### Major Changes
|
|
12
|
+
|
|
13
|
+
- 6f3299f: objectui moves to major 18, following `@objectstack/*` from 17 to 18 (objectui#12334). objectui's major is not a count of its own breaking changes: it tracks the major of the `@objectstack/spec` its packages depend on, so that the same major means compatible across the two projects. These packages already declare `@objectstack/*` on the 18 line (objectui#12030), so every package in the fixed release group crosses to 18 together. It is the one `major` that rule admits: objectui's own breaking changes ship as `minor`, described in their entries.
|
|
14
|
+
|
|
15
|
+
The 18 line is released from Changesets pre mode on the `next` tag (`.changeset/pre.json`), as `@objectstack/*` 18 is: these releases are `18.0.0-next.N` on the npm `next` dist-tag, and they do not move `latest`. The first `18.0.0` on `latest` comes when `@objectstack` 18 is generally available and this repository leaves pre mode.
|
|
16
|
+
|
|
17
|
+
What a consumer must do to take it is in the `@objectstack/*` 18 entry of this release (objectui#12030): move its own `@objectstack/*` packages to the 18 line at the same time.
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- f1a177c: `LoginForm` shows its "Don't have an account? Sign up" row only when the caller passes `registerUrl` (objectui#11634). The prop used to default to `'/register'`, so a caller that passed `undefined` to withhold the link got it back. Both console login pages pass `undefined` when the server's `/auth/config` reports `emailPassword.disableSignUp: true`, so a deployment with sign-up turned off still offered a sign-up link. The server refused the sign-up and `/register` sent the user back to the login page.
|
|
22
|
+
|
|
23
|
+
**Behaviour change.** A `LoginForm` rendered without `registerUrl` no longer shows a sign-up link. A caller that left the prop out and relied on the `'/register'` default now passes the URL itself, `registerUrl="/register"`, to keep the link. The console app's login page and `@object-ui/app-shell`'s `DefaultLoginPage` already pass the URL whenever sign-up is on, so neither changes. Leaving the prop out, or passing `undefined`, is the one way to render no link: no second "off" value is added.
|
|
24
|
+
|
|
25
|
+
**Clause-②: no.** The prop's type is unchanged (`registerUrl?: string`). No export, prop, type member or i18n key is added or removed.
|
|
26
|
+
- daa7caf: The console's login and register pages offer a generic sign-up only where the server would accept one (objectui#11691). `/api/v1/auth/config` states the sign-up rule as two keys, `emailPassword.disableSignUp` and `features.audiencePosture`, and the server deliberately does not force the first from the second: under `invite_only` its sign-up route still admits a pending invitee. The pages read only `disableSignUp`, so under the default `invite_only` posture `/login` offered "Sign up" and `/register` refused the finished form with `SELF_REGISTRATION_CLOSED`.
|
|
27
|
+
|
|
28
|
+
Both pages now read both keys:
|
|
29
|
+
|
|
30
|
+
- `disableSignUp: true` still hides sign-up outright, invitation links included.
|
|
31
|
+
- Under `open` or `email_domain`, nothing changes.
|
|
32
|
+
- Under `invite_only`, `/login` shows no "Sign up" link. A visitor who arrived from an invitation (`?redirect=/accept-invitation/ID`, the signed-out bounce of the invitation page) still gets the link, and `/register` still renders the form for them. A deployment with no owner yet (`GET /api/v1/auth/bootstrap-status` answers `hasOwner: false`) keeps the link for its first owner, because the server admits the first account under every posture.
|
|
33
|
+
- Otherwise `/register` says that self-registration is not open and points back to sign-in, before any field is filled in, instead of refusing the submitted form.
|
|
34
|
+
- A server that sends no `audiencePosture` is answered as before, by `disableSignUp` alone. A posture value the console does not recognise reads as closed.
|
|
35
|
+
|
|
36
|
+
**Clause-②: yes.** `@object-ui/auth`'s published `AuthPublicConfig.features` gains an optional `audiencePosture` member, typed as `@objectstack/spec`'s `AudiencePosture`. The package's `@objectstack/spec` range moves from `^17.0.0` to `^17.3.0`, the first release that declares that type. No export, prop or i18n key is added or removed: the register page's explanation reuses the existing `auth.register.errors.selfRegistrationClosed` sentence.
|
|
37
|
+
- dfa15c8: A new workspace now takes its creator's browser timezone when it is created, so its first administrator is no longer asked for a zone the browser already knows (objectui#11908).
|
|
38
|
+
|
|
39
|
+
`createOrganization` accepts an optional `timezone`, an IANA zone such as `Asia/Shanghai`, on both `AuthClient` and the `useAuth()` context. The client sends it as the `timezone` query parameter of `POST /organization/create`; the request body stays `{ name, slug }` (plus `logo` when given). When the zone is absent or empty, no parameter is sent and the server keeps its default zone. A server that does not read the parameter ignores it.
|
|
40
|
+
|
|
41
|
+
The console's create-workspace dialog and the first-run setup page's create branch both pass the browser's `Intl.DateTimeFormat().resolvedOptions().timeZone`. A browser that reports no zone sends none. Existing workspaces are unchanged, and the one-time timezone prompt still asks about a workspace whose zone is still the default.
|
|
42
|
+
- c5406c2: Depend on the `@objectstack/*` 18 prerelease line (objectui#12030).
|
|
43
|
+
|
|
44
|
+
Every `@objectstack/*` range these packages declare in `dependencies` or `peerDependencies` moves from `^17.x` to `^18.0.0-next.0`, the `next` prerelease of `@objectstack/*` 18 (raised to `^18.0.0-next.1` by objectui#12370 before release).
|
|
45
|
+
|
|
46
|
+
**Breaking for consumers:** an install that resolves `@objectstack/spec` (or `@objectstack/client`, `@objectstack/formula`, `@objectstack/lint`) 17.x no longer satisfies these ranges. A consumer must move its own `@objectstack/*` packages to the 18 line, starting with `18.0.0-next.1` (objectui#12370), at the same time as it takes this release.
|
|
47
|
+
|
|
48
|
+
**objectui's own surface moves with it:**
|
|
49
|
+
|
|
50
|
+
- `element:record_picker`'s registration stops publishing the `object`, `filter`, `sort` and `limit` inputs, which spec 18 retires; a JSX page that writes one now gets `unknown-prop`.
|
|
51
|
+
- The `CapabilityMultiSelectField` widget's curated capability set (`@object-ui/fields`) follows spec 18: `manage_mailboxes` in, `manage_org_presentation` out.
|
|
52
|
+
- `element:number` and `element:repeater` nodes that bind no object are accepted, as spec 18's row and `PageComponentSchema` accept them; the two `ELEMENT_*_OBJECT_REQUIRED` refusals are gone.
|
|
53
|
+
|
|
54
|
+
**Retired with the spec:** `@objectstack/spec` 18 tombstones `object-grid`'s `defaultFilters` and refuses it by name, and `ObjectGrid` and `ObjectView` stop reading it (`table.defaultFilters` on an `object-view` included). A view that still carries it renders unfiltered: rename the key to `filter`, whose value is the same rule array.
|
|
55
|
+
- 37db09f: A user whose administrator forced a password change gets the change-password overlay instead of raw `403` responses (objectui#12365).
|
|
56
|
+
|
|
57
|
+
The ObjectStack backend answers an administrator-forced password change with `403 PASSWORD_RESET_REQUIRED`, its own code distinct from natural expiry `PASSWORD_EXPIRED` (objectstack#23287 ruling C′, carried by objectstack#23440).
|
|
58
|
+
|
|
59
|
+
- **`@object-ui/auth`:** the authenticated fetch wrapper now treats `PASSWORD_RESET_REQUIRED` as an auth-policy gate, like `MFA_REQUIRED` and `PASSWORD_EXPIRED`, so `AuthProvider` sets `remediationRequired` for it. Before the backend emits the code this changes nothing.
|
|
60
|
+
- **`@object-ui/app-shell`:** `RemediationOverlay` routes `PASSWORD_RESET_REQUIRED` to the change-password form. It shows the title "You must change your password" for it, because "Your password has expired" is false for a forced reset. `PASSWORD_EXPIRED` keeps its title.
|
|
61
|
+
- **`@object-ui/i18n`:** adds `auth.remediation.password.resetTitle` to all ten built-in language packs.
|
|
62
|
+
|
|
63
|
+
No export is added or removed, and the type of `remediationRequired` (`{ code: string; message: string }`) is unchanged.
|
|
64
|
+
- ff9df88: Depend on `@objectstack/*` `18.0.0-next.1` (objectui#12370).
|
|
65
|
+
|
|
66
|
+
Every `@objectstack/*` range these packages declare moves from `^18.0.0-next.0` to `^18.0.0-next.1`; the console and the runner bundle `18.0.0-next.1`. A consumer moves its own `@objectstack/*` packages to `18.0.0-next.1` with this release.
|
|
67
|
+
|
|
68
|
+
**Accept sets that narrow with it.** `@object-ui/types` validates page action rows and view data bindings through the spec's own rows, so its faces (`safeValidateSchema` and `StrictAnyComponentSchema`) now refuse what `18.0.0-next.1` refuses. Each was accepted on `18.0.0-next.0`:
|
|
69
|
+
|
|
70
|
+
- `onSuccess` on an `action:button` / `action:icon` row, or an `action:group` / `action:menu` member, whose executor is the `url`, `navigation`, `modal`, `flow` or `form` built-in, or that has no executor at all. Write the hop on an `api` or `script` action (or a host handler name), or drop it.
|
|
71
|
+
- `onSuccess` beside `operation: 'update'`, and an `onSuccess` block carrying a key outside `{ navigate, openIn }`.
|
|
72
|
+
- `actionType: 'navigation'` on a row (and `type: 'navigation'` on a container member): write `'url'`.
|
|
73
|
+
- A `url` executor's `target` that is not openable, judged on the executor the runner runs, including a row whose `name` selects it.
|
|
74
|
+
- A view `data` binding with `provider: 'api'`: bind the object or static rows; an API call is the page element's binding.
|
|
75
|
+
- 8ed7e5a: `LoginForm` gains an optional `hideHeader` prop and a password reveal toggle, and the shared auth form styles take the console login artboard's sizes (objectui#12600).
|
|
76
|
+
|
|
77
|
+
- **`hideHeader`** (default `false`) renders no header — no icon disc, no title, no description — for a host that draws its own. Left unset, the header renders exactly as before. Under SSO-only mode a headless form still shows the SSO-only description, as one line above the federated buttons.
|
|
78
|
+
- **Password reveal toggle.** The password field carries a 28 px show/hide button. Its accessible name comes from two new optional `LoginFormLabels` members, `showPasswordText` and `hidePasswordText` (defaults "Show password" / "Hide password").
|
|
79
|
+
- **Shared styles, every auth form:** `AUTH_INPUT_CLASS` and `AUTH_PRIMARY_BUTTON_CLASS` are 40 px tall (`h-10`, were `h-11`); the primary button is one flat `bg-primary` fill (was a `--primary` gradient); `AUTH_FIELD_LABEL_CLASS` is 13 px. `LoginForm`'s "Sign up" line is 13 px and its enterprise SSO button is 40 px with the inputs' radius.
|
|
80
|
+
- `LoginForm`'s root caps its width at 400 px (`sm:max-w-[400px]`) instead of fixing it there (`sm:w-[400px]`), so it fits a host card narrower than 400 px; wherever it had 400 px or more, nothing changes.
|
|
81
|
+
|
|
82
|
+
**Clause-②: yes.** Added: `LoginFormProps.hideHeader`, `LoginFormLabels.showPasswordText`, `LoginFormLabels.hidePasswordText`, all optional. Nothing is removed or renamed.
|
|
83
|
+
- 9b69e13: `LoginForm` no longer tells an SSO-enforced environment's users to use a password (objectui#12682).
|
|
84
|
+
|
|
85
|
+
- **While the auth config resolves**, the header shows its title and no description. It used to show the password form's description ("Enter your email and password to continue", or the `description` the host passed) because SSO enforcement was not known yet. Once the config answers, the description is what it was: `labels.ssoOnlyDescription` under SSO-only mode, `description` otherwise. A failed config read still falls back to the password form and its description.
|
|
86
|
+
- **Under SSO-only mode** (`features.ssoEnforced`), the provider buttons are no longer followed by an "or continue with email" divider with no email form under it. The divider now renders only while the password form is shown, so it comes back with the form when "Use a password instead" is clicked. A server that does not enforce SSO keeps the divider exactly as before.
|
|
87
|
+
- **`SocialSignInButtons` gains `hideDivider`** (optional, default `false`), which omits the divider under the buttons. Left unset, the component renders exactly as before, so `RegisterForm` and every other caller are unchanged.
|
|
88
|
+
|
|
89
|
+
**Clause-②: yes.** Added: `SocialSignInButtonsProps.hideDivider`, optional. Nothing is removed or renamed.
|
|
90
|
+
- 84b8ffa: The console splash no longer waits for requests the first screen does not need (objectui#12809).
|
|
91
|
+
|
|
92
|
+
- **The organization chain runs in parallel.** When the `get-session` answer carries the organization plugin's `activeOrganizationId`, `AuthProvider` names the active organization from that id and the `organization/list` answer, and asks for the member row (`get-active-member`) at the same time as the list. It no longer asks for `get-full-organization` on boot: nothing the console reads uses the members or invitations that answer adds. An id the list does not carry names no organization, and the stored id is cleared, as before. A session answer without the key (a sign-in response, or a server without the organization plugin) takes the old path. Switching organization, the single-membership repair, sign-in and sign-out are unchanged.
|
|
93
|
+
- **`AuthClientSession.activeOrganizationId`** is a new optional member: the id better-auth's organization plugin puts on the session, or `null` when the session holds none.
|
|
94
|
+
- **The console waits only for the metadata the current route reads.** `AppContent` used to hold the splash until the object, dashboard, report and page lists had all loaded, on every route. It now waits for the lists the current route reads (an object page waits for objects; a dashboard for dashboards and objects). After the first screen, opening a route whose lists have not loaded shows a skeleton where the page will be, inside the console, instead of an empty list or a "not found". A route a host adds through `extraRoutes` still waits for all four lists.
|
|
95
|
+
|
|
96
|
+
### Patch Changes
|
|
97
|
+
|
|
98
|
+
- d75f212: Published text no longer teaches the retired view-level `provider: 'api'` data source (objectui#12364). A view binds `provider: 'object'` or `provider: 'value'`; a metadata-declared API call is the page element's `dataSource` binding. Text only: no schema accepts or refuses anything new, and no renderer behaves differently.
|
|
99
|
+
|
|
100
|
+
- **`@object-ui/types`:** the `object-tree` `data` description drops the `api` provider from the providers it names.
|
|
101
|
+
- **`@object-ui/plugin-grid` / `@object-ui/plugin-map`:** the `data` input descriptions drop the `api` provider.
|
|
102
|
+
- **`@object-ui/plugin-calendar` / `@object-ui/plugin-gantt` / `@object-ui/plugin-map`:** the error thrown when no `DataSource` is available now reads `DataSource required for the object provider` (was `DataSource required for object/api providers`). It fires in exactly the same conditions as before.
|
|
103
|
+
- **`@object-ui/react` / `@object-ui/auth`:** the README and docblocks describe `SchemaRendererProvider`'s `apiFetch` as what it is now: the host's authenticated fetch the renderers use for their own reads of the host's API (chart metadata, dataset dimension labels, record-permission probes). It is not a channel for a view's data source.
|
|
104
|
+
- **`@object-ui/app-shell`:** comment wording only in `ConsoleShell`, `useConsoleActionRuntime` and `ObjectView` (these comments ship in the built JS): no behaviour change.
|
|
105
|
+
- **`@object-ui/core`:** docblock wording only on `normalizeListViewSchema` and `resolveRecordSourceObjectName` (these ship in `dist/*.d.ts` and the built JS): no behaviour change.
|
|
106
|
+
- 6e141d9: After a reload, the console no longer keeps the previous organization's id once the server says the session has no active organization (objectui#12698). better-auth clears a session's active organization before it refuses a `set-active` to an organization the user is not a member of. On the next boot `AuthProvider` asked the server, heard "none", and left the old id in `ActiveOrganizationStorage`, so `X-Tenant-ID` and the metadata seed cache kept naming that organization. `refreshOrganizations` now clears the stored id whenever the server answers with no active organization, including when the single-membership repair (ADR-0081) is refused. When the repair succeeds, the server is re-stamped and the stored id follows the server's answer, as before. Nothing is added to or removed from the package entry.
|
|
107
|
+
|
|
108
|
+
The console header (`WorkspaceSwitcher`, `CurrentOrganizationIndicator`) now names only the organization the server reports as active, and shows no organization at all when there is none, instead of falling back to the first membership.
|
|
109
|
+
- 5d69b0e: `AuthProvider` no longer asks `get-session` a second time when the console has just validated the stored session before rendering. The validated answer becomes the provider's first state, once per page load; signing in or out, switching organisation and refreshing the session read it from the server as before.
|
|
110
|
+
|
|
3
111
|
## 17.7.0
|
|
4
112
|
|
|
5
113
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -144,6 +144,14 @@ is a template whose `{provider}` is replaced with the provider's display name
|
|
|
144
144
|
on `RegisterForm`), and `orText` is the divider under the buttons (default
|
|
145
145
|
"or continue with email").
|
|
146
146
|
|
|
147
|
+
The divider introduces the email form, so `LoginForm` draws it only while that
|
|
148
|
+
form is shown: under SSO-only mode (`features.ssoEnforced`) the buttons stand
|
|
149
|
+
alone until "Use a password instead" reveals the form. A host rendering
|
|
150
|
+
`SocialSignInButtons` itself omits the divider with `hideDivider` (default
|
|
151
|
+
`false`). While the auth config is still loading, `LoginForm`'s header shows
|
|
152
|
+
its title and no description, because the description may describe a password
|
|
153
|
+
form the server does not offer.
|
|
154
|
+
|
|
147
155
|
When the server requires email verification, a registration ends in the user's
|
|
148
156
|
inbox, and the verification link is the only way back. `verificationCallbackURL`
|
|
149
157
|
says where that link lands, e.g. the invitation the user signed up from. It is
|
|
@@ -184,10 +192,10 @@ import { createAuthenticatedFetch } from '@object-ui/auth';
|
|
|
184
192
|
|
|
185
193
|
const authedFetch = createAuthenticatedFetch();
|
|
186
194
|
|
|
187
|
-
// For fetches whose target URL
|
|
188
|
-
//
|
|
189
|
-
// so the platform token never leaks to third-party hosts:
|
|
190
|
-
const
|
|
195
|
+
// For fetches whose target URL may come from metadata (an API-call
|
|
196
|
+
// action's target), restrict credential injection to the current page's
|
|
197
|
+
// origin so the platform token never leaks to third-party hosts:
|
|
198
|
+
const sameOriginFetch = createAuthenticatedFetch({ sameOriginOnly: true });
|
|
191
199
|
```
|
|
192
200
|
|
|
193
201
|
### getSessionOwnerChangeCount / subscribeSessionOwnerChange
|
|
@@ -371,6 +379,21 @@ gates the "Sign in with SSO" button, `features.phoneNumberOtp` gates the
|
|
|
371
379
|
verification-code mode, `features.deviceAuthorization` gates the device-approval page,
|
|
372
380
|
and so on.
|
|
373
381
|
|
|
382
|
+
### `features.audiencePosture` — read it beside `emailPassword.disableSignUp`
|
|
383
|
+
|
|
384
|
+
`features.audiencePosture` reports which audience posture is in force: `invite_only` (the
|
|
385
|
+
default when the deployment declares none), `email_domain` or `open`. It is a value, not a
|
|
386
|
+
flag, and the server does **not** force `emailPassword.disableSignUp` from it: under
|
|
387
|
+
`invite_only` the sign-up route still admits a pending invitee, and a fresh deployment's
|
|
388
|
+
first owner. So `disableSignUp: false` on its own does not mean a stranger may register.
|
|
389
|
+
|
|
390
|
+
A surface that offers a generic sign-up reads both keys. `disableSignUp: true` hides
|
|
391
|
+
sign-up outright. Otherwise offer it when the posture is `open` or `email_domain`, or when
|
|
392
|
+
the visitor came from an invitation; under `invite_only` say that registration is by
|
|
393
|
+
invitation instead of rendering a form the server will refuse. A server that sends no
|
|
394
|
+
`audiencePosture` predates the key, and `disableSignUp` alone decides there. The
|
|
395
|
+
console's login and register pages are that reader (objectui#11691).
|
|
396
|
+
|
|
374
397
|
### Reserved flags — advertised by the server, consumed by nothing
|
|
375
398
|
|
|
376
399
|
Two members of that map are **declared but deliberately not consumed** by this package:
|
package/dist/AuthContext.d.ts
CHANGED
|
@@ -143,11 +143,16 @@ export interface AuthContextValue {
|
|
|
143
143
|
isMembershipResolved: boolean;
|
|
144
144
|
/** Switch the active organization (workspace) */
|
|
145
145
|
switchOrganization: (orgId: string) => Promise<void>;
|
|
146
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* Create a new organization. `timezone` is the creator's IANA zone, sent as
|
|
148
|
+
* the `timezone` query parameter, never in the body; see
|
|
149
|
+
* `AuthClient.createOrganization` (objectui#11908).
|
|
150
|
+
*/
|
|
147
151
|
createOrganization: (data: {
|
|
148
152
|
name: string;
|
|
149
153
|
slug: string;
|
|
150
154
|
logo?: string;
|
|
155
|
+
timezone?: string;
|
|
151
156
|
}) => Promise<AuthOrganization>;
|
|
152
157
|
/**
|
|
153
158
|
* Refresh the organizations list. Returns the freshly fetched list so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../src/AuthContext.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../src/AuthContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAEjF,OAAO,KAAK,EAAwB,mBAAmB,EAAE,kBAAkB,EAAyG,MAAM,YAAY,CAAC;AA4GvM,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EAAE,cAAc,EACtB,iBAAiB,EACjB,OAAc,EACd,WAAW,EACX,QAAQ,GACT,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAEjF,OAAO,KAAK,EAAwB,mBAAmB,EAAE,kBAAkB,EAAyG,MAAM,YAAY,CAAC;AA4GvM,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EAAE,cAAc,EACtB,iBAAiB,EACjB,OAAc,EACd,WAAW,EACX,QAAQ,GACT,EAAE,iBAAiB,qBAq9BnB"}
|
package/dist/AuthProvider.js
CHANGED
|
@@ -9,7 +9,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
9
9
|
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
|
10
10
|
import { authGateEvents } from './auth-gate-events.js';
|
|
11
11
|
import { AuthCtx } from './AuthContext.js';
|
|
12
|
-
import { createAuthClient, TokenStorage } from './createAuthClient.js';
|
|
12
|
+
import { createAuthClient, takePreflightSession, TokenStorage } from './createAuthClient.js';
|
|
13
13
|
import { ActiveOrganizationStorage, SessionUserScope } from './ActiveOrganizationStorage.js';
|
|
14
14
|
/**
|
|
15
15
|
* Prefix of every `MetadataProvider` seed entry in `sessionStorage`
|
|
@@ -141,9 +141,20 @@ function purgeSignedOutClientCaches() {
|
|
|
141
141
|
*/
|
|
142
142
|
export function AuthProvider({ authUrl, client: externalClient, onAuthStateChange, enabled = true, previewMode, children, }) {
|
|
143
143
|
const client = useMemo(() => externalClient ?? createAuthClient({ baseURL: authUrl }), [externalClient, authUrl]);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
// objectui#12808 — the console's pre-React preflight already asked
|
|
145
|
+
// `get-session` with the stored Bearer. When that answer is still the one we
|
|
146
|
+
// would get, it IS the first state, and the mount below does not ask again.
|
|
147
|
+
// Taken once per page load, in an initializer so it runs once per instance
|
|
148
|
+
// (StrictMode keeps the first call's result). Only for a provider that builds
|
|
149
|
+
// its own client from `authUrl`: an injected client may talk to another server.
|
|
150
|
+
const [bootSession] = useState(() => (enabled && previewMode == null && !externalClient ? takePreflightSession(authUrl) : null));
|
|
151
|
+
const [user, setUser] = useState(() => bootSession?.user ?? null);
|
|
152
|
+
const [session, setSession] = useState(() => bootSession?.session ?? null);
|
|
153
|
+
const [isLoading, setIsLoading] = useState(() => bootSession === null);
|
|
154
|
+
// What the boot answer was taken for. The mount effect skips its load while
|
|
155
|
+
// it still holds (a StrictMode effect replay included); the first run that
|
|
156
|
+
// finds anything else drops it for good, so every later run loads as before.
|
|
157
|
+
const bootSessionFor = useRef(bootSession ? authUrl : null);
|
|
147
158
|
const [error, setError] = useState(null);
|
|
148
159
|
const [remediationRequired, setRemediationRequired] = useState(null);
|
|
149
160
|
// Organization / workspace state
|
|
@@ -181,6 +192,16 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
181
192
|
// stale, previous-organization data over the newer one (see
|
|
182
193
|
// `switchOrganization`).
|
|
183
194
|
const sessionLoadGeneration = useRef(0);
|
|
195
|
+
// objectui#12809 — the member row asked for at the same time as
|
|
196
|
+
// `organization/list`, when the session already names its active
|
|
197
|
+
// organization. `get-active-member` answers for the session's active
|
|
198
|
+
// organization, so it does not have to wait for the list. Held with the org
|
|
199
|
+
// and user it was asked for and taken once, by the first
|
|
200
|
+
// `refreshActiveMember` for that same organization; anything else drops it
|
|
201
|
+
// (a switch, a sign-out, an active organization the list did not confirm).
|
|
202
|
+
// Not a cache: the answer is used for the moment it was asked in, never for
|
|
203
|
+
// a later question.
|
|
204
|
+
const bootMemberRef = useRef(null);
|
|
184
205
|
/**
|
|
185
206
|
* The ONE session loader. Runs on mount (below) and on every later
|
|
186
207
|
* re-resolution — `refreshSession` on the context, and the rotation
|
|
@@ -260,6 +281,9 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
260
281
|
}, [enabled, isPreviewMode, loadSession]);
|
|
261
282
|
// Load session on mount (only if auth is enabled and not in preview mode)
|
|
262
283
|
useEffect(() => {
|
|
284
|
+
if (isPreviewMode || !enabled || externalClient || bootSessionFor.current !== authUrl) {
|
|
285
|
+
bootSessionFor.current = null;
|
|
286
|
+
}
|
|
263
287
|
if (isPreviewMode) {
|
|
264
288
|
// Preview mode: simulate a user based on previewMode config
|
|
265
289
|
const role = previewMode.simulatedRole ?? 'admin';
|
|
@@ -303,10 +327,13 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
303
327
|
setIsLoading(false);
|
|
304
328
|
return;
|
|
305
329
|
}
|
|
330
|
+
// The first state already holds the preflight's answer (see `bootSession`).
|
|
331
|
+
if (bootSessionFor.current !== null)
|
|
332
|
+
return;
|
|
306
333
|
let cancelled = false;
|
|
307
334
|
loadSession(() => cancelled);
|
|
308
335
|
return () => { cancelled = true; };
|
|
309
|
-
}, [client, enabled, isPreviewMode, previewMode, loadSession]);
|
|
336
|
+
}, [client, enabled, isPreviewMode, previewMode, loadSession, authUrl, externalClient]);
|
|
310
337
|
// Notify on auth state changes
|
|
311
338
|
useEffect(() => {
|
|
312
339
|
onAuthStateChange?.({
|
|
@@ -377,6 +404,7 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
377
404
|
// this file: drop the in-memory reference and the stored id together.
|
|
378
405
|
setOrganizations([]);
|
|
379
406
|
setActiveOrganization(null);
|
|
407
|
+
bootMemberRef.current = null;
|
|
380
408
|
}
|
|
381
409
|
catch (err) {
|
|
382
410
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
@@ -547,7 +575,17 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
547
575
|
}
|
|
548
576
|
}, [client]);
|
|
549
577
|
// --- Organization methods ---
|
|
550
|
-
|
|
578
|
+
/**
|
|
579
|
+
* `sessionActiveOrgId` — objectui#12809. Passed only by the load that follows
|
|
580
|
+
* a `get-session` answer (the effect below), and only when that answer
|
|
581
|
+
* carries the organization plugin's `activeOrganizationId`. The session
|
|
582
|
+
* already names the server's active organization, so it is named from that
|
|
583
|
+
* id and the list answer: no `get-full-organization` round trip, whose extra
|
|
584
|
+
* fields (members, invitations) nothing the console reads. Every other caller
|
|
585
|
+
* (a mutation, `refreshOrganizations()` on the context) asks the server, as
|
|
586
|
+
* before: the session in hand may predate what that mutation changed.
|
|
587
|
+
*/
|
|
588
|
+
const refreshOrganizations = useCallback(async (isCancelled, sessionActiveOrgId) => {
|
|
551
589
|
if (!enabled || isPreviewMode)
|
|
552
590
|
return;
|
|
553
591
|
setIsOrganizationsLoading(true);
|
|
@@ -559,12 +597,19 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
559
597
|
// If no active org is set but orgs exist, try to get active from server
|
|
560
598
|
if (orgs.length > 0 && !activeOrganization) {
|
|
561
599
|
let active = null;
|
|
562
|
-
|
|
563
|
-
|
|
600
|
+
if (sessionActiveOrgId !== undefined) {
|
|
601
|
+
// An id the list does not carry is no membership of this user's: the
|
|
602
|
+
// server's full-organization read refuses it the same way.
|
|
603
|
+
active = (sessionActiveOrgId && orgs.find((o) => o.id === sessionActiveOrgId)) || null;
|
|
564
604
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
605
|
+
else {
|
|
606
|
+
try {
|
|
607
|
+
active = await client.getActiveOrganization();
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
// No active org set — that's fine
|
|
611
|
+
active = null;
|
|
612
|
+
}
|
|
568
613
|
}
|
|
569
614
|
// Single-membership repair (ADR-0081): a session created before the
|
|
570
615
|
// server-side active-org stamp existed carries no active org even
|
|
@@ -579,10 +624,27 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
579
624
|
active = null;
|
|
580
625
|
}
|
|
581
626
|
}
|
|
582
|
-
if (
|
|
627
|
+
if (isCancelled?.())
|
|
628
|
+
return orgs;
|
|
629
|
+
if (bootMemberRef.current && bootMemberRef.current.orgId !== active?.id) {
|
|
630
|
+
bootMemberRef.current = null;
|
|
631
|
+
}
|
|
632
|
+
if (active) {
|
|
583
633
|
setActiveOrganization(active);
|
|
584
634
|
ActiveOrganizationStorage.set(active.id);
|
|
585
635
|
}
|
|
636
|
+
else if (activeOrgIdRef.current === null) {
|
|
637
|
+
// objectui#12698 — the server answered "no active organization"
|
|
638
|
+
// (and, at one membership, refused the repair above). The stored id
|
|
639
|
+
// is a cache of a SERVER-owned fact, so it goes with that answer:
|
|
640
|
+
// left in place it keeps stamping `X-Tenant-ID` and keying the
|
|
641
|
+
// metadata seed cache for an organization this session does not
|
|
642
|
+
// hold — e.g. the one a refused `set-active` cleared server-side
|
|
643
|
+
// (better-auth nulls the session's active org before it refuses a
|
|
644
|
+
// non-member). The `activeOrgIdRef` guard keeps a user switch that
|
|
645
|
+
// landed while this request was in flight from being wiped.
|
|
646
|
+
ActiveOrganizationStorage.clear();
|
|
647
|
+
}
|
|
586
648
|
}
|
|
587
649
|
return orgs;
|
|
588
650
|
}
|
|
@@ -634,8 +696,12 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
634
696
|
// A switch re-opens the question: until `getActiveMember()` answers for the
|
|
635
697
|
// NEW org, `activeMember` still holds the OLD org's row.
|
|
636
698
|
setActiveMemberResolved(false);
|
|
699
|
+
const boot = bootMemberRef.current;
|
|
700
|
+
bootMemberRef.current = null;
|
|
637
701
|
try {
|
|
638
|
-
const member =
|
|
702
|
+
const member = boot && boot.orgId === activeOrganization.id
|
|
703
|
+
? await boot.answer
|
|
704
|
+
: await client.getActiveMember();
|
|
639
705
|
setActiveMember(member);
|
|
640
706
|
}
|
|
641
707
|
catch (err) {
|
|
@@ -682,11 +748,27 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
682
748
|
refreshActiveMember();
|
|
683
749
|
}, [refreshActiveMember]);
|
|
684
750
|
// Load organizations once user is authenticated
|
|
751
|
+
//
|
|
752
|
+
// objectui#12809 — a session answer that carries the organization plugin's
|
|
753
|
+
// `activeOrganizationId` already names the server's active organization, so
|
|
754
|
+
// the member row is asked for now, beside `organization/list`, instead of
|
|
755
|
+
// after the active organization lands. A StrictMode effect replay finds the
|
|
756
|
+
// same question already asked and does not ask again.
|
|
685
757
|
useEffect(() => {
|
|
686
758
|
if (!(user && enabled && !isPreviewMode))
|
|
687
759
|
return;
|
|
688
760
|
let cancelled = false;
|
|
689
|
-
|
|
761
|
+
const sessionActiveOrgId = session?.activeOrganizationId;
|
|
762
|
+
if (typeof sessionActiveOrgId === 'string' && !activeOrganization) {
|
|
763
|
+
const asked = bootMemberRef.current;
|
|
764
|
+
if (!asked || asked.orgId !== sessionActiveOrgId || asked.userId !== user.id) {
|
|
765
|
+
const answer = client.getActiveMember();
|
|
766
|
+
// Taken later, or dropped: either way it must not surface as unhandled.
|
|
767
|
+
answer.catch(() => undefined);
|
|
768
|
+
bootMemberRef.current = { orgId: sessionActiveOrgId, userId: user.id, answer };
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
refreshOrganizations(() => cancelled, sessionActiveOrgId);
|
|
690
772
|
return () => { cancelled = true; };
|
|
691
773
|
}, [user, enabled, isPreviewMode]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
692
774
|
// objectui#5750 — keep `activeOrgIdRef` current for every path that can
|
|
@@ -728,6 +810,7 @@ export function AuthProvider({ authUrl, client: externalClient, onAuthStateChang
|
|
|
728
810
|
const switchOrganization = useCallback(async (orgId) => {
|
|
729
811
|
setError(null);
|
|
730
812
|
const previousOrgId = activeOrgIdRef.current;
|
|
813
|
+
bootMemberRef.current = null;
|
|
731
814
|
sessionLoadInFlight.current = true;
|
|
732
815
|
try {
|
|
733
816
|
const org = await client.setActiveOrganization(orgId);
|
package/dist/LoginForm.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ export interface LoginFormLabels {
|
|
|
25
25
|
/**
|
|
26
26
|
* Divider label between the social provider buttons and the email/password
|
|
27
27
|
* form (defaults to "or continue with email"). Rendered only when the server
|
|
28
|
-
* reports at least one social provider
|
|
28
|
+
* reports at least one social provider, and only while that form is shown:
|
|
29
|
+
* under SSO-only mode it appears once "Use a password instead" reveals it.
|
|
29
30
|
*/
|
|
30
31
|
orText?: string;
|
|
31
32
|
/**
|
|
@@ -61,19 +62,32 @@ export interface LoginFormLabels {
|
|
|
61
62
|
usePasswordSignInText?: string;
|
|
62
63
|
/** Description shown above the form in SSO-only mode (defaults to "Sign in with your organization's single sign-on"). */
|
|
63
64
|
ssoOnlyDescription?: string;
|
|
65
|
+
/** Accessible name of the password field's reveal toggle while the password is hidden (defaults to "Show password"). */
|
|
66
|
+
showPasswordText?: string;
|
|
67
|
+
/** Accessible name of the password field's reveal toggle while the password is shown (defaults to "Hide password"). */
|
|
68
|
+
hidePasswordText?: string;
|
|
64
69
|
}
|
|
65
70
|
export interface LoginFormProps {
|
|
66
71
|
/** Callback on successful login */
|
|
67
72
|
onSuccess?: () => void;
|
|
68
73
|
/** Callback on login error */
|
|
69
74
|
onError?: (error: Error) => void;
|
|
70
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Link to registration page. The "Sign up" link renders only when this is
|
|
77
|
+
* set: there is no default, so leaving it out (or passing `undefined`, as a
|
|
78
|
+
* caller does when the server reports `emailPassword.disableSignUp`) renders
|
|
79
|
+
* no link.
|
|
80
|
+
*/
|
|
71
81
|
registerUrl?: string;
|
|
72
82
|
/** Link to forgot password page */
|
|
73
83
|
forgotPasswordUrl?: string;
|
|
74
84
|
/** Custom title */
|
|
75
85
|
title?: string;
|
|
76
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Custom description. Not shown while the server's auth config is still
|
|
88
|
+
* resolving, since it may describe a password form the server does not
|
|
89
|
+
* offer; under SSO-only mode `labels.ssoOnlyDescription` replaces it.
|
|
90
|
+
*/
|
|
77
91
|
description?: string;
|
|
78
92
|
/** Custom icon shown above the title (defaults to a small lock disc) */
|
|
79
93
|
icon?: React.ReactNode;
|
|
@@ -90,6 +104,14 @@ export interface LoginFormProps {
|
|
|
90
104
|
errorMessages?: Record<string, string>;
|
|
91
105
|
/** Hide the icon disc above the form title. Defaults to false. */
|
|
92
106
|
hideIcon?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Render no header — no icon disc, no title, no description — because the
|
|
109
|
+
* host draws its own above the form. Defaults to false, which renders the
|
|
110
|
+
* header exactly as before. Under SSO-only ("enforced") mode the form still
|
|
111
|
+
* says how to sign in: the SSO-only description, which the header would
|
|
112
|
+
* otherwise carry, renders as one muted line above the federated buttons.
|
|
113
|
+
*/
|
|
114
|
+
hideHeader?: boolean;
|
|
93
115
|
}
|
|
94
116
|
/**
|
|
95
117
|
* Login form component with email/password authentication.
|
|
@@ -105,5 +127,5 @@ export interface LoginFormProps {
|
|
|
105
127
|
* />
|
|
106
128
|
* ```
|
|
107
129
|
*/
|
|
108
|
-
export declare function LoginForm({ onSuccess, onError, registerUrl, forgotPasswordUrl, title, description, icon, hideIcon, linkComponent: LinkComp, labels, errorMessages, }: LoginFormProps): React.JSX.Element;
|
|
130
|
+
export declare function LoginForm({ onSuccess, onError, registerUrl, forgotPasswordUrl, title, description, icon, hideIcon, hideHeader, linkComponent: LinkComp, labels, errorMessages, }: LoginFormProps): React.JSX.Element;
|
|
109
131
|
//# sourceMappingURL=LoginForm.d.ts.map
|
package/dist/LoginForm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginForm.d.ts","sourceRoot":"","sources":["../src/LoginForm.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAWzD,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB
|
|
1
|
+
{"version":3,"file":"LoginForm.d.ts","sourceRoot":"","sources":["../src/LoginForm.tsx"],"names":[],"mappings":"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"}
|
package/dist/LoginForm.js
CHANGED
|
@@ -12,6 +12,8 @@ import { SocialSignInButtons } from './SocialSignInButtons.js';
|
|
|
12
12
|
import { looksLikePhoneIdentifier, normalizePhoneIdentifier } from './phone-identifier.js';
|
|
13
13
|
import { AUTH_FIELD_LABEL_CLASS, AUTH_INPUT_CLASS, AUTH_LINK_CLASS, AUTH_PRIMARY_BUTTON_CLASS, AuthErrorBanner, AuthFormHeader, AuthSpinner, } from './authStyles.js';
|
|
14
14
|
const DefaultLink = ({ href, className, children }) => (_jsx("a", { href: href, className: className, children: children }));
|
|
15
|
+
const EyeIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4", "aria-hidden": "true", children: [_jsx("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] }));
|
|
16
|
+
const EyeOffIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4", "aria-hidden": "true", children: [_jsx("path", { d: "M10.73 5.08A10.4 10.4 0 0 1 12 5c7 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68" }), _jsx("path", { d: "M6.61 6.61A13.5 13.5 0 0 0 2 12s3 7 10 7a9.7 9.7 0 0 0 5.39-1.61" }), _jsx("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), _jsx("path", { d: "m2 2 20 20" })] }));
|
|
15
17
|
const DefaultLockIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", className: "h-6 w-6", "aria-hidden": "true", children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2" }), _jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }));
|
|
16
18
|
/**
|
|
17
19
|
* Login form component with email/password authentication.
|
|
@@ -27,10 +29,13 @@ const DefaultLockIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none"
|
|
|
27
29
|
* />
|
|
28
30
|
* ```
|
|
29
31
|
*/
|
|
30
|
-
export function LoginForm({ onSuccess, onError, registerUrl
|
|
32
|
+
export function LoginForm({ onSuccess, onError, registerUrl, forgotPasswordUrl = '/forgot-password', title = 'Sign in to your account', description = 'Enter your email and password to continue', icon, hideIcon = false, hideHeader = false, linkComponent: LinkComp = DefaultLink, labels = {}, errorMessages, }) {
|
|
31
33
|
const { signIn, isLoading, getAuthConfig, sendPhoneOtp, signInWithPhoneOtp, signInWithPhonePassword } = useAuth();
|
|
32
34
|
const [email, setEmail] = useState('');
|
|
33
35
|
const [password, setPassword] = useState('');
|
|
36
|
+
// The password field's reveal toggle. Local and ephemeral on purpose: a
|
|
37
|
+
// revealed password must not survive a remount or reach any URL or storage.
|
|
38
|
+
const [passwordVisible, setPasswordVisible] = useState(false);
|
|
34
39
|
const [error, setError] = useState(null);
|
|
35
40
|
// Phone-OTP sign-in (framework#2780). Only offered when the server reports
|
|
36
41
|
// `features.phoneNumberOtp` — i.e. the phoneNumber plugin is on AND a
|
|
@@ -129,6 +134,8 @@ export function LoginForm({ onSuccess, onError, registerUrl = '/register', forgo
|
|
|
129
134
|
resendOtpCountdownText: labels.resendOtpCountdownText ?? 'Resend in {seconds}s',
|
|
130
135
|
usePhoneOtpText: labels.usePhoneOtpText ?? 'Sign in with verification code',
|
|
131
136
|
usePasswordSignInText: labels.usePasswordSignInText ?? 'Sign in with password instead',
|
|
137
|
+
showPasswordText: labels.showPasswordText ?? 'Show password',
|
|
138
|
+
hidePasswordText: labels.hidePasswordText ?? 'Hide password',
|
|
132
139
|
};
|
|
133
140
|
// Tick the resend cooldown down once per second while it's armed.
|
|
134
141
|
useEffect(() => {
|
|
@@ -233,10 +240,16 @@ export function LoginForm({ onSuccess, onError, registerUrl = '/register', forgo
|
|
|
233
240
|
setSsoSubmitting(false);
|
|
234
241
|
}
|
|
235
242
|
};
|
|
236
|
-
|
|
243
|
+
// The SSO-only view: enforced mode with the break-glass form still closed.
|
|
244
|
+
const ssoOnlyView = ssoEnforced && !showPasswordFallback;
|
|
245
|
+
return (_jsxs("div", { className: `mx-auto flex w-full flex-col justify-center sm:max-w-[400px] ${hideHeader ? 'space-y-4' : 'space-y-7'}`, children: [hideHeader ? (ssoOnlyView ? (_jsx("p", { className: "text-[13px] leading-relaxed text-muted-foreground", children: l.ssoOnlyDescription })) : null) : (_jsx(AuthFormHeader, { icon: hideIcon ? undefined : (icon ?? _jsx(DefaultLockIcon, {})), title: title,
|
|
246
|
+
// No description while the config resolves: `ssoEnforced` is not
|
|
247
|
+
// known yet, and the password form's copy would greet an SSO-only
|
|
248
|
+
// deployment (objectui#12682).
|
|
249
|
+
description: configPending ? undefined : ssoOnlyView ? l.ssoOnlyDescription : description })), _jsx("div", { className: "space-y-5", children: configPending ? (_jsx("div", { className: "flex justify-center py-10", role: "status", "aria-live": "polite", "data-testid": "login-config-loading", children: _jsx(AuthSpinner, {}) })) : (_jsxs(_Fragment, { children: [_jsx(SocialSignInButtons, { mode: "sign-in", dividerText: labels.orText, hideDivider: !passwordFormVisible, buttonText: labels.socialButton, onProvidersResolved: (hasProviders) => setHasSocialProviders(hasProviders) }), passwordFormVisible ? (mode === 'phone-otp' ? (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [error && _jsx(AuthErrorBanner, { message: error }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "login-phone", className: AUTH_FIELD_LABEL_CLASS, children: l.phoneLabel }), _jsx("input", { id: "login-phone", type: "tel", placeholder: l.phonePlaceholder, value: phone, onChange: (e) => setPhone(e.target.value), required: true, autoComplete: "tel", disabled: isLoading, className: AUTH_INPUT_CLASS })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "login-otp", className: AUTH_FIELD_LABEL_CLASS, children: l.otpCodeLabel }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { id: "login-otp", type: "text", inputMode: "numeric", placeholder: l.otpCodePlaceholder, value: otpCode, onChange: (e) => setOtpCode(e.target.value), required: true, autoComplete: "one-time-code", disabled: isLoading, className: AUTH_INPUT_CLASS }), _jsxs("button", { type: "button", onClick: handleSendOtp, disabled: otpSending || otpCooldown > 0 || !phone.trim() || isLoading, className: "shrink-0 rounded-md border border-input bg-background px-3 py-2 text-sm font-medium transition-colors hover:bg-accent disabled:opacity-50", children: [otpSending && _jsx(AuthSpinner, {}), otpCooldown > 0
|
|
237
250
|
? l.resendOtpCountdownText.replace('{seconds}', String(otpCooldown))
|
|
238
251
|
: l.sendOtpButton] })] })] }), _jsxs("button", { type: "submit", disabled: isLoading || !otpSent, className: AUTH_PRIMARY_BUTTON_CLASS, children: [isLoading && _jsx(AuthSpinner, {}), isLoading ? l.submittingButton : l.submitButton] }), _jsx("button", { type: "button", onClick: () => switchMode('password'), className: "w-full text-center text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-primary hover:underline", children: l.usePasswordSignInText })] })) : (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [error && _jsx(AuthErrorBanner, { message: error }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "login-email", className: AUTH_FIELD_LABEL_CLASS, children: phonePasswordEnabled ? l.emailOrPhoneLabel : l.emailLabel }), _jsx("input", { id: "login-email",
|
|
239
252
|
// `text` (not `email`) when a phone number is also accepted, so the
|
|
240
253
|
// browser doesn't reject a phone-shaped value on native validation.
|
|
241
|
-
type: phonePasswordEnabled ? 'text' : 'email', inputMode: phonePasswordEnabled ? 'text' : 'email', placeholder: phonePasswordEnabled ? l.emailOrPhonePlaceholder : l.emailPlaceholder, value: email, onChange: (e) => setEmail(e.target.value), required: true, autoComplete: "username", disabled: isLoading, className: AUTH_INPUT_CLASS })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { htmlFor: "login-password", className: AUTH_FIELD_LABEL_CLASS, children: l.passwordLabel }), forgotPasswordUrl && (_jsx(LinkComp, { href: forgotPasswordUrl, className: "text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-primary hover:underline", children: l.forgotPasswordText }))] }), _jsx("input", { id: "login-password", type:
|
|
254
|
+
type: phonePasswordEnabled ? 'text' : 'email', inputMode: phonePasswordEnabled ? 'text' : 'email', placeholder: phonePasswordEnabled ? l.emailOrPhonePlaceholder : l.emailPlaceholder, value: email, onChange: (e) => setEmail(e.target.value), required: true, autoComplete: "username", disabled: isLoading, className: AUTH_INPUT_CLASS })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { htmlFor: "login-password", className: AUTH_FIELD_LABEL_CLASS, children: l.passwordLabel }), forgotPasswordUrl && (_jsx(LinkComp, { href: forgotPasswordUrl, className: "text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-primary hover:underline", children: l.forgotPasswordText }))] }), _jsxs("div", { className: "relative", children: [_jsx("input", { id: "login-password", type: passwordVisible ? 'text' : 'password', placeholder: l.passwordPlaceholder, value: password, onChange: (e) => setPassword(e.target.value), required: true, autoComplete: "current-password", disabled: isLoading, className: `${AUTH_INPUT_CLASS} pr-10` }), _jsx("button", { type: "button", "data-testid": "login-password-toggle", onClick: () => setPasswordVisible((v) => !v), "aria-label": passwordVisible ? l.hidePasswordText : l.showPasswordText, "aria-controls": "login-password", disabled: isLoading, className: "absolute right-1.5 top-1/2 inline-flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 disabled:pointer-events-none disabled:opacity-50", children: passwordVisible ? _jsx(EyeOffIcon, {}) : _jsx(EyeIcon, {}) })] })] }), _jsxs("button", { type: "submit", disabled: isLoading, className: AUTH_PRIMARY_BUTTON_CLASS, children: [isLoading && _jsx(AuthSpinner, {}), isLoading ? l.submittingButton : l.submitButton] }), ssoEnabled && (_jsxs("button", { type: "button", onClick: handleSso, disabled: isLoading || ssoSubmitting, "aria-busy": ssoSubmitting, className: "flex h-10 w-full items-center justify-center gap-2 rounded-lg border border-input bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent disabled:opacity-50", children: [ssoSubmitting && _jsx(AuthSpinner, {}), l.ssoButton] })), phoneOtpEnabled && (_jsx("button", { type: "button", onClick: () => switchMode('phone-otp'), className: "w-full text-center text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-primary hover:underline", children: l.usePhoneOtpText })), ssoEnforced && showPasswordFallback && (_jsx("button", { type: "button", onClick: () => setShowPasswordFallback(false), className: "w-full text-center text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-primary hover:underline", children: l.backToSsoText }))] }))) : (_jsxs("div", { className: "space-y-4", children: [error && _jsx(AuthErrorBanner, { message: error }), _jsx("button", { type: "button", onClick: () => setShowPasswordFallback(true), className: "w-full text-center text-xs text-muted-foreground underline-offset-4 transition-colors hover:text-primary hover:underline", children: l.usePasswordText })] }))] })) }), registerUrl && !ssoEnforced && (_jsxs("p", { className: "px-8 text-center text-[13px] text-muted-foreground", children: [l.noAccountText, ' ', _jsx(LinkComp, { href: registerUrl, className: AUTH_LINK_CLASS, children: l.signUpText })] }))] }));
|
|
242
255
|
}
|
|
@@ -15,6 +15,12 @@ export interface SocialSignInButtonsProps {
|
|
|
15
15
|
errorCallbackURL?: string;
|
|
16
16
|
/** Divider text shown between social buttons and the email form (defaults to "or continue with email") */
|
|
17
17
|
dividerText?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Omit the divider under the buttons. For a host that draws no email form
|
|
20
|
+
* beneath them at that moment, e.g. `LoginForm` under SSO-only mode until
|
|
21
|
+
* the password form is revealed. Defaults to false: the divider renders.
|
|
22
|
+
*/
|
|
23
|
+
hideDivider?: boolean;
|
|
18
24
|
/**
|
|
19
25
|
* Provider button label, for i18n. `{provider}` is replaced with the
|
|
20
26
|
* provider's display name, so a translation places the name where its
|
|
@@ -32,5 +38,5 @@ export interface SocialSignInButtonsProps {
|
|
|
32
38
|
*
|
|
33
39
|
* Returns `null` while loading or when the server reports no providers.
|
|
34
40
|
*/
|
|
35
|
-
export declare function SocialSignInButtons({ mode, callbackURL, errorCallbackURL, dividerText, buttonText, onProvidersResolved, }: SocialSignInButtonsProps): React.JSX.Element | null;
|
|
41
|
+
export declare function SocialSignInButtons({ mode, callbackURL, errorCallbackURL, dividerText, hideDivider, buttonText, onProvidersResolved, }: SocialSignInButtonsProps): React.JSX.Element | null;
|
|
36
42
|
//# sourceMappingURL=SocialSignInButtons.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SocialSignInButtons.d.ts","sourceRoot":"","sources":["../src/SocialSignInButtons.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAkFnD,MAAM,WAAW,wBAAwB;IACvC,sFAAsF;IACtF,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0GAA0G;IAC1G,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACvD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,WAAsC,EACtC,UAAU,EACV,mBAAmB,GACpB,EAAE,wBAAwB,
|
|
1
|
+
{"version":3,"file":"SocialSignInButtons.d.ts","sourceRoot":"","sources":["../src/SocialSignInButtons.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAkFnD,MAAM,WAAW,wBAAwB;IACvC,sFAAsF;IACtF,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0GAA0G;IAC1G,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACvD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,WAAsC,EACtC,WAAmB,EACnB,UAAU,EACV,mBAAmB,GACpB,EAAE,wBAAwB,4BA6F1B"}
|
|
@@ -48,7 +48,7 @@ function ProviderIcon({ id }) {
|
|
|
48
48
|
*
|
|
49
49
|
* Returns `null` while loading or when the server reports no providers.
|
|
50
50
|
*/
|
|
51
|
-
export function SocialSignInButtons({ mode = 'sign-in', callbackURL, errorCallbackURL, dividerText = 'or continue with email', buttonText, onProvidersResolved, }) {
|
|
51
|
+
export function SocialSignInButtons({ mode = 'sign-in', callbackURL, errorCallbackURL, dividerText = 'or continue with email', hideDivider = false, buttonText, onProvidersResolved, }) {
|
|
52
52
|
const { getAuthConfig, signInWithProvider } = useAuth();
|
|
53
53
|
const [providers, setProviders] = useState([]);
|
|
54
54
|
const [loading, setLoading] = useState(true);
|
|
@@ -107,5 +107,5 @@ export function SocialSignInButtons({ mode = 'sign-in', callbackURL, errorCallba
|
|
|
107
107
|
setPendingId(null);
|
|
108
108
|
}
|
|
109
109
|
};
|
|
110
|
-
return (_jsxs("div", { className: "flex flex-col gap-2", children: [error && (_jsx("div", { className: "rounded-md bg-destructive/15 p-3 text-sm text-destructive", role: "alert", children: error })), providers.map((p) => (_jsxs("button", { type: "button", onClick: () => onClick(p), disabled: pendingId !== null, "aria-busy": pendingId === p.id, className: "inline-flex h-10 w-full items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: [pendingId === p.id ? _jsx(AuthSpinner, {}) : _jsx(ProviderIcon, { id: p.id }), labelTemplate.replace('{provider}', () => PROVIDER_LABEL[p.id] ?? p.name)] }, p.id))), _jsxs("div", { className: "relative my-1", children: [_jsx("div", { className: "absolute inset-0 flex items-center", children: _jsx("span", { className: "w-full border-t" }) }), _jsx("div", { className: "relative flex justify-center text-xs uppercase", children: _jsx("span", { className: "bg-background px-2 text-muted-foreground", children: dividerText }) })] })] }));
|
|
110
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [error && (_jsx("div", { className: "rounded-md bg-destructive/15 p-3 text-sm text-destructive", role: "alert", children: error })), providers.map((p) => (_jsxs("button", { type: "button", onClick: () => onClick(p), disabled: pendingId !== null, "aria-busy": pendingId === p.id, className: "inline-flex h-10 w-full items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", children: [pendingId === p.id ? _jsx(AuthSpinner, {}) : _jsx(ProviderIcon, { id: p.id }), labelTemplate.replace('{provider}', () => PROVIDER_LABEL[p.id] ?? p.name)] }, p.id))), hideDivider ? null : (_jsxs("div", { className: "relative my-1", children: [_jsx("div", { className: "absolute inset-0 flex items-center", children: _jsx("span", { className: "w-full border-t" }) }), _jsx("div", { className: "relative flex justify-center text-xs uppercase", children: _jsx("span", { className: "bg-background px-2 text-muted-foreground", children: dividerText }) })] }))] }));
|
|
111
111
|
}
|
|
@@ -9,16 +9,18 @@
|
|
|
9
9
|
* ADR-0069 — client side of the authentication-policy session gate.
|
|
10
10
|
*
|
|
11
11
|
* The ObjectStack backend returns `403 { error: { code: 'MFA_REQUIRED' |
|
|
12
|
-
* 'PASSWORD_EXPIRED', message } }` from protected
|
|
13
|
-
* user must remediate.
|
|
12
|
+
* 'PASSWORD_EXPIRED' | 'PASSWORD_RESET_REQUIRED', message } }` from protected
|
|
13
|
+
* endpoints when a logged-in user must remediate. `PASSWORD_RESET_REQUIRED` is
|
|
14
|
+
* an administrator-forced password change, distinct on the wire from natural
|
|
15
|
+
* expiry (objectstack#23287 ruling C′). The authenticated fetch wrapper detects this on EVERY
|
|
14
16
|
* API response and emits it here; `AuthProvider` subscribes and raises a
|
|
15
17
|
* full-screen remediation overlay. Decoupling via a tiny module-level emitter
|
|
16
18
|
* keeps the plain fetch wrapper free of React.
|
|
17
19
|
*/
|
|
18
20
|
/** A triggered auth-policy gate the user must clear before continuing. */
|
|
19
21
|
export interface AuthGateInfo {
|
|
20
|
-
/** Stable machine code
|
|
21
|
-
code: 'MFA_REQUIRED' | 'PASSWORD_EXPIRED' | string;
|
|
22
|
+
/** Stable machine code: one of the codes `detectAuthGate` recognises. */
|
|
23
|
+
code: 'MFA_REQUIRED' | 'PASSWORD_EXPIRED' | 'PASSWORD_RESET_REQUIRED' | string;
|
|
22
24
|
/** Human-facing message from the server (may be empty). */
|
|
23
25
|
message: string;
|
|
24
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-gate-events.d.ts","sourceRoot":"","sources":["../src/auth-gate-events.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH
|
|
1
|
+
{"version":3,"file":"auth-gate-events.d.ts","sourceRoot":"","sources":["../src/auth-gate-events.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AAEH,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,IAAI,EAAE,cAAc,GAAG,kBAAkB,GAAG,yBAAyB,GAAG,MAAM,CAAC;IAC/E,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;AAG7C,eAAO,MAAM,cAAc;IACzB,qEAAqE;eAC1D,YAAY,GAAG,IAAI;IAK9B,4CAA4C;wBACxB,QAAQ,GAAG,MAAM,IAAI;CAI1C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAUjF"}
|
package/dist/auth-gate-events.js
CHANGED
|
@@ -5,6 +5,16 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* The auth-policy codes the backend raises as a remediation gate. Hand-written:
|
|
10
|
+
* the spec vocabulary (`AuthGateCodeSchema`) is not in the `@objectstack/spec`
|
|
11
|
+
* this package builds against yet; derive from it once it is.
|
|
12
|
+
*/
|
|
13
|
+
const AUTH_GATE_CODES = new Set([
|
|
14
|
+
'MFA_REQUIRED',
|
|
15
|
+
'PASSWORD_EXPIRED',
|
|
16
|
+
'PASSWORD_RESET_REQUIRED',
|
|
17
|
+
]);
|
|
8
18
|
const listeners = new Set();
|
|
9
19
|
export const authGateEvents = {
|
|
10
20
|
/** Broadcast a gate to all subscribers (the active AuthProvider). */
|
|
@@ -33,7 +43,7 @@ export function detectAuthGate(status, body) {
|
|
|
33
43
|
if (!err || typeof err !== 'object')
|
|
34
44
|
return null;
|
|
35
45
|
const code = err.code;
|
|
36
|
-
if (code === '
|
|
46
|
+
if (typeof code === 'string' && AUTH_GATE_CODES.has(code)) {
|
|
37
47
|
const message = err.message;
|
|
38
48
|
return { code, message: typeof message === 'string' ? message : '' };
|
|
39
49
|
}
|
package/dist/authStyles.d.ts
CHANGED
|
@@ -13,10 +13,15 @@ import React from 'react';
|
|
|
13
13
|
*
|
|
14
14
|
* Consumers can override styling per-form by passing their own children
|
|
15
15
|
* inside an AuthShell, but the defaults aim to look premium out of the box.
|
|
16
|
+
*
|
|
17
|
+
* Sizes follow the console login artboard (objectui#12600): 40 px (`h-10`)
|
|
18
|
+
* fields and primary button, 13 px / 500 labels, one flat `--primary` fill on
|
|
19
|
+
* the button. Radii are the theme's own `rounded-lg`, so a host theme that
|
|
20
|
+
* re-scales `--radius` re-scales these with it.
|
|
16
21
|
*/
|
|
17
|
-
export declare const AUTH_INPUT_CLASS = "flex h-
|
|
18
|
-
export declare const AUTH_PRIMARY_BUTTON_CLASS = "inline-flex h-
|
|
19
|
-
export declare const AUTH_FIELD_LABEL_CLASS = "text-
|
|
22
|
+
export declare const AUTH_INPUT_CLASS = "flex h-10 w-full rounded-lg border border-input bg-background px-3.5 py-2 text-sm shadow-sm ring-offset-background transition-colors placeholder:text-muted-foreground hover:border-primary/40 focus-visible:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50";
|
|
23
|
+
export declare const AUTH_PRIMARY_BUTTON_CLASS = "inline-flex h-10 w-full items-center justify-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm ring-offset-background transition-[box-shadow,background-color,transform] hover:bg-primary/90 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 active:scale-[0.99] disabled:pointer-events-none disabled:opacity-60 motion-reduce:transition-none motion-reduce:active:transform-none";
|
|
24
|
+
export declare const AUTH_FIELD_LABEL_CLASS = "text-[13px] font-medium leading-none text-foreground";
|
|
20
25
|
export declare const AUTH_LINK_CLASS = "font-medium text-primary underline-offset-4 hover:underline focus-visible:outline-none focus-visible:underline";
|
|
21
26
|
/** Decorative spinner shown while a submit is pending. */
|
|
22
27
|
export declare function AuthSpinner(): React.JSX.Element;
|
package/dist/authStyles.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authStyles.d.ts","sourceRoot":"","sources":["../src/authStyles.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B
|
|
1
|
+
{"version":3,"file":"authStyles.d.ts","sourceRoot":"","sources":["../src/authStyles.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,0XAC4V,CAAC;AAE1X,eAAO,MAAM,yBAAyB,mfAC4c,CAAC;AAEnf,eAAO,MAAM,sBAAsB,yDAAyD,CAAC;AAE7F,eAAO,MAAM,eAAe,mHACsF,CAAC;AAEnH,0DAA0D;AAC1D,wBAAgB,WAAW,sBAyB1B;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBAiBlE;AAED,yDAAyD;AACzD,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBAgBlE;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBAgBjE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,KAAK,EACL,WAAW,GACZ,EAAE;IACD,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC/B,qBAgBA;AAED,kDAAkD;AAClD,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,qBAU/D;AAED,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,EAAE,KAAY,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,qBAW/D"}
|
package/dist/authStyles.js
CHANGED
|
@@ -6,10 +6,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
*
|
|
7
7
|
* Consumers can override styling per-form by passing their own children
|
|
8
8
|
* inside an AuthShell, but the defaults aim to look premium out of the box.
|
|
9
|
+
*
|
|
10
|
+
* Sizes follow the console login artboard (objectui#12600): 40 px (`h-10`)
|
|
11
|
+
* fields and primary button, 13 px / 500 labels, one flat `--primary` fill on
|
|
12
|
+
* the button. Radii are the theme's own `rounded-lg`, so a host theme that
|
|
13
|
+
* re-scales `--radius` re-scales these with it.
|
|
9
14
|
*/
|
|
10
|
-
export const AUTH_INPUT_CLASS = 'flex h-
|
|
11
|
-
export const AUTH_PRIMARY_BUTTON_CLASS = 'inline-flex h-
|
|
12
|
-
export const AUTH_FIELD_LABEL_CLASS = 'text-
|
|
15
|
+
export const AUTH_INPUT_CLASS = 'flex h-10 w-full rounded-lg border border-input bg-background px-3.5 py-2 text-sm shadow-sm ring-offset-background transition-colors placeholder:text-muted-foreground hover:border-primary/40 focus-visible:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50';
|
|
16
|
+
export const AUTH_PRIMARY_BUTTON_CLASS = 'inline-flex h-10 w-full items-center justify-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm ring-offset-background transition-[box-shadow,background-color,transform] hover:bg-primary/90 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 active:scale-[0.99] disabled:pointer-events-none disabled:opacity-60 motion-reduce:transition-none motion-reduce:active:transform-none';
|
|
17
|
+
export const AUTH_FIELD_LABEL_CLASS = 'text-[13px] font-medium leading-none text-foreground';
|
|
13
18
|
export const AUTH_LINK_CLASS = 'font-medium text-primary underline-offset-4 hover:underline focus-visible:outline-none focus-visible:underline';
|
|
14
19
|
/** Decorative spinner shown while a submit is pending. */
|
|
15
20
|
export function AuthSpinner() {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import type { AuthClient, AuthClientConfig } from './types.js';
|
|
8
|
+
import type { AuthClient, AuthClientConfig, AuthUser, AuthClientSession } from './types.js';
|
|
9
9
|
/**
|
|
10
10
|
* Simple token storage backed by localStorage.
|
|
11
11
|
* Falls back to in-memory storage when localStorage is unavailable (SSR, tests).
|
|
@@ -38,6 +38,20 @@ export declare const TokenStorage: {
|
|
|
38
38
|
*/
|
|
39
39
|
subscribeRotation(listener: () => void): () => void;
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* Take the preflight's `get-session` answer, once per page load (objectui#12808).
|
|
43
|
+
*
|
|
44
|
+
* Returns it only when it answers what the caller would ask: the same auth URL,
|
|
45
|
+
* and the Bearer still stored is the one the preflight validated (the server
|
|
46
|
+
* prefers the Bearer, so the cookie the preflight omitted cannot change the
|
|
47
|
+
* answer). Parsed exactly as `getSession()` parses the wire body, with the same
|
|
48
|
+
* token sync. Anything else, or a second call, returns `null` and the caller asks
|
|
49
|
+
* the server as before.
|
|
50
|
+
*/
|
|
51
|
+
export declare function takePreflightSession(authUrl: string): {
|
|
52
|
+
user: AuthUser;
|
|
53
|
+
session: AuthClientSession;
|
|
54
|
+
} | null;
|
|
41
55
|
/**
|
|
42
56
|
* Create an auth client instance backed by the official better-auth client.
|
|
43
57
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createAuthClient.d.ts","sourceRoot":"","sources":["../src/createAuthClient.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,UAAU,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"createAuthClient.d.ts","sourceRoot":"","sources":["../src/createAuthClient.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,EAE1D,MAAM,YAAY,CAAC;AAKpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY;kBACD,MAAM,GAAG,IAAI;kCACD,IAAI;WAE/B,MAAM,GAAG,IAAI;eAST,MAAM,GAAG,IAAI;aAkBf,IAAI;IASb;;;OAGG;gCACyB,MAAM,IAAI,GAAG,MAAM,IAAI;CAMpD,CAAC;AAgKF;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,GACd;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GAAG,IAAI,CAgBvD;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAksBrE"}
|
package/dist/createAuthClient.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import { createAuthClient as createBetterAuthClient } from 'better-auth/client';
|
|
8
|
+
import { createAuthClient as createBetterAuthClient, parseJSON } from 'better-auth/client';
|
|
9
9
|
import { organizationClient, twoFactorClient } from 'better-auth/client/plugins';
|
|
10
10
|
import { AUTH_INVITATION_STATUSES, isAuthInvitationStatus } from './invitation-status.js';
|
|
11
11
|
const TOKEN_STORAGE_KEY = 'auth-session-token';
|
|
@@ -202,6 +202,46 @@ function resolveResetPasswordRedirect() {
|
|
|
202
202
|
}
|
|
203
203
|
return '/reset-password';
|
|
204
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* The slot the console's pre-React auth preflight (`apps/console/src/lib/
|
|
207
|
+
* auth-preflight.ts`) leaves its `get-session` answer in when the stored Bearer
|
|
208
|
+
* was valid: the auth URL it asked, the token it sent and the body as JSON text. The
|
|
209
|
+
* preflight runs before any bundle chunk that could import this package, so the
|
|
210
|
+
* slot is spelled by hand on both sides; the console's
|
|
211
|
+
* `sessionBootDedup.test.tsx` runs the preflight against this reader.
|
|
212
|
+
*/
|
|
213
|
+
const PREFLIGHT_SESSION_KEY = Symbol.for('objectui.authPreflightSession');
|
|
214
|
+
/**
|
|
215
|
+
* Take the preflight's `get-session` answer, once per page load (objectui#12808).
|
|
216
|
+
*
|
|
217
|
+
* Returns it only when it answers what the caller would ask: the same auth URL,
|
|
218
|
+
* and the Bearer still stored is the one the preflight validated (the server
|
|
219
|
+
* prefers the Bearer, so the cookie the preflight omitted cannot change the
|
|
220
|
+
* answer). Parsed exactly as `getSession()` parses the wire body, with the same
|
|
221
|
+
* token sync. Anything else, or a second call, returns `null` and the caller asks
|
|
222
|
+
* the server as before.
|
|
223
|
+
*/
|
|
224
|
+
export function takePreflightSession(authUrl) {
|
|
225
|
+
const holder = globalThis;
|
|
226
|
+
const slot = holder[PREFLIGHT_SESSION_KEY];
|
|
227
|
+
if (!slot || slot.taken)
|
|
228
|
+
return null;
|
|
229
|
+
slot.taken = true;
|
|
230
|
+
if (slot.authUrl !== authUrl || slot.token !== TokenStorage.get())
|
|
231
|
+
return null;
|
|
232
|
+
let payload;
|
|
233
|
+
try {
|
|
234
|
+
payload = parseJSON(slot.body, { strict: false });
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
if (!payload?.user || !payload.session)
|
|
240
|
+
return null;
|
|
241
|
+
if (payload.session.token)
|
|
242
|
+
TokenStorage.set(payload.session.token);
|
|
243
|
+
return { user: payload.user, session: payload.session };
|
|
244
|
+
}
|
|
205
245
|
/**
|
|
206
246
|
* Create a fetch wrapper that injects Bearer token from localStorage
|
|
207
247
|
* and captures updated tokens from the `set-auth-token` response header
|
|
@@ -727,10 +767,17 @@ export function createAuthClient(config) {
|
|
|
727
767
|
return (data ?? []);
|
|
728
768
|
},
|
|
729
769
|
async createOrganization(orgData) {
|
|
770
|
+
// objectui#11908: the creator's zone rides the QUERY (`?timezone=`), the
|
|
771
|
+
// shape the server's create route reads it from; the body stays
|
|
772
|
+
// `{ name, slug, logo }`. better-auth's client proxy lifts
|
|
773
|
+
// `fetchOptions.query` onto the URL and keeps it out of the body. An
|
|
774
|
+
// absent or empty zone sends no parameter at all.
|
|
775
|
+
const { timezone } = orgData;
|
|
730
776
|
const { data, error } = await betterAuth.organization.create({
|
|
731
777
|
name: orgData.name,
|
|
732
778
|
slug: orgData.slug,
|
|
733
779
|
logo: orgData.logo,
|
|
780
|
+
...(timezone ? { fetchOptions: { query: { timezone } } } : {}),
|
|
734
781
|
});
|
|
735
782
|
if (error)
|
|
736
783
|
throw toAuthError(error, 'Failed to create organization');
|
|
@@ -28,9 +28,10 @@ export interface CreateAuthenticatedFetchOptions {
|
|
|
28
28
|
* current page are passed through to the bare global fetch — no
|
|
29
29
|
* Authorization, X-Tenant-ID, or Accept-Language headers are attached.
|
|
30
30
|
*
|
|
31
|
-
* Use this for fetches whose target URL
|
|
32
|
-
*
|
|
33
|
-
* that must never see the
|
|
31
|
+
* Use this for fetches whose target URL may come from metadata (a
|
|
32
|
+
* `type: 'api'` action's target) or that a host hands to the renderers as
|
|
33
|
+
* `apiFetch`: those may point at third-party hosts that must never see the
|
|
34
|
+
* platform bearer token.
|
|
34
35
|
*/
|
|
35
36
|
sameOriginOnly?: boolean;
|
|
36
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createAuthenticatedFetch.d.ts","sourceRoot":"","sources":["../src/createAuthenticatedFetch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,gCAAgC,CAAC;AAExC,MAAM,WAAW,+BAA+B;IAC9C
|
|
1
|
+
{"version":3,"file":"createAuthenticatedFetch.d.ts","sourceRoot":"","sources":["../src/createAuthenticatedFetch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,gCAAgC,CAAC;AAExC,MAAM,WAAW,+BAA+B;IAC9C;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,+BAA+B,GACxC,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CA+GrE"}
|
|
@@ -122,7 +122,7 @@ export function createAuthenticatedFetch(options) {
|
|
|
122
122
|
//
|
|
123
123
|
// The console injects the same localStorage bearer from two places: the
|
|
124
124
|
// AUTH lane (sign-in / get-session / the auth endpoints) and THIS one — the
|
|
125
|
-
// adapter,
|
|
125
|
+
// adapter, the renderers' `apiFetch`, and every metadata `type: 'api'`
|
|
126
126
|
// action (`useConsoleActionRuntime`'s `apiHandler`). better-auth's
|
|
127
127
|
// server-side bearer plugin hands a rotated session token back in
|
|
128
128
|
// `set-auth-token` on whichever lane the call arrived over. Only the auth
|
package/dist/types.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type React from 'react';
|
|
9
9
|
import type { AuthUser as SpecAuthUser, DelegableScope, DelegableAdminScope } from '@objectstack/spec/contracts';
|
|
10
10
|
import type { TenancyPosture } from '@objectstack/spec/security';
|
|
11
|
+
import type { AudiencePosture } from '@objectstack/spec/system';
|
|
11
12
|
import type { AuthInvitationStatus } from './invitation-status.js';
|
|
12
13
|
/**
|
|
13
14
|
* Authentication types for @object-ui/auth
|
|
@@ -96,6 +97,17 @@ export interface AuthClientSession {
|
|
|
96
97
|
* session, which is why the banner costs a normal session nothing.
|
|
97
98
|
*/
|
|
98
99
|
impersonatedBy?: string;
|
|
100
|
+
/**
|
|
101
|
+
* objectui#12809 — set by better-auth's organization plugin on the
|
|
102
|
+
* `GET /auth/get-session` answer: the id of the organization this session is
|
|
103
|
+
* active in, or `null` when it holds none. `AuthProvider` names the active
|
|
104
|
+
* organization from it and the `organization/list` answer, and asks for the
|
|
105
|
+
* member row at the same time. Absent when the answer did not come from
|
|
106
|
+
* `get-session` (a sign-in response) or the server has no organization
|
|
107
|
+
* plugin; the provider then asks the server for the active organization, as
|
|
108
|
+
* before.
|
|
109
|
+
*/
|
|
110
|
+
activeOrganizationId?: string | null;
|
|
99
111
|
}
|
|
100
112
|
/** Authentication state */
|
|
101
113
|
export interface AuthState {
|
|
@@ -258,6 +270,23 @@ export interface AuthPublicConfig {
|
|
|
258
270
|
* Absent (older server / config not yet fetched) ⇒ no group affordances.
|
|
259
271
|
*/
|
|
260
272
|
tenancyPosture?: TenancyPosture;
|
|
273
|
+
/**
|
|
274
|
+
* Which audience posture is in force — the deployment's declared answer
|
|
275
|
+
* to "who may self-register here" (`invite_only`, the undeclared default;
|
|
276
|
+
* `email_domain`; `open`). A value, not a flag: the spec lists it in
|
|
277
|
+
* `PUBLIC_AUTH_CONFIG_NON_FLAG_KEYS` beside `tenancyPosture`.
|
|
278
|
+
*
|
|
279
|
+
* It does NOT replace `emailPassword.disableSignUp`, and the server does
|
|
280
|
+
* not force that flag from it: under `invite_only` the sign-up route still
|
|
281
|
+
* admits a pending invitee (and a fresh deployment's first owner), so the
|
|
282
|
+
* two keys together read "sign-up is open to invitees only". A surface
|
|
283
|
+
* that offers a generic sign-up therefore reads both — the console's
|
|
284
|
+
* login and register pages and `@object-ui/app-shell`'s exported default
|
|
285
|
+
* ones do, through app-shell's `decideSignUpOffer` (objectui#11691,
|
|
286
|
+
* objectui#11705). Absent (older server / config not yet fetched) ⇒
|
|
287
|
+
* `disableSignUp` alone decides, as it did before the key existed.
|
|
288
|
+
*/
|
|
289
|
+
audiencePosture?: AudiencePosture;
|
|
261
290
|
};
|
|
262
291
|
}
|
|
263
292
|
/** Options when initiating a third-party sign-in */
|
|
@@ -343,11 +372,20 @@ export interface AuthClient {
|
|
|
343
372
|
signInWithProvider: (providerId: string, options?: SignInWithProviderOptions) => Promise<void>;
|
|
344
373
|
/** List organizations the current user belongs to */
|
|
345
374
|
listOrganizations: () => Promise<AuthOrganization[]>;
|
|
346
|
-
/**
|
|
375
|
+
/**
|
|
376
|
+
* Create a new organization.
|
|
377
|
+
*
|
|
378
|
+
* `timezone` (objectui#11908) is the creator's IANA zone, for example the
|
|
379
|
+
* browser's `Intl.DateTimeFormat().resolvedOptions().timeZone`. It travels
|
|
380
|
+
* as the `timezone` QUERY parameter of `POST /organization/create`, never in
|
|
381
|
+
* the body, which stays `{ name, slug, logo }`. Omitted or empty, nothing is
|
|
382
|
+
* sent and the server keeps its default zone; the server judges the value.
|
|
383
|
+
*/
|
|
347
384
|
createOrganization: (data: {
|
|
348
385
|
name: string;
|
|
349
386
|
slug: string;
|
|
350
387
|
logo?: string;
|
|
388
|
+
timezone?: string;
|
|
351
389
|
}) => Promise<AuthOrganization>;
|
|
352
390
|
/** Set the active organization for the current session */
|
|
353
391
|
setActiveOrganization: (orgId: string) => Promise<AuthOrganization | null>;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EACV,QAAQ,IAAI,YAAY,EACxB,cAAc,EACd,mBAAmB,EACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnE;;GAEG;AAEH;;;;;;;;;;;;GAYG;AACH,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B;;;;;;;;;;;GAWG;AACH,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,QAAS,SAAQ,YAAY;IAC5C,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IAUd,gCAAgC;IAChC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+BAA+B;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAWjG;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/auth",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "18.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Authentication system for Object UI with AuthProvider, useAuth hook, AuthGuard, and form components.",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"react": "^18.0.0 || ^19.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@objectstack/spec": "^
|
|
33
|
+
"@objectstack/spec": "^18.0.0",
|
|
34
34
|
"better-auth": "^1.7.2"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|