@feelflow/ffid-sdk 1.11.0 → 1.13.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.
@@ -437,7 +437,7 @@ function createBillingMethods(deps) {
437
437
  }
438
438
 
439
439
  // src/client/version-check.ts
440
- var SDK_VERSION = "1.11.0";
440
+ var SDK_VERSION = "1.13.0";
441
441
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
442
442
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
443
443
  function sdkHeaders() {
@@ -483,6 +483,22 @@ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8
483
483
  var OAUTH_TOKEN_ENDPOINT = "/api/v1/oauth/token";
484
484
  var OAUTH_REVOKE_ENDPOINT = "/api/v1/oauth/revoke";
485
485
  var MS_PER_SECOND = 1e3;
486
+ function validateTokenResponse(tokenResponse) {
487
+ const invalid = [];
488
+ if (!tokenResponse.access_token) {
489
+ invalid.push("access_token");
490
+ }
491
+ if (!tokenResponse.refresh_token) {
492
+ invalid.push("refresh_token");
493
+ }
494
+ if (typeof tokenResponse.expires_in !== "number" || tokenResponse.expires_in <= 0) {
495
+ invalid.push("expires_in");
496
+ }
497
+ if (invalid.length > 0) {
498
+ return `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306B\u4E0D\u6B63\u306A\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u3042\u308A\u307E\u3059: ${invalid.join(", ")}`;
499
+ }
500
+ return null;
501
+ }
486
502
  function createOAuthTokenMethods(deps) {
487
503
  const {
488
504
  baseUrl,
@@ -552,6 +568,16 @@ function createOAuthTokenMethods(deps) {
552
568
  }
553
569
  };
554
570
  }
571
+ const validationError = validateTokenResponse(tokenResponse);
572
+ if (validationError) {
573
+ logger.error("Token exchange validation failed:", validationError);
574
+ return {
575
+ error: {
576
+ code: errorCodes.TOKEN_EXCHANGE_ERROR,
577
+ message: validationError
578
+ }
579
+ };
580
+ }
555
581
  tokenStore.setTokens({
556
582
  accessToken: tokenResponse.access_token,
557
583
  refreshToken: tokenResponse.refresh_token,
@@ -620,6 +646,16 @@ function createOAuthTokenMethods(deps) {
620
646
  }
621
647
  };
622
648
  }
649
+ const validationError = validateTokenResponse(tokenResponse);
650
+ if (validationError) {
651
+ logger.error("Token refresh validation failed:", validationError);
652
+ return {
653
+ error: {
654
+ code: errorCodes.TOKEN_REFRESH_ERROR,
655
+ message: validationError
656
+ }
657
+ };
658
+ }
623
659
  tokenStore.setTokens({
624
660
  accessToken: tokenResponse.access_token,
625
661
  refreshToken: tokenResponse.refresh_token,
@@ -873,14 +909,20 @@ async function generateCodeChallenge(verifier) {
873
909
  const digest = await crypto.subtle.digest("SHA-256", data);
874
910
  return base64UrlEncode(digest);
875
911
  }
876
- function storeCodeVerifier(verifier) {
912
+ function storeCodeVerifier(verifier, logger) {
877
913
  try {
878
- if (typeof window === "undefined") return;
914
+ if (typeof window === "undefined") {
915
+ logger?.warn("storeCodeVerifier: sessionStorage is not available in SSR context");
916
+ return false;
917
+ }
879
918
  window.sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
880
- } catch {
919
+ return true;
920
+ } catch (error) {
921
+ logger?.warn("storeCodeVerifier: sessionStorage \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
922
+ return false;
881
923
  }
882
924
  }
883
- function retrieveCodeVerifier() {
925
+ function retrieveCodeVerifier(logger) {
884
926
  try {
885
927
  if (typeof window === "undefined") return null;
886
928
  const verifier = window.sessionStorage.getItem(VERIFIER_STORAGE_KEY);
@@ -888,7 +930,8 @@ function retrieveCodeVerifier() {
888
930
  window.sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
889
931
  }
890
932
  return verifier;
891
- } catch {
933
+ } catch (error) {
934
+ logger?.warn("retrieveCodeVerifier: sessionStorage \u304B\u3089\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
892
935
  return null;
893
936
  }
894
937
  }
@@ -921,32 +964,34 @@ function createRedirectMethods(deps) {
921
964
  } = deps;
922
965
  async function redirectToAuthorize() {
923
966
  const verifier = generateCodeVerifier();
924
- storeCodeVerifier(verifier);
967
+ storeCodeVerifier(verifier, logger);
968
+ let challenge;
925
969
  try {
926
- const challenge = await generateCodeChallenge(verifier);
927
- const state = generateRandomState();
928
- const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
929
- const params = new URLSearchParams({
930
- response_type: "code",
931
- client_id: clientId,
932
- redirect_uri: redirectUri,
933
- state,
934
- code_challenge: challenge,
935
- code_challenge_method: "S256"
936
- });
937
- const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
938
- logger.debug("Redirecting to authorize:", authorizeUrl);
939
- window.location.href = authorizeUrl;
940
- return true;
970
+ challenge = await generateCodeChallenge(verifier);
941
971
  } catch (error) {
972
+ const errorMessage = error instanceof Error ? error.message : "PKCE \u30B3\u30FC\u30C9\u30C1\u30E3\u30EC\u30F3\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
942
973
  logger.error("PKCE \u30B3\u30FC\u30C9\u30C1\u30E3\u30EC\u30F3\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
943
- return false;
974
+ return { success: false, error: errorMessage };
944
975
  }
976
+ const state = generateRandomState();
977
+ const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
978
+ const params = new URLSearchParams({
979
+ response_type: "code",
980
+ client_id: clientId,
981
+ redirect_uri: redirectUri,
982
+ state,
983
+ code_challenge: challenge,
984
+ code_challenge_method: "S256"
985
+ });
986
+ const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
987
+ logger.debug("Redirecting to authorize:", authorizeUrl);
988
+ window.location.href = authorizeUrl;
989
+ return { success: true };
945
990
  }
946
991
  async function redirectToLogin() {
947
992
  if (typeof window === "undefined") {
948
- logger.debug("Cannot redirect in SSR context");
949
- return false;
993
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
994
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
950
995
  }
951
996
  if (authMode === "token") {
952
997
  return redirectToAuthorize();
@@ -955,19 +1000,273 @@ function createRedirectMethods(deps) {
955
1000
  const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(serviceCode)}`;
956
1001
  logger.debug("Redirecting to login:", loginUrl);
957
1002
  window.location.href = loginUrl;
958
- return true;
1003
+ return { success: true };
959
1004
  }
960
1005
  function getLoginUrl(redirectUrl) {
961
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1006
+ let redirect;
1007
+ if (redirectUrl != null) {
1008
+ redirect = redirectUrl;
1009
+ } else if (typeof window !== "undefined") {
1010
+ redirect = window.location.href;
1011
+ } else {
1012
+ logger.warn("getLoginUrl: 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");
1013
+ redirect = "";
1014
+ }
962
1015
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
963
1016
  }
964
1017
  function getSignupUrl(redirectUrl) {
965
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1018
+ let redirect;
1019
+ if (redirectUrl != null) {
1020
+ redirect = redirectUrl;
1021
+ } else if (typeof window !== "undefined") {
1022
+ redirect = window.location.href;
1023
+ } else {
1024
+ 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");
1025
+ redirect = "";
1026
+ }
966
1027
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
967
1028
  }
968
1029
  return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
969
1030
  }
970
1031
 
1032
+ // src/client/password-reset.ts
1033
+ var RESET_PASSWORD_BASE = "/api/v1/auth/reset-password";
1034
+ function isBlank(value) {
1035
+ return !value || !value.trim();
1036
+ }
1037
+ function createPasswordResetMethods(deps) {
1038
+ const { baseUrl, logger, createError, fetchWithAuth, errorCodes } = deps;
1039
+ async function fetchPublic(endpoint, options = {}) {
1040
+ const url = `${baseUrl}${endpoint}`;
1041
+ logger.debug("Fetching (public):", url);
1042
+ let response;
1043
+ try {
1044
+ response = await fetch(url, {
1045
+ ...options,
1046
+ credentials: "include",
1047
+ headers: {
1048
+ "Content-Type": "application/json",
1049
+ ...sdkHeaders(),
1050
+ ...options.headers
1051
+ }
1052
+ });
1053
+ } catch (error) {
1054
+ logger.error("Network error:", error);
1055
+ return {
1056
+ error: {
1057
+ code: errorCodes.NETWORK_ERROR,
1058
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1059
+ }
1060
+ };
1061
+ }
1062
+ let raw;
1063
+ try {
1064
+ raw = await response.json();
1065
+ } catch (parseError) {
1066
+ logger.error("Parse error:", parseError, "Status:", response.status);
1067
+ return {
1068
+ error: {
1069
+ code: errorCodes.PARSE_ERROR,
1070
+ message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
1071
+ }
1072
+ };
1073
+ }
1074
+ logger.debug("Response (public):", response.status, raw);
1075
+ checkVersionHeader(response, logger);
1076
+ if (!response.ok) {
1077
+ return {
1078
+ error: raw.error ?? {
1079
+ code: errorCodes.UNKNOWN_ERROR,
1080
+ message: "\u30D1\u30B9\u30EF\u30FC\u30C9\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1081
+ }
1082
+ };
1083
+ }
1084
+ if (raw.data === void 0) {
1085
+ return {
1086
+ error: {
1087
+ code: errorCodes.UNKNOWN_ERROR,
1088
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1089
+ }
1090
+ };
1091
+ }
1092
+ return { data: raw.data };
1093
+ }
1094
+ async function requestPasswordReset(email) {
1095
+ if (isBlank(email)) {
1096
+ return {
1097
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1098
+ };
1099
+ }
1100
+ return fetchPublic(
1101
+ RESET_PASSWORD_BASE,
1102
+ {
1103
+ method: "POST",
1104
+ body: JSON.stringify({ email })
1105
+ }
1106
+ );
1107
+ }
1108
+ async function verifyPasswordResetToken(accessToken) {
1109
+ if (isBlank(accessToken)) {
1110
+ return {
1111
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1112
+ };
1113
+ }
1114
+ const query = new URLSearchParams({
1115
+ access_token: accessToken,
1116
+ type: "recovery"
1117
+ });
1118
+ return fetchPublic(
1119
+ `${RESET_PASSWORD_BASE}/verify?${query.toString()}`
1120
+ );
1121
+ }
1122
+ async function establishResetSession(accessToken, refreshToken) {
1123
+ if (isBlank(accessToken)) {
1124
+ return {
1125
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1126
+ };
1127
+ }
1128
+ if (isBlank(refreshToken)) {
1129
+ return {
1130
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1131
+ };
1132
+ }
1133
+ return fetchPublic(
1134
+ `${RESET_PASSWORD_BASE}/session`,
1135
+ {
1136
+ method: "POST",
1137
+ body: JSON.stringify({ accessToken, refreshToken })
1138
+ }
1139
+ );
1140
+ }
1141
+ async function confirmPasswordReset(password) {
1142
+ if (isBlank(password)) {
1143
+ return {
1144
+ error: createError("VALIDATION_ERROR", "\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u5FC5\u9808\u3067\u3059")
1145
+ };
1146
+ }
1147
+ return fetchWithAuth(
1148
+ `${RESET_PASSWORD_BASE}/confirm`,
1149
+ {
1150
+ method: "POST",
1151
+ body: JSON.stringify({ password })
1152
+ }
1153
+ );
1154
+ }
1155
+ return {
1156
+ requestPasswordReset,
1157
+ verifyPasswordResetToken,
1158
+ establishResetSession,
1159
+ confirmPasswordReset
1160
+ };
1161
+ }
1162
+
1163
+ // src/client/otp.ts
1164
+ var OTP_BASE = "/api/v1/auth/otp";
1165
+ function isBlank2(value) {
1166
+ return !value || !value.trim();
1167
+ }
1168
+ function createOtpMethods(deps) {
1169
+ const { baseUrl, logger, createError, errorCodes } = deps;
1170
+ async function fetchPublic(endpoint, options = {}) {
1171
+ const url = `${baseUrl}${endpoint}`;
1172
+ logger.debug("Fetching (public):", url);
1173
+ let response;
1174
+ try {
1175
+ response = await fetch(url, {
1176
+ ...options,
1177
+ credentials: "include",
1178
+ headers: {
1179
+ "Content-Type": "application/json",
1180
+ ...sdkHeaders(),
1181
+ ...options.headers
1182
+ }
1183
+ });
1184
+ } catch (error) {
1185
+ logger.error("Network error:", error);
1186
+ return {
1187
+ error: {
1188
+ code: errorCodes.NETWORK_ERROR,
1189
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1190
+ }
1191
+ };
1192
+ }
1193
+ let raw;
1194
+ try {
1195
+ raw = await response.json();
1196
+ } catch (parseError) {
1197
+ logger.error("Parse error:", parseError, "Status:", response.status);
1198
+ return {
1199
+ error: {
1200
+ code: errorCodes.PARSE_ERROR,
1201
+ message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
1202
+ }
1203
+ };
1204
+ }
1205
+ logger.debug("Response (public):", response.status, raw);
1206
+ checkVersionHeader(response, logger);
1207
+ if (!response.ok) {
1208
+ return {
1209
+ error: raw.error ?? {
1210
+ code: errorCodes.UNKNOWN_ERROR,
1211
+ message: "OTP\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1212
+ }
1213
+ };
1214
+ }
1215
+ if (raw.data === void 0) {
1216
+ return {
1217
+ error: {
1218
+ code: errorCodes.UNKNOWN_ERROR,
1219
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1220
+ }
1221
+ };
1222
+ }
1223
+ return { data: raw.data };
1224
+ }
1225
+ async function sendOtp(email, options) {
1226
+ if (isBlank2(email)) {
1227
+ return {
1228
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1229
+ };
1230
+ }
1231
+ return fetchPublic(
1232
+ `${OTP_BASE}/send`,
1233
+ {
1234
+ method: "POST",
1235
+ body: JSON.stringify({
1236
+ email,
1237
+ ...options?.redirectUrl ? { redirectUrl: options.redirectUrl } : {}
1238
+ })
1239
+ }
1240
+ );
1241
+ }
1242
+ async function verifyOtp(params) {
1243
+ if (isBlank2(params.accessToken)) {
1244
+ return {
1245
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1246
+ };
1247
+ }
1248
+ if (isBlank2(params.refreshToken)) {
1249
+ return {
1250
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1251
+ };
1252
+ }
1253
+ return fetchPublic(
1254
+ `${OTP_BASE}/verify`,
1255
+ {
1256
+ method: "POST",
1257
+ body: JSON.stringify({
1258
+ accessToken: params.accessToken,
1259
+ refreshToken: params.refreshToken
1260
+ })
1261
+ }
1262
+ );
1263
+ }
1264
+ return {
1265
+ sendOtp,
1266
+ verifyOtp
1267
+ };
1268
+ }
1269
+
971
1270
  // src/client/ffid-client.ts
972
1271
  var UNAUTHORIZED_STATUS2 = 401;
973
1272
  var SDK_LOG_PREFIX = "[FFID SDK]";
@@ -1100,6 +1399,9 @@ function createFFIDClient(config) {
1100
1399
  }
1101
1400
  };
1102
1401
  }
1402
+ } else {
1403
+ logger.warn("Token refresh failed, returning refresh error:", refreshResult.error);
1404
+ return { error: refreshResult.error };
1103
1405
  }
1104
1406
  }
1105
1407
  let raw;
@@ -1177,6 +1479,24 @@ function createFFIDClient(config) {
1177
1479
  fetchWithAuth,
1178
1480
  createError
1179
1481
  });
1482
+ const {
1483
+ requestPasswordReset,
1484
+ verifyPasswordResetToken,
1485
+ establishResetSession,
1486
+ confirmPasswordReset
1487
+ } = createPasswordResetMethods({
1488
+ baseUrl,
1489
+ logger,
1490
+ createError,
1491
+ fetchWithAuth,
1492
+ errorCodes: FFID_ERROR_CODES
1493
+ });
1494
+ const { sendOtp, verifyOtp } = createOtpMethods({
1495
+ baseUrl,
1496
+ logger,
1497
+ createError,
1498
+ errorCodes: FFID_ERROR_CODES
1499
+ });
1180
1500
  const verifyAccessToken = createVerifyAccessToken({
1181
1501
  authMode,
1182
1502
  baseUrl,
@@ -1202,6 +1522,12 @@ function createFFIDClient(config) {
1202
1522
  createCheckoutSession,
1203
1523
  createPortalSession,
1204
1524
  verifyAccessToken,
1525
+ requestPasswordReset,
1526
+ verifyPasswordResetToken,
1527
+ establishResetSession,
1528
+ confirmPasswordReset,
1529
+ sendOtp,
1530
+ verifyOtp,
1205
1531
  /** Token store (token mode only) */
1206
1532
  tokenStore,
1207
1533
  /** Resolved auth mode */