@authyon/server 0.2.0-beta.1 → 0.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __typeError = (msg) => {
7
+ throw TypeError(msg);
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -16,6 +19,10 @@ var __copyProps = (to, from, except, desc) => {
16
19
  return to;
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
23
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
24
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
25
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
19
26
 
20
27
  // src/index.ts
21
28
  var index_exports = {};
@@ -30,8 +37,11 @@ __export(index_exports, {
30
37
  FetchHttpAdapter: () => FetchHttpAdapter,
31
38
  JwksTokenVerifier: () => JwksTokenVerifier,
32
39
  LoggingHttpAdapter: () => LoggingHttpAdapter,
40
+ PlatformScopedClient: () => PlatformScopedClient,
33
41
  TenantScopedClient: () => TenantScopedClient,
34
42
  TokenVerificationError: () => TokenVerificationError,
43
+ UserScopedClient: () => UserScopedClient,
44
+ WorkspaceInvitesClient: () => WorkspaceInvitesClient,
35
45
  authorizeRequest: () => authorizeRequest,
36
46
  authorizeToken: () => authorizeToken,
37
47
  createAuthorizationErrorResponse: () => createAuthorizationErrorResponse,
@@ -39,39 +49,287 @@ __export(index_exports, {
39
49
  createAuthyonRules: () => createAuthyonRules,
40
50
  createClient: () => createClient,
41
51
  createExpressAuthorizationMiddleware: () => createExpressAuthorizationMiddleware,
52
+ hasPermission: () => hasPermission,
42
53
  normalizeClientIp: () => normalizeClientIp
43
54
  });
44
55
  module.exports = __toCommonJS(index_exports);
45
56
 
57
+ // src/client/credentialManagement.ts
58
+ function segment(value) {
59
+ if (typeof value !== "string" || !value.trim() || value === "." || value === ".." || [...value].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)) {
60
+ throw new TypeError("Authyon: invalid route identifier.");
61
+ }
62
+ return encodeURIComponent(value);
63
+ }
64
+ function permissionsBody(input) {
65
+ if (input.permissions !== void 0 && input.scopes !== void 0) {
66
+ throw new TypeError("Authyon: supply permissions or scopes, not both.");
67
+ }
68
+ const values = input.permissions ?? input.scopes;
69
+ if (!Array.isArray(values) || values.length === 0) {
70
+ throw new TypeError("Authyon: explicit credential permissions are required.");
71
+ }
72
+ const permissions = values.map((value) => {
73
+ if (typeof value !== "string") throw new TypeError("Authyon: invalid permission.");
74
+ const normalized = value.trim().toLowerCase();
75
+ if (!/^(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*):(?:\*|[a-z][a-z0-9_-]*)$/u.test(normalized)) {
76
+ throw new TypeError("Authyon: permissions must have namespace:resource:action format.");
77
+ }
78
+ return normalized;
79
+ });
80
+ return { permissions: [...new Set(permissions)] };
81
+ }
82
+ function lifetimeBody(input) {
83
+ if (input.lifetimeDays === void 0 || input.lifetimeDays === null) return {};
84
+ if (!Number.isInteger(input.lifetimeDays) || input.lifetimeDays < 1 || input.lifetimeDays > 3650) {
85
+ throw new RangeError("Authyon: lifetimeDays must be between 1 and 3650.");
86
+ }
87
+ return { lifetimeDays: input.lifetimeDays };
88
+ }
89
+ function credentialQuery(options = {}) {
90
+ const { skip, take, search } = options;
91
+ if (skip !== void 0 && (!Number.isInteger(skip) || skip < 0 || skip > 2147483647))
92
+ throw new RangeError("Authyon: invalid skip.");
93
+ if (take !== void 0 && (!Number.isInteger(take) || take < 1 || take > 100))
94
+ throw new RangeError("Authyon: take must be between 1 and 100.");
95
+ if (search !== void 0 && (typeof search !== "string" || search.trim().length > 200))
96
+ throw new RangeError("Authyon: search must contain at most 200 characters.");
97
+ return { search: search?.trim() || void 0, skip, take };
98
+ }
99
+ function credentialSummary(value) {
100
+ return {
101
+ id: value.id,
102
+ clientId: value.clientId,
103
+ description: value.description,
104
+ createdAt: value.createdAt,
105
+ lastUsedAt: value.lastUsedAt,
106
+ revokedAt: value.revokedAt,
107
+ expiresAt: value.expiresAt ?? null,
108
+ isActive: value.isActive,
109
+ permissions: [...value.permissions]
110
+ };
111
+ }
112
+ function credentialDetail(value) {
113
+ return {
114
+ ...credentialSummary(value),
115
+ createdBy: value.createdBy == null ? null : {
116
+ id: value.createdBy.id,
117
+ type: value.createdBy.type,
118
+ displayName: value.createdBy.displayName
119
+ },
120
+ updatedAt: value.updatedAt,
121
+ secretRotatedAt: value.secretRotatedAt,
122
+ expiresAt: value.expiresAt ?? null,
123
+ ageSeconds: value.ageSeconds,
124
+ lifetimeSeconds: value.lifetimeSeconds,
125
+ accessTokenLifetimeSeconds: value.accessTokenLifetimeSeconds
126
+ };
127
+ }
128
+ async function credentialPage(request, path, options = {}) {
129
+ const query = credentialQuery(options);
130
+ const page = await request(path, { query });
131
+ if (!page || !Array.isArray(page.data))
132
+ throw new TypeError("Authyon: expected a paginated credential response.");
133
+ return {
134
+ data: page.data.map(credentialSummary),
135
+ ...page.perPage === void 0 ? {} : { perPage: page.perPage },
136
+ pageSize: page.pageSize,
137
+ total: page.total,
138
+ pages: page.pages,
139
+ hasNext: page.hasNext,
140
+ hasPrev: page.hasPrev
141
+ };
142
+ }
143
+ async function allCredentials(request, path) {
144
+ const result = [];
145
+ const seen = /* @__PURE__ */ new Set();
146
+ let skip = 0;
147
+ for (let iteration = 0; iteration < 1e3; iteration++) {
148
+ const page = await request(
149
+ path,
150
+ iteration === 0 ? {} : { query: { skip, take: 100 } }
151
+ );
152
+ if (Array.isArray(page)) {
153
+ if (iteration !== 0)
154
+ throw new Error("Authyon: credential pagination changed during listing.");
155
+ return page.map(credentialSummary);
156
+ }
157
+ if (!page || !Array.isArray(page.data))
158
+ throw new TypeError("Authyon: invalid credential page.");
159
+ for (const row of page.data) {
160
+ if (seen.has(row.id))
161
+ throw new Error("Authyon: credential pages overlapped; retry the listing.");
162
+ seen.add(row.id);
163
+ result.push(credentialSummary(row));
164
+ }
165
+ if (!page.hasNext) return result;
166
+ if (page.data.length === 0) throw new Error("Authyon: credential pagination made no progress.");
167
+ skip += page.data.length;
168
+ }
169
+ throw new Error("Authyon: use listPage for more than 1000 credential pages.");
170
+ }
171
+
172
+ // src/client/scopedManagementClient.ts
173
+ function scopedRequest(request, source) {
174
+ return async (path, options = {}) => {
175
+ const token = typeof source === "function" ? await source() : source;
176
+ if (typeof token !== "string" || !token.trim() || /\s/u.test(token))
177
+ throw new TypeError("Authyon: a valid access token is required.");
178
+ return request(path, {
179
+ ...options,
180
+ headers: { ...options.headers, Authorization: `Bearer ${token}` }
181
+ });
182
+ };
183
+ }
184
+ function environmentPath(scope) {
185
+ return `/platform/workspaces/${segment(scope.workspaceId)}/environments/${segment(scope.environmentId)}`;
186
+ }
187
+ function tenantPath(scope) {
188
+ return `${environmentPath(scope)}/tenants/${segment(scope.tenantId)}`;
189
+ }
190
+ function credentialPath(scope) {
191
+ return `${scope.tenantId === void 0 ? environmentPath(scope) : tenantPath(scope)}/credentials`;
192
+ }
193
+ function workspacePath(id) {
194
+ return `/platform/workspaces/${segment(id)}`;
195
+ }
196
+ function workspaceRole(role) {
197
+ if (role !== "admin" && role !== "auditor")
198
+ throw new TypeError("Authyon: workspace role must be admin or auditor.");
199
+ return role;
200
+ }
201
+ var _request;
202
+ var PlatformScopedClient = class {
203
+ /** @hidden */
204
+ constructor(request, token) {
205
+ __privateAdd(this, _request);
206
+ this.credentials = {
207
+ list: (scope, options = {}) => credentialPage(__privateGet(this, _request), credentialPath(scope), options),
208
+ get: async (scope, credentialId) => credentialDetail(await __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`)),
209
+ create: (scope, input) => __privateGet(this, _request).call(this, credentialPath(scope), {
210
+ method: "POST",
211
+ body: { description: input.description, ...lifetimeBody(input), ...permissionsBody(input) }
212
+ }),
213
+ updatePermissions: (scope, credentialId, input) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/permissions`, {
214
+ method: "PUT",
215
+ body: permissionsBody(input)
216
+ }),
217
+ updateScopes: (scope, credentialId, scopes) => this.credentials.updatePermissions(scope, credentialId, { scopes }),
218
+ /** Returns a new secret once. API may require an explicit step-up first. */
219
+ rotate: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}/rotate`, { method: "POST" }),
220
+ revoke: (scope, credentialId) => __privateGet(this, _request).call(this, `${credentialPath(scope)}/${segment(credentialId)}`, { method: "DELETE" })
221
+ };
222
+ this.workspaces = {
223
+ team: (workspaceId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/team`),
224
+ members: {
225
+ invite: (workspaceId, input) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites`, {
226
+ method: "POST",
227
+ body: { email: input.email, role: workspaceRole(input.role) }
228
+ }),
229
+ /** memberId comes from team.members; it is not the platform userId. */
230
+ remove: (workspaceId, memberId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
231
+ method: "DELETE"
232
+ }),
233
+ changeRole: (workspaceId, memberId, role) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/members/${segment(memberId)}`, {
234
+ method: "PATCH",
235
+ body: { role: workspaceRole(role) }
236
+ })
237
+ },
238
+ invites: {
239
+ create: (workspaceId, input) => this.workspaces.members.invite(workspaceId, input),
240
+ revoke: (workspaceId, inviteId) => __privateGet(this, _request).call(this, `${workspacePath(workspaceId)}/invites/${segment(inviteId)}`, {
241
+ method: "DELETE"
242
+ })
243
+ }
244
+ };
245
+ this.tenants = {
246
+ members: {
247
+ list: (scope, options = {}) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { query: options }),
248
+ add: (scope, userId, roles) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members`, { method: "POST", body: { userId, roles } }),
249
+ remove: (scope, userId) => __privateGet(this, _request).call(this, `${tenantPath(scope)}/members/${segment(userId)}`, { method: "DELETE" })
250
+ }
251
+ };
252
+ this.auth = {
253
+ /** Explicitly confirms the current platform session; passwords are not retained. */
254
+ stepUp: (currentPassword) => __privateGet(this, _request).call(this, "/platform/auth/step-up", { method: "POST", body: { currentPassword } })
255
+ };
256
+ __privateSet(this, _request, scopedRequest(request, token));
257
+ }
258
+ };
259
+ _request = new WeakMap();
260
+ var _request2;
261
+ var UserScopedClient = class {
262
+ /** @hidden */
263
+ constructor(request, token) {
264
+ __privateAdd(this, _request2);
265
+ this.tenants = {
266
+ members: {
267
+ list: (tenantId, options = {}) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, { query: options }),
268
+ /** Adds an existing environment user by email; does not send an invitation email. */
269
+ invite: (tenantId, input) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members`, {
270
+ method: "POST",
271
+ body: { email: input.email, roles: input.roles }
272
+ }),
273
+ remove: (tenantId, userId) => __privateGet(this, _request2).call(this, `/auth/tenants/${segment(tenantId)}/members/${segment(userId)}`, {
274
+ method: "DELETE"
275
+ })
276
+ }
277
+ };
278
+ __privateSet(this, _request2, scopedRequest(request, token));
279
+ }
280
+ };
281
+ _request2 = new WeakMap();
282
+ var _request3;
283
+ var WorkspaceInvitesClient = class {
284
+ /** @hidden */
285
+ constructor(request) {
286
+ __privateAdd(this, _request3);
287
+ __privateSet(this, _request3, request);
288
+ }
289
+ preview(token) {
290
+ return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}`);
291
+ }
292
+ accept(token, input = {}) {
293
+ return __privateGet(this, _request3).call(this, `/platform/workspace-invites/${segment(token)}/accept`, {
294
+ method: "POST",
295
+ body: { name: input.name, password: input.password }
296
+ });
297
+ }
298
+ };
299
+ _request3 = new WeakMap();
300
+
46
301
  // ../../internal/core/config/defaults.ts
47
302
  var DEFAULT_BASE_URL = "https://api.authyon.com";
48
303
  var DEFAULT_EXPIRY_SKEW_MS = 3e4;
49
304
 
50
305
  // ../../internal/core/auth/expiringTokenProvider.ts
306
+ var _token, _inFlight;
51
307
  var ExpiringTokenProvider = class {
52
308
  constructor(acquire, expirySkewMs = DEFAULT_EXPIRY_SKEW_MS) {
53
309
  this.acquire = acquire;
54
310
  this.expirySkewMs = expirySkewMs;
55
- this.token = null;
56
- this.inFlight = null;
311
+ __privateAdd(this, _token, null);
312
+ __privateAdd(this, _inFlight, null);
57
313
  }
58
314
  async getAccessToken() {
59
- if (this.token && Date.now() < this.token.expiresAt - this.expirySkewMs) {
60
- return this.token.accessToken;
315
+ if (__privateGet(this, _token) && Date.now() < __privateGet(this, _token).expiresAt - this.expirySkewMs) {
316
+ return __privateGet(this, _token).accessToken;
61
317
  }
62
- if (this.inFlight) return this.inFlight;
63
- this.inFlight = this.acquire().then((token) => {
64
- this.token = token;
318
+ if (__privateGet(this, _inFlight)) return __privateGet(this, _inFlight);
319
+ __privateSet(this, _inFlight, this.acquire().then((token) => {
320
+ __privateSet(this, _token, token);
65
321
  return token.accessToken;
66
322
  }).finally(() => {
67
- this.inFlight = null;
68
- });
69
- return this.inFlight;
323
+ __privateSet(this, _inFlight, null);
324
+ }));
325
+ return __privateGet(this, _inFlight);
70
326
  }
71
327
  clear() {
72
- this.token = null;
328
+ __privateSet(this, _token, null);
73
329
  }
74
330
  };
331
+ _token = new WeakMap();
332
+ _inFlight = new WeakMap();
75
333
 
76
334
  // ../../internal/core/errors/authyonError.ts
77
335
  var ErrorCodes = {
@@ -202,7 +460,8 @@ var FetchHttpAdapter = class {
202
460
  method: request.method,
203
461
  headers: request.headers,
204
462
  body: request.body,
205
- signal: request.signal
463
+ signal: request.signal,
464
+ redirect: "error"
206
465
  });
207
466
  }
208
467
  };
@@ -254,6 +513,10 @@ function defaultHttpLogger(event) {
254
513
  }
255
514
  function sanitizeUrl(value) {
256
515
  const url = new URL(value);
516
+ url.username = "";
517
+ url.password = "";
518
+ url.hash = "";
519
+ url.pathname = url.pathname.replace(/(\/platform\/workspace-invites\/)[^/]+/u, "$1REDACTED");
257
520
  const queryKeys = /* @__PURE__ */ new Set();
258
521
  url.searchParams.forEach((_value, key) => queryKeys.add(key));
259
522
  url.search = queryKeys.size > 0 ? [...queryKeys].map((key) => `${encodeURIComponent(key)}=REDACTED`).join("&") : "";
@@ -535,8 +798,10 @@ function isRecord(value) {
535
798
  }
536
799
 
537
800
  // src/client/authyonServerClient.ts
801
+ var _clientSecret;
538
802
  var AuthyonServerClient = class {
539
803
  constructor(options = {}) {
804
+ __privateAdd(this, _clientSecret);
540
805
  // ── Discovery ──────────────────────────────────────────────────────────────
541
806
  this.discovery = {
542
807
  /** GET /.well-known/jwks.json — the environment's public signing keys. */
@@ -572,6 +837,9 @@ var AuthyonServerClient = class {
572
837
  }
573
838
  })
574
839
  };
840
+ this.workspaceInvites = new WorkspaceInvitesClient(
841
+ (path, options) => this.http.request(path, options)
842
+ );
575
843
  // ── Environment management (users, tenants, roles, permissions, audit) ───
576
844
  this.environment = {
577
845
  users: {
@@ -663,8 +931,49 @@ var AuthyonServerClient = class {
663
931
  )
664
932
  },
665
933
  tenants: {
666
- /** GET /env/tenants — list tenants in the environment. */
667
- list: () => this.request("/env/tenants", { envBearer: true }),
934
+ credentials: {
935
+ /** GET /env/tenants/{tenantId}/credentials. Returns metadata only, including revoked credentials. */
936
+ list: (tenantId) => allCredentials(
937
+ (path, options) => this.request(path, { ...options, envBearer: true }),
938
+ `/env/tenants/${segment(tenantId)}/credentials`
939
+ ),
940
+ /** Paginated searchable listing. Prefer this over loading every credential. */
941
+ listPage: (tenantId, options = {}) => credentialPage(
942
+ (path, requestOptions) => this.request(path, { ...requestOptions, envBearer: true }),
943
+ `/env/tenants/${segment(tenantId)}/credentials`,
944
+ options
945
+ ),
946
+ get: async (tenantId, credentialId) => credentialDetail(
947
+ await this.request(
948
+ `/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`,
949
+ { envBearer: true }
950
+ )
951
+ ),
952
+ /** Explicit scopes/permissions; only issuance returns the client secret. */
953
+ create: (tenantId, input) => this.request(`/env/tenants/${segment(tenantId)}/credentials`, {
954
+ method: "POST",
955
+ envBearer: true,
956
+ body: { description: input.description, ...lifetimeBody(input), ...permissionsBody(input) }
957
+ }),
958
+ /** Returns a new secret once while keeping the same client id. */
959
+ rotate: (tenantId, credentialId) => this.request(
960
+ `/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/rotate`,
961
+ { method: "POST", envBearer: true }
962
+ ),
963
+ updatePermissions: (tenantId, credentialId, input) => this.request(
964
+ `/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}/permissions`,
965
+ { method: "PUT", envBearer: true, body: permissionsBody(input) }
966
+ ),
967
+ updateScopes: (tenantId, credentialId, scopes) => this.environment.tenants.credentials.updatePermissions(tenantId, credentialId, {
968
+ scopes
969
+ }),
970
+ revoke: (tenantId, credentialId) => this.request(`/env/tenants/${segment(tenantId)}/credentials/${segment(credentialId)}`, {
971
+ method: "DELETE",
972
+ envBearer: true
973
+ })
974
+ },
975
+ /** GET /env/tenants — paginated, searchable list of tenants in the environment. */
976
+ list: (params = {}) => this.request("/env/tenants", { envBearer: true, query: params }),
668
977
  /** POST /env/tenants — create a tenant in the environment. */
669
978
  create: (input) => this.request("/env/tenants", { method: "POST", envBearer: true, body: input }),
670
979
  /** GET /env/tenants/{tenantId} — fetch a tenant by id. */
@@ -809,7 +1118,7 @@ var AuthyonServerClient = class {
809
1118
  };
810
1119
  this.envKey = options.envKey;
811
1120
  this.clientId = options.clientId;
812
- this.clientSecret = options.clientSecret;
1121
+ __privateSet(this, _clientSecret, options.clientSecret);
813
1122
  this.transport = createSharedTransport({
814
1123
  baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
815
1124
  allowInsecureHttp: options.allowInsecureHttp,
@@ -820,14 +1129,14 @@ var AuthyonServerClient = class {
820
1129
  });
821
1130
  this.http = new JsonHttpClient(this.transport);
822
1131
  this.environmentTokenProvider = new ExpiringTokenProvider(async () => {
823
- if (!this.clientId || !this.clientSecret) {
1132
+ if (!this.clientId || !__privateGet(this, _clientSecret)) {
824
1133
  throw new Error(
825
1134
  "Authyon: `clientId`/`clientSecret` are required for environment management calls (mint a pair in the console, under the environment's OAuth clients)."
826
1135
  );
827
1136
  }
828
1137
  const token = await this.environmentAuth.token({
829
1138
  clientId: this.clientId,
830
- clientSecret: this.clientSecret
1139
+ clientSecret: __privateGet(this, _clientSecret)
831
1140
  });
832
1141
  return {
833
1142
  accessToken: token.access_token,
@@ -896,6 +1205,14 @@ var AuthyonServerClient = class {
896
1205
  tenant(credentials) {
897
1206
  return new TenantScopedClient(this, credentials);
898
1207
  }
1208
+ /** Uses only the supplied platform-user access token. */
1209
+ platform(token) {
1210
+ return new PlatformScopedClient((path, options) => this.http.request(path, options), token);
1211
+ }
1212
+ /** Uses an end-user token; the configured environment key selects the environment. */
1213
+ user(token) {
1214
+ return new UserScopedClient((path, options) => this.request(path, options), token);
1215
+ }
899
1216
  /** @internal used by {@link TenantScopedClient} to share the base URL/fetch/error handling. */
900
1217
  _requestAsTenant(accessToken, path, options = {}) {
901
1218
  return this.request(path, {
@@ -905,6 +1222,7 @@ var AuthyonServerClient = class {
905
1222
  });
906
1223
  }
907
1224
  };
1225
+ _clientSecret = new WeakMap();
908
1226
  var TenantScopedClient = class {
909
1227
  constructor(server, credentials) {
910
1228
  this.members = {
@@ -983,40 +1301,42 @@ function createClient(options = {}) {
983
1301
  }
984
1302
 
985
1303
  // src/client/authyonServerClientBuilder.ts
1304
+ var _options;
986
1305
  var AuthyonServerClientBuilder = class {
987
1306
  constructor() {
988
- this.options = {};
1307
+ __privateAdd(this, _options, {});
989
1308
  }
990
1309
  withEnvironmentKey(envKey) {
991
- this.options.envKey = envKey;
1310
+ __privateGet(this, _options).envKey = envKey;
992
1311
  return this;
993
1312
  }
994
1313
  withEnvironmentCredentials(clientId, clientSecret) {
995
- this.options.clientId = clientId;
996
- this.options.clientSecret = clientSecret;
1314
+ __privateGet(this, _options).clientId = clientId;
1315
+ __privateGet(this, _options).clientSecret = clientSecret;
997
1316
  return this;
998
1317
  }
999
1318
  withBaseUrl(baseUrl, allowInsecureHttp = false) {
1000
- this.options.baseUrl = baseUrl;
1001
- this.options.allowInsecureHttp = allowInsecureHttp;
1319
+ __privateGet(this, _options).baseUrl = baseUrl;
1320
+ __privateGet(this, _options).allowInsecureHttp = allowInsecureHttp;
1002
1321
  return this;
1003
1322
  }
1004
1323
  withTimeout(timeoutMs) {
1005
- this.options.timeoutMs = timeoutMs;
1324
+ __privateGet(this, _options).timeoutMs = timeoutMs;
1006
1325
  return this;
1007
1326
  }
1008
1327
  withHttpAdapter(httpAdapter) {
1009
- this.options.httpAdapter = httpAdapter;
1328
+ __privateGet(this, _options).httpAdapter = httpAdapter;
1010
1329
  return this;
1011
1330
  }
1012
1331
  withHttpLogger(httpLogger) {
1013
- this.options.httpLogger = httpLogger;
1332
+ __privateGet(this, _options).httpLogger = httpLogger;
1014
1333
  return this;
1015
1334
  }
1016
1335
  build() {
1017
- return new AuthyonServerClient({ ...this.options });
1336
+ return new AuthyonServerClient({ ...__privateGet(this, _options) });
1018
1337
  }
1019
1338
  };
1339
+ _options = new WeakMap();
1020
1340
 
1021
1341
  // ../../internal/core/authorization/ability.ts
1022
1342
  var AuthyonAbility = class {
@@ -1080,6 +1400,17 @@ var AuthyonAbilityBuilder = class {
1080
1400
  function createAuthyonAbility(source = {}, options = {}) {
1081
1401
  return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
1082
1402
  }
1403
+ function hasPermission(source, requiredPermission) {
1404
+ const requirement = permissionToRule(requiredPermission);
1405
+ if (!requirement || typeof requirement.action !== "string" || typeof requirement.subject !== "string") {
1406
+ return false;
1407
+ }
1408
+ const permissionSource = isPermissionList(source) ? { permissions: source } : source;
1409
+ return createAuthyonAbility(permissionSource).can(requirement.action, requirement.subject);
1410
+ }
1411
+ function isPermissionList(source) {
1412
+ return Array.isArray(source);
1413
+ }
1083
1414
  function createAuthyonRules(source = {}, options = {}) {
1084
1415
  const permissions = /* @__PURE__ */ new Set([
1085
1416
  ...source.permissions ?? [],
@@ -1109,9 +1440,14 @@ function permissionToRule(permission) {
1109
1440
  }
1110
1441
  function matchesToken(value, expected, wildcard) {
1111
1442
  return (Array.isArray(value) ? value : [value]).some(
1112
- (candidate) => candidate === expected || candidate === wildcard || candidate === "*"
1443
+ (candidate) => candidate === expected || candidate === wildcard || candidate === "*" || matchesSegments(candidate, expected)
1113
1444
  );
1114
1445
  }
1446
+ function matchesSegments(pattern, value) {
1447
+ const patternSegments = pattern.split(":");
1448
+ const valueSegments = value.split(":");
1449
+ return patternSegments.length === valueSegments.length && patternSegments.every((segment2, index) => segment2 === "*" || segment2 === valueSegments[index]);
1450
+ }
1115
1451
  function matchesField(pattern, field) {
1116
1452
  if (pattern === "*" || pattern === field) return true;
1117
1453
  return pattern.endsWith(".*") && field.startsWith(pattern.slice(0, -1));
@@ -1315,8 +1651,11 @@ function unauthenticated() {
1315
1651
  FetchHttpAdapter,
1316
1652
  JwksTokenVerifier,
1317
1653
  LoggingHttpAdapter,
1654
+ PlatformScopedClient,
1318
1655
  TenantScopedClient,
1319
1656
  TokenVerificationError,
1657
+ UserScopedClient,
1658
+ WorkspaceInvitesClient,
1320
1659
  authorizeRequest,
1321
1660
  authorizeToken,
1322
1661
  createAuthorizationErrorResponse,
@@ -1324,5 +1663,6 @@ function unauthenticated() {
1324
1663
  createAuthyonRules,
1325
1664
  createClient,
1326
1665
  createExpressAuthorizationMiddleware,
1666
+ hasPermission,
1327
1667
  normalizeClientIp
1328
1668
  });