@productcraft/auth 0.6.0 → 0.8.0

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
@@ -765,11 +765,15 @@ function getApiKeyControllerListApiKeysUrl({
765
765
  const res = { method: "GET", url: `/v1/apps/${app_id}/api-keys` };
766
766
  return res;
767
767
  }
768
- async function apiKeyControllerListApiKeys({ appId }, config = {}) {
768
+ async function apiKeyControllerListApiKeys({
769
+ appId,
770
+ params
771
+ }, config = {}) {
769
772
  const { client: request = client, ...requestConfig } = config;
770
773
  const res = await request({
771
774
  method: "GET",
772
775
  url: getApiKeyControllerListApiKeysUrl({ appId }).url.toString(),
776
+ params,
773
777
  ...requestConfig
774
778
  });
775
779
  return res.data;
@@ -1031,7 +1035,9 @@ async function appAuditControllerGetAuditLogs({
1031
1035
  limit: params.limit,
1032
1036
  cursor: params.cursor,
1033
1037
  action: params.action,
1034
- actor_id: params.actorId
1038
+ actor_id: params.actorId,
1039
+ since: params.since,
1040
+ until: params.until
1035
1041
  } : void 0;
1036
1042
  const res = await request({
1037
1043
  method: "GET",
@@ -1557,6 +1563,220 @@ async function consumerIdpControllerNativeSignIn({
1557
1563
  return res.data;
1558
1564
  }
1559
1565
 
1566
+ // src/_generated/clients/consumerAuth/consumerAuthControllerPasskeyOptions.ts
1567
+ function getConsumerAuthControllerPasskeyOptionsUrl({
1568
+ appSlug
1569
+ }) {
1570
+ const app_slug = appSlug;
1571
+ const res = {
1572
+ method: "POST",
1573
+ url: `/${app_slug}/v1/auth/passkey/options`
1574
+ };
1575
+ return res;
1576
+ }
1577
+ async function consumerAuthControllerPasskeyOptions({
1578
+ appSlug,
1579
+ data
1580
+ }, config = {}) {
1581
+ const { client: request = client, ...requestConfig } = config;
1582
+ const requestData = data;
1583
+ const res = await request({
1584
+ method: "POST",
1585
+ url: getConsumerAuthControllerPasskeyOptionsUrl({ appSlug }).url.toString(),
1586
+ data: requestData,
1587
+ ...requestConfig
1588
+ });
1589
+ return res.data;
1590
+ }
1591
+
1592
+ // src/_generated/clients/consumerAuth/consumerAuthControllerPasskeyVerify.ts
1593
+ function getConsumerAuthControllerPasskeyVerifyUrl({
1594
+ appSlug
1595
+ }) {
1596
+ const app_slug = appSlug;
1597
+ const res = {
1598
+ method: "POST",
1599
+ url: `/${app_slug}/v1/auth/passkey/verify`
1600
+ };
1601
+ return res;
1602
+ }
1603
+ async function consumerAuthControllerPasskeyVerify({
1604
+ appSlug,
1605
+ data
1606
+ }, config = {}) {
1607
+ const { client: request = client, ...requestConfig } = config;
1608
+ const requestData = data;
1609
+ const res = await request({
1610
+ method: "POST",
1611
+ url: getConsumerAuthControllerPasskeyVerifyUrl({ appSlug }).url.toString(),
1612
+ data: requestData,
1613
+ ...requestConfig
1614
+ });
1615
+ return res.data;
1616
+ }
1617
+
1618
+ // src/_generated/clients/consumerAuth/consumerAuthControllerMfaWebauthnOptions.ts
1619
+ function getConsumerAuthControllerMfaWebauthnOptionsUrl({
1620
+ appSlug
1621
+ }) {
1622
+ const app_slug = appSlug;
1623
+ const res = {
1624
+ method: "POST",
1625
+ url: `/${app_slug}/v1/auth/mfa/webauthn/options`
1626
+ };
1627
+ return res;
1628
+ }
1629
+ async function consumerAuthControllerMfaWebauthnOptions({
1630
+ appSlug,
1631
+ data
1632
+ }, config = {}) {
1633
+ const { client: request = client, ...requestConfig } = config;
1634
+ const requestData = data;
1635
+ const res = await request({
1636
+ method: "POST",
1637
+ url: getConsumerAuthControllerMfaWebauthnOptionsUrl({
1638
+ appSlug
1639
+ }).url.toString(),
1640
+ data: requestData,
1641
+ ...requestConfig
1642
+ });
1643
+ return res.data;
1644
+ }
1645
+
1646
+ // src/_generated/clients/consumerAuth/consumerAuthControllerMfaVerify.ts
1647
+ function getConsumerAuthControllerMfaVerifyUrl({
1648
+ appSlug
1649
+ }) {
1650
+ const app_slug = appSlug;
1651
+ const res = {
1652
+ method: "POST",
1653
+ url: `/${app_slug}/v1/auth/mfa/verify`
1654
+ };
1655
+ return res;
1656
+ }
1657
+ async function consumerAuthControllerMfaVerify({
1658
+ appSlug,
1659
+ data
1660
+ }, config = {}) {
1661
+ const { client: request = client, ...requestConfig } = config;
1662
+ const requestData = data;
1663
+ const res = await request({
1664
+ method: "POST",
1665
+ url: getConsumerAuthControllerMfaVerifyUrl({ appSlug }).url.toString(),
1666
+ data: requestData,
1667
+ ...requestConfig
1668
+ });
1669
+ return res.data;
1670
+ }
1671
+
1672
+ // src/_generated/clients/consumerMfa/consumerMfaControllerStartEnrollment.ts
1673
+ function getConsumerMfaControllerStartEnrollmentUrl({
1674
+ appSlug
1675
+ }) {
1676
+ const app_slug = appSlug;
1677
+ const res = {
1678
+ method: "POST",
1679
+ url: `/${app_slug}/v1/me/mfa/factors`
1680
+ };
1681
+ return res;
1682
+ }
1683
+ async function consumerMfaControllerStartEnrollment({
1684
+ appSlug,
1685
+ data
1686
+ }, config = {}) {
1687
+ const { client: request = client, ...requestConfig } = config;
1688
+ const requestData = data;
1689
+ const res = await request({
1690
+ method: "POST",
1691
+ url: getConsumerMfaControllerStartEnrollmentUrl({ appSlug }).url.toString(),
1692
+ data: requestData,
1693
+ ...requestConfig
1694
+ });
1695
+ return res.data;
1696
+ }
1697
+
1698
+ // src/_generated/clients/consumerMfa/consumerMfaControllerConfirmEnrollment.ts
1699
+ function getConsumerMfaControllerConfirmEnrollmentUrl({
1700
+ id,
1701
+ appSlug
1702
+ }) {
1703
+ const app_slug = appSlug;
1704
+ const res = {
1705
+ method: "POST",
1706
+ url: `/${app_slug}/v1/me/mfa/factors/${id}/verify`
1707
+ };
1708
+ return res;
1709
+ }
1710
+ async function consumerMfaControllerConfirmEnrollment({
1711
+ id,
1712
+ appSlug,
1713
+ data
1714
+ }, config = {}) {
1715
+ const { client: request = client, ...requestConfig } = config;
1716
+ const requestData = data;
1717
+ const res = await request({
1718
+ method: "POST",
1719
+ url: getConsumerMfaControllerConfirmEnrollmentUrl({
1720
+ id,
1721
+ appSlug
1722
+ }).url.toString(),
1723
+ data: requestData,
1724
+ ...requestConfig
1725
+ });
1726
+ return res.data;
1727
+ }
1728
+
1729
+ // src/_generated/clients/consumerMfa/consumerMfaControllerStepUpWebauthnOptions.ts
1730
+ function getConsumerMfaControllerStepUpWebauthnOptionsUrl({
1731
+ appSlug
1732
+ }) {
1733
+ const app_slug = appSlug;
1734
+ const res = {
1735
+ method: "POST",
1736
+ url: `/${app_slug}/v1/me/mfa/step-up/webauthn/options`
1737
+ };
1738
+ return res;
1739
+ }
1740
+ async function consumerMfaControllerStepUpWebauthnOptions({
1741
+ appSlug
1742
+ }, config = {}) {
1743
+ const { client: request = client, ...requestConfig } = config;
1744
+ const res = await request({
1745
+ method: "POST",
1746
+ url: getConsumerMfaControllerStepUpWebauthnOptionsUrl({
1747
+ appSlug
1748
+ }).url.toString(),
1749
+ ...requestConfig
1750
+ });
1751
+ return res.data;
1752
+ }
1753
+
1754
+ // src/_generated/clients/consumerMfa/consumerMfaControllerStepUp.ts
1755
+ function getConsumerMfaControllerStepUpUrl({
1756
+ appSlug
1757
+ }) {
1758
+ const app_slug = appSlug;
1759
+ const res = {
1760
+ method: "POST",
1761
+ url: `/${app_slug}/v1/me/mfa/step-up`
1762
+ };
1763
+ return res;
1764
+ }
1765
+ async function consumerMfaControllerStepUp({
1766
+ appSlug,
1767
+ data
1768
+ }, config = {}) {
1769
+ const { client: request = client, ...requestConfig } = config;
1770
+ const requestData = data;
1771
+ const res = await request({
1772
+ method: "POST",
1773
+ url: getConsumerMfaControllerStepUpUrl({ appSlug }).url.toString(),
1774
+ data: requestData,
1775
+ ...requestConfig
1776
+ });
1777
+ return res.data;
1778
+ }
1779
+
1560
1780
  // src/_generated/clients/consumerVerify/consumerVerifyControllerVerify.ts
1561
1781
  function getConsumerVerifyControllerVerifyUrl({
1562
1782
  appSlug
@@ -1662,6 +1882,165 @@ async function consumerOAuthControllerClientCredentials({
1662
1882
  return res.data;
1663
1883
  }
1664
1884
 
1885
+ // src/passkeys.ts
1886
+ function toB64u(buf) {
1887
+ const bytes = buf instanceof ArrayBuffer ? new Uint8Array(buf) : new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
1888
+ let bin = "";
1889
+ for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
1890
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1891
+ }
1892
+ function fromB64u(s) {
1893
+ const b64 = s.replace(/-/g, "+").replace(/_/g, "/");
1894
+ const bin = atob(b64.padEnd(Math.ceil(b64.length / 4) * 4, "="));
1895
+ return Uint8Array.from(bin, (c) => c.charCodeAt(0));
1896
+ }
1897
+ var snakeToCamel = (k) => k.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
1898
+ function camelizeKeys(value) {
1899
+ if (Array.isArray(value)) return value.map(camelizeKeys);
1900
+ if (value && typeof value === "object" && !(value instanceof Uint8Array)) {
1901
+ const out = {};
1902
+ for (const [k, v] of Object.entries(value)) {
1903
+ out[snakeToCamel(k)] = camelizeKeys(v);
1904
+ }
1905
+ return out;
1906
+ }
1907
+ return value;
1908
+ }
1909
+ function decodeOptions(publicKey) {
1910
+ const pk = camelizeKeys(publicKey);
1911
+ const out = {
1912
+ ...pk,
1913
+ challenge: fromB64u(pk.challenge)
1914
+ };
1915
+ if (pk.user) {
1916
+ out.user = { ...pk.user, id: fromB64u(pk.user.id) };
1917
+ }
1918
+ for (const list of ["excludeCredentials", "allowCredentials"]) {
1919
+ const entries = pk[list];
1920
+ if (entries) {
1921
+ out[list] = entries.map((c) => ({ ...c, id: fromB64u(c.id) }));
1922
+ }
1923
+ }
1924
+ return out;
1925
+ }
1926
+ function encodeCredential(cred) {
1927
+ const r = cred.response;
1928
+ const response = {
1929
+ clientDataJSON: toB64u(r.clientDataJSON)
1930
+ };
1931
+ if (r.attestationObject) response.attestationObject = toB64u(r.attestationObject);
1932
+ if (r.authenticatorData) response.authenticatorData = toB64u(r.authenticatorData);
1933
+ if (r.signature) response.signature = toB64u(r.signature);
1934
+ if (r.userHandle) response.userHandle = toB64u(r.userHandle);
1935
+ return { id: cred.id, response };
1936
+ }
1937
+ var PasskeyError = class extends Error {
1938
+ code;
1939
+ cause;
1940
+ constructor(code, message, cause) {
1941
+ super(message);
1942
+ this.name = "PasskeyError";
1943
+ this.code = code;
1944
+ this.cause = cause;
1945
+ }
1946
+ };
1947
+ function browserCredentials() {
1948
+ const nav = globalThis.navigator;
1949
+ const creds = nav?.credentials;
1950
+ if (!creds || typeof creds.create !== "function" || typeof creds.get !== "function") {
1951
+ return void 0;
1952
+ }
1953
+ return creds;
1954
+ }
1955
+ function isPasskeySupported() {
1956
+ const g = globalThis;
1957
+ if (typeof g.window === "undefined") return false;
1958
+ if (typeof g.PublicKeyCredential !== "function") return false;
1959
+ if (g.isSecureContext === false) return false;
1960
+ return browserCredentials() !== void 0;
1961
+ }
1962
+ function mapDomError(err) {
1963
+ const name = err?.name;
1964
+ const message = err?.message ?? String(err);
1965
+ switch (name) {
1966
+ case "NotAllowedError":
1967
+ return new PasskeyError(
1968
+ "cancelled",
1969
+ `Passkey prompt was cancelled, timed out, or no passkey matched: ${message}`,
1970
+ err
1971
+ );
1972
+ case "InvalidStateError":
1973
+ return new PasskeyError("invalid_state", `Authenticator refused: ${message}`, err);
1974
+ case "SecurityError":
1975
+ return new PasskeyError(
1976
+ "security",
1977
+ `Page origin does not match the app's rp_id: ${message}`,
1978
+ err
1979
+ );
1980
+ case "AbortError":
1981
+ return new PasskeyError("cancelled", `Passkey ceremony aborted: ${message}`, err);
1982
+ case "TimeoutError":
1983
+ return new PasskeyError(
1984
+ "timeout",
1985
+ `Passkey prompt timed out before a credential was produced: ${message}`,
1986
+ err
1987
+ );
1988
+ default:
1989
+ return new PasskeyError("unknown", `navigator.credentials failed: ${message}`, err);
1990
+ }
1991
+ }
1992
+ function resolveCredentials(opts = {}) {
1993
+ const creds = opts.credentials ?? browserCredentials();
1994
+ if (!creds) {
1995
+ throw new PasskeyError(
1996
+ "unsupported",
1997
+ "navigator.credentials is not available \u2014 passkeys need a browser with WebAuthn (check isPasskeySupported())"
1998
+ );
1999
+ }
2000
+ return creds;
2001
+ }
2002
+ function isCredentialLike(value) {
2003
+ if (!value || typeof value !== "object") return false;
2004
+ const v = value;
2005
+ return typeof v.id === "string" && !!v.response && typeof v.response === "object";
2006
+ }
2007
+ async function createPasskeyCredential(publicKey, opts = {}) {
2008
+ const creds = resolveCredentials(opts);
2009
+ let cred;
2010
+ try {
2011
+ cred = await creds.create({ publicKey: decodeOptions(publicKey), signal: opts.signal });
2012
+ } catch (err) {
2013
+ throw mapDomError(err);
2014
+ }
2015
+ if (!isCredentialLike(cred)) {
2016
+ throw new PasskeyError(
2017
+ "no_credential",
2018
+ "navigator.credentials.create() returned no usable credential"
2019
+ );
2020
+ }
2021
+ return encodeCredential(cred);
2022
+ }
2023
+ async function getPasskeyCredential(publicKey, opts = {}) {
2024
+ const creds = resolveCredentials(opts);
2025
+ let cred;
2026
+ try {
2027
+ cred = await creds.get({
2028
+ publicKey: decodeOptions(publicKey),
2029
+ mediation: opts.mediation,
2030
+ signal: opts.signal
2031
+ });
2032
+ } catch (err) {
2033
+ throw mapDomError(err);
2034
+ }
2035
+ if (!isCredentialLike(cred)) {
2036
+ throw new PasskeyError(
2037
+ "no_credential",
2038
+ "navigator.credentials.get() returned no usable credential"
2039
+ );
2040
+ }
2041
+ return encodeCredential(cred);
2042
+ }
2043
+
1665
2044
  // src/jwt/errors.ts
1666
2045
  var JwtVerifyError = class extends Error {
1667
2046
  code;
@@ -1691,6 +2070,12 @@ var JwtNotYetValidError = class extends JwtVerifyError {
1691
2070
  this.name = "JwtNotYetValidError";
1692
2071
  }
1693
2072
  };
2073
+ var JwtRefreshTokenError = class extends JwtVerifyError {
2074
+ constructor(message = "Refresh tokens cannot be used as access tokens", options) {
2075
+ super("ERR_JWT_REFRESH_TOKEN", message, options);
2076
+ this.name = "JwtRefreshTokenError";
2077
+ }
2078
+ };
1694
2079
  var JwtIssuerMismatchError = class extends JwtVerifyError {
1695
2080
  constructor(message, options) {
1696
2081
  super("ERR_JWT_ISS_MISMATCH", message, options);
@@ -1771,6 +2156,10 @@ var JwksCache = class {
1771
2156
  refresh() {
1772
2157
  }
1773
2158
  };
2159
+ function isRefreshToken(claims) {
2160
+ if (!claims) return false;
2161
+ return claims.token_use === "refresh" || claims.type === "refresh";
2162
+ }
1774
2163
  async function verifyAuthToken(token, getKey, opts = {}) {
1775
2164
  try {
1776
2165
  const { payload } = await jose.jwtVerify(token, getKey, {
@@ -1782,6 +2171,7 @@ async function verifyAuthToken(token, getKey, opts = {}) {
1782
2171
  currentDate: opts.currentDate,
1783
2172
  maxTokenAge: opts.maxTokenAge
1784
2173
  });
2174
+ if (isRefreshToken(payload)) throw new JwtRefreshTokenError();
1785
2175
  return payload;
1786
2176
  } catch (err) {
1787
2177
  throw translateJoseError(err);
@@ -2116,6 +2506,141 @@ var ConsumerScope = class {
2116
2506
  )
2117
2507
  };
2118
2508
  // ─────────────────────────────────────────────────────────────
2509
+ // Passkeys — WebAuthn ceremonies, one call each. BROWSER ONLY at
2510
+ // call time (they drive navigator.credentials); importing is safe
2511
+ // anywhere. Base64url in both directions is handled here; the
2512
+ // wire stays snake_case except the spec-fixed response members
2513
+ // (clientDataJSON, attestationObject, authenticatorData, signature,
2514
+ // userHandle), which travel verbatim.
2515
+ // ─────────────────────────────────────────────────────────────
2516
+ passkeys = {
2517
+ /**
2518
+ * Enrol a passkey as an MFA factor for the signed-in end-user.
2519
+ * Requires the scope to carry the user's access token
2520
+ * (`new Auth({ auth: { type: "bearer", token } })`).
2521
+ *
2522
+ * `POST /me/mfa/factors { type: "webauthn" }` →
2523
+ * `navigator.credentials.create()` →
2524
+ * `POST /me/mfa/factors/:id/verify { credential }`.
2525
+ *
2526
+ * Resolves with the now-enabled factor and the user's recovery
2527
+ * codes — show those once, they are not retrievable later. The
2528
+ * factor stays disabled until the verify call succeeds, so an
2529
+ * abandoned prompt leaves nothing usable behind.
2530
+ */
2531
+ enroll: async (opts = {}) => {
2532
+ const credentials = resolveCredentials(opts);
2533
+ const start = await consumerMfaControllerStartEnrollment(
2534
+ {
2535
+ appSlug: this.appSlug,
2536
+ data: { type: "webauthn", ...opts.label ? { label: opts.label } : {} }
2537
+ },
2538
+ { client: this.client }
2539
+ );
2540
+ const publicKey = start.enrollment.public_key;
2541
+ const credential = await createPasskeyCredential(publicKey, { ...opts, credentials });
2542
+ return await consumerMfaControllerConfirmEnrollment(
2543
+ { appSlug: this.appSlug, id: start.factor.id, data: { credential } },
2544
+ { client: this.client }
2545
+ );
2546
+ },
2547
+ /**
2548
+ * Passwordless sign-in with a discoverable passkey. No password,
2549
+ * no email address: the authenticator finds its own passkey for
2550
+ * the app's rp_id.
2551
+ *
2552
+ * `POST /auth/passkey/options {}` → `navigator.credentials.get()`
2553
+ * → `POST /auth/passkey/verify { credential }`.
2554
+ *
2555
+ * Resolves with the session (`access_token`, `refresh_token`,
2556
+ * …; `amr: ["webauthn"]`). Rejects with `PasskeyError`
2557
+ * (`code: "cancelled"`) when the user dismisses the prompt or no
2558
+ * passkey matches, and with `AuthHttpError` 401 for a forged,
2559
+ * unknown, replayed or expired assertion — the server answers
2560
+ * those uniformly on purpose.
2561
+ *
2562
+ * The options call deliberately takes no identifier (it would be
2563
+ * an account-existence oracle); `tenant_id` is the only
2564
+ * accepted body member and only matters for multi-tenant apps.
2565
+ * Pass `mediation: "conditional"` for autofill-style UI.
2566
+ */
2567
+ signIn: async (opts = {}) => {
2568
+ const credentials = resolveCredentials(opts);
2569
+ const { public_key } = await consumerAuthControllerPasskeyOptions(
2570
+ {
2571
+ appSlug: this.appSlug,
2572
+ data: opts.tenant_id ? { tenant_id: opts.tenant_id } : {}
2573
+ },
2574
+ { client: this.client }
2575
+ );
2576
+ const credential = await getPasskeyCredential(
2577
+ public_key,
2578
+ { ...opts, credentials }
2579
+ );
2580
+ return await consumerAuthControllerPasskeyVerify(
2581
+ { appSlug: this.appSlug, data: { credential } },
2582
+ { client: this.client }
2583
+ );
2584
+ },
2585
+ /**
2586
+ * Satisfy the MFA step of a password sign-in with a passkey.
2587
+ * Call it when `auth.signin()` answered
2588
+ * `{ mfa_required: true, mfa_token, factors }` and `factors`
2589
+ * contains a `webauthn` entry.
2590
+ *
2591
+ * `POST /auth/mfa/webauthn/options { mfa_token }` →
2592
+ * `navigator.credentials.get()` →
2593
+ * `POST /auth/mfa/verify { mfa_token, credential }`.
2594
+ *
2595
+ * Resolves with the session (`amr: ["pwd", "webauthn"]`, fresh
2596
+ * `mfa_at`). Possession of the `mfa_token` — a correct password
2597
+ * — is the authority for the options call.
2598
+ */
2599
+ completeMfaChallenge: async (args) => {
2600
+ const { mfa_token, ...browserOpts } = args;
2601
+ const credentials = resolveCredentials(browserOpts);
2602
+ const { public_key } = await consumerAuthControllerMfaWebauthnOptions(
2603
+ { appSlug: this.appSlug, data: { mfa_token } },
2604
+ { client: this.client }
2605
+ );
2606
+ const credential = await getPasskeyCredential(
2607
+ public_key,
2608
+ { ...browserOpts, credentials }
2609
+ );
2610
+ return await consumerAuthControllerMfaVerify(
2611
+ { appSlug: this.appSlug, data: { mfa_token, credential } },
2612
+ { client: this.client }
2613
+ );
2614
+ },
2615
+ /**
2616
+ * Re-confirm the signed-in user's identity inside a live session
2617
+ * (before a sensitive action). Requires the scope to carry the
2618
+ * user's access token.
2619
+ *
2620
+ * `POST /me/mfa/step-up/webauthn/options` →
2621
+ * `navigator.credentials.get()` →
2622
+ * `POST /me/mfa/step-up { credential }`.
2623
+ *
2624
+ * Resolves with `{ amr, mfa_at }`. A passkey assertion is never
2625
+ * tried as a recovery code, so a failed step-up never burns one.
2626
+ */
2627
+ stepUp: async (opts = {}) => {
2628
+ const credentials = resolveCredentials(opts);
2629
+ const { public_key } = await consumerMfaControllerStepUpWebauthnOptions(
2630
+ { appSlug: this.appSlug },
2631
+ { client: this.client }
2632
+ );
2633
+ const credential = await getPasskeyCredential(
2634
+ public_key,
2635
+ { ...opts, credentials }
2636
+ );
2637
+ return await consumerMfaControllerStepUp(
2638
+ { appSlug: this.appSlug, data: { credential } },
2639
+ { client: this.client }
2640
+ );
2641
+ }
2642
+ };
2643
+ // ─────────────────────────────────────────────────────────────
2119
2644
  // OAuth M2M — client_credentials grant for service-to-service tokens
2120
2645
  // ─────────────────────────────────────────────────────────────
2121
2646
  oauth = {
@@ -2134,9 +2659,9 @@ function getAppControllerListMyAppsUrl() {
2134
2659
  async function appControllerListMyApps({ params } = {}, config = {}) {
2135
2660
  const { client: request = client, ...requestConfig } = config;
2136
2661
  const mappedParams = params ? {
2662
+ workspace_id: params.workspaceId,
2137
2663
  limit: params.limit,
2138
- cursor: params.cursor,
2139
- workspace_id: params.workspaceId
2664
+ cursor: params.cursor
2140
2665
  } : void 0;
2141
2666
  const res = await request({
2142
2667
  method: "GET",
@@ -2188,7 +2713,15 @@ async function statsControllerGetMyStats({ params } = {}, config = {}) {
2188
2713
 
2189
2714
  // src/index.ts
2190
2715
  var Auth = class {
2716
+ /** Admin client: explicit `auth`, else `PRODUCTCRAFT_API_KEY`. */
2191
2717
  client;
2718
+ /**
2719
+ * Consumer client: explicit `auth` only. Consumer routes act for an
2720
+ * end user, so a workspace key from the environment must never leak
2721
+ * onto them. Pass `auth` explicitly for PAK-only consumer routes
2722
+ * (`signup_requires_pak`, tenants, code mint, factors).
2723
+ */
2724
+ consumerClient;
2192
2725
  baseUrl;
2193
2726
  fetch;
2194
2727
  jwtConfig;
@@ -2196,6 +2729,11 @@ var Auth = class {
2196
2729
  this.baseUrl = config.baseUrl ?? core.PC_BASE_URL.auth;
2197
2730
  this.fetch = config.fetch;
2198
2731
  this.client = makeAuthHttpClient({
2732
+ baseUrl: this.baseUrl,
2733
+ auth: core.resolveAuth(config.auth),
2734
+ fetch: this.fetch
2735
+ });
2736
+ this.consumerClient = makeAuthHttpClient({
2199
2737
  baseUrl: this.baseUrl,
2200
2738
  auth: config.auth,
2201
2739
  fetch: this.fetch
@@ -2207,7 +2745,8 @@ var Auth = class {
2207
2745
  /**
2208
2746
  * Returns an `AppScope` bound to the given appId. All operations under
2209
2747
  * `/v1/apps/{appId}/...` are exposed as resource namespaces on the
2210
- * returned object.
2748
+ * returned object. Uses the explicit `auth`, else
2749
+ * `PRODUCTCRAFT_API_KEY` from the environment.
2211
2750
  */
2212
2751
  app(appId) {
2213
2752
  return new AppScope(appId, this.client);
@@ -2216,11 +2755,21 @@ var Auth = class {
2216
2755
  * Returns a `ConsumerScope` bound to the given appSlug. Exposes the
2217
2756
  * `/{appSlug}/v1/...` surface (sign-in, sign-up, me, verify, oauth)
2218
2757
  * and the JWT-verify helper for tokens issued by this app.
2758
+ *
2759
+ * `appSlug` defaults to `PRODUCTCRAFT_AUTH_APP` from the environment.
2760
+ * No credential is ever read from the environment for consumer
2761
+ * routes; only an explicit `auth` is sent.
2219
2762
  */
2220
2763
  consumer(appSlug) {
2764
+ const slug = appSlug ?? core.readEnv(core.PC_AUTH_APP_ENV);
2765
+ if (!slug) {
2766
+ throw new Error(
2767
+ `@productcraft/auth: consumer() needs an app slug. Pass one, or set ${core.PC_AUTH_APP_ENV}.`
2768
+ );
2769
+ }
2221
2770
  return new ConsumerScope(
2222
- appSlug,
2223
- { client: this.client, baseUrl: this.baseUrl, fetch: this.fetch },
2771
+ slug,
2772
+ { client: this.consumerClient, baseUrl: this.baseUrl, fetch: this.fetch },
2224
2773
  this.jwtConfig
2225
2774
  );
2226
2775
  }
@@ -2272,7 +2821,17 @@ exports.JwtExpiredError = JwtExpiredError;
2272
2821
  exports.JwtInvalidError = JwtInvalidError;
2273
2822
  exports.JwtIssuerMismatchError = JwtIssuerMismatchError;
2274
2823
  exports.JwtNotYetValidError = JwtNotYetValidError;
2824
+ exports.JwtRefreshTokenError = JwtRefreshTokenError;
2275
2825
  exports.JwtVerifyError = JwtVerifyError;
2826
+ exports.PasskeyError = PasskeyError;
2827
+ exports.createPasskeyCredential = createPasskeyCredential;
2828
+ exports.decodeOptions = decodeOptions;
2829
+ exports.encodeCredential = encodeCredential;
2830
+ exports.fromB64u = fromB64u;
2831
+ exports.getPasskeyCredential = getPasskeyCredential;
2832
+ exports.isPasskeySupported = isPasskeySupported;
2833
+ exports.isRefreshToken = isRefreshToken;
2834
+ exports.toB64u = toB64u;
2276
2835
  exports.verifyAuthToken = verifyAuthToken;
2277
2836
  //# sourceMappingURL=index.cjs.map
2278
2837
  //# sourceMappingURL=index.cjs.map