@feelflow/ffid-sdk 7.1.0 → 8.0.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 +115 -5
- package/dist/{chunk-CI4N7KJK.cjs → chunk-RNCK5E3Y.cjs} +211 -14
- package/dist/{chunk-Y4TUICQZ.js → chunk-YKBAFPX5.js} +202 -15
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.d.cts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/{ffid-client-QNEFA3mv.d.ts → ffid-client-5GAPcRIG.d.ts} +105 -23
- package/dist/{ffid-client-BFgoVPOZ.d.cts → ffid-client-BaEmw7jP.d.cts} +105 -23
- package/dist/{index-BMKhKhzF.d.cts → index-BG7g99pK.d.cts} +1 -0
- package/dist/{index-BMKhKhzF.d.ts → index-BG7g99pK.d.ts} +1 -0
- package/dist/index.cjs +104 -64
- package/dist/index.d.cts +378 -26
- package/dist/index.d.ts +378 -26
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +135 -12
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +135 -12
- package/dist/server/test/index.d.cts +1 -1
- package/dist/server/test/index.d.ts +1 -1
- package/dist/webhooks/index.d.cts +55 -6
- package/dist/webhooks/index.d.ts +55 -6
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -155,7 +155,7 @@ function normalizeUserinfo(raw) {
|
|
|
155
155
|
emailVerified: raw.email_verified,
|
|
156
156
|
name: raw.name,
|
|
157
157
|
picture: raw.picture,
|
|
158
|
-
companyName:
|
|
158
|
+
companyName: null,
|
|
159
159
|
department: raw.department ?? null,
|
|
160
160
|
position: raw.position ?? null,
|
|
161
161
|
phoneNumber: raw.phone_number ?? null,
|
|
@@ -223,13 +223,18 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
|
|
|
223
223
|
];
|
|
224
224
|
}
|
|
225
225
|
var JWKS_ENDPOINT = "/.well-known/jwks.json";
|
|
226
|
-
var
|
|
226
|
+
var LEGACY_JWT_ISSUER = "https://id.feelflow.net";
|
|
227
227
|
var JWT_ALGORITHM = "ES256";
|
|
228
228
|
var JWT_CLOCK_TOLERANCE_SECONDS = 30;
|
|
229
|
+
function resolveAcceptedIssuers(baseUrl) {
|
|
230
|
+
const deployedOrigin = new URL(baseUrl).origin;
|
|
231
|
+
return deployedOrigin === LEGACY_JWT_ISSUER ? [LEGACY_JWT_ISSUER] : [deployedOrigin, LEGACY_JWT_ISSUER];
|
|
232
|
+
}
|
|
229
233
|
function createJwtVerifier(deps) {
|
|
230
234
|
const { baseUrl, serviceCode, logger, createError, errorCodes } = deps;
|
|
231
235
|
const jwksUrl = new URL(JWKS_ENDPOINT, baseUrl);
|
|
232
236
|
const jwks = jose.createRemoteJWKSet(jwksUrl);
|
|
237
|
+
const acceptedIssuers = resolveAcceptedIssuers(baseUrl);
|
|
233
238
|
async function verifyJwt(accessToken) {
|
|
234
239
|
if (!accessToken || !accessToken.trim()) {
|
|
235
240
|
return {
|
|
@@ -242,7 +247,7 @@ function createJwtVerifier(deps) {
|
|
|
242
247
|
try {
|
|
243
248
|
const { payload } = await jose.jwtVerify(accessToken, jwks, {
|
|
244
249
|
algorithms: [JWT_ALGORITHM],
|
|
245
|
-
issuer:
|
|
250
|
+
issuer: acceptedIssuers,
|
|
246
251
|
audience: serviceCode,
|
|
247
252
|
clockTolerance: JWT_CLOCK_TOLERANCE_SECONDS
|
|
248
253
|
});
|
|
@@ -459,7 +464,7 @@ function createVerifyAccessToken(deps) {
|
|
|
459
464
|
email_verified: introspectResponse.email_verified,
|
|
460
465
|
name: introspectResponse.name ?? null,
|
|
461
466
|
picture: introspectResponse.picture ?? null,
|
|
462
|
-
company_name:
|
|
467
|
+
company_name: null,
|
|
463
468
|
department: introspectResponse.department ?? null,
|
|
464
469
|
position: introspectResponse.position ?? null,
|
|
465
470
|
phone_number: introspectResponse.phone_number ?? null,
|
|
@@ -710,6 +715,24 @@ function validateSeatChangePreview(preview) {
|
|
|
710
715
|
`SDK: server returned malformed SeatChangePreview \u2014 estimateReason must be one of 'no_stripe_data' | 'custom_pricing' | 'stripe_error' (got ${JSON.stringify(p.estimateReason)})`
|
|
711
716
|
);
|
|
712
717
|
}
|
|
718
|
+
if (p.willApplyAtPeriodEnd !== void 0 && typeof p.willApplyAtPeriodEnd !== "boolean") {
|
|
719
|
+
throw new FFIDSDKError(
|
|
720
|
+
MALFORMED_SEAT_PREVIEW_CODE,
|
|
721
|
+
`SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd must be boolean when present (got ${typeof p.willApplyAtPeriodEnd})`
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
if (p.effectiveDate !== void 0 && p.effectiveDate !== null && typeof p.effectiveDate !== "string") {
|
|
725
|
+
throw new FFIDSDKError(
|
|
726
|
+
MALFORMED_SEAT_PREVIEW_CODE,
|
|
727
|
+
`SDK: server returned malformed SeatChangePreview \u2014 effectiveDate must be string or null when present (got ${typeof p.effectiveDate})`
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
if (p.willApplyAtPeriodEnd === true && p.proratedAmount !== 0) {
|
|
731
|
+
throw new FFIDSDKError(
|
|
732
|
+
MALFORMED_SEAT_PREVIEW_CODE,
|
|
733
|
+
`SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd=true requires proratedAmount=0 (got ${JSON.stringify(p.proratedAmount)})`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
713
736
|
if (!Array.isArray(p.lineItems)) {
|
|
714
737
|
throw new FFIDSDKError(
|
|
715
738
|
MALFORMED_SEAT_PREVIEW_CODE,
|
|
@@ -1350,7 +1373,7 @@ var FFID_ORGANIZATION_PROFILE_ERROR_CODES = {
|
|
|
1350
1373
|
};
|
|
1351
1374
|
|
|
1352
1375
|
// src/client/version-check.ts
|
|
1353
|
-
var SDK_VERSION = "
|
|
1376
|
+
var SDK_VERSION = "8.0.0";
|
|
1354
1377
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
1355
1378
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
1356
1379
|
function sdkHeaders() {
|
|
@@ -2509,6 +2532,86 @@ function cleanupStateStorage(logger) {
|
|
|
2509
2532
|
}
|
|
2510
2533
|
}
|
|
2511
2534
|
|
|
2535
|
+
// src/client/agency-invite-token.ts
|
|
2536
|
+
var AGENCY_INVITE_TOKEN_PARAM = "invite";
|
|
2537
|
+
var AGENCY_INVITE_COOKIE_NAME = "ffid_invite";
|
|
2538
|
+
var AGENCY_INVITE_TOKEN_REDACTION = "[REDACTED]";
|
|
2539
|
+
function hasDocument() {
|
|
2540
|
+
return typeof document !== "undefined";
|
|
2541
|
+
}
|
|
2542
|
+
function toQueryString(source) {
|
|
2543
|
+
const queryStart = source.indexOf("?");
|
|
2544
|
+
if (queryStart < 0) return "";
|
|
2545
|
+
const afterQuery = source.slice(queryStart);
|
|
2546
|
+
const fragmentStart = afterQuery.indexOf("#");
|
|
2547
|
+
return fragmentStart < 0 ? afterQuery : afterQuery.slice(0, fragmentStart);
|
|
2548
|
+
}
|
|
2549
|
+
function readTokenFromUrl(options) {
|
|
2550
|
+
const source = options.url ?? (typeof window !== "undefined" ? window.location.search : void 0);
|
|
2551
|
+
if (source === void 0) return void 0;
|
|
2552
|
+
const params = new URLSearchParams(toQueryString(source));
|
|
2553
|
+
return params.get(AGENCY_INVITE_TOKEN_PARAM)?.trim() || void 0;
|
|
2554
|
+
}
|
|
2555
|
+
function readAgencyInviteCookie() {
|
|
2556
|
+
if (!hasDocument()) return void 0;
|
|
2557
|
+
const prefix = `${AGENCY_INVITE_COOKIE_NAME}=`;
|
|
2558
|
+
const raw = document.cookie.split(";").map((entry) => entry.trim()).find((entry) => entry.startsWith(prefix));
|
|
2559
|
+
if (!raw) return void 0;
|
|
2560
|
+
try {
|
|
2561
|
+
return decodeURIComponent(raw.slice(prefix.length)).trim() || void 0;
|
|
2562
|
+
} catch {
|
|
2563
|
+
return void 0;
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
function readAgencyInviteToken(options = {}) {
|
|
2567
|
+
return readTokenFromUrl(options) ?? readAgencyInviteCookie();
|
|
2568
|
+
}
|
|
2569
|
+
var REDACT_BOUNDARIES = [
|
|
2570
|
+
"%2526",
|
|
2571
|
+
"%2523",
|
|
2572
|
+
"%253B",
|
|
2573
|
+
"%26",
|
|
2574
|
+
"%23",
|
|
2575
|
+
"%3B",
|
|
2576
|
+
"&",
|
|
2577
|
+
"#",
|
|
2578
|
+
";"
|
|
2579
|
+
];
|
|
2580
|
+
var REDACT_LEADS = ["%253F", "%3F", "?", ...REDACT_BOUNDARIES];
|
|
2581
|
+
var REDACT_SEPARATORS = ["%253D", "%3D", "="];
|
|
2582
|
+
function escapeForRegExp(literal) {
|
|
2583
|
+
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2584
|
+
}
|
|
2585
|
+
var REDACT_PATTERN = new RegExp(
|
|
2586
|
+
[
|
|
2587
|
+
// 先頭区切り。`\b` が使えないのは、入れ子形 (`%3Finvite%3D`) では `F` と `i` が
|
|
2588
|
+
// どちらも単語文字なので単語境界が存在しないため。
|
|
2589
|
+
`(^|${REDACT_LEADS.map(escapeForRegExp).join("|")})`,
|
|
2590
|
+
// パラメータ名と区切りはキャプチャして再出力する。`i` フラグがあるので、
|
|
2591
|
+
// 入力の `INVITE=` / `%3d` などの元の表記を保つ必要がある。
|
|
2592
|
+
`(${AGENCY_INVITE_TOKEN_PARAM})`,
|
|
2593
|
+
`(${REDACT_SEPARATORS.map(escapeForRegExp).join("|")})`,
|
|
2594
|
+
// 値。境界の直前まで貪欲に食う。`\s` は終端にしない — 生の空白や改行を含む
|
|
2595
|
+
// トークンでマスクが途中で切れ、後半が漏れる。
|
|
2596
|
+
`(?:(?!${REDACT_BOUNDARIES.map(escapeForRegExp).join("|")})[^&#;])*`
|
|
2597
|
+
].join(""),
|
|
2598
|
+
"gi"
|
|
2599
|
+
);
|
|
2600
|
+
function redactAgencyInviteToken(url) {
|
|
2601
|
+
return url.replace(
|
|
2602
|
+
REDACT_PATTERN,
|
|
2603
|
+
// 文字列 replacement(`'$1$2$3[REDACTED]'`)にしないのは、
|
|
2604
|
+
// `AGENCY_INVITE_TOKEN_REDACTION` に `$` が入った瞬間に置換パターンとして
|
|
2605
|
+
// 解釈される罠を作らないため。
|
|
2606
|
+
(_match, lead, name, separator) => `${lead}${name}${separator}${AGENCY_INVITE_TOKEN_REDACTION}`
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2609
|
+
function appendAgencyInviteToken(signupUrl, token) {
|
|
2610
|
+
if (token === void 0) return signupUrl;
|
|
2611
|
+
const separator = signupUrl.includes("?") ? "&" : "?";
|
|
2612
|
+
return `${signupUrl}${separator}${AGENCY_INVITE_TOKEN_PARAM}=${encodeURIComponent(token)}`;
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2512
2615
|
// src/client/redirect.ts
|
|
2513
2616
|
var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
2514
2617
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
@@ -2521,6 +2624,7 @@ var REDIRECT_LOOP_WINDOW_MS = 6e4;
|
|
|
2521
2624
|
var REDIRECT_LOOP_THRESHOLD = 3;
|
|
2522
2625
|
var storageReadFailureLogged = false;
|
|
2523
2626
|
var storageWriteFailureLogged = false;
|
|
2627
|
+
var ssrInviteTokenUnavailableLogged = false;
|
|
2524
2628
|
function isLoopDetectionDegraded() {
|
|
2525
2629
|
return storageReadFailureLogged || storageWriteFailureLogged;
|
|
2526
2630
|
}
|
|
@@ -2699,12 +2803,16 @@ function createRedirectMethods(deps) {
|
|
|
2699
2803
|
}
|
|
2700
2804
|
if (options?.screenHint === SCREEN_HINT_SIGNUP) {
|
|
2701
2805
|
params.set(SCREEN_HINT_PARAM, SCREEN_HINT_SIGNUP);
|
|
2806
|
+
const inviteToken = readAgencyInviteToken();
|
|
2807
|
+
if (inviteToken !== void 0) {
|
|
2808
|
+
params.set(AGENCY_INVITE_TOKEN_PARAM, inviteToken);
|
|
2809
|
+
}
|
|
2702
2810
|
}
|
|
2703
2811
|
if (options?.prompt) {
|
|
2704
2812
|
params.set("prompt", options.prompt);
|
|
2705
2813
|
}
|
|
2706
2814
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
2707
|
-
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
2815
|
+
logger.debug("Redirecting to authorize:", redactAgencyInviteToken(authorizeUrl));
|
|
2708
2816
|
recordRedirectAttempt(authorizeKey, now, logger);
|
|
2709
2817
|
try {
|
|
2710
2818
|
window.location.href = authorizeUrl;
|
|
@@ -2723,7 +2831,7 @@ function createRedirectMethods(deps) {
|
|
|
2723
2831
|
return redirectToAuthorize(options);
|
|
2724
2832
|
}
|
|
2725
2833
|
const targetUrl = options?.screenHint === SCREEN_HINT_SIGNUP ? getSignupUrl() : getLoginUrl(void 0, options?.prompt ? { prompt: options.prompt } : void 0);
|
|
2726
|
-
logger.debug("Redirecting to auth page:", targetUrl);
|
|
2834
|
+
logger.debug("Redirecting to auth page:", redactAgencyInviteToken(targetUrl));
|
|
2727
2835
|
window.location.href = targetUrl;
|
|
2728
2836
|
return { success: true };
|
|
2729
2837
|
}
|
|
@@ -2740,7 +2848,7 @@ function createRedirectMethods(deps) {
|
|
|
2740
2848
|
const promptSuffix = options?.prompt ? `&prompt=${encodeURIComponent(options.prompt)}` : "";
|
|
2741
2849
|
return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}${promptSuffix}`;
|
|
2742
2850
|
}
|
|
2743
|
-
function getSignupUrl(redirectUrl) {
|
|
2851
|
+
function getSignupUrl(redirectUrl, options) {
|
|
2744
2852
|
let redirect;
|
|
2745
2853
|
if (redirectUrl != null) {
|
|
2746
2854
|
redirect = redirectUrl;
|
|
@@ -2750,7 +2858,22 @@ function createRedirectMethods(deps) {
|
|
|
2750
2858
|
logger.warn("getSignupUrl: SSR \u74B0\u5883\u3067 redirectUrl \u304C\u672A\u6307\u5B9A\u306E\u305F\u3081\u7A7A\u6587\u5B57\u306B\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3057\u307E\u3059");
|
|
2751
2859
|
redirect = "";
|
|
2752
2860
|
}
|
|
2753
|
-
|
|
2861
|
+
let inviteToken = options?.inviteToken;
|
|
2862
|
+
if (inviteToken === void 0) {
|
|
2863
|
+
inviteToken = readAgencyInviteToken();
|
|
2864
|
+
if (inviteToken === void 0 && typeof window === "undefined") {
|
|
2865
|
+
if (!ssrInviteTokenUnavailableLogged) {
|
|
2866
|
+
ssrInviteTokenUnavailableLogged = true;
|
|
2867
|
+
logger.warn(
|
|
2868
|
+
"[FFID SDK] getSignupUrl: SSR \u3067\u306F\u62DB\u5F85\u30C8\u30FC\u30AF\u30F3\uFF08invite\uFF09\u3092\u8AAD\u3081\u307E\u305B\u3093\u3002\u4EE3\u7406\u5E97\u306E\u62DB\u5F85\u30EA\u30F3\u30AF\u3092\u4F7F\u3046\u5834\u5408\u306F options.inviteToken \u306B\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F7F\u3063\u3066\u3044\u306A\u3044\u5834\u5408\u306F\u3053\u306E\u8B66\u544A\u3092\u7121\u8996\u3057\u3066\u3088\u3044\uFF09"
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
return appendAgencyInviteToken(
|
|
2874
|
+
`${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`,
|
|
2875
|
+
inviteToken
|
|
2876
|
+
);
|
|
2754
2877
|
}
|
|
2755
2878
|
function getLogoutUrl(postLogoutRedirectUri) {
|
|
2756
2879
|
const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
|
|
@@ -2766,7 +2889,7 @@ function createRedirectMethods(deps) {
|
|
|
2766
2889
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
2767
2890
|
}
|
|
2768
2891
|
const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
|
|
2769
|
-
logger.debug("Redirecting to logout:", logoutUrl);
|
|
2892
|
+
logger.debug("Redirecting to logout:", redactAgencyInviteToken(logoutUrl));
|
|
2770
2893
|
window.location.href = logoutUrl;
|
|
2771
2894
|
return { success: true };
|
|
2772
2895
|
}
|
|
@@ -4320,9 +4443,9 @@ function createServerAuthClient(config) {
|
|
|
4320
4443
|
var FFID_SESSION_COOKIE_NAME = "ffid_session";
|
|
4321
4444
|
var DEFAULT_PATH = "/";
|
|
4322
4445
|
var DEFAULT_SAME_SITE = "lax";
|
|
4323
|
-
var
|
|
4446
|
+
var HOURS_PER_DAY3 = 24;
|
|
4324
4447
|
var DEFAULT_MAX_AGE_DAYS = 30;
|
|
4325
|
-
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 *
|
|
4448
|
+
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * HOURS_PER_DAY3 * DEFAULT_MAX_AGE_DAYS;
|
|
4326
4449
|
var BASE64_GROUP = 4;
|
|
4327
4450
|
function base64UrlEncode2(input) {
|
|
4328
4451
|
const bytes = new TextEncoder().encode(input);
|
package/dist/server/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-
|
|
2
|
-
export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-
|
|
1
|
+
import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-BaEmw7jP.cjs';
|
|
2
|
+
export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-BaEmw7jP.cjs';
|
|
3
3
|
export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-Ce2eXQtB.cjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import '../types-Cjb9J0rL.cjs';
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-
|
|
2
|
-
export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-
|
|
1
|
+
import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-5GAPcRIG.js';
|
|
2
|
+
export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAllowedLogoMimeType, n as FFIDAssignableMemberRole, o as FFIDCacheConfig, p as FFIDCatalogPublication, q as FFIDCatalogPublicationStatus, r as FFIDClient, s as FFIDCompanySizeCode, t as FFIDConfig, u as FFIDIndustryCode, v as FFIDListMembersResponse, w as FFIDListingConsentInput, x as FFIDLogoWallEntry, y as FFIDLogoWallResponse, z as FFIDMemberRole, A as FFIDOrganization, B as FFIDOrganizationLogoUploadResponse, C as FFIDOrganizationMember, D as FFIDOrganizationProfile, E as FFIDOrganizationProfileErrorCode, G as FFIDOrganizationProfileResponse, H as FFIDOtpSendResponse, I as FFIDOtpVerifyResponse, J as FFIDPasswordResetConfirmResponse, K as FFIDPasswordResetResponse, L as FFIDPasswordResetVerifyResponse, M as FFIDProfileCallOptions, N as FFIDProvisionMemberPlan, O as FFIDProvisionMemberPlanStatus, P as FFIDProvisionMemberResult, Q as FFIDProvisionMemberStatus, R as FFIDProvisionOrganizationDryRun, S as FFIDProvisionOrganizationMemberInput, T as FFIDProvisionOrganizationOutcome, U as FFIDProvisionOrganizationParams, V as FFIDProvisionOrganizationResponse, W as FFIDProvisionUserDryRun, X as FFIDProvisionUserOutcome, Y as FFIDProvisionUserParams, Z as FFIDProvisionUserProfileInput, _ as FFIDProvisionUserResponse, $ as FFIDProvisionedOrganization, a0 as FFIDProvisionedUser, a1 as FFIDPublishedPlan, a2 as FFIDRemoveMemberResponse, a3 as FFIDResetSessionResponse, a4 as FFIDSubscription, a5 as FFIDUpdateMemberRoleResponse, a6 as FFIDUpdateUserProfileRequest, a7 as FFIDUploadOrganizationLogoParams, a8 as FFIDUpsertOrganizationProfileParams, a9 as FFIDUpsertOrganizationProfileRequest, aa as FFIDUser, ab as FFIDUserProfile, ac as FFID_ALLOWED_ORGANIZATION_LOGO_MIME_TYPES, ad as FFID_CATALOG_ENVIRONMENTS, ae as FFID_CATALOG_PUBLICATION_STATUSES, af as FFID_COMPANY_SIZE_CODES, ag as FFID_INDUSTRY_CODES, ah as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_BYTES, ai as FFID_MAX_ORGANIZATION_LOGO_FILE_SIZE_MB, aj as FFID_ORGANIZATION_PROFILE_ERROR_CODES, ak as TokenData, al as TokenStore, am as createFFIDClient, an as createTokenStore } from '../ffid-client-5GAPcRIG.js';
|
|
3
3
|
export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-Ce2eXQtB.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import '../types-Cjb9J0rL.js';
|
package/dist/server/index.js
CHANGED
|
@@ -154,7 +154,7 @@ function normalizeUserinfo(raw) {
|
|
|
154
154
|
emailVerified: raw.email_verified,
|
|
155
155
|
name: raw.name,
|
|
156
156
|
picture: raw.picture,
|
|
157
|
-
companyName:
|
|
157
|
+
companyName: null,
|
|
158
158
|
department: raw.department ?? null,
|
|
159
159
|
position: raw.position ?? null,
|
|
160
160
|
phoneNumber: raw.phone_number ?? null,
|
|
@@ -222,13 +222,18 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
|
|
|
222
222
|
];
|
|
223
223
|
}
|
|
224
224
|
var JWKS_ENDPOINT = "/.well-known/jwks.json";
|
|
225
|
-
var
|
|
225
|
+
var LEGACY_JWT_ISSUER = "https://id.feelflow.net";
|
|
226
226
|
var JWT_ALGORITHM = "ES256";
|
|
227
227
|
var JWT_CLOCK_TOLERANCE_SECONDS = 30;
|
|
228
|
+
function resolveAcceptedIssuers(baseUrl) {
|
|
229
|
+
const deployedOrigin = new URL(baseUrl).origin;
|
|
230
|
+
return deployedOrigin === LEGACY_JWT_ISSUER ? [LEGACY_JWT_ISSUER] : [deployedOrigin, LEGACY_JWT_ISSUER];
|
|
231
|
+
}
|
|
228
232
|
function createJwtVerifier(deps) {
|
|
229
233
|
const { baseUrl, serviceCode, logger, createError, errorCodes } = deps;
|
|
230
234
|
const jwksUrl = new URL(JWKS_ENDPOINT, baseUrl);
|
|
231
235
|
const jwks = createRemoteJWKSet(jwksUrl);
|
|
236
|
+
const acceptedIssuers = resolveAcceptedIssuers(baseUrl);
|
|
232
237
|
async function verifyJwt(accessToken) {
|
|
233
238
|
if (!accessToken || !accessToken.trim()) {
|
|
234
239
|
return {
|
|
@@ -241,7 +246,7 @@ function createJwtVerifier(deps) {
|
|
|
241
246
|
try {
|
|
242
247
|
const { payload } = await jwtVerify(accessToken, jwks, {
|
|
243
248
|
algorithms: [JWT_ALGORITHM],
|
|
244
|
-
issuer:
|
|
249
|
+
issuer: acceptedIssuers,
|
|
245
250
|
audience: serviceCode,
|
|
246
251
|
clockTolerance: JWT_CLOCK_TOLERANCE_SECONDS
|
|
247
252
|
});
|
|
@@ -458,7 +463,7 @@ function createVerifyAccessToken(deps) {
|
|
|
458
463
|
email_verified: introspectResponse.email_verified,
|
|
459
464
|
name: introspectResponse.name ?? null,
|
|
460
465
|
picture: introspectResponse.picture ?? null,
|
|
461
|
-
company_name:
|
|
466
|
+
company_name: null,
|
|
462
467
|
department: introspectResponse.department ?? null,
|
|
463
468
|
position: introspectResponse.position ?? null,
|
|
464
469
|
phone_number: introspectResponse.phone_number ?? null,
|
|
@@ -709,6 +714,24 @@ function validateSeatChangePreview(preview) {
|
|
|
709
714
|
`SDK: server returned malformed SeatChangePreview \u2014 estimateReason must be one of 'no_stripe_data' | 'custom_pricing' | 'stripe_error' (got ${JSON.stringify(p.estimateReason)})`
|
|
710
715
|
);
|
|
711
716
|
}
|
|
717
|
+
if (p.willApplyAtPeriodEnd !== void 0 && typeof p.willApplyAtPeriodEnd !== "boolean") {
|
|
718
|
+
throw new FFIDSDKError(
|
|
719
|
+
MALFORMED_SEAT_PREVIEW_CODE,
|
|
720
|
+
`SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd must be boolean when present (got ${typeof p.willApplyAtPeriodEnd})`
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
if (p.effectiveDate !== void 0 && p.effectiveDate !== null && typeof p.effectiveDate !== "string") {
|
|
724
|
+
throw new FFIDSDKError(
|
|
725
|
+
MALFORMED_SEAT_PREVIEW_CODE,
|
|
726
|
+
`SDK: server returned malformed SeatChangePreview \u2014 effectiveDate must be string or null when present (got ${typeof p.effectiveDate})`
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
if (p.willApplyAtPeriodEnd === true && p.proratedAmount !== 0) {
|
|
730
|
+
throw new FFIDSDKError(
|
|
731
|
+
MALFORMED_SEAT_PREVIEW_CODE,
|
|
732
|
+
`SDK: server returned malformed SeatChangePreview \u2014 willApplyAtPeriodEnd=true requires proratedAmount=0 (got ${JSON.stringify(p.proratedAmount)})`
|
|
733
|
+
);
|
|
734
|
+
}
|
|
712
735
|
if (!Array.isArray(p.lineItems)) {
|
|
713
736
|
throw new FFIDSDKError(
|
|
714
737
|
MALFORMED_SEAT_PREVIEW_CODE,
|
|
@@ -1349,7 +1372,7 @@ var FFID_ORGANIZATION_PROFILE_ERROR_CODES = {
|
|
|
1349
1372
|
};
|
|
1350
1373
|
|
|
1351
1374
|
// src/client/version-check.ts
|
|
1352
|
-
var SDK_VERSION = "
|
|
1375
|
+
var SDK_VERSION = "8.0.0";
|
|
1353
1376
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
1354
1377
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
1355
1378
|
function sdkHeaders() {
|
|
@@ -2508,6 +2531,86 @@ function cleanupStateStorage(logger) {
|
|
|
2508
2531
|
}
|
|
2509
2532
|
}
|
|
2510
2533
|
|
|
2534
|
+
// src/client/agency-invite-token.ts
|
|
2535
|
+
var AGENCY_INVITE_TOKEN_PARAM = "invite";
|
|
2536
|
+
var AGENCY_INVITE_COOKIE_NAME = "ffid_invite";
|
|
2537
|
+
var AGENCY_INVITE_TOKEN_REDACTION = "[REDACTED]";
|
|
2538
|
+
function hasDocument() {
|
|
2539
|
+
return typeof document !== "undefined";
|
|
2540
|
+
}
|
|
2541
|
+
function toQueryString(source) {
|
|
2542
|
+
const queryStart = source.indexOf("?");
|
|
2543
|
+
if (queryStart < 0) return "";
|
|
2544
|
+
const afterQuery = source.slice(queryStart);
|
|
2545
|
+
const fragmentStart = afterQuery.indexOf("#");
|
|
2546
|
+
return fragmentStart < 0 ? afterQuery : afterQuery.slice(0, fragmentStart);
|
|
2547
|
+
}
|
|
2548
|
+
function readTokenFromUrl(options) {
|
|
2549
|
+
const source = options.url ?? (typeof window !== "undefined" ? window.location.search : void 0);
|
|
2550
|
+
if (source === void 0) return void 0;
|
|
2551
|
+
const params = new URLSearchParams(toQueryString(source));
|
|
2552
|
+
return params.get(AGENCY_INVITE_TOKEN_PARAM)?.trim() || void 0;
|
|
2553
|
+
}
|
|
2554
|
+
function readAgencyInviteCookie() {
|
|
2555
|
+
if (!hasDocument()) return void 0;
|
|
2556
|
+
const prefix = `${AGENCY_INVITE_COOKIE_NAME}=`;
|
|
2557
|
+
const raw = document.cookie.split(";").map((entry) => entry.trim()).find((entry) => entry.startsWith(prefix));
|
|
2558
|
+
if (!raw) return void 0;
|
|
2559
|
+
try {
|
|
2560
|
+
return decodeURIComponent(raw.slice(prefix.length)).trim() || void 0;
|
|
2561
|
+
} catch {
|
|
2562
|
+
return void 0;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
function readAgencyInviteToken(options = {}) {
|
|
2566
|
+
return readTokenFromUrl(options) ?? readAgencyInviteCookie();
|
|
2567
|
+
}
|
|
2568
|
+
var REDACT_BOUNDARIES = [
|
|
2569
|
+
"%2526",
|
|
2570
|
+
"%2523",
|
|
2571
|
+
"%253B",
|
|
2572
|
+
"%26",
|
|
2573
|
+
"%23",
|
|
2574
|
+
"%3B",
|
|
2575
|
+
"&",
|
|
2576
|
+
"#",
|
|
2577
|
+
";"
|
|
2578
|
+
];
|
|
2579
|
+
var REDACT_LEADS = ["%253F", "%3F", "?", ...REDACT_BOUNDARIES];
|
|
2580
|
+
var REDACT_SEPARATORS = ["%253D", "%3D", "="];
|
|
2581
|
+
function escapeForRegExp(literal) {
|
|
2582
|
+
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2583
|
+
}
|
|
2584
|
+
var REDACT_PATTERN = new RegExp(
|
|
2585
|
+
[
|
|
2586
|
+
// 先頭区切り。`\b` が使えないのは、入れ子形 (`%3Finvite%3D`) では `F` と `i` が
|
|
2587
|
+
// どちらも単語文字なので単語境界が存在しないため。
|
|
2588
|
+
`(^|${REDACT_LEADS.map(escapeForRegExp).join("|")})`,
|
|
2589
|
+
// パラメータ名と区切りはキャプチャして再出力する。`i` フラグがあるので、
|
|
2590
|
+
// 入力の `INVITE=` / `%3d` などの元の表記を保つ必要がある。
|
|
2591
|
+
`(${AGENCY_INVITE_TOKEN_PARAM})`,
|
|
2592
|
+
`(${REDACT_SEPARATORS.map(escapeForRegExp).join("|")})`,
|
|
2593
|
+
// 値。境界の直前まで貪欲に食う。`\s` は終端にしない — 生の空白や改行を含む
|
|
2594
|
+
// トークンでマスクが途中で切れ、後半が漏れる。
|
|
2595
|
+
`(?:(?!${REDACT_BOUNDARIES.map(escapeForRegExp).join("|")})[^&#;])*`
|
|
2596
|
+
].join(""),
|
|
2597
|
+
"gi"
|
|
2598
|
+
);
|
|
2599
|
+
function redactAgencyInviteToken(url) {
|
|
2600
|
+
return url.replace(
|
|
2601
|
+
REDACT_PATTERN,
|
|
2602
|
+
// 文字列 replacement(`'$1$2$3[REDACTED]'`)にしないのは、
|
|
2603
|
+
// `AGENCY_INVITE_TOKEN_REDACTION` に `$` が入った瞬間に置換パターンとして
|
|
2604
|
+
// 解釈される罠を作らないため。
|
|
2605
|
+
(_match, lead, name, separator) => `${lead}${name}${separator}${AGENCY_INVITE_TOKEN_REDACTION}`
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
function appendAgencyInviteToken(signupUrl, token) {
|
|
2609
|
+
if (token === void 0) return signupUrl;
|
|
2610
|
+
const separator = signupUrl.includes("?") ? "&" : "?";
|
|
2611
|
+
return `${signupUrl}${separator}${AGENCY_INVITE_TOKEN_PARAM}=${encodeURIComponent(token)}`;
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2511
2614
|
// src/client/redirect.ts
|
|
2512
2615
|
var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
2513
2616
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
@@ -2520,6 +2623,7 @@ var REDIRECT_LOOP_WINDOW_MS = 6e4;
|
|
|
2520
2623
|
var REDIRECT_LOOP_THRESHOLD = 3;
|
|
2521
2624
|
var storageReadFailureLogged = false;
|
|
2522
2625
|
var storageWriteFailureLogged = false;
|
|
2626
|
+
var ssrInviteTokenUnavailableLogged = false;
|
|
2523
2627
|
function isLoopDetectionDegraded() {
|
|
2524
2628
|
return storageReadFailureLogged || storageWriteFailureLogged;
|
|
2525
2629
|
}
|
|
@@ -2698,12 +2802,16 @@ function createRedirectMethods(deps) {
|
|
|
2698
2802
|
}
|
|
2699
2803
|
if (options?.screenHint === SCREEN_HINT_SIGNUP) {
|
|
2700
2804
|
params.set(SCREEN_HINT_PARAM, SCREEN_HINT_SIGNUP);
|
|
2805
|
+
const inviteToken = readAgencyInviteToken();
|
|
2806
|
+
if (inviteToken !== void 0) {
|
|
2807
|
+
params.set(AGENCY_INVITE_TOKEN_PARAM, inviteToken);
|
|
2808
|
+
}
|
|
2701
2809
|
}
|
|
2702
2810
|
if (options?.prompt) {
|
|
2703
2811
|
params.set("prompt", options.prompt);
|
|
2704
2812
|
}
|
|
2705
2813
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
2706
|
-
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
2814
|
+
logger.debug("Redirecting to authorize:", redactAgencyInviteToken(authorizeUrl));
|
|
2707
2815
|
recordRedirectAttempt(authorizeKey, now, logger);
|
|
2708
2816
|
try {
|
|
2709
2817
|
window.location.href = authorizeUrl;
|
|
@@ -2722,7 +2830,7 @@ function createRedirectMethods(deps) {
|
|
|
2722
2830
|
return redirectToAuthorize(options);
|
|
2723
2831
|
}
|
|
2724
2832
|
const targetUrl = options?.screenHint === SCREEN_HINT_SIGNUP ? getSignupUrl() : getLoginUrl(void 0, options?.prompt ? { prompt: options.prompt } : void 0);
|
|
2725
|
-
logger.debug("Redirecting to auth page:", targetUrl);
|
|
2833
|
+
logger.debug("Redirecting to auth page:", redactAgencyInviteToken(targetUrl));
|
|
2726
2834
|
window.location.href = targetUrl;
|
|
2727
2835
|
return { success: true };
|
|
2728
2836
|
}
|
|
@@ -2739,7 +2847,7 @@ function createRedirectMethods(deps) {
|
|
|
2739
2847
|
const promptSuffix = options?.prompt ? `&prompt=${encodeURIComponent(options.prompt)}` : "";
|
|
2740
2848
|
return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}${promptSuffix}`;
|
|
2741
2849
|
}
|
|
2742
|
-
function getSignupUrl(redirectUrl) {
|
|
2850
|
+
function getSignupUrl(redirectUrl, options) {
|
|
2743
2851
|
let redirect;
|
|
2744
2852
|
if (redirectUrl != null) {
|
|
2745
2853
|
redirect = redirectUrl;
|
|
@@ -2749,7 +2857,22 @@ function createRedirectMethods(deps) {
|
|
|
2749
2857
|
logger.warn("getSignupUrl: SSR \u74B0\u5883\u3067 redirectUrl \u304C\u672A\u6307\u5B9A\u306E\u305F\u3081\u7A7A\u6587\u5B57\u306B\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3057\u307E\u3059");
|
|
2750
2858
|
redirect = "";
|
|
2751
2859
|
}
|
|
2752
|
-
|
|
2860
|
+
let inviteToken = options?.inviteToken;
|
|
2861
|
+
if (inviteToken === void 0) {
|
|
2862
|
+
inviteToken = readAgencyInviteToken();
|
|
2863
|
+
if (inviteToken === void 0 && typeof window === "undefined") {
|
|
2864
|
+
if (!ssrInviteTokenUnavailableLogged) {
|
|
2865
|
+
ssrInviteTokenUnavailableLogged = true;
|
|
2866
|
+
logger.warn(
|
|
2867
|
+
"[FFID SDK] getSignupUrl: SSR \u3067\u306F\u62DB\u5F85\u30C8\u30FC\u30AF\u30F3\uFF08invite\uFF09\u3092\u8AAD\u3081\u307E\u305B\u3093\u3002\u4EE3\u7406\u5E97\u306E\u62DB\u5F85\u30EA\u30F3\u30AF\u3092\u4F7F\u3046\u5834\u5408\u306F options.inviteToken \u306B\u6E21\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F7F\u3063\u3066\u3044\u306A\u3044\u5834\u5408\u306F\u3053\u306E\u8B66\u544A\u3092\u7121\u8996\u3057\u3066\u3088\u3044\uFF09"
|
|
2868
|
+
);
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
return appendAgencyInviteToken(
|
|
2873
|
+
`${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`,
|
|
2874
|
+
inviteToken
|
|
2875
|
+
);
|
|
2753
2876
|
}
|
|
2754
2877
|
function getLogoutUrl(postLogoutRedirectUri) {
|
|
2755
2878
|
const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
|
|
@@ -2765,7 +2888,7 @@ function createRedirectMethods(deps) {
|
|
|
2765
2888
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
2766
2889
|
}
|
|
2767
2890
|
const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
|
|
2768
|
-
logger.debug("Redirecting to logout:", logoutUrl);
|
|
2891
|
+
logger.debug("Redirecting to logout:", redactAgencyInviteToken(logoutUrl));
|
|
2769
2892
|
window.location.href = logoutUrl;
|
|
2770
2893
|
return { success: true };
|
|
2771
2894
|
}
|
|
@@ -4319,9 +4442,9 @@ function createServerAuthClient(config) {
|
|
|
4319
4442
|
var FFID_SESSION_COOKIE_NAME = "ffid_session";
|
|
4320
4443
|
var DEFAULT_PATH = "/";
|
|
4321
4444
|
var DEFAULT_SAME_SITE = "lax";
|
|
4322
|
-
var
|
|
4445
|
+
var HOURS_PER_DAY3 = 24;
|
|
4323
4446
|
var DEFAULT_MAX_AGE_DAYS = 30;
|
|
4324
|
-
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 *
|
|
4447
|
+
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * HOURS_PER_DAY3 * DEFAULT_MAX_AGE_DAYS;
|
|
4325
4448
|
var BASE64_GROUP = 4;
|
|
4326
4449
|
function base64UrlEncode2(input) {
|
|
4327
4450
|
const bytes = new TextEncoder().encode(input);
|
|
@@ -47,7 +47,7 @@ declare class FFIDWebhookPayloadError extends FFIDWebhookError {
|
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
/** All supported webhook event types */
|
|
50
|
-
type FFIDWebhookEventType = 'subscription.created' | 'subscription.updated' | 'subscription.canceled' | 'subscription.downgrade_scheduled' | 'subscription.downgrade_applied' | 'subscription.downgrade_canceled' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'user.created' | 'user.updated' | 'user.deleted' | 'user.deletion_requested' | 'organization.created' | 'organization.updated' | 'organization.member.added' | 'organization.member.removed' | 'organization.member.role_changed' | 'legal.document.updated' | 'legal.agreement.required' | 'system.maintenance.scheduled' | 'system.maintenance.started' | 'system.maintenance.completed' | 'announcement.published' | 'test.ping';
|
|
50
|
+
type FFIDWebhookEventType = 'subscription.created' | 'subscription.reactivated' | 'subscription.updated' | 'subscription.canceled' | 'subscription.downgrade_scheduled' | 'subscription.downgrade_applied' | 'subscription.downgrade_canceled' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'user.created' | 'user.updated' | 'user.deleted' | 'user.deletion_requested' | 'organization.created' | 'organization.updated' | 'organization.member.added' | 'organization.member.removed' | 'organization.member.role_changed' | 'legal.document.updated' | 'legal.agreement.required' | 'system.maintenance.scheduled' | 'system.maintenance.started' | 'system.maintenance.completed' | 'announcement.published' | 'test.ping';
|
|
51
51
|
interface FFIDSubscriptionCreatedPayload {
|
|
52
52
|
subscriptionId: string;
|
|
53
53
|
organizationId?: string;
|
|
@@ -65,6 +65,46 @@ interface FFIDSubscriptionUpdatedPayload {
|
|
|
65
65
|
effectiveStatus?: EffectiveSubscriptionStatus;
|
|
66
66
|
gracePeriodEndsAt?: string | null;
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* `subscription.reactivated` (8.0.0): a cancellation reserved until the period end was
|
|
70
|
+
* withdrawn in the FFID portal, so the subscription continues. It pairs with the
|
|
71
|
+
* reservation-time `subscription.canceled` (`source: 'user_initiated'`,
|
|
72
|
+
* `reactivatable: true`). Sent only for contracts billed through Stripe.
|
|
73
|
+
*/
|
|
74
|
+
interface FFIDSubscriptionReactivatedPayload {
|
|
75
|
+
subscriptionId: string;
|
|
76
|
+
organizationId?: string;
|
|
77
|
+
/** Plan code */
|
|
78
|
+
plan?: string;
|
|
79
|
+
status?: string;
|
|
80
|
+
effectiveStatus?: EffectiveSubscriptionStatus;
|
|
81
|
+
gracePeriodEndsAt?: string | null;
|
|
82
|
+
stripeSubscriptionId: string;
|
|
83
|
+
stripeCustomerId: string;
|
|
84
|
+
/** Code of the service the subscription belongs to (resolved by FFID when the event is sent) */
|
|
85
|
+
serviceCode?: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* `subscription.canceled`.
|
|
89
|
+
*
|
|
90
|
+
* A contract billed through Stripe is canceled in two steps (FFID #6327), so **two events
|
|
91
|
+
* arrive per cancellation**:
|
|
92
|
+
*
|
|
93
|
+
* 1. At the reservation — `source: 'user_initiated'`, `reactivatable: true`, `cancelAt` /
|
|
94
|
+
* `expiredAt` = the period end, no `effectiveStatus`. **Access continues until `cancelAt`**;
|
|
95
|
+
* do not revoke it here. The reservation may still be withdrawn
|
|
96
|
+
* (`subscription.reactivated`).
|
|
97
|
+
* 2. At the period end — `source: 'stripe_confirmed'`, `effectiveStatus: 'canceled'`. Revoke
|
|
98
|
+
* access on this one.
|
|
99
|
+
*
|
|
100
|
+
* **Rule for consumers:** only an event with `source === 'user_initiated'` **and** a
|
|
101
|
+
* `cancelAt` is a reservation (keep access until `cancelAt`). Every other
|
|
102
|
+
* `subscription.canceled` ends access: the final `stripe_confirmed`, an immediate
|
|
103
|
+
* cancellation (`user_initiated` without `cancelAt` — a contract that cannot be reserved,
|
|
104
|
+
* e.g. no Stripe subscription or no period end yet; `expiredAt` is the cancellation time),
|
|
105
|
+
* and `payment_failure_auto_cancel` (automatic cancellation after unpaid invoices).
|
|
106
|
+
* Do not key the decision on `effectiveStatus`: it is set only on Stripe's deletion notice.
|
|
107
|
+
*/
|
|
68
108
|
interface FFIDSubscriptionCanceledPayload {
|
|
69
109
|
/** FFID subscription UUID. */
|
|
70
110
|
subscriptionId: string;
|
|
@@ -80,20 +120,28 @@ interface FFIDSubscriptionCanceledPayload {
|
|
|
80
120
|
*/
|
|
81
121
|
stripeSubscriptionId: string | null;
|
|
82
122
|
reason?: string;
|
|
123
|
+
/** When a reserved cancellation takes effect (the period end) */
|
|
83
124
|
cancelAt?: string;
|
|
84
125
|
/**
|
|
85
126
|
* Origin of the cancellation. Always emitted by the FFID server — required
|
|
86
|
-
* by the wire shape in `SubscriptionCanceledPayload`.
|
|
127
|
+
* by the wire shape in `SubscriptionCanceledPayload`. Distinguishes the reservation
|
|
128
|
+
* (`user_initiated`) from the final event (`stripe_confirmed`); see the interface doc.
|
|
87
129
|
*/
|
|
88
130
|
source: 'user_initiated' | 'stripe_confirmed' | 'payment_failure_auto_cancel';
|
|
89
131
|
/** Whether this cancellation can be resumed via resubscribe flow. */
|
|
90
132
|
reactivatable?: boolean;
|
|
91
133
|
/**
|
|
92
|
-
* ISO timestamp at which access was
|
|
93
|
-
* when Stripe runs out the paid period
|
|
134
|
+
* ISO timestamp at which access is (or was) lost. On the reservation event this is the
|
|
135
|
+
* future period end. May differ from `cancelAt` when Stripe runs out the paid period
|
|
136
|
+
* before emitting the deletion event.
|
|
94
137
|
*/
|
|
95
138
|
expiredAt?: string;
|
|
96
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* `canceled` only on Stripe's deletion notice (e.g. the final `stripe_confirmed`). Absent on
|
|
141
|
+
* the reservation event, on immediate cancellations and on the cron's
|
|
142
|
+
* `payment_failure_auto_cancel` — so its absence does not mean "still usable"; decide by
|
|
143
|
+
* `source` + `cancelAt` (see the interface doc).
|
|
144
|
+
*/
|
|
97
145
|
effectiveStatus?: EffectiveSubscriptionStatus;
|
|
98
146
|
/** Always null for cancellation events because no grace window remains. */
|
|
99
147
|
gracePeriodEndsAt?: string | null;
|
|
@@ -279,6 +327,7 @@ interface FFIDTestPingPayload {
|
|
|
279
327
|
/** Maps event type strings to their typed payloads */
|
|
280
328
|
interface FFIDWebhookEventMap {
|
|
281
329
|
'subscription.created': FFIDSubscriptionCreatedPayload;
|
|
330
|
+
'subscription.reactivated': FFIDSubscriptionReactivatedPayload;
|
|
282
331
|
'subscription.updated': FFIDSubscriptionUpdatedPayload;
|
|
283
332
|
'subscription.canceled': FFIDSubscriptionCanceledPayload;
|
|
284
333
|
'subscription.downgrade_scheduled': FFIDSubscriptionDowngradeScheduledPayload;
|
|
@@ -426,4 +475,4 @@ declare function createFFIDWebhookHandler(config: FFIDWebhookHandlerConfig): {
|
|
|
426
475
|
/** Type of the FFID Webhook handler */
|
|
427
476
|
type FFIDWebhookHandler = ReturnType<typeof createFFIDWebhookHandler>;
|
|
428
477
|
|
|
429
|
-
export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionDowngradeAppliedPayload, type FFIDSubscriptionDowngradeCanceledPayload, type FFIDSubscriptionDowngradeScheduledPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };
|
|
478
|
+
export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionDowngradeAppliedPayload, type FFIDSubscriptionDowngradeCanceledPayload, type FFIDSubscriptionDowngradeScheduledPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionReactivatedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };
|