@feelflow/ffid-sdk 12.0.0 → 12.1.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/README.md +33 -0
- package/dist/agency/index.d.cts +19 -0
- package/dist/agency/index.d.ts +19 -0
- package/dist/{chunk-OL7KVPQ4.js → chunk-3MYUVPLD.js} +6 -3
- package/dist/{chunk-XRWDTYA7.cjs → chunk-62MINYOA.cjs} +6 -3
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.js +1 -1
- package/dist/{ffid-client-ByfZA9sJ.d.ts → ffid-client-5dVFeHt8.d.ts} +9 -0
- package/dist/{ffid-client-DW8_m1_M.d.cts → ffid-client-aUNBn5SW.d.cts} +9 -0
- package/dist/index.cjs +91 -91
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +197 -6
- package/dist/server/index.d.cts +146 -3
- package/dist/server/index.d.ts +146 -3
- package/dist/server/index.js +189 -8
- package/dist/server/test/index.d.cts +1 -1
- package/dist/server/test/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_API_BASE_URL } from '../chunk-JEVK2XUM.js';
|
|
2
2
|
export { DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES } from '../chunk-JEVK2XUM.js';
|
|
3
|
-
import {
|
|
3
|
+
import { SignJWT, importJWK, jwtVerify, createRemoteJWKSet, errors } from 'jose';
|
|
4
4
|
|
|
5
5
|
// src/auth/token-store.ts
|
|
6
6
|
var STORAGE_KEY = "ffid_tokens";
|
|
@@ -309,13 +309,13 @@ var HTTP_SERVER_ERROR_MIN = 500;
|
|
|
309
309
|
var HTTP_SERVER_ERROR_LIMIT = 600;
|
|
310
310
|
function createVerifyAccessToken(deps) {
|
|
311
311
|
const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes, cache, timeout } = deps;
|
|
312
|
-
let
|
|
312
|
+
let jwtVerify3 = null;
|
|
313
313
|
let cacheReadFailureEscalated = false;
|
|
314
314
|
let cacheWriteFailureEscalated = false;
|
|
315
315
|
let missingSubtleCryptoLogged = false;
|
|
316
316
|
function getJwtVerifier() {
|
|
317
|
-
if (!
|
|
318
|
-
|
|
317
|
+
if (!jwtVerify3) {
|
|
318
|
+
jwtVerify3 = createJwtVerifier({
|
|
319
319
|
baseUrl,
|
|
320
320
|
serviceCode,
|
|
321
321
|
logger,
|
|
@@ -325,7 +325,7 @@ function createVerifyAccessToken(deps) {
|
|
|
325
325
|
errorCodes
|
|
326
326
|
});
|
|
327
327
|
}
|
|
328
|
-
return
|
|
328
|
+
return jwtVerify3;
|
|
329
329
|
}
|
|
330
330
|
async function verifyAccessToken(accessToken, options) {
|
|
331
331
|
if (authMode !== "service-key") {
|
|
@@ -573,7 +573,8 @@ function createBillingMethods(deps) {
|
|
|
573
573
|
subscriptionId: params.subscriptionId,
|
|
574
574
|
successUrl: params.successUrl,
|
|
575
575
|
cancelUrl: params.cancelUrl,
|
|
576
|
-
...params.planId ? { planId: params.planId } : {}
|
|
576
|
+
...params.planId ? { planId: params.planId } : {},
|
|
577
|
+
...params.quantity !== void 0 ? { quantity: params.quantity } : {}
|
|
577
578
|
})
|
|
578
579
|
}
|
|
579
580
|
);
|
|
@@ -1386,7 +1387,7 @@ var FFID_ORGANIZATION_PROFILE_ERROR_CODES = {
|
|
|
1386
1387
|
};
|
|
1387
1388
|
|
|
1388
1389
|
// src/client/version-check.ts
|
|
1389
|
-
var SDK_VERSION = "12.
|
|
1390
|
+
var SDK_VERSION = "12.1.0";
|
|
1390
1391
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
1391
1392
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
1392
1393
|
function sdkHeaders() {
|
|
@@ -4864,4 +4865,184 @@ function clear(opts = {}) {
|
|
|
4864
4865
|
}
|
|
4865
4866
|
var sessionCookies = { serialize, read, clear };
|
|
4866
4867
|
|
|
4867
|
-
|
|
4868
|
+
// src/server/db-token/constants.ts
|
|
4869
|
+
var FFID_DB_TOKEN_ALGORITHM = "ES256";
|
|
4870
|
+
var FFID_DB_TOKEN_ROLE = "authenticated";
|
|
4871
|
+
var FFID_DB_TOKEN_AUDIENCE = "authenticated";
|
|
4872
|
+
var DEFAULT_DB_TOKEN_TTL_SECONDS = 300;
|
|
4873
|
+
var MAX_DB_TOKEN_TTL_SECONDS = 900;
|
|
4874
|
+
var FFID_DB_TOKEN_ORGANIZATION_CLAIM = "organization_id";
|
|
4875
|
+
var FFID_DB_TOKEN_RESERVED_CLAIMS = [
|
|
4876
|
+
"sub",
|
|
4877
|
+
"role",
|
|
4878
|
+
"aud",
|
|
4879
|
+
"iss",
|
|
4880
|
+
"iat",
|
|
4881
|
+
"exp",
|
|
4882
|
+
"nbf",
|
|
4883
|
+
"jti",
|
|
4884
|
+
FFID_DB_TOKEN_ORGANIZATION_CLAIM
|
|
4885
|
+
];
|
|
4886
|
+
var MILLISECONDS_PER_SECOND = 1e3;
|
|
4887
|
+
|
|
4888
|
+
// src/server/db-token/errors.ts
|
|
4889
|
+
var FFID_DB_TOKEN_ERROR_CODES = {
|
|
4890
|
+
/** Signing key is not a private P-256 JWK with a `kid`, or cannot be imported. */
|
|
4891
|
+
INVALID_SIGNING_KEY: "INVALID_SIGNING_KEY",
|
|
4892
|
+
/** `ttlSeconds` is not a positive integer within `MAX_DB_TOKEN_TTL_SECONDS`. */
|
|
4893
|
+
INVALID_TTL: "INVALID_TTL",
|
|
4894
|
+
/** `userId` is not a UUID. */
|
|
4895
|
+
INVALID_USER_ID: "INVALID_USER_ID",
|
|
4896
|
+
/** `organizationId` is not a UUID. */
|
|
4897
|
+
INVALID_ORGANIZATION_ID: "INVALID_ORGANIZATION_ID",
|
|
4898
|
+
/** A product claim uses a name the minter reserves (`sub`, `role`, `exp`, ...). */
|
|
4899
|
+
RESERVED_CLAIM: "RESERVED_CLAIM",
|
|
4900
|
+
/** `now` is not a valid `Date` (an Invalid Date would sign `iat` / `exp` as null). */
|
|
4901
|
+
INVALID_CLOCK: "INVALID_CLOCK"
|
|
4902
|
+
};
|
|
4903
|
+
var FFIDDbTokenError = class extends Error {
|
|
4904
|
+
code;
|
|
4905
|
+
constructor(code, message, options) {
|
|
4906
|
+
super(message, options);
|
|
4907
|
+
this.name = "FFIDDbTokenError";
|
|
4908
|
+
this.code = code;
|
|
4909
|
+
}
|
|
4910
|
+
};
|
|
4911
|
+
|
|
4912
|
+
// src/server/db-token/mint.ts
|
|
4913
|
+
var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4914
|
+
var RESERVED_CLAIM_SET = new Set(FFID_DB_TOKEN_RESERVED_CLAIMS);
|
|
4915
|
+
var isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
|
|
4916
|
+
var invalidKey = (reason, cause) => new FFIDDbTokenError(
|
|
4917
|
+
FFID_DB_TOKEN_ERROR_CODES.INVALID_SIGNING_KEY,
|
|
4918
|
+
`Invalid DB token signing key: ${reason}`,
|
|
4919
|
+
cause === void 0 ? void 0 : { cause }
|
|
4920
|
+
);
|
|
4921
|
+
var describeCause = (cause) => cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
|
|
4922
|
+
function parseSigningKey(signingKey, kidOverride) {
|
|
4923
|
+
let candidate = signingKey;
|
|
4924
|
+
if (typeof signingKey === "string") {
|
|
4925
|
+
if (signingKey.trim().length === 0) {
|
|
4926
|
+
throw invalidKey("signingKey is empty (is the environment variable set?)");
|
|
4927
|
+
}
|
|
4928
|
+
try {
|
|
4929
|
+
candidate = JSON.parse(signingKey);
|
|
4930
|
+
} catch {
|
|
4931
|
+
throw invalidKey("not valid JSON");
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) {
|
|
4935
|
+
throw invalidKey("expected a JWK object");
|
|
4936
|
+
}
|
|
4937
|
+
const jwk = candidate;
|
|
4938
|
+
if (jwk.kty !== "EC" || jwk.crv !== "P-256") {
|
|
4939
|
+
throw invalidKey(`expected kty=EC crv=P-256 for ${FFID_DB_TOKEN_ALGORITHM}`);
|
|
4940
|
+
}
|
|
4941
|
+
if (!isNonEmptyString(jwk.d) || !isNonEmptyString(jwk.x) || !isNonEmptyString(jwk.y)) {
|
|
4942
|
+
throw invalidKey("expected a private key (d, x, y)");
|
|
4943
|
+
}
|
|
4944
|
+
const kid = kidOverride ?? jwk.kid;
|
|
4945
|
+
if (!isNonEmptyString(kid)) {
|
|
4946
|
+
throw invalidKey("kid is required (Supabase matches the header kid against the imported key)");
|
|
4947
|
+
}
|
|
4948
|
+
return { jwk, kid };
|
|
4949
|
+
}
|
|
4950
|
+
function resolveTtlSeconds(ttlSeconds) {
|
|
4951
|
+
const ttl = ttlSeconds ?? DEFAULT_DB_TOKEN_TTL_SECONDS;
|
|
4952
|
+
if (!Number.isInteger(ttl) || ttl < 1 || ttl > MAX_DB_TOKEN_TTL_SECONDS) {
|
|
4953
|
+
throw new FFIDDbTokenError(
|
|
4954
|
+
FFID_DB_TOKEN_ERROR_CODES.INVALID_TTL,
|
|
4955
|
+
`ttlSeconds must be an integer between 1 and ${MAX_DB_TOKEN_TTL_SECONDS}`
|
|
4956
|
+
);
|
|
4957
|
+
}
|
|
4958
|
+
return ttl;
|
|
4959
|
+
}
|
|
4960
|
+
function requireUuid(value, code, label) {
|
|
4961
|
+
if (typeof value !== "string" || !UUID_PATTERN2.test(value)) {
|
|
4962
|
+
throw new FFIDDbTokenError(FFID_DB_TOKEN_ERROR_CODES[code], `${label} must be a UUID`);
|
|
4963
|
+
}
|
|
4964
|
+
return value.toLowerCase();
|
|
4965
|
+
}
|
|
4966
|
+
function resolveNow(now) {
|
|
4967
|
+
if (now === void 0) return /* @__PURE__ */ new Date();
|
|
4968
|
+
if (!(now instanceof Date) || Number.isNaN(now.getTime())) {
|
|
4969
|
+
throw new FFIDDbTokenError(FFID_DB_TOKEN_ERROR_CODES.INVALID_CLOCK, "now must be a valid Date");
|
|
4970
|
+
}
|
|
4971
|
+
return now;
|
|
4972
|
+
}
|
|
4973
|
+
function assertNoReservedClaims(claims) {
|
|
4974
|
+
if (!claims) return;
|
|
4975
|
+
for (const name of Object.keys(claims)) {
|
|
4976
|
+
if (RESERVED_CLAIM_SET.has(name)) {
|
|
4977
|
+
throw new FFIDDbTokenError(
|
|
4978
|
+
FFID_DB_TOKEN_ERROR_CODES.RESERVED_CLAIM,
|
|
4979
|
+
`Claim "${name}" is reserved by the DB token minter`
|
|
4980
|
+
);
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
async function importSigningKey(jwk, kid) {
|
|
4985
|
+
const ecParameters = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
|
|
4986
|
+
let privateKey;
|
|
4987
|
+
let publicKey;
|
|
4988
|
+
try {
|
|
4989
|
+
const imported = await Promise.all([
|
|
4990
|
+
importJWK({ ...ecParameters, d: jwk.d, kid, alg: FFID_DB_TOKEN_ALGORITHM }, FFID_DB_TOKEN_ALGORITHM),
|
|
4991
|
+
importJWK({ ...ecParameters, kid, alg: FFID_DB_TOKEN_ALGORITHM }, FFID_DB_TOKEN_ALGORITHM)
|
|
4992
|
+
]);
|
|
4993
|
+
if (imported[0] instanceof Uint8Array || imported[1] instanceof Uint8Array) {
|
|
4994
|
+
throw new Error("expected an asymmetric key");
|
|
4995
|
+
}
|
|
4996
|
+
;
|
|
4997
|
+
[privateKey, publicKey] = imported;
|
|
4998
|
+
} catch (cause) {
|
|
4999
|
+
throw invalidKey(`key material could not be imported (${describeCause(cause)})`, cause);
|
|
5000
|
+
}
|
|
5001
|
+
try {
|
|
5002
|
+
const probe = await new SignJWT({}).setProtectedHeader({ alg: FFID_DB_TOKEN_ALGORITHM, kid }).sign(privateKey);
|
|
5003
|
+
await jwtVerify(probe, publicKey);
|
|
5004
|
+
} catch (cause) {
|
|
5005
|
+
throw invalidKey("private part (d) does not match the public part (x, y)", cause);
|
|
5006
|
+
}
|
|
5007
|
+
return privateKey;
|
|
5008
|
+
}
|
|
5009
|
+
function createDbTokenMinter(config) {
|
|
5010
|
+
const { jwk, kid } = parseSigningKey(config.signingKey, config.kid);
|
|
5011
|
+
const ttlSeconds = resolveTtlSeconds(config.ttlSeconds);
|
|
5012
|
+
const issuer = config.issuer;
|
|
5013
|
+
let keyPromise;
|
|
5014
|
+
const loadKey = () => {
|
|
5015
|
+
keyPromise ??= importSigningKey(jwk, kid).catch((error) => {
|
|
5016
|
+
keyPromise = void 0;
|
|
5017
|
+
throw error;
|
|
5018
|
+
});
|
|
5019
|
+
return keyPromise;
|
|
5020
|
+
};
|
|
5021
|
+
return {
|
|
5022
|
+
kid,
|
|
5023
|
+
ttlSeconds,
|
|
5024
|
+
async mint(input) {
|
|
5025
|
+
const sub = requireUuid(input.userId, "INVALID_USER_ID", "userId");
|
|
5026
|
+
const organizationId = input.organizationId === void 0 || input.organizationId === null ? void 0 : requireUuid(input.organizationId, "INVALID_ORGANIZATION_ID", "organizationId");
|
|
5027
|
+
assertNoReservedClaims(input.claims);
|
|
5028
|
+
const now = resolveNow(input.now);
|
|
5029
|
+
const iat = Math.floor(now.getTime() / MILLISECONDS_PER_SECOND);
|
|
5030
|
+
const exp = iat + ttlSeconds;
|
|
5031
|
+
const claims = {
|
|
5032
|
+
...input.claims,
|
|
5033
|
+
sub,
|
|
5034
|
+
role: FFID_DB_TOKEN_ROLE,
|
|
5035
|
+
aud: FFID_DB_TOKEN_AUDIENCE,
|
|
5036
|
+
iat,
|
|
5037
|
+
exp,
|
|
5038
|
+
...issuer === void 0 ? {} : { iss: issuer },
|
|
5039
|
+
...organizationId === void 0 ? {} : { [FFID_DB_TOKEN_ORGANIZATION_CLAIM]: organizationId }
|
|
5040
|
+
};
|
|
5041
|
+
const key = await loadKey();
|
|
5042
|
+
const token = await new SignJWT(claims).setProtectedHeader({ alg: FFID_DB_TOKEN_ALGORITHM, kid, typ: "JWT" }).sign(key);
|
|
5043
|
+
return { token, expiresAt: new Date(exp * MILLISECONDS_PER_SECOND), claims };
|
|
5044
|
+
}
|
|
5045
|
+
};
|
|
5046
|
+
}
|
|
5047
|
+
|
|
5048
|
+
export { ALL_DENIED_EXCEPT_NECESSARY, CATALOG_HASH_PATTERN, CONSENT_COOKIE_NAME, COOKIE_VERSION, DEFAULT_DB_TOKEN_TTL_SECONDS, FFIDCatalogHashError, FFIDDbTokenError, FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, FFID_CATALOG_ENVIRONMENTS, FFID_CATALOG_PUBLICATION_STATUSES, FFID_COMPANY_SIZE_CODES, FFID_DB_TOKEN_ALGORITHM, FFID_DB_TOKEN_AUDIENCE, FFID_DB_TOKEN_ERROR_CODES, FFID_DB_TOKEN_ORGANIZATION_CLAIM, FFID_DB_TOKEN_RESERVED_CLAIMS, FFID_DB_TOKEN_ROLE, FFID_ERROR_CODES, FFID_INDUSTRY_CODES, FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, FFID_OAUTH_DENIAL_REASONS, FFID_ORGANIZATION_PROFILE_ERROR_CODES, FFID_SESSION_COOKIE_NAME, MAX_DB_TOKEN_TTL_SECONDS, canonicalizeCatalogValue, computeFFIDCheckoutCopyHash, computeLegalDisclosureHash, computePaymentConfigurationHash, computePraxisCopyHash, computeScopeHash, computeTaxConfigurationHash, createDbTokenMinter, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createServerAuthClient, createTokenStore, createVerifyAccessToken, decodeConsentCookie, encodeConsentCookie, generateServerState, getFFIDAuthorizationError, getFFIDConsentFromCookieHeader, getFFIDConsentFromCookies, hashCanonicalJson, isFFIDOAuthDenialReason, sessionCookies, sha256Hex, validatePublishedCatalog };
|