@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/README.md +541 -473
- package/dist/{authorization-DfFCoDQ6.d.ts → authorization-BLENxur8.d.cts} +18 -4
- package/dist/{authorization-DcA5oKZT.d.cts → authorization-Clf0Jx-U.d.ts} +18 -4
- package/dist/{authyonServerClient-UA7HdjtD.d.cts → authyonServerClient-CW46xIRD.d.cts} +233 -11
- package/dist/{authyonServerClient-UA7HdjtD.d.ts → authyonServerClient-CW46xIRD.d.ts} +233 -11
- package/dist/bff.cjs +8 -1
- package/dist/bff.d.cts +1 -1
- package/dist/bff.d.ts +1 -1
- package/dist/bff.js +1 -1
- package/dist/{chunk-TDJNBD3Z.js → chunk-SUL2HLRR.js} +8 -1
- package/dist/{chunk-NKWQMYB3.js → chunk-WF26WYSX.js} +37 -7
- package/dist/index.cjs +239 -16
- package/dist/index.d.cts +54 -4
- package/dist/index.d.ts +54 -4
- package/dist/index.js +190 -9
- package/dist/next.cjs +11 -1
- package/dist/next.d.cts +2 -2
- package/dist/next.d.ts +2 -2
- package/dist/next.js +1 -1
- package/package.json +81 -81
|
@@ -171,6 +171,43 @@ interface IntrospectResult {
|
|
|
171
171
|
token_type?: string;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Builders for the tenant list's structured filters.
|
|
176
|
+
*
|
|
177
|
+
* The wire form of one filter is `<scope>.<key>:<op>[:<value>]`, repeated once
|
|
178
|
+
* per condition: `?filter=public.kind:in:reseller,partner&filter=custom.cnpj:eq:000`.
|
|
179
|
+
* Repeating the parameter ANDs the conditions and the comma inside `in` is the
|
|
180
|
+
* OR, which is the whole reason to filter server-side — the conditions are part
|
|
181
|
+
* of the SQL statement, so `total`, `pages` and `hasNext` describe the filtered
|
|
182
|
+
* set. A filter applied to a page after it arrives cannot do that.
|
|
183
|
+
*
|
|
184
|
+
* Scopes map to the three JSON documents a tenant carries: `public` →
|
|
185
|
+
* `publicMetadata`, `private` → `privateMetadata`, `custom` → `customFields`
|
|
186
|
+
* (the one the free-text `search` never covered).
|
|
187
|
+
*
|
|
188
|
+
* The rules below mirror the server's parser, so a typo raises `RangeError`
|
|
189
|
+
* here instead of coming back as a 400 `tenant.filter.invalid`.
|
|
190
|
+
*/
|
|
191
|
+
/** One `filter` parameter, ready to send. */
|
|
192
|
+
type TenantFilterExpression = string;
|
|
193
|
+
/** Values the filters accept; numbers and booleans are compared as text, the
|
|
194
|
+
* way `jsonb ->> key` returns them. */
|
|
195
|
+
type TenantFilterValue = string | number | boolean;
|
|
196
|
+
declare const TENANT_FILTER_MAX_VALUES = 25;
|
|
197
|
+
declare const TENANT_FILTER_MAX_VALUE_LENGTH = 200;
|
|
198
|
+
declare const TENANT_FILTER_MAX_DEPTH = 3;
|
|
199
|
+
declare const tenantFilter: {
|
|
200
|
+
/** `public.kind:eq:reseller` — the key equals this value. */
|
|
201
|
+
eq(path: string, value: TenantFilterValue): TenantFilterExpression;
|
|
202
|
+
/** `public.kind:in:reseller,partner` — the key equals any of these (SQL IN). */
|
|
203
|
+
in(path: string, values: readonly TenantFilterValue[]): TenantFilterExpression;
|
|
204
|
+
/** `public.notes:like:nacional` — the key contains this text, case-insensitive.
|
|
205
|
+
* The server escapes the LIKE metacharacters, so `100%` means `100%`. */
|
|
206
|
+
like(path: string, text: string): TenantFilterExpression;
|
|
207
|
+
/** `custom.cnpj:exists` — the key is present, whatever its value. */
|
|
208
|
+
exists(path: string): TenantFilterExpression;
|
|
209
|
+
};
|
|
210
|
+
|
|
174
211
|
/** GET/POST /env/tenants — confirmed against the live API. */
|
|
175
212
|
interface Organization {
|
|
176
213
|
id: string;
|
|
@@ -189,6 +226,55 @@ interface Organization {
|
|
|
189
226
|
publicMetadata?: string;
|
|
190
227
|
/** JSON-encoded metadata available only through administrative APIs. */
|
|
191
228
|
privateMetadata?: string;
|
|
229
|
+
/**
|
|
230
|
+
* The tenant this one sits under, or `null` for a root tenant. With `depth`
|
|
231
|
+
* it is what lets a flat page be rendered as the tree it describes.
|
|
232
|
+
*
|
|
233
|
+
* Absent on an API older than 2026-10-31, where every tenant was a root.
|
|
234
|
+
*/
|
|
235
|
+
parentTenantId?: string | null;
|
|
236
|
+
/** Distance from the root: `0` for a root tenant. */
|
|
237
|
+
depth?: number;
|
|
238
|
+
/**
|
|
239
|
+
* Direct sub-tenants. A tree view expands only where this is above zero,
|
|
240
|
+
* instead of fetching every node to find out it has no children.
|
|
241
|
+
*/
|
|
242
|
+
childCount?: number;
|
|
243
|
+
/** Custom roles defined inside the tenant. Only `get()` returns it. */
|
|
244
|
+
roleCount?: number;
|
|
245
|
+
/**
|
|
246
|
+
* The chain above this tenant, root first, excluding itself — a breadcrumb
|
|
247
|
+
* without a request per level. Empty for a root tenant, and only `get()`
|
|
248
|
+
* returns it.
|
|
249
|
+
*/
|
|
250
|
+
ancestors?: TenantAncestor[];
|
|
251
|
+
}
|
|
252
|
+
/** One step of the chain above a tenant, as `Organization.ancestors` carries it. */
|
|
253
|
+
interface TenantAncestor {
|
|
254
|
+
id: string;
|
|
255
|
+
name: string;
|
|
256
|
+
slug: string;
|
|
257
|
+
isDisabled: boolean;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* What `environment.tenants.list()` accepts. `search` is the free-text half
|
|
261
|
+
* (one substring, ORed across name, slug, description and both metadata
|
|
262
|
+
* documents); `filter` is the structured half, ANDed, one entry per condition
|
|
263
|
+
* — see `tenantFilter` for the builders and the wire format.
|
|
264
|
+
*/
|
|
265
|
+
interface TenantListOptions extends PaginationOptions {
|
|
266
|
+
search?: string;
|
|
267
|
+
filter?: TenantFilterExpression[];
|
|
268
|
+
/**
|
|
269
|
+
* Narrows the page to one level of the tree: a tenant id lists that tenant's
|
|
270
|
+
* direct sub-tenants, and `null` lists the roots. Omitted lists the
|
|
271
|
+
* environment's tenants regardless of depth — the behaviour from before the
|
|
272
|
+
* hierarchy existed, and still the right one for a flat picker.
|
|
273
|
+
*
|
|
274
|
+
* `search` and `filter` keep applying inside the level, so the envelope goes
|
|
275
|
+
* on describing what you asked for.
|
|
276
|
+
*/
|
|
277
|
+
parentId?: string | null;
|
|
192
278
|
}
|
|
193
279
|
/** One of a user's tenant memberships, as embedded in `EnvironmentUser.tenantMemberships`. */
|
|
194
280
|
interface TenantMembership {
|
|
@@ -336,6 +422,17 @@ interface CreateOrganizationInput {
|
|
|
336
422
|
customFields?: Record<string, unknown>;
|
|
337
423
|
publicMetadata?: Record<string, unknown>;
|
|
338
424
|
privateMetadata?: Record<string, unknown>;
|
|
425
|
+
/**
|
|
426
|
+
* Creates the tenant underneath an existing one in the same environment.
|
|
427
|
+
* Omitted creates a root tenant.
|
|
428
|
+
*
|
|
429
|
+
* The environment's `maxTenantDepth` has to allow the level — it is `1` by
|
|
430
|
+
* default, which means roots only and refuses this with
|
|
431
|
+
* `tenant.depth.not_allowed` until an operator raises it. The new node counts
|
|
432
|
+
* against the plan's tenant cap like any other, so nesting cannot be used to
|
|
433
|
+
* get around it.
|
|
434
|
+
*/
|
|
435
|
+
parentTenantId?: string;
|
|
339
436
|
}
|
|
340
437
|
interface UpdateOrganizationInput {
|
|
341
438
|
name: string;
|
|
@@ -371,6 +468,12 @@ interface AuditEvent {
|
|
|
371
468
|
environmentId?: string;
|
|
372
469
|
actorUserId?: string | null;
|
|
373
470
|
actorType?: string;
|
|
471
|
+
/**
|
|
472
|
+
* The address the event is recorded against — the vouched one when a machine
|
|
473
|
+
* credential named it. Where that differs from the socket peer, the peer is
|
|
474
|
+
* kept inside `payloadJson` as `peerIp`: which backend made the call is its
|
|
475
|
+
* own question, and the answer is not the caller's to choose.
|
|
476
|
+
*/
|
|
374
477
|
actorIp?: string;
|
|
375
478
|
userAgent?: string;
|
|
376
479
|
tenantId?: string | null;
|
|
@@ -465,8 +568,17 @@ interface AuthyonServerClientOptions {
|
|
|
465
568
|
clientId?: string;
|
|
466
569
|
clientSecret?: string;
|
|
467
570
|
/**
|
|
468
|
-
*
|
|
469
|
-
*
|
|
571
|
+
* The end user's address, forwarded on every API call as
|
|
572
|
+
* `X-Authyon-Client-Ip` (and `X-Forwarded-For`, for your own proxies).
|
|
573
|
+
*
|
|
574
|
+
* Worth setting: a call from your backend has no browser in it, so without
|
|
575
|
+
* this every audit row Authyon writes for your integration names the
|
|
576
|
+
* container gateway, which identifies nobody. The API believes the header
|
|
577
|
+
* from a machine credential and records the address beside the event, where
|
|
578
|
+
* the console's IP filter can find it. It only ever changes the trail — no
|
|
579
|
+
* IP-keyed gate reads a vouched address.
|
|
580
|
+
*
|
|
581
|
+
* Only set it from infrastructure you trust; the SDK rejects comma-separated
|
|
470
582
|
* or whitespace-containing values to avoid forwarded-header injection.
|
|
471
583
|
*/
|
|
472
584
|
clientIp?: string;
|
|
@@ -787,10 +899,58 @@ interface ClientRequestContext {
|
|
|
787
899
|
/** Real end-user IP, resolved only from infrastructure trusted by the application. */
|
|
788
900
|
clientIp?: string;
|
|
789
901
|
}
|
|
902
|
+
/**
|
|
903
|
+
* The header Authyon reads the real caller address from. `X-Forwarded-For` is
|
|
904
|
+
* not it: the API rewrites the socket peer from the edge proxy's chain and then
|
|
905
|
+
* resolves the client address from this header alone, so an address sent only
|
|
906
|
+
* in XFF is dropped before anything reads it.
|
|
907
|
+
*/
|
|
908
|
+
declare const CLIENT_IP_HEADER = "X-Authyon-Client-Ip";
|
|
790
909
|
/** Rejects header injection and values that are not a single IPv4 or IPv6 address. */
|
|
791
910
|
declare function normalizeClientIp(value: string): string;
|
|
792
911
|
|
|
793
912
|
type SafeJwtAlgorithm = "RS256" | "PS256" | "ES256" | "EdDSA";
|
|
913
|
+
/**
|
|
914
|
+
* The active tenant, as the `tenant` claim carries it. Present only on a
|
|
915
|
+
* session scoped to one — a token minted before any tenant switch has no
|
|
916
|
+
* tenant and no claim.
|
|
917
|
+
*
|
|
918
|
+
* Snake_case because these are wire names, not SDK ones: the claim is signed,
|
|
919
|
+
* and renaming its fields on the way in would hide what was actually asserted.
|
|
920
|
+
*/
|
|
921
|
+
interface AuthyonTenantClaim {
|
|
922
|
+
id: string;
|
|
923
|
+
slug: string;
|
|
924
|
+
name: string;
|
|
925
|
+
/** The tenant above this one, or `null` at a root. */
|
|
926
|
+
parent_id?: string | null;
|
|
927
|
+
/**
|
|
928
|
+
* The materialised chain from the root down to this tenant. Signed, so a
|
|
929
|
+
* consumer can answer "is that tenant under mine?" with a prefix comparison
|
|
930
|
+
* and no round trip — which is the whole reason it is in the token.
|
|
931
|
+
*/
|
|
932
|
+
path?: string;
|
|
933
|
+
/** Distance from the root: `0` at a root tenant. */
|
|
934
|
+
depth?: number;
|
|
935
|
+
/**
|
|
936
|
+
* Version of `path`. A tenant that gets moved bumps it, which is what makes
|
|
937
|
+
* the signed path safe to trust: a token minted before the move no longer
|
|
938
|
+
* matches and the API refuses it with `tenant.path_stale`, instead of
|
|
939
|
+
* letting a stale prefix keep answering yes.
|
|
940
|
+
*/
|
|
941
|
+
pv?: number;
|
|
942
|
+
roles?: string[];
|
|
943
|
+
/** Effective permissions AT this tenant. */
|
|
944
|
+
permissions?: string[];
|
|
945
|
+
/**
|
|
946
|
+
* Set only when the session descended into this tenant from an ancestor,
|
|
947
|
+
* naming the node the permissions came from — the user holds no membership
|
|
948
|
+
* here. Treat it as borrowed authority if that matters to you (a banner, a
|
|
949
|
+
* tighter trail, a refusal); ignore it and you have a session scoped to a
|
|
950
|
+
* tenant, which is what it is. Absent on any other session.
|
|
951
|
+
*/
|
|
952
|
+
via?: string | null;
|
|
953
|
+
}
|
|
794
954
|
interface AuthyonJwtClaims extends JWTPayload {
|
|
795
955
|
sub: string;
|
|
796
956
|
iss: string;
|
|
@@ -802,6 +962,8 @@ interface AuthyonJwtClaims extends JWTPayload {
|
|
|
802
962
|
roles?: string[];
|
|
803
963
|
permissions?: string[];
|
|
804
964
|
scope?: string;
|
|
965
|
+
/** The active tenant and the grants held inside it. Absent until a switch. */
|
|
966
|
+
tenant?: AuthyonTenantClaim;
|
|
805
967
|
}
|
|
806
968
|
interface VerifiedAccessToken {
|
|
807
969
|
claims: AuthyonJwtClaims;
|
|
@@ -984,23 +1146,83 @@ declare class AuthyonServerClient {
|
|
|
984
1146
|
* is found by the number, and `cnpj` alone finds every tenant carrying
|
|
985
1147
|
* that key. `take` defaults to 25 and is capped at 100.
|
|
986
1148
|
*
|
|
1149
|
+
* `filter` is the structured half, and the one to reach for when more
|
|
1150
|
+
* than one condition has to hold: each entry is a condition on a JSON
|
|
1151
|
+
* document, they are ANDed with each other and with `search`, and they
|
|
1152
|
+
* run inside the statement — so the envelope keeps describing the
|
|
1153
|
+
* filtered set and you can page through it. Build them with
|
|
1154
|
+
* `tenantFilter`:
|
|
1155
|
+
*
|
|
1156
|
+
* ```ts
|
|
1157
|
+
* const page = await authyon.environment.tenants.list({
|
|
1158
|
+
* filter: [
|
|
1159
|
+
* tenantFilter.in("public.kind", ["reseller", "partner"]),
|
|
1160
|
+
* tenantFilter.in("custom.cnpj", [a, b]),
|
|
1161
|
+
* ],
|
|
1162
|
+
* take: 100,
|
|
1163
|
+
* });
|
|
1164
|
+
* ```
|
|
1165
|
+
*
|
|
1166
|
+
* `parentId` narrows the page to one level of the tree — `children()`
|
|
1167
|
+
* and `roots()` below are the same call with that one option set.
|
|
1168
|
+
*
|
|
987
1169
|
* Against an Authyon API older than 2026-10-21 this route ignored
|
|
988
|
-
* skip/take/search and answered with a bare array
|
|
1170
|
+
* skip/take/search and answered with a bare array; one older than
|
|
1171
|
+
* 2026-10-22 ignores `filter` and one older than 2026-10-31 ignores
|
|
1172
|
+
* `parentId`, which read as a page that is too wide rather than an error.
|
|
1173
|
+
*/
|
|
1174
|
+
list: (params?: TenantListOptions) => Promise<Paged<Organization>>;
|
|
1175
|
+
/**
|
|
1176
|
+
* One page of a tenant's direct sub-tenants — `list()` with `parentId`.
|
|
1177
|
+
*
|
|
1178
|
+
* Only one level: a node's own children carry `childCount`, so a tree
|
|
1179
|
+
* view asks again where that is above zero instead of reading the whole
|
|
1180
|
+
* subtree to draw the first level of it.
|
|
1181
|
+
*/
|
|
1182
|
+
children: (parentTenantId: string, params?: Omit<TenantListOptions, "parentId">) => Promise<Paged<Organization>>;
|
|
1183
|
+
/**
|
|
1184
|
+
* One page of the environment's root tenants — `list()` with no parent.
|
|
1185
|
+
*
|
|
1186
|
+
* This is the first level of the tree, and the difference from `list()`
|
|
1187
|
+
* is worth keeping straight: `list()` returns every tenant at every
|
|
1188
|
+
* depth, which is what a flat picker wants and what a tree view must not
|
|
1189
|
+
* start from.
|
|
1190
|
+
*/
|
|
1191
|
+
roots: (params?: Omit<TenantListOptions, "parentId">) => Promise<Paged<Organization>>;
|
|
1192
|
+
/**
|
|
1193
|
+
* POST /env/tenants — create a tenant in the environment.
|
|
1194
|
+
*
|
|
1195
|
+
* `parentTenantId` nests it under an existing tenant. The environment's
|
|
1196
|
+
* `maxTenantDepth` has to allow the level — it ships as `1`, roots only,
|
|
1197
|
+
* so this answers `tenant.depth.not_allowed` until an operator raises it
|
|
1198
|
+
* in the console.
|
|
989
1199
|
*/
|
|
990
|
-
list: (params?: {
|
|
991
|
-
search?: string;
|
|
992
|
-
} & PaginationOptions) => Promise<Paged<Organization>>;
|
|
993
|
-
/** POST /env/tenants — create a tenant in the environment. */
|
|
994
1200
|
create: (input: CreateOrganizationInput) => Promise<Organization>;
|
|
995
|
-
/**
|
|
1201
|
+
/**
|
|
1202
|
+
* GET /env/tenants/{tenantId} — fetch a tenant by id.
|
|
1203
|
+
*
|
|
1204
|
+
* Returns `ancestors` as well, the chain above it root first, so a
|
|
1205
|
+
* breadcrumb costs this one call rather than one per level.
|
|
1206
|
+
*/
|
|
996
1207
|
get: (tenantId: string) => Promise<Organization>;
|
|
997
1208
|
/** PUT /env/tenants/{tenantId} — update a tenant. */
|
|
998
1209
|
update: (tenantId: string, input: UpdateOrganizationInput) => Promise<Organization>;
|
|
999
1210
|
/** PUT /env/tenants/{tenantId}/metadata - replace public and private metadata. */
|
|
1000
1211
|
updateMetadata: (tenantId: string, input: Pick<CreateOrganizationInput, "publicMetadata" | "privateMetadata">) => Promise<void>;
|
|
1001
|
-
/**
|
|
1212
|
+
/**
|
|
1213
|
+
* POST /env/tenants/{tenantId}/disable — disable without removing tenant
|
|
1214
|
+
* data.
|
|
1215
|
+
*
|
|
1216
|
+
* Disables the branch below it too. A sub-tenant left live under a
|
|
1217
|
+
* disabled parent would go on authenticating its users, which is the one
|
|
1218
|
+
* thing switching a tenant off is there to stop — so check `childCount`
|
|
1219
|
+
* before calling this on a node with children.
|
|
1220
|
+
*/
|
|
1002
1221
|
disable: (tenantId: string) => Promise<void>;
|
|
1003
|
-
/**
|
|
1222
|
+
/**
|
|
1223
|
+
* DELETE /env/tenants/{tenantId} — logically delete (disable) a tenant,
|
|
1224
|
+
* and the branch below it. Same handler as `disable()`.
|
|
1225
|
+
*/
|
|
1004
1226
|
delete: (tenantId: string) => Promise<void>;
|
|
1005
1227
|
members: {
|
|
1006
1228
|
/**
|
|
@@ -1229,4 +1451,4 @@ declare class TenantScopedClient {
|
|
|
1229
1451
|
/** Convenience factory: `const authyon = createClient({ clientId, clientSecret })`. */
|
|
1230
1452
|
declare function createClient(options?: AuthyonServerClientOptions): AuthyonServerClient;
|
|
1231
1453
|
|
|
1232
|
-
export { type
|
|
1454
|
+
export { type ReservedPermissions as $, AuthyonServerClient as A, type CredentialPermissionsInput as B, CLIENT_IP_HEADER as C, type HttpAdapterRequest as D, type EnvironmentUser as E, FetchHttpAdapter as F, type HttpLogEvent as G, type HttpAdapter as H, type HttpLogger as I, JwksTokenVerifier as J, type IntrospectResult as K, type InviteTenantMemberInput as L, type IssuePasswordResetInput as M, type JsonWebKeySet as N, type JwksTokenVerifierOptions as O, LoggingHttpAdapter as P, type LoginActivity as Q, type OpenIdConfiguration as R, type Organization as S, type OtpCheckResult as T, type Paged as U, type PaginationOptions as V, type PasswordResetCompleted as W, type PasswordResetIssued as X, type PasswordResetPreview as Y, type Permission as Z, type PermissionsByRole as _, type HttpLoggerOptions as a, type Role as a0, type RoleGrant as a1, type RoleInput as a2, type RoleList as a3, type RolePermissions as a4, type SafeJwtAlgorithm as a5, type SecurityMethods as a6, type SubjectApprovals as a7, type SubjectSecurity as a8, TENANT_FILTER_MAX_DEPTH as a9, type VerifiedAccessToken as aA, createClient as aB, normalizeClientIp as aC, tenantFilter as aD, TENANT_FILTER_MAX_VALUES as aa, TENANT_FILTER_MAX_VALUE_LENGTH as ab, type TenantAncestor as ac, type TenantClientValidationResult as ad, type TenantCredentialIssued as ae, type TenantCredentialSummary as af, type TenantFilterExpression as ag, type TenantFilterValue as ah, type TenantInvite as ai, type TenantInviteAccepted as aj, type TenantInviteInput as ak, type TenantInviteIssued as al, type TenantInvitePreview as am, type TenantInviteStatus as an, type TenantListOptions as ao, type TenantMember as ap, type TenantMemberPage as aq, type TenantMembership as ar, TenantScopedClient as as, type TokenResult as at, TokenVerificationError as au, type TokenVerificationErrorCode as av, type UpdateOrganizationInput as aw, type User as ax, UserScopedClient as ay, type ValidateResult as az, type AcceptTenantInviteInput as b, type AccessTokenSource as c, type Approval as d, type ApprovalAssurance as e, type ApprovalPayload as f, type ApprovalStatus as g, type ApprovalWebAuthnOptions as h, type AuditEvent as i, type AuthyonJwksDiscoveryOptions as j, type AuthyonJwtClaims as k, type AuthyonServerClientOptions as l, type AuthyonTenantClaim as m, type ClientCredentials as n, type ClientRequestContext as o, type ConfirmApprovalInput as p, type ConsumedApproval as q, type CreateApprovalInput as r, type CreateCredentialInput as s, type CreateOrganizationInput as t, type CreatePermissionInput as u, type CreateTenantCredentialInput as v, type CreateUserInput as w, type CredentialCreator as x, type CredentialDetail as y, type CredentialListOptions as z };
|
package/dist/bff.cjs
CHANGED
|
@@ -746,7 +746,14 @@ function appendQuery(path, params) {
|
|
|
746
746
|
if (!params) return path;
|
|
747
747
|
const query = new URLSearchParams();
|
|
748
748
|
for (const [key, value2] of Object.entries(params)) {
|
|
749
|
-
if (value2
|
|
749
|
+
if (value2 === void 0) continue;
|
|
750
|
+
if (Array.isArray(value2)) {
|
|
751
|
+
for (const item of value2) {
|
|
752
|
+
if (item !== void 0) query.append(key, String(item));
|
|
753
|
+
}
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
query.set(key, String(value2));
|
|
750
757
|
}
|
|
751
758
|
const value = query.toString();
|
|
752
759
|
return value ? `${path}${path.includes("?") ? "&" : "?"}${value}` : path;
|
package/dist/bff.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-
|
|
1
|
+
import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-CW46xIRD.cjs';
|
|
2
2
|
import 'jose';
|
|
3
3
|
|
|
4
4
|
interface BffLoginInput {
|
package/dist/bff.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-
|
|
1
|
+
import { H as HttpAdapter, a as HttpLoggerOptions, A as AuthyonServerClient } from './authyonServerClient-CW46xIRD.js';
|
|
2
2
|
import 'jose';
|
|
3
3
|
|
|
4
4
|
interface BffLoginInput {
|
package/dist/bff.js
CHANGED
|
@@ -298,7 +298,14 @@ function appendQuery(path, params) {
|
|
|
298
298
|
if (!params) return path;
|
|
299
299
|
const query = new URLSearchParams();
|
|
300
300
|
for (const [key, value2] of Object.entries(params)) {
|
|
301
|
-
if (value2
|
|
301
|
+
if (value2 === void 0) continue;
|
|
302
|
+
if (Array.isArray(value2)) {
|
|
303
|
+
for (const item of value2) {
|
|
304
|
+
if (item !== void 0) query.append(key, String(item));
|
|
305
|
+
}
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
query.set(key, String(value2));
|
|
302
309
|
}
|
|
303
310
|
const value = query.toString();
|
|
304
311
|
return value ? `${path}${path.includes("?") ? "&" : "?"}${value}` : path;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// ../../internal/core/http/clientIp.ts
|
|
2
|
+
var CLIENT_IP_HEADER = "X-Authyon-Client-Ip";
|
|
2
3
|
function clientIpHeaders(context) {
|
|
3
4
|
if (!context?.clientIp) return {};
|
|
4
|
-
|
|
5
|
+
const ip = normalizeClientIp(context.clientIp);
|
|
6
|
+
return { "X-Forwarded-For": ip, [CLIENT_IP_HEADER]: ip };
|
|
5
7
|
}
|
|
6
8
|
function normalizeClientIp(value) {
|
|
7
9
|
let ip = value.trim();
|
|
@@ -222,12 +224,29 @@ function createAuthyonAbility(source = {}, options = {}) {
|
|
|
222
224
|
return new AuthyonAbility(createAuthyonRules(source, options), options.detectSubjectType);
|
|
223
225
|
}
|
|
224
226
|
function hasPermission(source, requiredPermission) {
|
|
225
|
-
const
|
|
226
|
-
if (!
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
227
|
+
const required = requiredPermission?.trim() ?? "";
|
|
228
|
+
if (!isWellFormedPermission(required)) return false;
|
|
229
229
|
const permissionSource = isPermissionList(source) ? { permissions: source } : source;
|
|
230
|
-
|
|
230
|
+
const granted = [
|
|
231
|
+
...permissionSource.permissions ?? [],
|
|
232
|
+
...permissionSource.scope?.split(/\s+/).filter(Boolean) ?? []
|
|
233
|
+
];
|
|
234
|
+
return granted.some((permission) => grants(permission.trim(), required));
|
|
235
|
+
}
|
|
236
|
+
function grants(granted, required) {
|
|
237
|
+
if (!isWellFormedPermission(granted)) return false;
|
|
238
|
+
if (isFullWildcard(granted)) return true;
|
|
239
|
+
const grantedSegments = granted.split(":");
|
|
240
|
+
const requiredSegments = required.split(":");
|
|
241
|
+
if (grantedSegments.length !== requiredSegments.length) return false;
|
|
242
|
+
return grantedSegments.every(
|
|
243
|
+
(segment, index) => segment === "*" || segment === requiredSegments[index]
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
function isWellFormedPermission(permission) {
|
|
247
|
+
if (!permission) return false;
|
|
248
|
+
const segments = permission.split(":");
|
|
249
|
+
return segments.length >= 2 && segments.every((segment) => segment.length > 0);
|
|
231
250
|
}
|
|
232
251
|
function hasPermissionGroup(source, group) {
|
|
233
252
|
const hasAll = (group.allOf ?? []).every((permission) => hasPermission(source, permission));
|
|
@@ -242,13 +261,23 @@ function createAuthyonRules(source = {}, options = {}) {
|
|
|
242
261
|
...source.permissions ?? [],
|
|
243
262
|
...source.scope?.split(/\s+/).filter(Boolean) ?? []
|
|
244
263
|
]);
|
|
245
|
-
const rules = [...permissions].map(
|
|
264
|
+
const rules = [...permissions].map(grantToRule).filter(isAbilityRule);
|
|
246
265
|
for (const role of /* @__PURE__ */ new Set([...source.roles ?? [], ...options.roles ?? []])) {
|
|
247
266
|
rules.push(...(options.roleRules?.[role] ?? []).map(cloneRule));
|
|
248
267
|
}
|
|
249
268
|
rules.push(...(options.rules ?? []).map(cloneRule));
|
|
250
269
|
return rules;
|
|
251
270
|
}
|
|
271
|
+
function grantToRule(permission) {
|
|
272
|
+
const normalized = permission.trim();
|
|
273
|
+
if (normalized && isFullWildcard(normalized)) {
|
|
274
|
+
return { action: "manage", subject: "all" };
|
|
275
|
+
}
|
|
276
|
+
return permissionToRule(normalized);
|
|
277
|
+
}
|
|
278
|
+
function isFullWildcard(permission) {
|
|
279
|
+
return permission.split(":").every((segment) => segment === "*");
|
|
280
|
+
}
|
|
252
281
|
function permissionToRule(permission) {
|
|
253
282
|
const normalized = permission.trim();
|
|
254
283
|
if (!normalized) return null;
|
|
@@ -467,6 +496,7 @@ function unauthenticated() {
|
|
|
467
496
|
}
|
|
468
497
|
|
|
469
498
|
export {
|
|
499
|
+
CLIENT_IP_HEADER,
|
|
470
500
|
clientIpHeaders,
|
|
471
501
|
normalizeClientIp,
|
|
472
502
|
TokenVerificationError,
|