@authyon/auth 0.2.0-beta.5 → 0.2.0-beta.8

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/README.md CHANGED
@@ -229,7 +229,7 @@ Métodos de sessão/auth ficam soltos no client; os que giram em torno de um rec
229
229
 
230
230
  | Método | Endpoint |
231
231
  | ----------------------------------------------------- | ---------------------------------------------- |
232
- | `organization.list()` | `GET /auth/tenants` |
232
+ | `organization.list({ search?, skip?, take? })` | `GET /auth/tenants` |
233
233
  | `organization.create(params?)` | `POST /auth/tenants` |
234
234
  | `organization.get(organizationId)` | `GET /auth/tenants/{id}` |
235
235
  | `organization.rename(organizationId, name)` | `PATCH /auth/tenants/{id}` |
@@ -93,6 +93,8 @@ interface Organization {
93
93
  name?: string;
94
94
  description?: string;
95
95
  roles?: string[];
96
+ /** JSON-encoded metadata visible to members of the organization. */
97
+ publicMetadata?: string;
96
98
  }
97
99
  /** POST /auth/tenants — creates an organization owned by the signed-in user. */
98
100
  interface CreateOrganizationInput {
@@ -289,6 +291,12 @@ type AuthState = "signed_out" | "authenticated" | "expired";
289
291
  interface AuthyonClientOptions {
290
292
  /** Publishable environment key (`pk_live_...` / `pk_test_...`). */
291
293
  envKey: string;
294
+ /**
295
+ * Originating client IP to forward on every API call as `X-Forwarded-For`.
296
+ * Browsers cannot reliably discover their public IP by themselves; set this
297
+ * only when your application receives it from trusted infrastructure.
298
+ */
299
+ clientIp?: string;
292
300
  /** API origin. Defaults to `https://api.authyon.com`; HTTPS is required outside loopback. */
293
301
  baseUrl?: string;
294
302
  /** Allow an HTTP `baseUrl`. Intended only for explicitly trusted local development. */
@@ -312,6 +320,7 @@ interface AuthyonClientOptions {
312
320
 
313
321
  declare class AuthyonClient {
314
322
  private readonly envKey;
323
+ private readonly clientIp?;
315
324
  private readonly baseUrl;
316
325
  private readonly storage;
317
326
  private readonly autoRefresh;
@@ -421,8 +430,10 @@ declare class AuthyonClient {
421
430
  confirmPasswordReset: (token: string, newPassword: string) => Promise<void>;
422
431
  };
423
432
  readonly organization: {
424
- /** GET /auth/tenants — all organization memberships. */
425
- list: () => Promise<Organization[]>;
433
+ /** GET /auth/tenants — filterable list of organization memberships. */
434
+ list: (params?: {
435
+ search?: string;
436
+ } & PaginationOptions) => Promise<Organization[]>;
426
437
  /**
427
438
  * POST /auth/tenants — creates an organization owned by the signed-in
428
439
  * user (only available when self-service organization creation is
@@ -578,6 +589,13 @@ interface AuthyonPermissionSource {
578
589
  /** OAuth scopes are also accepted and interpreted as Authyon permissions. */
579
590
  scope?: string | null;
580
591
  }
592
+ /** A declarative group of permissions evaluated against an Authyon permission source. */
593
+ interface PermissionGroup {
594
+ /** Permissions that must all be granted. */
595
+ allOf?: readonly string[];
596
+ /** Permissions where at least one must be granted. */
597
+ anyOf?: readonly string[];
598
+ }
581
599
  interface AuthyonAbilityOptions {
582
600
  rules?: readonly AbilityRule[];
583
601
  /** Rules added when the source contains a matching Authyon role. */
@@ -611,7 +629,12 @@ declare class AuthyonAbilityBuilder {
611
629
  declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
612
630
  /** Checks an Authyon permission string using the same wildcard rules as an ability. */
613
631
  declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
632
+ /**
633
+ * Checks a permission group using Authyon's standard permission and wildcard rules.
634
+ * Empty or omitted groups do not add authorization requirements.
635
+ */
636
+ declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly string[], group: PermissionGroup): boolean;
614
637
  /** Converts Authyon's `subject:action` permission strings to authorization rules. */
615
638
  declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
616
639
 
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 };
640
+ export { createAuthyonAbility as $, AuthyonClient as A, type Role as B, type CreateOrganizationInput as C, type SessionControllerOptions as D, type SessionInfo as E, FetchHttpAdapter as F, type SessionSnapshot as G, type HttpAdapter as H, type IntrospectResult as I, type SessionSnapshotListener as J, type SessionStatus as K, LoggingHttpAdapter as L, type SsoProvider as M, type TwoFactorChallenge as N, type Organization as O, type Paged as P, type TwoFactorMethod as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type TwoFactorStatus as U, type User as V, type ValidateResult as W, type VerifyTwoFactorInput as X, type WebAuthnAssertion as Y, type WebAuthnCeremonyStart as Z, type WebAuthnCredential as _, type HttpLoggerOptions as a, createAuthyonRules as a0, createClient as a1, hasPermission as a2, hasPermissionGroup as a3, 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 PermissionGroup as z };
@@ -93,6 +93,8 @@ interface Organization {
93
93
  name?: string;
94
94
  description?: string;
95
95
  roles?: string[];
96
+ /** JSON-encoded metadata visible to members of the organization. */
97
+ publicMetadata?: string;
96
98
  }
97
99
  /** POST /auth/tenants — creates an organization owned by the signed-in user. */
98
100
  interface CreateOrganizationInput {
@@ -289,6 +291,12 @@ type AuthState = "signed_out" | "authenticated" | "expired";
289
291
  interface AuthyonClientOptions {
290
292
  /** Publishable environment key (`pk_live_...` / `pk_test_...`). */
291
293
  envKey: string;
294
+ /**
295
+ * Originating client IP to forward on every API call as `X-Forwarded-For`.
296
+ * Browsers cannot reliably discover their public IP by themselves; set this
297
+ * only when your application receives it from trusted infrastructure.
298
+ */
299
+ clientIp?: string;
292
300
  /** API origin. Defaults to `https://api.authyon.com`; HTTPS is required outside loopback. */
293
301
  baseUrl?: string;
294
302
  /** Allow an HTTP `baseUrl`. Intended only for explicitly trusted local development. */
@@ -312,6 +320,7 @@ interface AuthyonClientOptions {
312
320
 
313
321
  declare class AuthyonClient {
314
322
  private readonly envKey;
323
+ private readonly clientIp?;
315
324
  private readonly baseUrl;
316
325
  private readonly storage;
317
326
  private readonly autoRefresh;
@@ -421,8 +430,10 @@ declare class AuthyonClient {
421
430
  confirmPasswordReset: (token: string, newPassword: string) => Promise<void>;
422
431
  };
423
432
  readonly organization: {
424
- /** GET /auth/tenants — all organization memberships. */
425
- list: () => Promise<Organization[]>;
433
+ /** GET /auth/tenants — filterable list of organization memberships. */
434
+ list: (params?: {
435
+ search?: string;
436
+ } & PaginationOptions) => Promise<Organization[]>;
426
437
  /**
427
438
  * POST /auth/tenants — creates an organization owned by the signed-in
428
439
  * user (only available when self-service organization creation is
@@ -578,6 +589,13 @@ interface AuthyonPermissionSource {
578
589
  /** OAuth scopes are also accepted and interpreted as Authyon permissions. */
579
590
  scope?: string | null;
580
591
  }
592
+ /** A declarative group of permissions evaluated against an Authyon permission source. */
593
+ interface PermissionGroup {
594
+ /** Permissions that must all be granted. */
595
+ allOf?: readonly string[];
596
+ /** Permissions where at least one must be granted. */
597
+ anyOf?: readonly string[];
598
+ }
581
599
  interface AuthyonAbilityOptions {
582
600
  rules?: readonly AbilityRule[];
583
601
  /** Rules added when the source contains a matching Authyon role. */
@@ -611,7 +629,12 @@ declare class AuthyonAbilityBuilder {
611
629
  declare function createAuthyonAbility(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AuthyonAbility;
612
630
  /** Checks an Authyon permission string using the same wildcard rules as an ability. */
613
631
  declare function hasPermission(source: AuthyonPermissionSource | readonly string[], requiredPermission: string): boolean;
632
+ /**
633
+ * Checks a permission group using Authyon's standard permission and wildcard rules.
634
+ * Empty or omitted groups do not add authorization requirements.
635
+ */
636
+ declare function hasPermissionGroup(source: AuthyonPermissionSource | readonly string[], group: PermissionGroup): boolean;
614
637
  /** Converts Authyon's `subject:action` permission strings to authorization rules. */
615
638
  declare function createAuthyonRules(source?: AuthyonPermissionSource, options?: AuthyonAbilityOptions): AbilityRule[];
616
639
 
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 };
640
+ export { createAuthyonAbility as $, AuthyonClient as A, type Role as B, type CreateOrganizationInput as C, type SessionControllerOptions as D, type SessionInfo as E, FetchHttpAdapter as F, type SessionSnapshot as G, type HttpAdapter as H, type IntrospectResult as I, type SessionSnapshotListener as J, type SessionStatus as K, LoggingHttpAdapter as L, type SsoProvider as M, type TwoFactorChallenge as N, type Organization as O, type Paged as P, type TwoFactorMethod as Q, type RegisterInput as R, type Session as S, type TokenStorage as T, type TwoFactorStatus as U, type User as V, type ValidateResult as W, type VerifyTwoFactorInput as X, type WebAuthnAssertion as Y, type WebAuthnCeremonyStart as Z, type WebAuthnCredential as _, type HttpLoggerOptions as a, createAuthyonRules as a0, createClient as a1, hasPermission as a2, hasPermissionGroup as a3, 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 PermissionGroup 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
  };
package/dist/index.cjs CHANGED
@@ -35,7 +35,8 @@ __export(index_exports, {
35
35
  createDefaultStorage: () => createDefaultStorage,
36
36
  createLocalStorage: () => createLocalStorage,
37
37
  createMemoryStorage: () => createMemoryStorage,
38
- hasPermission: () => hasPermission
38
+ hasPermission: () => hasPermission,
39
+ hasPermissionGroup: () => hasPermissionGroup
39
40
  });
40
41
  module.exports = __toCommonJS(index_exports);
41
42
 
@@ -403,6 +404,28 @@ var JsonHttpClient = class {
403
404
  }
404
405
  };
405
406
 
407
+ // ../../internal/core/http/clientIp.ts
408
+ function clientIpHeaders(context) {
409
+ if (!context?.clientIp) return {};
410
+ return { "X-Forwarded-For": normalizeClientIp(context.clientIp) };
411
+ }
412
+ function normalizeClientIp(value) {
413
+ let ip = value.trim();
414
+ if (ip.startsWith("[") && ip.endsWith("]")) ip = ip.slice(1, -1);
415
+ if (!ip || ip.length > 45 || ip.includes(",") || /[\r\n\s]/.test(ip) || !/^[0-9a-f:.]+$/i.test(ip) || !ip.includes(".") && !ip.includes(":")) {
416
+ throw invalidClientIp();
417
+ }
418
+ try {
419
+ new URL(ip.includes(":") ? `http://[${ip}]` : `http://${ip}`);
420
+ } catch {
421
+ throw invalidClientIp();
422
+ }
423
+ return ip;
424
+ }
425
+ function invalidClientIp() {
426
+ return new Error("Authyon: `clientIp` must be a single valid IPv4 or IPv6 address");
427
+ }
428
+
406
429
  // src/client/authyonClient.ts
407
430
  var FALLBACK_EXPIRES_IN = 1800;
408
431
  function readTokens(raw) {
@@ -490,8 +513,8 @@ var AuthyonClient = class {
490
513
  };
491
514
  // ── Organization ─────────────────────────────────────────────────────────
492
515
  this.organization = {
493
- /** GET /auth/tenants — all organization memberships. */
494
- list: () => this.request("/auth/tenants", { bearer: true }),
516
+ /** GET /auth/tenants — filterable list of organization memberships. */
517
+ list: (params = {}) => this.request(appendQuery("/auth/tenants", params), { bearer: true }),
495
518
  /**
496
519
  * POST /auth/tenants — creates an organization owned by the signed-in
497
520
  * user (only available when self-service organization creation is
@@ -618,6 +641,7 @@ var AuthyonClient = class {
618
641
  if (!options.envKey)
619
642
  throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
620
643
  this.envKey = options.envKey;
644
+ this.clientIp = options.clientIp;
621
645
  this.transport = createSharedTransport({
622
646
  baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
623
647
  allowInsecureHttp: options.allowInsecureHttp,
@@ -730,6 +754,7 @@ var AuthyonClient = class {
730
754
  async request(path, options = {}, isRetry = false) {
731
755
  const headers = {
732
756
  "X-Authyon-Environment": this.envKey,
757
+ ...clientIpHeaders({ clientIp: this.clientIp }),
733
758
  ...options.headers
734
759
  };
735
760
  if (options.bearer) {
@@ -894,6 +919,10 @@ var AuthyonClientBuilder = class {
894
919
  this.options.autoRefresh = enabled;
895
920
  return this;
896
921
  }
922
+ withClientIp(clientIp) {
923
+ this.options.clientIp = clientIp;
924
+ return this;
925
+ }
897
926
  withTimeout(timeoutMs) {
898
927
  this.options.timeoutMs = timeoutMs;
899
928
  return this;
@@ -1114,6 +1143,11 @@ function hasPermission(source, requiredPermission) {
1114
1143
  const permissionSource = isPermissionList(source) ? { permissions: source } : source;
1115
1144
  return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
1116
1145
  }
1146
+ function hasPermissionGroup(source, group) {
1147
+ const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
1148
+ const hasAny = !group.anyOf?.length || group.anyOf.some((permission) => hasPermission(source, permission));
1149
+ return hasAll && hasAny;
1150
+ }
1117
1151
  function isPermissionList(source) {
1118
1152
  return Array.isArray(source);
1119
1153
  }
@@ -1242,5 +1276,6 @@ function isAbilityRule(value) {
1242
1276
  createDefaultStorage,
1243
1277
  createLocalStorage,
1244
1278
  createMemoryStorage,
1245
- hasPermission
1279
+ hasPermission,
1280
+ hasPermissionGroup
1246
1281
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-g6nBpOQM.cjs';
2
- export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, R as RegisterInput, z as Role, S as Session, B as SessionControllerOptions, D as SessionInfo, E as SessionSnapshot, G as SessionSnapshotListener, J as SessionStatus, K as SsoProvider, M as TwoFactorChallenge, N as TwoFactorMethod, Q as TwoFactorStatus, U as User, V as ValidateResult, W as VerifyTwoFactorInput, X as WebAuthnAssertion, Y as WebAuthnCeremonyStart, Z as WebAuthnCredential, _ as createAuthyonAbility, $ as createAuthyonRules, a0 as createClient, a1 as hasPermission } from './ability-g6nBpOQM.cjs';
1
+ import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-Ckd9YAQI.cjs';
2
+ export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, z as PermissionGroup, R as RegisterInput, B as Role, S as Session, D as SessionControllerOptions, E as SessionInfo, G as SessionSnapshot, J as SessionSnapshotListener, K as SessionStatus, M as SsoProvider, N as TwoFactorChallenge, Q as TwoFactorMethod, U as TwoFactorStatus, V as User, W as ValidateResult, X as VerifyTwoFactorInput, Y as WebAuthnAssertion, Z as WebAuthnCeremonyStart, _ as WebAuthnCredential, $ as createAuthyonAbility, a0 as createAuthyonRules, a1 as createClient, a2 as hasPermission, a3 as hasPermissionGroup } from './ability-Ckd9YAQI.cjs';
3
3
 
4
4
  /** Builds an Authyon browser client through explicit, progressive configuration. */
5
5
  declare class AuthyonClientBuilder {
@@ -8,6 +8,7 @@ declare class AuthyonClientBuilder {
8
8
  withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
9
9
  withStorage(storage: TokenStorage): this;
10
10
  withAutomaticRefresh(enabled?: boolean): this;
11
+ withClientIp(clientIp: string): this;
11
12
  withTimeout(timeoutMs: number): this;
12
13
  withHttpAdapter(httpAdapter: HttpAdapter): this;
13
14
  withHttpLogger(httpLogger: HttpLoggerOptions): this;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-g6nBpOQM.js';
2
- export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, R as RegisterInput, z as Role, S as Session, B as SessionControllerOptions, D as SessionInfo, E as SessionSnapshot, G as SessionSnapshotListener, J as SessionStatus, K as SsoProvider, M as TwoFactorChallenge, N as TwoFactorMethod, Q as TwoFactorStatus, U as User, V as ValidateResult, W as VerifyTwoFactorInput, X as WebAuthnAssertion, Y as WebAuthnCeremonyStart, Z as WebAuthnCredential, _ as createAuthyonAbility, $ as createAuthyonRules, a0 as createClient, a1 as hasPermission } from './ability-g6nBpOQM.js';
1
+ import { T as TokenStorage, H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonClient } from './ability-Ckd9YAQI.js';
2
+ export { b as AbilityConditions, c as AbilityEvent, d as AbilityListener, e as AbilityRule, f as AbilitySubject, g as Activity, h as AuthEvent, i as AuthState, j as AuthStateListener, k as AuthenticatorSetup, l as AuthyonAbility, m as AuthyonAbilityBuilder, n as AuthyonAbilityOptions, o as AuthyonClientOptions, p as AuthyonPermissionSource, q as AuthyonSessionController, C as CreateOrganizationInput, F as FetchHttpAdapter, r as HttpAdapterRequest, s as HttpLogEvent, t as HttpLogger, I as IntrospectResult, u as InviteMemberInput, L as LoggingHttpAdapter, v as LoginInput, w as LoginResult, O as Organization, x as OrganizationMember, P as Paged, y as PaginationOptions, z as PermissionGroup, R as RegisterInput, B as Role, S as Session, D as SessionControllerOptions, E as SessionInfo, G as SessionSnapshot, J as SessionSnapshotListener, K as SessionStatus, M as SsoProvider, N as TwoFactorChallenge, Q as TwoFactorMethod, U as TwoFactorStatus, V as User, W as ValidateResult, X as VerifyTwoFactorInput, Y as WebAuthnAssertion, Z as WebAuthnCeremonyStart, _ as WebAuthnCredential, $ as createAuthyonAbility, a0 as createAuthyonRules, a1 as createClient, a2 as hasPermission, a3 as hasPermissionGroup } from './ability-Ckd9YAQI.js';
3
3
 
4
4
  /** Builds an Authyon browser client through explicit, progressive configuration. */
5
5
  declare class AuthyonClientBuilder {
@@ -8,6 +8,7 @@ declare class AuthyonClientBuilder {
8
8
  withBaseUrl(baseUrl: string, allowInsecureHttp?: boolean): this;
9
9
  withStorage(storage: TokenStorage): this;
10
10
  withAutomaticRefresh(enabled?: boolean): this;
11
+ withClientIp(clientIp: string): this;
11
12
  withTimeout(timeoutMs: number): this;
12
13
  withHttpAdapter(httpAdapter: HttpAdapter): this;
13
14
  withHttpLogger(httpLogger: HttpLoggerOptions): this;
package/dist/index.js CHANGED
@@ -4,8 +4,9 @@ import {
4
4
  AuthyonSessionController,
5
5
  createAuthyonAbility,
6
6
  createAuthyonRules,
7
- hasPermission
8
- } from "./chunk-EHZEUM47.js";
7
+ hasPermission,
8
+ hasPermissionGroup
9
+ } from "./chunk-3XJY6BCI.js";
9
10
 
10
11
  // ../../internal/core/errors/authyonError.ts
11
12
  var ErrorCodes = {
@@ -371,6 +372,28 @@ var JsonHttpClient = class {
371
372
  }
372
373
  };
373
374
 
375
+ // ../../internal/core/http/clientIp.ts
376
+ function clientIpHeaders(context) {
377
+ if (!context?.clientIp) return {};
378
+ return { "X-Forwarded-For": normalizeClientIp(context.clientIp) };
379
+ }
380
+ function normalizeClientIp(value) {
381
+ let ip = value.trim();
382
+ if (ip.startsWith("[") && ip.endsWith("]")) ip = ip.slice(1, -1);
383
+ if (!ip || ip.length > 45 || ip.includes(",") || /[\r\n\s]/.test(ip) || !/^[0-9a-f:.]+$/i.test(ip) || !ip.includes(".") && !ip.includes(":")) {
384
+ throw invalidClientIp();
385
+ }
386
+ try {
387
+ new URL(ip.includes(":") ? `http://[${ip}]` : `http://${ip}`);
388
+ } catch {
389
+ throw invalidClientIp();
390
+ }
391
+ return ip;
392
+ }
393
+ function invalidClientIp() {
394
+ return new Error("Authyon: `clientIp` must be a single valid IPv4 or IPv6 address");
395
+ }
396
+
374
397
  // src/client/authyonClient.ts
375
398
  var FALLBACK_EXPIRES_IN = 1800;
376
399
  function readTokens(raw) {
@@ -458,8 +481,8 @@ var AuthyonClient = class {
458
481
  };
459
482
  // ── Organization ─────────────────────────────────────────────────────────
460
483
  this.organization = {
461
- /** GET /auth/tenants — all organization memberships. */
462
- list: () => this.request("/auth/tenants", { bearer: true }),
484
+ /** GET /auth/tenants — filterable list of organization memberships. */
485
+ list: (params = {}) => this.request(appendQuery("/auth/tenants", params), { bearer: true }),
463
486
  /**
464
487
  * POST /auth/tenants — creates an organization owned by the signed-in
465
488
  * user (only available when self-service organization creation is
@@ -586,6 +609,7 @@ var AuthyonClient = class {
586
609
  if (!options.envKey)
587
610
  throw new Error("Authyon: `envKey` is required (pk_live_... / pk_test_...)");
588
611
  this.envKey = options.envKey;
612
+ this.clientIp = options.clientIp;
589
613
  this.transport = createSharedTransport({
590
614
  baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
591
615
  allowInsecureHttp: options.allowInsecureHttp,
@@ -698,6 +722,7 @@ var AuthyonClient = class {
698
722
  async request(path, options = {}, isRetry = false) {
699
723
  const headers = {
700
724
  "X-Authyon-Environment": this.envKey,
725
+ ...clientIpHeaders({ clientIp: this.clientIp }),
701
726
  ...options.headers
702
727
  };
703
728
  if (options.bearer) {
@@ -862,6 +887,10 @@ var AuthyonClientBuilder = class {
862
887
  this.options.autoRefresh = enabled;
863
888
  return this;
864
889
  }
890
+ withClientIp(clientIp) {
891
+ this.options.clientIp = clientIp;
892
+ return this;
893
+ }
865
894
  withTimeout(timeoutMs) {
866
895
  this.options.timeoutMs = timeoutMs;
867
896
  return this;
@@ -894,5 +923,6 @@ export {
894
923
  createDefaultStorage,
895
924
  createLocalStorage,
896
925
  createMemoryStorage,
897
- hasPermission
926
+ hasPermission,
927
+ hasPermissionGroup
898
928
  };
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { U as User, B as SessionControllerOptions, A as AuthyonClient, E as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-g6nBpOQM.cjs';
4
- export { q as AuthyonSessionController, G as SessionSnapshotListener, J as SessionStatus } from '../ability-g6nBpOQM.cjs';
3
+ import { V as User, D as SessionControllerOptions, A as AuthyonClient, G as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-Ckd9YAQI.cjs';
4
+ export { q as AuthyonSessionController, J as SessionSnapshotListener, K as SessionStatus } from '../ability-Ckd9YAQI.cjs';
5
5
 
6
6
  interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
7
7
  client: AuthyonClient;
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { U as User, B as SessionControllerOptions, A as AuthyonClient, E as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-g6nBpOQM.js';
4
- export { q as AuthyonSessionController, G as SessionSnapshotListener, J as SessionStatus } from '../ability-g6nBpOQM.js';
3
+ import { V as User, D as SessionControllerOptions, A as AuthyonClient, G as SessionSnapshot, S as Session, f as AbilitySubject, n as AuthyonAbilityOptions, l as AuthyonAbility } from '../ability-Ckd9YAQI.js';
4
+ export { q as AuthyonSessionController, J as SessionSnapshotListener, K as SessionStatus } from '../ability-Ckd9YAQI.js';
5
5
 
6
6
  interface AuthyonProviderProps<TUser extends User = User> extends SessionControllerOptions {
7
7
  client: AuthyonClient;
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  AuthyonSessionController,
4
4
  createAuthyonAbility
5
- } from "../chunk-EHZEUM47.js";
5
+ } from "../chunk-3XJY6BCI.js";
6
6
 
7
7
  // src/react/index.tsx
8
8
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authyon/auth",
3
- "version": "0.2.0-beta.5",
3
+ "version": "0.2.0-beta.8",
4
4
  "description": "Authyon SDK for browsers — auth, sessions, multi-tenant and 2FA for vanilla JS/TS.",
5
5
  "license": "MIT",
6
6
  "type": "module",