cauthy-auth 0.28.1 → 0.31.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.
Files changed (46) hide show
  1. package/README.md +61 -3
  2. package/dist/{cauthy-CTVg4kvo.d.mts → cauthy-2jtP2LVQ.d.mts} +19 -1
  3. package/dist/{cauthy-BAwAJChs.d.ts → cauthy-CKfvvv_i.d.ts} +19 -1
  4. package/dist/cli.js +163 -59
  5. package/dist/client.d.mts +91 -10
  6. package/dist/client.d.ts +91 -10
  7. package/dist/client.js +39 -3
  8. package/dist/client.js.map +1 -1
  9. package/dist/client.mjs +39 -3
  10. package/dist/client.mjs.map +1 -1
  11. package/dist/express.d.mts +2 -2
  12. package/dist/express.d.ts +2 -2
  13. package/dist/fastify.d.mts +2 -2
  14. package/dist/fastify.d.ts +2 -2
  15. package/dist/hono.d.mts +2 -2
  16. package/dist/hono.d.ts +2 -2
  17. package/dist/index.d.mts +2 -2
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.js +387 -148
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +385 -148
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/next.d.mts +2 -2
  24. package/dist/next.d.ts +2 -2
  25. package/dist/react.d.mts +275 -23
  26. package/dist/react.d.ts +275 -23
  27. package/dist/react.js +312 -78
  28. package/dist/react.js.map +1 -1
  29. package/dist/react.mjs +311 -78
  30. package/dist/react.mjs.map +1 -1
  31. package/dist/styles.css +8 -0
  32. package/dist/svelte.d.mts +2 -2
  33. package/dist/svelte.d.ts +2 -2
  34. package/dist/svelte.js +53 -4
  35. package/dist/svelte.js.map +1 -1
  36. package/dist/svelte.mjs +53 -4
  37. package/dist/svelte.mjs.map +1 -1
  38. package/dist/{system-CVBW_AV8.d.mts → system-BrrJsrFW.d.mts} +321 -109
  39. package/dist/{system-Bn8uckvQ.d.ts → system-BvvoNs2y.d.ts} +321 -109
  40. package/dist/vue.d.mts +2 -2
  41. package/dist/vue.d.ts +2 -2
  42. package/dist/vue.js +53 -4
  43. package/dist/vue.js.map +1 -1
  44. package/dist/vue.mjs +53 -4
  45. package/dist/vue.mjs.map +1 -1
  46. package/package.json +1 -1
package/dist/client.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CauthyTheme } from './theme.mjs';
2
- import { i as PublicSession } from './system-CVBW_AV8.mjs';
2
+ import { q as PublicSession, f as CheckQuestion, k as OrganizationRef, g as CheckResult } from './system-BrrJsrFW.mjs';
3
+ export { h as CheckRule } from './system-BrrJsrFW.mjs';
3
4
  import { A as AuthField, b as PasswordPolicy } from './validate-sEXG82po.mjs';
4
5
  export { D as DEFAULT_PASSWORD_POLICY, F as FieldProblem, M as MIN_PASSWORD, P as PasswordClass, a as PasswordLocale, c as PasswordReason, d as checkCode, e as checkEmail, f as checkName, g as checkNewPassword, h as checkPassword, i as describePasswordPolicy, j as explainPasswordProblems, p as passwordPolicyOf, k as passwordProblems } from './validate-sEXG82po.mjs';
5
6
  import 'jose';
@@ -73,11 +74,25 @@ type AuthResult<S extends string, More = unknown> = ({
73
74
  };
74
75
  /** What the screen may offer. */
75
76
  interface AuthMethods {
76
- /** The providers the workspace signs people in with: `{ id: 'google', name: 'Google' }`. */
77
+ /**
78
+ * The providers the workspace signs people in with and has turned on:
79
+ * `{ id: 'google', name: 'Google', iconUrl: 'https://…/v1/social/icons/google.svg' }`.
80
+ * `iconUrl` is the provider's logo, served by Cauthy; absent from a Cauthy
81
+ * that does not serve logos.
82
+ */
77
83
  providers: {
78
84
  id: string;
79
85
  name: string;
86
+ iconUrl?: string;
80
87
  }[];
88
+ /**
89
+ * Whether the workspace signs people in with a password. False: the
90
+ * password field, "forgot your password" and a sign-up with a password are
91
+ * not offered, and Cauthy refuses them (`password_disabled`). True from a
92
+ * server that does not say. (`password` is something else: the policy a new
93
+ * password must meet.)
94
+ */
95
+ passwordSignIn: boolean;
81
96
  /** Whether people may create an account themselves. */
82
97
  signUp: boolean;
83
98
  /** Set when the application's system makes an organization per sign-up: the form asks the company's name. */
@@ -86,7 +101,7 @@ interface AuthMethods {
86
101
  required: boolean;
87
102
  };
88
103
  magicLink: boolean;
89
- /** Whether to offer a passkey. */
104
+ /** Whether to offer a passkey. False when the workspace turned passkeys off. */
90
105
  passkey: boolean;
91
106
  /**
92
107
  * Whether the passkey is used on this page itself. False: `signIn.passkey`
@@ -138,6 +153,15 @@ interface AuthMethods {
138
153
  */
139
154
  password?: PasswordPolicy;
140
155
  }
156
+ /** An invitation, as its page shows it before the person chooses a password. */
157
+ interface Invitation {
158
+ /** The address invited: the account the person signs in as. */
159
+ email: string;
160
+ /** The workspace's name. */
161
+ workspace: string;
162
+ /** The organization the person was invited to, when there is one. */
163
+ organization?: string;
164
+ }
141
165
  /** A legal document the person owes: not yet accepted in its current version. */
142
166
  interface PendingLegal {
143
167
  kind: string;
@@ -201,19 +225,29 @@ interface ConnectedAgent {
201
225
  connectedAt: string;
202
226
  lastUsedAt: string;
203
227
  }
204
- /** An organization the person belongs to, with what their role lets them do with its people. */
228
+ /** An organization the person belongs to, with what their role lets them do with it and its people. */
205
229
  interface OrganizationSeat {
206
230
  id: string;
207
- /** The client system it is of (`pondari`). */
231
+ /** The client system it is of (`acme-crm`). */
208
232
  system: string;
209
233
  name: string;
210
234
  kind: string;
211
235
  /** The person's role in it. */
212
236
  role: string;
213
- /** Cauthy's member powers, from the system's catalog: cauthy:members read and manage. */
237
+ /**
238
+ * Cauthy's reserved powers, from the system's catalog. `manageMembers`
239
+ * (cauthy:members:manage) adds, invites and changes roles; removing another
240
+ * member is `removeMembers` (cauthy:members:remove), and anybody may leave.
241
+ * `update`, `archive` and `delete` are cauthy:organization's: rename it,
242
+ * archive it, delete it for good.
243
+ */
214
244
  can: {
215
245
  readMembers: boolean;
216
246
  manageMembers: boolean;
247
+ removeMembers: boolean;
248
+ update: boolean;
249
+ archive: boolean;
250
+ delete: boolean;
217
251
  };
218
252
  }
219
253
  /** A person of an organization, as a member who may see them reads it. */
@@ -243,8 +277,12 @@ interface OrganizationRoleChoice {
243
277
  }
244
278
  /**
245
279
  * The signed-in person's organizations, and their people. Seeing them needs
246
- * cauthy:members read in the person's role; changing them needs manage. Cauthy
247
- * decides, with the person's own session: the application authorizes nothing.
280
+ * cauthy:members read in the person's role; changing them needs manage, and
281
+ * removing someone needs remove. The organization itself is renamed, archived
282
+ * or deleted with cauthy:organization update, archive or delete. Only an
283
+ * `owner` gives the owner role or changes an owner, and the last owner stays
284
+ * (`last_owner`). Cauthy decides, with the person's own session: the
285
+ * application authorizes nothing.
248
286
  */
249
287
  interface OrganizationsClient {
250
288
  /** The organizations the person belongs to; one system's only with `system`. */
@@ -276,6 +314,31 @@ interface OrganizationsClient {
276
314
  }): Promise<AuthResult<'sent', {
277
315
  userId: string;
278
316
  }>>;
317
+ /** Gives the organization a new name (cauthy:organization update). */
318
+ rename(organizationId: string, name: string): Promise<AuthResult<'renamed', {
319
+ organization: OrganizationSeat;
320
+ }>>;
321
+ /** Archives the organization: its members no longer reach it, and only the workspace restores it. */
322
+ archive(organizationId: string): Promise<AuthResult<'archived', {
323
+ organizationId: string;
324
+ }>>;
325
+ /** Deletes the organization for good. */
326
+ delete(organizationId: string): Promise<AuthResult<'deleted', {
327
+ organizationId: string;
328
+ }>>;
329
+ /**
330
+ * Asks Cauthy, through the application's server, whether the person may do
331
+ * an action on a resource in the organization, with what its conditions
332
+ * read. A question that could not be asked is a no, with `error` saying why.
333
+ *
334
+ * const r = await organizations.check({ organization, resource: 'invoices', action: 'approve', attributes: { amount: 500 } })
335
+ * if (r.allowed) showApprove()
336
+ */
337
+ check(question: CheckQuestion & {
338
+ organization: OrganizationRef;
339
+ }): Promise<CheckResult & {
340
+ error?: AuthError;
341
+ }>;
279
342
  }
280
343
  interface AccountSession {
281
344
  /** "Mac · Chrome", or "" when the browser did not say. */
@@ -690,7 +753,25 @@ interface AuthClient {
690
753
  token: string;
691
754
  password: string;
692
755
  }): Promise<AuthResult<'changed'>>;
693
- /** Confirms the address, with the token the mailed link opened the page with (`token`). */
756
+ /**
757
+ * Who an invitation is for and to what, with the token the mailed link
758
+ * opened the page with (`token`). Reading it does not spend it.
759
+ * `invalid_token`: unknown, spent or expired.
760
+ */
761
+ invitation(token: string): Promise<AuthResult<'ok', {
762
+ invitation: Invitation;
763
+ }>>;
764
+ /**
765
+ * Accepts an invitation with the password the person chose (the workspace's
766
+ * policy, as for a sign-up) and signs them in. `mfa_required`: the account
767
+ * owes a second step; ask for the code and call `signIn.mfa`, as after
768
+ * `signIn.email`. A password refused leaves the invitation good.
769
+ */
770
+ acceptInvitation(token: string, password: string): Promise<AuthResult<'signed_in', {
771
+ session: PublicSession;
772
+ }> | AuthResult<'mfa_required', {
773
+ method?: 'email';
774
+ }>>;
694
775
  /** Confirms the address a mailed link is for. The person is signed in from the link when their account asks no second step. */
695
776
  verifyEmail(form: {
696
777
  token: string;
@@ -725,4 +806,4 @@ interface AuthClientOptions {
725
806
  }
726
807
  declare function createAuthClient(options?: AuthClientOptions): AuthClient;
727
808
 
728
- export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type ConnectedAgent, type LinkedIdentity, type OrganizationPerson, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, PasswordPolicy, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
809
+ export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, CheckQuestion, CheckResult, type ConnectedAgent, type Invitation, type LinkedIdentity, type OrganizationPerson, OrganizationRef, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, PasswordPolicy, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
package/dist/client.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CauthyTheme } from './theme.js';
2
- import { i as PublicSession } from './system-Bn8uckvQ.js';
2
+ import { q as PublicSession, f as CheckQuestion, k as OrganizationRef, g as CheckResult } from './system-BvvoNs2y.js';
3
+ export { h as CheckRule } from './system-BvvoNs2y.js';
3
4
  import { A as AuthField, b as PasswordPolicy } from './validate-sEXG82po.js';
4
5
  export { D as DEFAULT_PASSWORD_POLICY, F as FieldProblem, M as MIN_PASSWORD, P as PasswordClass, a as PasswordLocale, c as PasswordReason, d as checkCode, e as checkEmail, f as checkName, g as checkNewPassword, h as checkPassword, i as describePasswordPolicy, j as explainPasswordProblems, p as passwordPolicyOf, k as passwordProblems } from './validate-sEXG82po.js';
5
6
  import 'jose';
@@ -73,11 +74,25 @@ type AuthResult<S extends string, More = unknown> = ({
73
74
  };
74
75
  /** What the screen may offer. */
75
76
  interface AuthMethods {
76
- /** The providers the workspace signs people in with: `{ id: 'google', name: 'Google' }`. */
77
+ /**
78
+ * The providers the workspace signs people in with and has turned on:
79
+ * `{ id: 'google', name: 'Google', iconUrl: 'https://…/v1/social/icons/google.svg' }`.
80
+ * `iconUrl` is the provider's logo, served by Cauthy; absent from a Cauthy
81
+ * that does not serve logos.
82
+ */
77
83
  providers: {
78
84
  id: string;
79
85
  name: string;
86
+ iconUrl?: string;
80
87
  }[];
88
+ /**
89
+ * Whether the workspace signs people in with a password. False: the
90
+ * password field, "forgot your password" and a sign-up with a password are
91
+ * not offered, and Cauthy refuses them (`password_disabled`). True from a
92
+ * server that does not say. (`password` is something else: the policy a new
93
+ * password must meet.)
94
+ */
95
+ passwordSignIn: boolean;
81
96
  /** Whether people may create an account themselves. */
82
97
  signUp: boolean;
83
98
  /** Set when the application's system makes an organization per sign-up: the form asks the company's name. */
@@ -86,7 +101,7 @@ interface AuthMethods {
86
101
  required: boolean;
87
102
  };
88
103
  magicLink: boolean;
89
- /** Whether to offer a passkey. */
104
+ /** Whether to offer a passkey. False when the workspace turned passkeys off. */
90
105
  passkey: boolean;
91
106
  /**
92
107
  * Whether the passkey is used on this page itself. False: `signIn.passkey`
@@ -138,6 +153,15 @@ interface AuthMethods {
138
153
  */
139
154
  password?: PasswordPolicy;
140
155
  }
156
+ /** An invitation, as its page shows it before the person chooses a password. */
157
+ interface Invitation {
158
+ /** The address invited: the account the person signs in as. */
159
+ email: string;
160
+ /** The workspace's name. */
161
+ workspace: string;
162
+ /** The organization the person was invited to, when there is one. */
163
+ organization?: string;
164
+ }
141
165
  /** A legal document the person owes: not yet accepted in its current version. */
142
166
  interface PendingLegal {
143
167
  kind: string;
@@ -201,19 +225,29 @@ interface ConnectedAgent {
201
225
  connectedAt: string;
202
226
  lastUsedAt: string;
203
227
  }
204
- /** An organization the person belongs to, with what their role lets them do with its people. */
228
+ /** An organization the person belongs to, with what their role lets them do with it and its people. */
205
229
  interface OrganizationSeat {
206
230
  id: string;
207
- /** The client system it is of (`pondari`). */
231
+ /** The client system it is of (`acme-crm`). */
208
232
  system: string;
209
233
  name: string;
210
234
  kind: string;
211
235
  /** The person's role in it. */
212
236
  role: string;
213
- /** Cauthy's member powers, from the system's catalog: cauthy:members read and manage. */
237
+ /**
238
+ * Cauthy's reserved powers, from the system's catalog. `manageMembers`
239
+ * (cauthy:members:manage) adds, invites and changes roles; removing another
240
+ * member is `removeMembers` (cauthy:members:remove), and anybody may leave.
241
+ * `update`, `archive` and `delete` are cauthy:organization's: rename it,
242
+ * archive it, delete it for good.
243
+ */
214
244
  can: {
215
245
  readMembers: boolean;
216
246
  manageMembers: boolean;
247
+ removeMembers: boolean;
248
+ update: boolean;
249
+ archive: boolean;
250
+ delete: boolean;
217
251
  };
218
252
  }
219
253
  /** A person of an organization, as a member who may see them reads it. */
@@ -243,8 +277,12 @@ interface OrganizationRoleChoice {
243
277
  }
244
278
  /**
245
279
  * The signed-in person's organizations, and their people. Seeing them needs
246
- * cauthy:members read in the person's role; changing them needs manage. Cauthy
247
- * decides, with the person's own session: the application authorizes nothing.
280
+ * cauthy:members read in the person's role; changing them needs manage, and
281
+ * removing someone needs remove. The organization itself is renamed, archived
282
+ * or deleted with cauthy:organization update, archive or delete. Only an
283
+ * `owner` gives the owner role or changes an owner, and the last owner stays
284
+ * (`last_owner`). Cauthy decides, with the person's own session: the
285
+ * application authorizes nothing.
248
286
  */
249
287
  interface OrganizationsClient {
250
288
  /** The organizations the person belongs to; one system's only with `system`. */
@@ -276,6 +314,31 @@ interface OrganizationsClient {
276
314
  }): Promise<AuthResult<'sent', {
277
315
  userId: string;
278
316
  }>>;
317
+ /** Gives the organization a new name (cauthy:organization update). */
318
+ rename(organizationId: string, name: string): Promise<AuthResult<'renamed', {
319
+ organization: OrganizationSeat;
320
+ }>>;
321
+ /** Archives the organization: its members no longer reach it, and only the workspace restores it. */
322
+ archive(organizationId: string): Promise<AuthResult<'archived', {
323
+ organizationId: string;
324
+ }>>;
325
+ /** Deletes the organization for good. */
326
+ delete(organizationId: string): Promise<AuthResult<'deleted', {
327
+ organizationId: string;
328
+ }>>;
329
+ /**
330
+ * Asks Cauthy, through the application's server, whether the person may do
331
+ * an action on a resource in the organization, with what its conditions
332
+ * read. A question that could not be asked is a no, with `error` saying why.
333
+ *
334
+ * const r = await organizations.check({ organization, resource: 'invoices', action: 'approve', attributes: { amount: 500 } })
335
+ * if (r.allowed) showApprove()
336
+ */
337
+ check(question: CheckQuestion & {
338
+ organization: OrganizationRef;
339
+ }): Promise<CheckResult & {
340
+ error?: AuthError;
341
+ }>;
279
342
  }
280
343
  interface AccountSession {
281
344
  /** "Mac · Chrome", or "" when the browser did not say. */
@@ -690,7 +753,25 @@ interface AuthClient {
690
753
  token: string;
691
754
  password: string;
692
755
  }): Promise<AuthResult<'changed'>>;
693
- /** Confirms the address, with the token the mailed link opened the page with (`token`). */
756
+ /**
757
+ * Who an invitation is for and to what, with the token the mailed link
758
+ * opened the page with (`token`). Reading it does not spend it.
759
+ * `invalid_token`: unknown, spent or expired.
760
+ */
761
+ invitation(token: string): Promise<AuthResult<'ok', {
762
+ invitation: Invitation;
763
+ }>>;
764
+ /**
765
+ * Accepts an invitation with the password the person chose (the workspace's
766
+ * policy, as for a sign-up) and signs them in. `mfa_required`: the account
767
+ * owes a second step; ask for the code and call `signIn.mfa`, as after
768
+ * `signIn.email`. A password refused leaves the invitation good.
769
+ */
770
+ acceptInvitation(token: string, password: string): Promise<AuthResult<'signed_in', {
771
+ session: PublicSession;
772
+ }> | AuthResult<'mfa_required', {
773
+ method?: 'email';
774
+ }>>;
694
775
  /** Confirms the address a mailed link is for. The person is signed in from the link when their account asks no second step. */
695
776
  verifyEmail(form: {
696
777
  token: string;
@@ -725,4 +806,4 @@ interface AuthClientOptions {
725
806
  }
726
807
  declare function createAuthClient(options?: AuthClientOptions): AuthClient;
727
808
 
728
- export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type ConnectedAgent, type LinkedIdentity, type OrganizationPerson, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, PasswordPolicy, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
809
+ export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, CheckQuestion, CheckResult, type ConnectedAgent, type Invitation, type LinkedIdentity, type OrganizationPerson, OrganizationRef, type OrganizationRoleChoice, type OrganizationSeat, type OrganizationsClient, type Passkey, type PasskeyKind, PasswordPolicy, type PendingLegal, type SSOConnection, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
package/dist/client.js CHANGED
@@ -294,6 +294,12 @@ function checkOrganization(value, required = false) {
294
294
  if (v.length > MAX_ORGANIZATION) return { field: "organization", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
295
295
  return null;
296
296
  }
297
+ function checkOrganizationName(value) {
298
+ const v = typeof value === "string" ? value.trim() : "";
299
+ if (v === "") return { field: "name", code: "required", message: "Enter the organization's name." };
300
+ if (v.length > MAX_ORGANIZATION) return { field: "name", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
301
+ return null;
302
+ }
297
303
  function checkCode(value) {
298
304
  const v = typeof value === "string" ? value.trim() : "";
299
305
  if (v === "") return { field: "code", code: "required", message: "Enter the code." };
@@ -486,11 +492,11 @@ function createAuthClient(options = {}) {
486
492
  if (res.ok) {
487
493
  const m = await res.json();
488
494
  if (m.password) policy = passwordPolicyOf(m.password);
489
- return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
495
+ return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passwordSignIn: m.passwordSignIn !== false, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
490
496
  }
491
497
  } catch {
492
498
  }
493
- return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
499
+ return { providers: [], signUp: true, passwordSignIn: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
494
500
  }
495
501
  async function ceremony(begin, finish, form, run, more = {}) {
496
502
  const begun = await post(begin, form, null);
@@ -637,6 +643,20 @@ function createAuthClient(options = {}) {
637
643
  forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
638
644
  resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password, policy)),
639
645
  verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
646
+ async invitation(token) {
647
+ const bad = { ok: false, error: { code: "invalid_token", message: "This invitation is not valid, or it has expired." } };
648
+ if (typeof token !== "string" || token.trim() === "") return bad;
649
+ try {
650
+ const res = await doFetch(`${base}/invitation?token=${encodeURIComponent(token.trim())}`, { credentials: "same-origin", headers: { accept: "application/json" } });
651
+ const body = await res.json().catch(() => null);
652
+ if (res.ok && body?.invitation) return { ok: true, status: "ok", invitation: body.invitation };
653
+ const e = body?.error;
654
+ return { ok: false, error: e && typeof e.code === "string" ? { code: e.code, message: String(e.message ?? "") } : UNAVAILABLE };
655
+ } catch {
656
+ return { ok: false, error: UNAVAILABLE };
657
+ }
658
+ },
659
+ acceptInvitation: (token, password) => post("/invitation/accept", { token, password }, checkNewPassword(password, policy)),
640
660
  signOut: () => post("/sign-out", {}, null),
641
661
  organizations: {
642
662
  async list(system) {
@@ -654,7 +674,23 @@ function createAuthClient(options = {}) {
654
674
  add: (id, f) => post("/me/organizations/members/add", { organizationId: id, email: f.email, role: f.role, name: f.name ?? "", locale: f.locale ?? "" }, checkEmail(f.email)),
655
675
  setRole: (id, userId, role) => post("/me/organizations/members/role", { organizationId: id, userId, role }, null),
656
676
  remove: (id, userId) => post("/me/organizations/members/remove", { organizationId: id, userId }, null),
657
- inviteAgain: (id, userId, f = {}) => post("/me/organizations/members/invite", { organizationId: id, userId, locale: f.locale ?? "" }, null)
677
+ inviteAgain: (id, userId, f = {}) => post("/me/organizations/members/invite", { organizationId: id, userId, locale: f.locale ?? "" }, null),
678
+ rename: (id, name) => post("/me/organizations/rename", { organizationId: id, name }, checkOrganizationName(name)),
679
+ archive: (id) => post("/me/organizations/archive", { organizationId: id }, null),
680
+ delete: (id) => post("/me/organizations/delete", { organizationId: id }, null),
681
+ async check(q) {
682
+ const organizationId = typeof q.organization === "string" ? q.organization : q.organization?.id ?? "";
683
+ const r = await post("/me/organizations/check", {
684
+ organizationId,
685
+ resource: q.resource,
686
+ action: q.action,
687
+ ...q.attributes !== void 0 ? { attributes: q.attributes } : {},
688
+ ...q.resourceId !== void 0 ? { resourceId: q.resourceId } : {},
689
+ ...q.ownerId !== void 0 ? { ownerId: q.ownerId } : {}
690
+ }, null);
691
+ if (!r.ok) return { allowed: false, reason: r.error.message, error: r.error };
692
+ return { allowed: r.allowed === true, reason: String(r.reason ?? ""), ...r.rule ? { rule: r.rule } : {} };
693
+ }
658
694
  },
659
695
  account: {
660
696
  async get() {