@authyon/server 0.2.0-beta.20 → 0.2.0-beta.22

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.js CHANGED
@@ -7,11 +7,12 @@ import {
7
7
  JsonHttpClient,
8
8
  LoggingHttpAdapter,
9
9
  createSharedTransport
10
- } from "./chunk-TDJNBD3Z.js";
10
+ } from "./chunk-SUL2HLRR.js";
11
11
  import {
12
12
  AuthorizationError,
13
13
  AuthyonAbility,
14
14
  AuthyonAbilityBuilder,
15
+ CLIENT_IP_HEADER,
15
16
  JwksTokenVerifier,
16
17
  TokenVerificationError,
17
18
  authorizeRequest,
@@ -24,7 +25,7 @@ import {
24
25
  hasPermission,
25
26
  hasPermissionGroup,
26
27
  normalizeClientIp
27
- } from "./chunk-NKWQMYB3.js";
28
+ } from "./chunk-WF26WYSX.js";
28
29
  import {
29
30
  __privateAdd,
30
31
  __privateGet,
@@ -268,6 +269,11 @@ _token = new WeakMap();
268
269
  _inFlight = new WeakMap();
269
270
 
270
271
  // src/client/authyonServerClient.ts
272
+ var ROOT_PARENT_ID = "00000000-0000-0000-0000-000000000000";
273
+ function tenantListQuery({ parentId, ...rest }) {
274
+ if (parentId === void 0) return rest;
275
+ return { ...rest, parentId: parentId === null ? ROOT_PARENT_ID : parentId };
276
+ }
271
277
  var _clientSecret;
272
278
  var AuthyonServerClient = class {
273
279
  constructor(options = {}) {
@@ -493,13 +499,64 @@ var AuthyonServerClient = class {
493
499
  * is found by the number, and `cnpj` alone finds every tenant carrying
494
500
  * that key. `take` defaults to 25 and is capped at 100.
495
501
  *
502
+ * `filter` is the structured half, and the one to reach for when more
503
+ * than one condition has to hold: each entry is a condition on a JSON
504
+ * document, they are ANDed with each other and with `search`, and they
505
+ * run inside the statement — so the envelope keeps describing the
506
+ * filtered set and you can page through it. Build them with
507
+ * `tenantFilter`:
508
+ *
509
+ * ```ts
510
+ * const page = await authyon.environment.tenants.list({
511
+ * filter: [
512
+ * tenantFilter.in("public.kind", ["reseller", "partner"]),
513
+ * tenantFilter.in("custom.cnpj", [a, b]),
514
+ * ],
515
+ * take: 100,
516
+ * });
517
+ * ```
518
+ *
519
+ * `parentId` narrows the page to one level of the tree — `children()`
520
+ * and `roots()` below are the same call with that one option set.
521
+ *
496
522
  * Against an Authyon API older than 2026-10-21 this route ignored
497
- * skip/take/search and answered with a bare array.
523
+ * skip/take/search and answered with a bare array; one older than
524
+ * 2026-10-22 ignores `filter` and one older than 2026-10-31 ignores
525
+ * `parentId`, which read as a page that is too wide rather than an error.
526
+ */
527
+ list: (params = {}) => this.request("/env/tenants", { envBearer: true, query: tenantListQuery(params) }),
528
+ /**
529
+ * One page of a tenant's direct sub-tenants — `list()` with `parentId`.
530
+ *
531
+ * Only one level: a node's own children carry `childCount`, so a tree
532
+ * view asks again where that is above zero instead of reading the whole
533
+ * subtree to draw the first level of it.
534
+ */
535
+ children: (parentTenantId, params = {}) => this.environment.tenants.list({ ...params, parentId: parentTenantId }),
536
+ /**
537
+ * One page of the environment's root tenants — `list()` with no parent.
538
+ *
539
+ * This is the first level of the tree, and the difference from `list()`
540
+ * is worth keeping straight: `list()` returns every tenant at every
541
+ * depth, which is what a flat picker wants and what a tree view must not
542
+ * start from.
543
+ */
544
+ roots: (params = {}) => this.environment.tenants.list({ ...params, parentId: null }),
545
+ /**
546
+ * POST /env/tenants — create a tenant in the environment.
547
+ *
548
+ * `parentTenantId` nests it under an existing tenant. The environment's
549
+ * `maxTenantDepth` has to allow the level — it ships as `1`, roots only,
550
+ * so this answers `tenant.depth.not_allowed` until an operator raises it
551
+ * in the console.
498
552
  */
499
- list: (params = {}) => this.request("/env/tenants", { envBearer: true, query: params }),
500
- /** POST /env/tenants — create a tenant in the environment. */
501
553
  create: (input) => this.request("/env/tenants", { method: "POST", envBearer: true, body: input }),
502
- /** GET /env/tenants/{tenantId} — fetch a tenant by id. */
554
+ /**
555
+ * GET /env/tenants/{tenantId} — fetch a tenant by id.
556
+ *
557
+ * Returns `ancestors` as well, the chain above it root first, so a
558
+ * breadcrumb costs this one call rather than one per level.
559
+ */
503
560
  get: (tenantId) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}`, { envBearer: true }),
504
561
  /** PUT /env/tenants/{tenantId} — update a tenant. */
505
562
  update: (tenantId, input) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}`, {
@@ -513,12 +570,23 @@ var AuthyonServerClient = class {
513
570
  envBearer: true,
514
571
  body: input
515
572
  }),
516
- /** POST /env/tenants/{tenantId}/disable — disable without removing tenant data. */
573
+ /**
574
+ * POST /env/tenants/{tenantId}/disable — disable without removing tenant
575
+ * data.
576
+ *
577
+ * Disables the branch below it too. A sub-tenant left live under a
578
+ * disabled parent would go on authenticating its users, which is the one
579
+ * thing switching a tenant off is there to stop — so check `childCount`
580
+ * before calling this on a node with children.
581
+ */
517
582
  disable: (tenantId) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}/disable`, {
518
583
  method: "POST",
519
584
  envBearer: true
520
585
  }),
521
- /** DELETE /env/tenants/{tenantId} — logically delete (disable) a tenant. */
586
+ /**
587
+ * DELETE /env/tenants/{tenantId} — logically delete (disable) a tenant,
588
+ * and the branch below it. Same handler as `disable()`.
589
+ */
522
590
  delete: (tenantId) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}`, {
523
591
  method: "DELETE",
524
592
  envBearer: true
@@ -1015,6 +1083,112 @@ var AuthyonServerClientBuilder = class {
1015
1083
  }
1016
1084
  };
1017
1085
  _options = new WeakMap();
1086
+
1087
+ // src/client/tenantFilters.ts
1088
+ var TENANT_FILTER_MAX_VALUES = 25;
1089
+ var TENANT_FILTER_MAX_VALUE_LENGTH = 200;
1090
+ var TENANT_FILTER_MAX_DEPTH = 3;
1091
+ var SCOPES = ["public", "private", "custom"];
1092
+ var KEY_SEGMENT = /^[A-Za-z0-9_-]{1,64}$/;
1093
+ function assertPath(path2) {
1094
+ if (typeof path2 !== "string" || path2.length === 0) {
1095
+ throw new RangeError("Authyon: a filter path is required, e.g. public.kind.");
1096
+ }
1097
+ const [scope, ...keys] = path2.split(".");
1098
+ if (!SCOPES.includes(scope)) {
1099
+ throw new RangeError(
1100
+ `Authyon: unknown filter scope "${scope}" \u2014 use ${SCOPES.join(", ")} (e.g. public.kind).`
1101
+ );
1102
+ }
1103
+ if (keys.length === 0) {
1104
+ throw new RangeError(`Authyon: filter path "${path2}" needs a key after the scope.`);
1105
+ }
1106
+ if (keys.length > TENANT_FILTER_MAX_DEPTH) {
1107
+ throw new RangeError(
1108
+ `Authyon: filter path "${path2}" goes deeper than ${TENANT_FILTER_MAX_DEPTH} levels.`
1109
+ );
1110
+ }
1111
+ for (const key of keys) {
1112
+ if (!KEY_SEGMENT.test(key)) {
1113
+ throw new RangeError(
1114
+ `Authyon: invalid filter key "${key}" \u2014 letters, digits, "_" and "-", up to 64 characters.`
1115
+ );
1116
+ }
1117
+ }
1118
+ return path2;
1119
+ }
1120
+ function assertValue(value, forIn) {
1121
+ if (value === void 0 || value === null) {
1122
+ throw new RangeError("Authyon: a filter value is required.");
1123
+ }
1124
+ const text = String(value);
1125
+ if (text.length === 0) {
1126
+ throw new RangeError("Authyon: a filter value may not be empty.");
1127
+ }
1128
+ if (text.length > TENANT_FILTER_MAX_VALUE_LENGTH) {
1129
+ throw new RangeError(
1130
+ `Authyon: a filter value may hold at most ${TENANT_FILTER_MAX_VALUE_LENGTH} characters.`
1131
+ );
1132
+ }
1133
+ if (text.includes(",")) {
1134
+ throw new RangeError(
1135
+ forIn ? "Authyon: an `in` value may not contain a comma \u2014 pass separate values instead." : "Authyon: a filter value may not contain a comma."
1136
+ );
1137
+ }
1138
+ return text;
1139
+ }
1140
+ var tenantFilter = {
1141
+ /** `public.kind:eq:reseller` — the key equals this value. */
1142
+ eq(path2, value) {
1143
+ return `${assertPath(path2)}:eq:${assertValue(value, false)}`;
1144
+ },
1145
+ /** `public.kind:in:reseller,partner` — the key equals any of these (SQL IN). */
1146
+ in(path2, values) {
1147
+ if (!Array.isArray(values) || values.length === 0) {
1148
+ throw new RangeError("Authyon: `in` needs at least one value.");
1149
+ }
1150
+ if (values.length > TENANT_FILTER_MAX_VALUES) {
1151
+ throw new RangeError(`Authyon: \`in\` takes at most ${TENANT_FILTER_MAX_VALUES} values.`);
1152
+ }
1153
+ const unique = [...new Set(values.map((value) => assertValue(value, true)))];
1154
+ return `${assertPath(path2)}:in:${unique.join(",")}`;
1155
+ },
1156
+ /** `public.notes:like:nacional` — the key contains this text, case-insensitive.
1157
+ * The server escapes the LIKE metacharacters, so `100%` means `100%`. */
1158
+ like(path2, text) {
1159
+ return `${assertPath(path2)}:like:${assertValue(text, false)}`;
1160
+ },
1161
+ /** `custom.cnpj:exists` — the key is present, whatever its value. */
1162
+ exists(path2) {
1163
+ return `${assertPath(path2)}:exists`;
1164
+ }
1165
+ };
1166
+
1167
+ // ../../internal/core/authorization/tenantPermissions.ts
1168
+ var TenantPermissions = {
1169
+ /** Rename the tenant and change its settings. */
1170
+ SettingsManage: "tenants:settings:manage",
1171
+ /** Invite an address to the tenant. */
1172
+ MembersInvite: "tenants:members:invite",
1173
+ /** Remove a member from the tenant. */
1174
+ MembersRemove: "tenants:members:remove"
1175
+ };
1176
+ var SubtenantPermissions = {
1177
+ /** Read any tenant below one you belong to. */
1178
+ TreeRead: "subtenants:tree:read",
1179
+ /** Rename a tenant below yours, and add or remove its members. */
1180
+ TreeManage: "subtenants:tree:manage",
1181
+ /** Create a tenant underneath one you belong to. */
1182
+ TreeCreate: "subtenants:tree:create",
1183
+ /**
1184
+ * Make a tenant below yours the ACTIVE tenant of the session — acting inside
1185
+ * it rather than on it from outside. Deliberately not the same permission as
1186
+ * {@link SubtenantPermissions.TreeManage}: administering a sub-tenant through
1187
+ * the management endpoints and operating as that sub-tenant are different
1188
+ * powers, and granting the first should not hand over the second.
1189
+ */
1190
+ TreeSwitch: "subtenants:tree:switch"
1191
+ };
1018
1192
  export {
1019
1193
  AuthorizationError,
1020
1194
  AuthyonAbility,
@@ -1022,10 +1196,16 @@ export {
1022
1196
  AuthyonError,
1023
1197
  AuthyonServerClient,
1024
1198
  AuthyonServerClientBuilder,
1199
+ CLIENT_IP_HEADER,
1025
1200
  ErrorCodes,
1026
1201
  FetchHttpAdapter,
1027
1202
  JwksTokenVerifier,
1028
1203
  LoggingHttpAdapter,
1204
+ SubtenantPermissions,
1205
+ TENANT_FILTER_MAX_DEPTH,
1206
+ TENANT_FILTER_MAX_VALUES,
1207
+ TENANT_FILTER_MAX_VALUE_LENGTH,
1208
+ TenantPermissions,
1029
1209
  TenantScopedClient,
1030
1210
  TokenVerificationError,
1031
1211
  UserScopedClient,
@@ -1038,5 +1218,6 @@ export {
1038
1218
  createExpressAuthorizationMiddleware,
1039
1219
  hasPermission,
1040
1220
  hasPermissionGroup,
1041
- normalizeClientIp
1221
+ normalizeClientIp,
1222
+ tenantFilter
1042
1223
  };
package/dist/next.cjs CHANGED
@@ -75,13 +75,23 @@ function createAuthyonRules(source = {}, options = {}) {
75
75
  ...source.permissions ?? [],
76
76
  ...source.scope?.split(/\s+/).filter(Boolean) ?? []
77
77
  ]);
78
- const rules = [...permissions].map(permissionToRule).filter(isAbilityRule);
78
+ const rules = [...permissions].map(grantToRule).filter(isAbilityRule);
79
79
  for (const role of /* @__PURE__ */ new Set([...source.roles ?? [], ...options.roles ?? []])) {
80
80
  rules.push(...(options.roleRules?.[role] ?? []).map(cloneRule));
81
81
  }
82
82
  rules.push(...(options.rules ?? []).map(cloneRule));
83
83
  return rules;
84
84
  }
85
+ function grantToRule(permission) {
86
+ const normalized = permission.trim();
87
+ if (normalized && isFullWildcard(normalized)) {
88
+ return { action: "manage", subject: "all" };
89
+ }
90
+ return permissionToRule(normalized);
91
+ }
92
+ function isFullWildcard(permission) {
93
+ return permission.split(":").every((segment) => segment === "*");
94
+ }
85
95
  function permissionToRule(permission) {
86
96
  const normalized = permission.trim();
87
97
  if (!normalized) return null;
package/dist/next.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { R as RequestAuthorizationOptions, A as AbilitySubject, a as AuthorizationError } from './authorization-DcA5oKZT.cjs';
3
- import { A as AuthyonServerClient } from './authyonServerClient-UA7HdjtD.cjs';
2
+ import { R as RequestAuthorizationOptions, A as AbilitySubject, a as AuthorizationError } from './authorization-BLENxur8.cjs';
3
+ import { A as AuthyonServerClient } from './authyonServerClient-CW46xIRD.cjs';
4
4
  import 'jose';
5
5
 
6
6
  interface ServerPermissionGuardFallbackContext {
package/dist/next.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
- import { R as RequestAuthorizationOptions, A as AbilitySubject, a as AuthorizationError } from './authorization-DfFCoDQ6.js';
3
- import { A as AuthyonServerClient } from './authyonServerClient-UA7HdjtD.js';
2
+ import { R as RequestAuthorizationOptions, A as AbilitySubject, a as AuthorizationError } from './authorization-Clf0Jx-U.js';
3
+ import { A as AuthyonServerClient } from './authyonServerClient-CW46xIRD.js';
4
4
  import 'jose';
5
5
 
6
6
  interface ServerPermissionGuardFallbackContext {
package/dist/next.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  AuthorizationError,
3
3
  authorizeRequest,
4
4
  authorizeToken
5
- } from "./chunk-NKWQMYB3.js";
5
+ } from "./chunk-WF26WYSX.js";
6
6
  import "./chunk-77J4NURK.js";
7
7
 
8
8
  // src/next.ts
package/package.json CHANGED
@@ -1,81 +1,81 @@
1
- {
2
- "name": "@authyon/server",
3
- "version": "0.2.0-beta.20",
4
- "description": "Authyon SDK for backends — server-to-server organization/member management and access-token verification.",
5
- "license": "MIT",
6
- "type": "module",
7
- "engines": {
8
- "node": ">=18"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/Synkestra/package-authyon.git",
13
- "directory": "packages/server"
14
- },
15
- "homepage": "https://authyon.com/docs",
16
- "bugs": {
17
- "url": "https://github.com/Synkestra/package-authyon/issues"
18
- },
19
- "main": "./dist/index.cjs",
20
- "module": "./dist/index.js",
21
- "types": "./dist/index.d.ts",
22
- "exports": {
23
- ".": {
24
- "types": "./dist/index.d.ts",
25
- "browser": "./dist/browser.js",
26
- "import": "./dist/index.js",
27
- "require": "./dist/index.cjs",
28
- "default": "./dist/index.js"
29
- },
30
- "./bff": {
31
- "types": "./dist/bff.d.ts",
32
- "browser": "./dist/browser.js",
33
- "import": "./dist/bff.js",
34
- "require": "./dist/bff.cjs",
35
- "default": "./dist/bff.js"
36
- },
37
- "./next": {
38
- "types": "./dist/next.d.ts",
39
- "import": "./dist/next.js",
40
- "require": "./dist/next.cjs",
41
- "default": "./dist/next.js"
42
- }
43
- },
44
- "files": [
45
- "dist"
46
- ],
47
- "sideEffects": false,
48
- "publishConfig": {
49
- "access": "public",
50
- "provenance": true
51
- },
52
- "scripts": {
53
- "build": "tsup src/index.ts src/browser.ts src/next.ts src/bff.ts --format esm,cjs --dts --clean",
54
- "typecheck": "tsc --noEmit",
55
- "prepack": "npm run build"
56
- },
57
- "keywords": [
58
- "authyon",
59
- "auth",
60
- "server-to-server",
61
- "management-api",
62
- "tokenVerification"
63
- ],
64
- "dependencies": {
65
- "jose": "^6.2.10"
66
- },
67
- "peerDependencies": {
68
- "react": ">=18.2.0"
69
- },
70
- "peerDependenciesMeta": {
71
- "react": {
72
- "optional": true
73
- }
74
- },
75
- "devDependencies": {
76
- "@types/node": "^22.20.2",
77
- "@types/react": "^19.2.18",
78
- "tsup": "^8.0.0",
79
- "typescript": "^5.5.0"
80
- }
81
- }
1
+ {
2
+ "name": "@authyon/server",
3
+ "version": "0.2.0-beta.22",
4
+ "description": "Authyon SDK for backends — server-to-server organization/member management and access-token verification.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/Synkestra/package-authyon.git",
13
+ "directory": "packages/server"
14
+ },
15
+ "homepage": "https://authyon.com/docs",
16
+ "bugs": {
17
+ "url": "https://github.com/Synkestra/package-authyon/issues"
18
+ },
19
+ "main": "./dist/index.cjs",
20
+ "module": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "browser": "./dist/browser.js",
26
+ "import": "./dist/index.js",
27
+ "require": "./dist/index.cjs",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "./bff": {
31
+ "types": "./dist/bff.d.ts",
32
+ "browser": "./dist/browser.js",
33
+ "import": "./dist/bff.js",
34
+ "require": "./dist/bff.cjs",
35
+ "default": "./dist/bff.js"
36
+ },
37
+ "./next": {
38
+ "types": "./dist/next.d.ts",
39
+ "import": "./dist/next.js",
40
+ "require": "./dist/next.cjs",
41
+ "default": "./dist/next.js"
42
+ }
43
+ },
44
+ "files": [
45
+ "dist"
46
+ ],
47
+ "sideEffects": false,
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "provenance": true
51
+ },
52
+ "scripts": {
53
+ "build": "tsup src/index.ts src/browser.ts src/next.ts src/bff.ts --format esm,cjs --dts --clean",
54
+ "typecheck": "tsc --noEmit",
55
+ "prepack": "npm run build"
56
+ },
57
+ "keywords": [
58
+ "authyon",
59
+ "auth",
60
+ "server-to-server",
61
+ "management-api",
62
+ "tokenVerification"
63
+ ],
64
+ "dependencies": {
65
+ "jose": "^6.2.10"
66
+ },
67
+ "peerDependencies": {
68
+ "react": ">=18.2.0"
69
+ },
70
+ "peerDependenciesMeta": {
71
+ "react": {
72
+ "optional": true
73
+ }
74
+ },
75
+ "devDependencies": {
76
+ "@types/node": "^22.20.2",
77
+ "@types/react": "^19.2.18",
78
+ "tsup": "^8.0.0",
79
+ "typescript": "^5.5.0"
80
+ }
81
+ }