@authyon/auth 0.2.0-beta.2 → 0.2.0-beta.20

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.
@@ -1,3 +1,79 @@
1
+ /**
2
+ * Security methods active on the user's account, as returned in the
3
+ * `security` field of `GET /auth/me` and `POST /auth/validate`.
4
+ */
5
+ interface SecurityMethods {
6
+ /** A password is set (false for accounts created only through SSO or magic link). */
7
+ password: boolean;
8
+ /** Authenticator app (TOTP one-time codes). */
9
+ authenticator: boolean;
10
+ /** One-time codes sent by email. */
11
+ emailCode: boolean;
12
+ /** At least one passkey registered. */
13
+ passkey: boolean;
14
+ passkeyCount: number;
15
+ /** Unused recovery codes left. */
16
+ recoveryCodes: number;
17
+ /** SSO providers linked to the account, e.g. `["google", "github"]`. */
18
+ sso: string[];
19
+ /** Any second factor is active (authenticator, email code or passkey). */
20
+ twoFactor: boolean;
21
+ }
22
+ /** Lifecycle of an approval. */
23
+ type ApprovalStatus = "pending" | "approved" | "denied" | "consumed" | "expired";
24
+ /**
25
+ * Free-form JSON object describing what the customer approves (up to 16 KiB).
26
+ * The API stores it, shows it back to the customer and binds its canonical
27
+ * hash (keys sorted) to the approval.
28
+ */
29
+ type ApprovalPayload = Record<string, unknown>;
30
+ /** Evidence of how the customer proved their identity when approving. */
31
+ interface ApprovalAssurance {
32
+ /** `urn:authyon:loa:3` (passkey) or `urn:authyon:loa:2` (authenticator code). */
33
+ acr: string;
34
+ /** Authentication methods, e.g. `["pwd", "otp"]` or `["pwd", "webauthn"]`. */
35
+ amr: string[];
36
+ /** When the second factor was verified (ISO 8601). */
37
+ authTime: string;
38
+ }
39
+ /** A request for the customer to approve something with a second factor. */
40
+ interface Approval {
41
+ id: string;
42
+ status: ApprovalStatus;
43
+ subjectId: string;
44
+ tenantId?: string | null;
45
+ /** The payload given to `create()`, with keys in canonical order. */
46
+ payload: ApprovalPayload;
47
+ /** SHA-256 (hex) of the canonical payload. */
48
+ payloadHash: string;
49
+ createdAt: string;
50
+ expiresAt: string;
51
+ decidedAt?: string | null;
52
+ consumedAt?: string | null;
53
+ /** Present once approved. */
54
+ assurance?: ApprovalAssurance | null;
55
+ }
56
+ /** Second factor sent with `confirm()`. */
57
+ type ConfirmApprovalInput = {
58
+ method: "authenticator";
59
+ /** Six-digit code from the customer's authenticator app. */
60
+ code: string;
61
+ } | {
62
+ method: "webauthn";
63
+ /** Assertion for the options returned by `webauthnOptions()`. */
64
+ webAuthn: {
65
+ ceremonyToken: string;
66
+ /** JSON-serialized `PublicKeyCredential` from `navigator.credentials.get()`. */
67
+ assertionJson: string;
68
+ };
69
+ };
70
+ /** Passkey assertion options for approving one approval. */
71
+ interface ApprovalWebAuthnOptions {
72
+ ceremonyToken: string;
73
+ /** JSON `PublicKeyCredentialRequestOptions` to pass to `navigator.credentials.get()`. */
74
+ optionsJson: string;
75
+ }
76
+
1
77
  interface HttpAdapterRequest {
2
78
  url: string;
3
79
  method: string;
@@ -55,14 +131,53 @@ declare class LoggingHttpAdapter implements HttpAdapter {
55
131
  private log;
56
132
  }
57
133
 
134
+ /** Customer-side operations on an approval (`/auth/authorizations`). */
135
+ interface SubjectApprovals {
136
+ /** GET /auth/authorizations/{id} — the pending payload, to show before asking for the code. */
137
+ get(id: string): Promise<Approval>;
138
+ /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving it. */
139
+ webauthnOptions(id: string): Promise<ApprovalWebAuthnOptions>;
140
+ /**
141
+ * POST /auth/authorizations/{id}/confirm — approves it.
142
+ *
143
+ * Pass the authenticator code or passkey assertion to verify the second
144
+ * factor inline (recommended). Without `input`, the session itself must
145
+ * have used a passkey or authenticator code within the last 5 minutes,
146
+ * otherwise the API answers `step_up_required`.
147
+ */
148
+ confirm(id: string, input?: ConfirmApprovalInput): Promise<Approval>;
149
+ /** POST /auth/authorizations/{id}/reject — the customer declines it. */
150
+ reject(id: string): Promise<Approval>;
151
+ }
152
+ /** The `security` namespace an end-user client exposes. */
153
+ interface SubjectSecurity {
154
+ /**
155
+ * GET /auth/me — which security methods the signed-in user has active
156
+ * (password, authenticator, email code, passkeys, recovery codes, SSO).
157
+ */
158
+ methods(): Promise<SecurityMethods>;
159
+ /** Approve or reject what your backend asked the customer to confirm. */
160
+ readonly approvals: SubjectApprovals;
161
+ }
162
+
58
163
  interface PaginationOptions {
59
164
  skip?: number;
60
165
  take?: number;
61
166
  }
167
+ /**
168
+ * The envelope every paged endpoint answers with.
169
+ *
170
+ * `page` is the 1-based current page and `perPage` is how many items a page
171
+ * holds — the same sense `take` has on the way in. Up to Authyon API
172
+ * 2026-10-21 the two were named `perPage` (holding the page number) and
173
+ * `pageSize` (holding the limit), so reading them by their names gave the
174
+ * wrong value without failing. Clients on the older API see `page` undefined
175
+ * and `perPage` holding a page number.
176
+ */
62
177
  interface Paged<T> {
63
178
  data: T[];
64
- perPage?: number;
65
- pageSize: number;
179
+ page: number;
180
+ perPage: number;
66
181
  total: number;
67
182
  pages: number;
68
183
  hasNext: boolean;
@@ -93,12 +208,17 @@ interface Organization {
93
208
  name?: string;
94
209
  description?: string;
95
210
  roles?: string[];
211
+ /** JSON-encoded values for the organization's configured custom fields. */
212
+ customFields?: string;
213
+ /** JSON-encoded metadata visible to members of the organization. */
214
+ publicMetadata?: string;
96
215
  }
97
216
  /** POST /auth/tenants — creates an organization owned by the signed-in user. */
98
217
  interface CreateOrganizationInput {
99
218
  name?: string;
100
219
  slug?: string;
101
220
  description?: string;
221
+ customFields?: Record<string, unknown>;
102
222
  }
103
223
  /** GET /auth/tenants/{organizationId}/members — confirmed against the live API. */
104
224
  interface OrganizationMember {
@@ -114,6 +234,109 @@ interface InviteMemberInput {
114
234
  email: string;
115
235
  roles: string[];
116
236
  }
237
+ /**
238
+ * POST /auth/tenants/{organizationId}/invites — invite an address that may not
239
+ * have an account yet; accepting creates it. `sendEmail` defaults to `true`;
240
+ * `false` only returns the link, for delivery through your own channel.
241
+ */
242
+ interface OrganizationInviteInput {
243
+ email: string;
244
+ roles?: string[];
245
+ sendEmail?: boolean;
246
+ /**
247
+ * Where to send the invitee after they accept. Must be registered exactly in
248
+ * the environment's redirect URIs (the same list SSO and magic link use);
249
+ * otherwise the API rejects the invite. Comes back on `preview` and `accept`.
250
+ */
251
+ redirectUri?: string;
252
+ }
253
+ /** Returned when an invite is created or re-issued. `acceptUrl` is only ever
254
+ * readable here — the API keeps a hash of the token, not the token. */
255
+ interface OrganizationInviteIssued {
256
+ id: string;
257
+ tenantId: string;
258
+ email: string;
259
+ roles: string[];
260
+ acceptUrl: string;
261
+ expiresAt: string;
262
+ emailSent: boolean;
263
+ redirectUri: string | null;
264
+ }
265
+ type OrganizationInviteStatus = "pending" | "accepted" | "revoked" | "expired";
266
+ interface OrganizationInvite {
267
+ id: string;
268
+ tenantId: string;
269
+ email: string;
270
+ roles: string[];
271
+ status: OrganizationInviteStatus;
272
+ createdAt: string;
273
+ expiresAt: string;
274
+ emailSentAt: string | null;
275
+ acceptedAt: string | null;
276
+ acceptedByUserId: string | null;
277
+ revokedAt: string | null;
278
+ invitedByUserId: string | null;
279
+ redirectUri: string | null;
280
+ }
281
+ /** POST /auth/tenant-invites/preview — what an accept page shows before the invitee commits. */
282
+ interface OrganizationInvitePreview {
283
+ tenantId: string;
284
+ tenantName: string;
285
+ email: string;
286
+ roles: string[];
287
+ expiresAt: string;
288
+ /** True: accepting only needs the token. False: send a password (and a username when `usernameRequired`). */
289
+ accountExists: boolean;
290
+ usernameRequired: boolean;
291
+ /** Where the issuer asked to send the invitee after accepting, or `null`. */
292
+ redirectUri: string | null;
293
+ }
294
+ /** Options for POST /auth/password-reset/request. */
295
+ interface PasswordResetRequestOptions {
296
+ /**
297
+ * Where to send the person after they change the password. Must be
298
+ * registered exactly in the environment's redirect URIs (the same list SSO
299
+ * and magic link use); otherwise the request fails with
300
+ * `user.password_reset.redirect_uri.not_allowed`. Comes back on
301
+ * `validatePasswordReset` and `confirmPasswordReset`.
302
+ */
303
+ redirectUri?: string;
304
+ }
305
+ /** POST /auth/password-reset/validate — what a live reset token is for. */
306
+ interface PasswordResetPreview {
307
+ email: string;
308
+ expiresAt: string;
309
+ /** Where the reset was asked to end, or `null`. */
310
+ redirectUri: string | null;
311
+ }
312
+ /** POST /auth/password-reset/confirm — the password is changed. */
313
+ interface PasswordResetCompleted {
314
+ /** Where the reset was asked to end, or `null`. Navigating there is up to you. */
315
+ redirectUri: string | null;
316
+ }
317
+ /** POST /auth/tenant-invites/accept — everything but `token` is ignored when the address already has an account. */
318
+ interface AcceptOrganizationInviteInput {
319
+ token: string;
320
+ username?: string;
321
+ password?: string;
322
+ firstName?: string;
323
+ lastName?: string;
324
+ /**
325
+ * @deprecated Use `publicMetadata` / `privateMetadata` through
326
+ * `@authyon/server` (`environment.tenants.invites.accept`), which runs on
327
+ * your backend with the environment credential. Still accepted for now.
328
+ */
329
+ customFields?: Record<string, unknown>;
330
+ }
331
+ interface OrganizationInviteAccepted {
332
+ tenantId: string;
333
+ tenantName: string;
334
+ userId: string;
335
+ /** True when accepting created the account (email already confirmed). The invitee still signs in normally. */
336
+ accountCreated: boolean;
337
+ /** Where the issuer asked to send the invitee, or `null`. Redirecting is up to the accept page. */
338
+ redirectUri: string | null;
339
+ }
117
340
  /** Authenticated user profile. */
118
341
  interface User {
119
342
  id: string;
@@ -130,6 +353,8 @@ interface User {
130
353
  activeOrganization?: Organization | null;
131
354
  /** Actions the user must complete before continuing (e.g. confirm e-mail). */
132
355
  pendencies?: string[];
356
+ /** Security methods active on the account (authenticator, email code, passkey, SSO…). */
357
+ security?: SecurityMethods;
133
358
  }
134
359
  /** Token pair issued by login / refresh / tenant switch. */
135
360
  interface Session {
@@ -157,6 +382,8 @@ interface RegisterInput {
157
382
  email: string;
158
383
  username?: string;
159
384
  password: string;
385
+ /** Values for custom user fields configured in the environment. */
386
+ customFields?: Record<string, unknown>;
160
387
  }
161
388
  interface LoginInput {
162
389
  /** Provide `email` or `username`. */
@@ -289,6 +516,12 @@ type AuthState = "signed_out" | "authenticated" | "expired";
289
516
  interface AuthyonClientOptions {
290
517
  /** Publishable environment key (`pk_live_...` / `pk_test_...`). */
291
518
  envKey: string;
519
+ /**
520
+ * Originating client IP to forward on every API call as `X-Forwarded-For`.
521
+ * Browsers cannot reliably discover their public IP by themselves; set this
522
+ * only when your application receives it from trusted infrastructure.
523
+ */
524
+ clientIp?: string;
292
525
  /** API origin. Defaults to `https://api.authyon.com`; HTTPS is required outside loopback. */
293
526
  baseUrl?: string;
294
527
  /** Allow an HTTP `baseUrl`. Intended only for explicitly trusted local development. */
@@ -312,6 +545,7 @@ interface AuthyonClientOptions {
312
545
 
313
546
  declare class AuthyonClient {
314
547
  private readonly envKey;
548
+ private readonly clientIp?;
315
549
  private readonly baseUrl;
316
550
  private readonly storage;
317
551
  private readonly autoRefresh;
@@ -415,14 +649,33 @@ declare class AuthyonClient {
415
649
  * the endpoint differs, override via a raw call to your own backend.
416
650
  */
417
651
  revokeSession: (sessionId: string) => Promise<void>;
418
- /** POST /auth/password-reset/request — always resolves (no account enumeration). */
419
- requestPasswordReset: (email: string) => Promise<void>;
420
- /** POST /auth/password-reset/confirm — sets a new password and revokes all refresh tokens. */
421
- confirmPasswordReset: (token: string, newPassword: string) => Promise<void>;
652
+ /**
653
+ * POST /auth/password-reset/request — resolves whether or not the address
654
+ * has an account (no account enumeration). `redirectUri` is where the
655
+ * person lands after changing the password; it must be one of the
656
+ * environment's registered redirect URIs, or the call rejects with
657
+ * `user.password_reset.redirect_uri.not_allowed`.
658
+ */
659
+ requestPasswordReset: (email: string, options?: PasswordResetRequestOptions) => Promise<void>;
660
+ /**
661
+ * POST /auth/password-reset/validate — checks a reset token without
662
+ * spending it, so the reset page can refuse a dead link before asking for
663
+ * a password. Rejects with `user.password_reset.invalid` (401) when the
664
+ * token is unknown, used or expired.
665
+ */
666
+ validatePasswordReset: (token: string) => Promise<PasswordResetPreview>;
667
+ /**
668
+ * POST /auth/password-reset/confirm — sets a new password and revokes all
669
+ * refresh tokens. Resolves with the `redirectUri` the reset was requested
670
+ * with (`null` when there was none); following it is up to the caller.
671
+ */
672
+ confirmPasswordReset: (token: string, newPassword: string) => Promise<PasswordResetCompleted>;
422
673
  };
423
674
  readonly organization: {
424
- /** GET /auth/tenants — all organization memberships. */
425
- list: () => Promise<Organization[]>;
675
+ /** GET /auth/tenants — filterable list of organization memberships. */
676
+ list: (params?: {
677
+ search?: string;
678
+ } & PaginationOptions) => Promise<Organization[]>;
426
679
  /**
427
680
  * POST /auth/tenants — creates an organization owned by the signed-in
428
681
  * user (only available when self-service organization creation is
@@ -448,11 +701,53 @@ declare class AuthyonClient {
448
701
  * (`user.activities()`, `@authyon/server`'s `environment.users.list()`).
449
702
  */
450
703
  list: (organizationId: string, params?: PaginationOptions) => Promise<Paged<OrganizationMember>>;
451
- /** POST /auth/tenants/{organizationId}/members — invite a member by e-mail. */
704
+ /**
705
+ * POST /auth/tenants/{organizationId}/members — add an existing
706
+ * environment user by e-mail (no e-mail is sent). To invite an address
707
+ * that may not have an account yet, use `organization.invites.create`.
708
+ */
452
709
  invite: (organizationId: string, params: InviteMemberInput) => Promise<void>;
453
710
  /** DELETE /auth/tenants/{organizationId}/members/{userId} — remove a member. */
454
711
  remove: (organizationId: string, userId: string) => Promise<void>;
455
712
  };
713
+ /**
714
+ * Invites by e-mail or by link. Managing them requires the
715
+ * `tenants:members:invite` permission on the organization; `preview` and
716
+ * `accept` are public — the token in the link is the credential.
717
+ */
718
+ invites: {
719
+ /** GET /auth/tenants/{organizationId}/invites — every invite with its status, newest first. */
720
+ list: (organizationId: string) => Promise<OrganizationInvite[]>;
721
+ /**
722
+ * POST /auth/tenants/{organizationId}/invites — invite an address.
723
+ * Always returns `acceptUrl`; with `sendEmail: false` Authyon does not
724
+ * mail it and you deliver the link yourself. The link is valid for 7
725
+ * days, once, and can't be read again later.
726
+ */
727
+ create: (organizationId: string, params: OrganizationInviteInput) => Promise<OrganizationInviteIssued>;
728
+ /**
729
+ * POST /auth/tenants/{organizationId}/invites/{inviteId}/resend — issue
730
+ * a new link (the previous one stops working), mailing it unless
731
+ * `sendEmail` is `false`.
732
+ */
733
+ resend: (organizationId: string, inviteId: string, params?: {
734
+ sendEmail?: boolean;
735
+ }) => Promise<OrganizationInviteIssued>;
736
+ /** DELETE /auth/tenants/{organizationId}/invites/{inviteId} — revoke a pending invite. */
737
+ revoke: (organizationId: string, inviteId: string) => Promise<void>;
738
+ /**
739
+ * POST /auth/tenant-invites/preview — public. Read the `token` query
740
+ * parameter of the invite link and pick the form: one click when
741
+ * `accountExists`, sign-up otherwise.
742
+ */
743
+ preview: (token: string) => Promise<OrganizationInvitePreview>;
744
+ /**
745
+ * POST /auth/tenant-invites/accept — public. Joins the organization; for
746
+ * an address with no account, also creates it (password required) with
747
+ * the e-mail already confirmed. Does not sign the user in.
748
+ */
749
+ accept: (params: AcceptOrganizationInviteInput) => Promise<OrganizationInviteAccepted>;
750
+ };
456
751
  roles: {
457
752
  /** GET /auth/tenants/{organizationId}/roles — roles available in the organization. */
458
753
  list: (organizationId: string) => Promise<Role[]>;
@@ -502,6 +797,13 @@ declare class AuthyonClient {
502
797
  assertionStart: (challengeToken: string) => Promise<WebAuthnCeremonyStart>;
503
798
  };
504
799
  };
800
+ /**
801
+ * `security.approvals`: approve or reject what your backend asked the
802
+ * customer to confirm (`@authyon/server` → `security.approvals.create`).
803
+ * Show the payload with `get()`, then `confirm()` with the customer's
804
+ * authenticator code or passkey, or `reject()`.
805
+ */
806
+ readonly security: SubjectSecurity;
505
807
  /**
506
808
  * POST /auth/introspect — lightweight token introspection (RFC 7662).
507
809
  *
@@ -578,6 +880,13 @@ interface AuthyonPermissionSource {
578
880
  /** OAuth scopes are also accepted and interpreted as Authyon permissions. */
579
881
  scope?: string | null;
580
882
  }
883
+ /** A declarative group of permissions evaluated against an Authyon permission source. */
884
+ interface PermissionGroup {
885
+ /** Permissions that must all be granted. */
886
+ allOf?: readonly string[];
887
+ /** Permissions where at least one must be granted. */
888
+ anyOf?: readonly string[];
889
+ }
581
890
  interface AuthyonAbilityOptions {
582
891
  rules?: readonly AbilityRule[];
583
892
  /** Rules added when the source contains a matching Authyon role. */
@@ -611,7 +920,12 @@ declare class AuthyonAbilityBuilder {
611
920
  declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
612
921
  /** Checks an Authyon permission string using the same wildcard rules as an ability. */
613
922
  declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
923
+ /**
924
+ * Checks a permission group using Authyon's standard permission and wildcard rules.
925
+ * Empty or omitted groups do not add authorization requirements.
926
+ */
927
+ declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly string[], group: PermissionGroup): boolean;
614
928
  /** Converts Authyon's `subject:action` permission strings to authorization rules. */
615
929
  declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
616
930
 
617
- export { createAuthyonRules as $, AuthyonClient as A, type SessionControllerOptions as B, type CreateOrganizationInput as C, type SessionInfo as D, type SessionSnapshot as E, FetchHttpAdapter as F, type SessionSnapshotListener as G, type HttpAdapter as H, type IntrospectResult as I, type SessionStatus as J, type SsoProvider as K, LoggingHttpAdapter as L, type TwoFactorChallenge as M, type TwoFactorMethod as N, type Organization as O, type Paged as P, type TwoFactorStatus as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type User as U, type ValidateResult as V, type VerifyTwoFactorInput as W, type WebAuthnAssertion as X, type WebAuthnCeremonyStart as Y, type WebAuthnCredential as Z, createAuthyonAbility as _, type HttpLoggerOptions as a, createClient as a0, hasPermission as a1, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type Activity as g, type AuthEvent as h, type AuthState as i, type AuthStateListener as j, type AuthenticatorSetup as k, AuthyonAbility as l, AuthyonAbilityBuilder as m, type AuthyonAbilityOptions as n, type AuthyonClientOptions as o, type AuthyonPermissionSource as p, AuthyonSessionController as q, type HttpAdapterRequest as r, type HttpLogEvent as s, type HttpLogger as t, type InviteMemberInput as u, type LoginInput as v, type LoginResult as w, type OrganizationMember as x, type PaginationOptions as y, type Role as z };
931
+ export { type SecurityMethods as $, AuthyonClient as A, type HttpLogger as B, type ConfirmApprovalInput as C, type InviteMemberInput as D, type LoginInput as E, FetchHttpAdapter as F, type LoginResult as G, type HttpAdapter as H, type IntrospectResult as I, type OrganizationInvite as J, type OrganizationInviteAccepted as K, LoggingHttpAdapter as L, type OrganizationInviteInput as M, type OrganizationInviteIssued as N, type Organization as O, type OrganizationInvitePreview as P, type OrganizationInviteStatus as Q, type OrganizationMember as R, type Paged as S, type TokenStorage as T, type PaginationOptions as U, type PasswordResetCompleted as V, type PasswordResetPreview as W, type PasswordResetRequestOptions as X, type PermissionGroup as Y, type RegisterInput as Z, type Role as _, type HttpLoggerOptions as a, type Session as a0, type SessionControllerOptions as a1, type SessionInfo as a2, type SessionSnapshot as a3, type SessionSnapshotListener as a4, type SessionStatus as a5, type SsoProvider as a6, type SubjectApprovals as a7, type SubjectSecurity as a8, type TwoFactorChallenge as a9, type TwoFactorMethod as aa, type TwoFactorStatus as ab, type User as ac, type ValidateResult as ad, type VerifyTwoFactorInput as ae, type WebAuthnAssertion as af, type WebAuthnCeremonyStart as ag, type WebAuthnCredential as ah, createAuthyonAbility as ai, createAuthyonRules as aj, createClient as ak, hasPermission as al, hasPermissionGroup as am, type AbilityConditions as b, type AbilityEvent as c, type AbilityListener as d, type AbilityRule as e, type AbilitySubject as f, type AcceptOrganizationInviteInput as g, type Activity as h, type Approval as i, type ApprovalAssurance as j, type ApprovalPayload as k, type ApprovalStatus as l, type ApprovalWebAuthnOptions as m, type AuthEvent as n, type AuthState as o, type AuthStateListener as p, type AuthenticatorSetup as q, AuthyonAbility as r, AuthyonAbilityBuilder as s, type AuthyonAbilityOptions as t, type AuthyonClientOptions as u, type AuthyonPermissionSource as v, AuthyonSessionController as w, type CreateOrganizationInput as x, type HttpAdapterRequest as y, type HttpLogEvent as z };
@@ -201,6 +201,11 @@ function hasPermission(source, requiredPermission) {
201
201
  const permissionSource = isPermissionList(source) ? { permissions: source } : source;
202
202
  return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
203
203
  }
204
+ function hasPermissionGroup(source, group) {
205
+ const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
206
+ const hasAny = !group.anyOf?.length || group.anyOf.some((permission) => hasPermission(source, permission));
207
+ return hasAll && hasAny;
208
+ }
204
209
  function isPermissionList(source) {
205
210
  return Array.isArray(source);
206
211
  }
@@ -319,5 +324,6 @@ export {
319
324
  AuthyonAbilityBuilder,
320
325
  createAuthyonAbility,
321
326
  hasPermission,
327
+ hasPermissionGroup,
322
328
  createAuthyonRules
323
329
  };