@authyon/server 0.2.0-beta.14 → 0.2.0-beta.16

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,7 +7,7 @@ import {
7
7
  JsonHttpClient,
8
8
  LoggingHttpAdapter,
9
9
  createSharedTransport
10
- } from "./chunk-TWUVXZOT.js";
10
+ } from "./chunk-TDJNBD3Z.js";
11
11
  import {
12
12
  AuthorizationError,
13
13
  AuthyonAbility,
@@ -146,29 +146,34 @@ async function allCredentials(request, path2) {
146
146
  throw new Error("Authyon: use listPage for more than 1000 credential pages.");
147
147
  }
148
148
 
149
- // ../../internal/core/financial/subjectFinancialAuthorizations.ts
149
+ // ../../internal/core/security/approvals.ts
150
150
  function path(id, suffix = "") {
151
151
  if (typeof id !== "string" || !id.trim())
152
- throw new TypeError("Authyon: a financial authorization id is required.");
152
+ throw new TypeError("Authyon: an approval id is required.");
153
153
  return `/auth/authorizations/${encodeURIComponent(id)}${suffix}`;
154
154
  }
155
- function subjectFinancialAuthorizations(request) {
155
+ var NO_METHODS = {
156
+ password: false,
157
+ authenticator: false,
158
+ emailCode: false,
159
+ passkey: false,
160
+ passkeyCount: 0,
161
+ recoveryCodes: 0,
162
+ sso: [],
163
+ twoFactor: false
164
+ };
165
+ function subjectSecurity(request) {
156
166
  return {
157
- /** GET /auth/authorizations/{id} — the pending transaction, to show before asking for the code. */
158
- get: (id) => request(path(id)),
159
- /** POST /auth/authorizations/{id}/webauthn/options — passkey options for approving this authorization. */
160
- webauthnOptions: (id) => request(path(id, "/webauthn/options"), { method: "POST" }),
161
- /**
162
- * POST /auth/authorizations/{id}/confirm — approves the transaction.
163
- *
164
- * Pass the authenticator code or passkey assertion to verify the second
165
- * factor inline (recommended). Without `input`, the session itself must
166
- * have used a passkey or authenticator code within the last 5 minutes,
167
- * otherwise the API answers `step_up_required`.
168
- */
169
- confirm: (id, input) => request(path(id, "/confirm"), { method: "POST", body: input }),
170
- /** POST /auth/authorizations/{id}/reject — the customer declines the transaction. */
171
- reject: (id) => request(path(id, "/reject"), { method: "POST" })
167
+ methods: async () => {
168
+ const me = await request("/auth/me");
169
+ return me.security ?? NO_METHODS;
170
+ },
171
+ approvals: {
172
+ get: (id) => request(path(id)),
173
+ webauthnOptions: (id) => request(path(id, "/webauthn/options"), { method: "POST" }),
174
+ confirm: (id, input) => request(path(id, "/confirm"), { method: "POST", body: input }),
175
+ reject: (id) => request(path(id, "/reject"), { method: "POST" })
176
+ }
172
177
  };
173
178
  }
174
179
 
@@ -200,10 +205,35 @@ var UserScopedClient = class {
200
205
  remove: (tenantId, userId) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/members/${segment(userId)}`, {
201
206
  method: "DELETE"
202
207
  })
208
+ },
209
+ /**
210
+ * Invites by e-mail or by link, acting as the user. Requires the
211
+ * `tenants:members:invite` permission on the tenant.
212
+ */
213
+ invites: {
214
+ list: (tenantId) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/invites`),
215
+ /** Always returns `acceptUrl`; `sendEmail: false` skips the e-mail. */
216
+ create: (tenantId, input) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/invites`, {
217
+ method: "POST",
218
+ body: {
219
+ email: input.email,
220
+ roles: input.roles,
221
+ sendEmail: input.sendEmail ?? true,
222
+ redirectUri: input.redirectUri
223
+ }
224
+ }),
225
+ /** New link (the previous one stops working), mailed unless `sendEmail` is `false`. */
226
+ resend: (tenantId, inviteId, input = {}) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/invites/${segment(inviteId)}/resend`, {
227
+ method: "POST",
228
+ body: { sendEmail: input.sendEmail ?? true }
229
+ }),
230
+ revoke: (tenantId, inviteId) => __privateGet(this, _request).call(this, `/auth/tenants/${segment(tenantId)}/invites/${segment(inviteId)}`, {
231
+ method: "DELETE"
232
+ })
203
233
  }
204
234
  };
205
235
  __privateSet(this, _request, scopedRequest(request, token));
206
- this.financialAuthorizations = subjectFinancialAuthorizations(__privateGet(this, _request));
236
+ this.security = subjectSecurity(__privateGet(this, _request));
207
237
  }
208
238
  };
209
239
  _request = new WeakMap();
@@ -493,6 +523,61 @@ var AuthyonServerClient = class {
493
523
  { method: "DELETE", envBearer: true }
494
524
  )
495
525
  },
526
+ /**
527
+ * Invites by e-mail or by link — for addresses that may not have an
528
+ * account yet; accepting creates it. Managing them needs the
529
+ * credential's `authyon:users:write` scope (listing, `authyon:tenants:read`).
530
+ */
531
+ invites: {
532
+ /** GET /env/tenants/{tenantId}/invites — every invite with its status, newest first. */
533
+ list: (tenantId) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}/invites`, { envBearer: true }),
534
+ /**
535
+ * POST /env/tenants/{tenantId}/invites — invite an address. Always
536
+ * returns `acceptUrl`; with `sendEmail: false` Authyon does not mail
537
+ * it and you deliver the link yourself. Valid 7 days, single use, and
538
+ * not readable again later.
539
+ */
540
+ create: (tenantId, input) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}/invites`, {
541
+ method: "POST",
542
+ envBearer: true,
543
+ body: {
544
+ email: input.email,
545
+ roles: input.roles,
546
+ sendEmail: input.sendEmail ?? true,
547
+ redirectUri: input.redirectUri
548
+ }
549
+ }),
550
+ /** POST .../invites/{inviteId}/resend — new link (the previous one stops working), mailed unless `sendEmail` is `false`. */
551
+ resend: (tenantId, inviteId, input = {}) => this.request(
552
+ `/env/tenants/${encodeURIComponent(tenantId)}/invites/${encodeURIComponent(inviteId)}/resend`,
553
+ { method: "POST", envBearer: true, body: { sendEmail: input.sendEmail ?? true } }
554
+ ),
555
+ /** DELETE /env/tenants/{tenantId}/invites/{inviteId} — revoke a pending invite. */
556
+ revoke: (tenantId, inviteId) => this.request(
557
+ `/env/tenants/${encodeURIComponent(tenantId)}/invites/${encodeURIComponent(inviteId)}`,
558
+ { method: "DELETE", envBearer: true }
559
+ ),
560
+ /**
561
+ * POST /auth/tenant-invites/preview — for a backend hosting the accept
562
+ * page. Public: needs only `envKey`, the token is the credential.
563
+ */
564
+ preview: (token) => this.request("/auth/tenant-invites/preview", { method: "POST", body: { token } }),
565
+ /**
566
+ * Joins the tenant; for an address with no account, also creates it
567
+ * (password required) with the e-mail already confirmed. Does not
568
+ * sign the user in.
569
+ *
570
+ * Without metadata: POST /auth/tenant-invites/accept — public, needs
571
+ * only `envKey`. With `publicMetadata`/`privateMetadata`: POST
572
+ * /env/tenant-invites/accept with the environment credential
573
+ * (`authyon:users:write`), since only your backend may set them.
574
+ */
575
+ accept: (input) => input.publicMetadata !== void 0 || input.privateMetadata !== void 0 ? this.request("/env/tenant-invites/accept", {
576
+ method: "POST",
577
+ envBearer: true,
578
+ body: input
579
+ }) : this.request("/auth/tenant-invites/accept", { method: "POST", body: input })
580
+ },
496
581
  roles: {
497
582
  /** GET /env/tenants/{tenantId}/roles — list a tenant's roles. */
498
583
  list: (tenantId) => this.request(`/env/tenants/${encodeURIComponent(tenantId)}/roles`, { envBearer: true }),
@@ -555,46 +640,65 @@ var AuthyonServerClient = class {
555
640
  /** GET /permissions/reserved — reserved permission names you can't redefine. */
556
641
  reserved: () => this.request("/permissions/reserved", { envBearer: true })
557
642
  },
643
+ audit: {
644
+ /** GET /env/audit — paginated list of the environment's audit events. */
645
+ list: (params = {}) => this.request("/env/audit", { envBearer: true, query: params }),
646
+ /** GET /env/audit/login-activity — paginated login activity in the environment. */
647
+ loginActivity: (params = {}) => this.request("/env/audit/login-activity", { envBearer: true, query: params })
648
+ }
649
+ };
650
+ // ── Security: one-time codes and approvals ───────────────────────────────
651
+ //
652
+ // Everything here runs with the environment credential: `otp` needs the
653
+ // `authyon:otp:verify` scope, `approvals` needs `authyon:financial:authorize`.
654
+ // Keep those credentials on the server.
655
+ this.security = {
656
+ otp: {
657
+ /**
658
+ * POST /env/users/{userId}/otp/verify — checks the user's authenticator
659
+ * app (TOTP) code. A wrong code returns `{ valid: false }` instead of
660
+ * throwing. Each code is accepted once; 3 wrong codes in a row sign the
661
+ * user out everywhere (`sessionsRevoked: true`); 5 lock the check for 15
662
+ * minutes (`rate_limited`). More than 50 wrong codes in 10 minutes from the
663
+ * same credential pause it (`credential_throttled`). Needs `authyon:otp:verify`.
664
+ */
665
+ check: (userId, code) => this.request(`/env/users/${segment(userId)}/otp/verify`, {
666
+ method: "POST",
667
+ envBearer: true,
668
+ body: { code }
669
+ })
670
+ },
558
671
  /**
559
- * Transaction-bound step-up for financial operations. The environment
560
- * credential needs the `authyon:financial:authorize` scope.
672
+ * The customer approves a payload with their authenticator code or
673
+ * passkey, once.
561
674
  *
562
- * 1. `create()` the authorization for the transaction and the customer.
563
- * 2. The customer approves it with their authenticator code or passkey
564
- * (`@authyon/auth` → `financialAuthorizations.confirm`).
565
- * 3. `consume()` it with the same transaction right before executing it.
675
+ * 1. `create()` the approval with the payload and the customer.
676
+ * 2. The customer confirms it (`@authyon/auth` → `security.approvals.confirm`).
677
+ * 3. `consume()` it right before executing, and execute the returned payload.
566
678
  */
567
- financialAuthorizations: {
568
- /** POST /env/authorizations — registers a transaction awaiting the customer's approval. */
679
+ approvals: {
680
+ /** POST /env/authorizations — registers a payload awaiting the customer's approval. */
569
681
  create: (input) => {
570
682
  const { idempotencyKey, ...body } = input;
571
- if (typeof idempotencyKey !== "string" || !idempotencyKey.trim())
572
- throw new TypeError("Authyon: `idempotencyKey` is required.");
573
683
  return this.request("/env/authorizations", {
574
684
  method: "POST",
575
685
  envBearer: true,
576
- headers: { "Idempotency-Key": idempotencyKey },
686
+ headers: idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0,
577
687
  body
578
688
  });
579
689
  },
580
690
  /** GET /env/authorizations/{id} — current status and, once approved, the assurance evidence. */
581
691
  get: (id) => this.request(`/env/authorizations/${segment(id)}`, { envBearer: true }),
582
692
  /**
583
- * POST /env/authorizations/{id}/consume — redeems an approved
584
- * authorization exactly once. Resend the same transaction given to
585
- * `create()`; any difference fails with `transaction_mismatch`.
693
+ * POST /env/authorizations/{id}/consume — redeems an approved approval
694
+ * exactly once and returns the approved payload. Pass `payload` to have
695
+ * the API check it matches (`payload_mismatch` otherwise).
586
696
  */
587
- consume: (id, transaction) => this.request(`/env/authorizations/${segment(id)}/consume`, {
697
+ consume: (id, payload) => this.request(`/env/authorizations/${segment(id)}/consume`, {
588
698
  method: "POST",
589
699
  envBearer: true,
590
- body: transaction
700
+ body: payload === void 0 ? void 0 : { payload }
591
701
  })
592
- },
593
- audit: {
594
- /** GET /env/audit — paginated list of the environment's audit events. */
595
- list: (params = {}) => this.request("/env/audit", { envBearer: true, query: params }),
596
- /** GET /env/audit/login-activity — paginated login activity in the environment. */
597
- loginActivity: (params = {}) => this.request("/env/audit/login-activity", { envBearer: true, query: params })
598
702
  }
599
703
  };
600
704
  // ── Generic permission grants ─────────────────────────────────────────────
@@ -720,6 +824,28 @@ var AuthyonServerClient = class {
720
824
  _clientSecret = new WeakMap();
721
825
  var TenantScopedClient = class {
722
826
  constructor(server, credentials) {
827
+ /** Invites to the token's tenant, by e-mail or by link. */
828
+ this.invites = {
829
+ /** GET /tenant/invites — every invite with its status, newest first. */
830
+ list: () => this.request("/tenant/invites"),
831
+ /** POST /tenant/invites — always returns `acceptUrl`; `sendEmail: false` skips the e-mail. */
832
+ create: (input) => this.request("/tenant/invites", {
833
+ method: "POST",
834
+ body: {
835
+ email: input.email,
836
+ roles: input.roles,
837
+ sendEmail: input.sendEmail ?? true,
838
+ redirectUri: input.redirectUri
839
+ }
840
+ }),
841
+ /** POST /tenant/invites/{inviteId}/resend — new link (the previous one stops working), mailed unless `sendEmail` is `false`. */
842
+ resend: (inviteId, input = {}) => this.request(`/tenant/invites/${encodeURIComponent(inviteId)}/resend`, {
843
+ method: "POST",
844
+ body: { sendEmail: input.sendEmail ?? true }
845
+ }),
846
+ /** DELETE /tenant/invites/{inviteId} — revoke a pending invite. */
847
+ revoke: (inviteId) => this.request(`/tenant/invites/${encodeURIComponent(inviteId)}`, { method: "DELETE" })
848
+ };
723
849
  this.members = {
724
850
  /**
725
851
  * GET /tenant/members — members of the token's tenant.
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-B7ynKAud.cjs';
3
- import { A as AuthyonServerClient } from './authyonServerClient-DmNnjw4z.cjs';
2
+ import { R as RequestAuthorizationOptions, A as AbilitySubject, a as AuthorizationError } from './authorization-C0Mg5gUv.cjs';
3
+ import { A as AuthyonServerClient } from './authyonServerClient-1_1qNRDa.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-BF015NDJ.js';
3
- import { A as AuthyonServerClient } from './authyonServerClient-DmNnjw4z.js';
2
+ import { R as RequestAuthorizationOptions, A as AbilitySubject, a as AuthorizationError } from './authorization-BvjIWPcU.js';
3
+ import { A as AuthyonServerClient } from './authyonServerClient-1_1qNRDa.js';
4
4
  import 'jose';
5
5
 
6
6
  interface ServerPermissionGuardFallbackContext {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authyon/server",
3
- "version": "0.2.0-beta.14",
3
+ "version": "0.2.0-beta.16",
4
4
  "description": "Authyon SDK for backends — server-to-server organization/member management and access-token verification.",
5
5
  "license": "MIT",
6
6
  "type": "module",