@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.
@@ -432,7 +432,7 @@ function createBillingMethods(deps) {
432
432
  }
433
433
 
434
434
  // src/client/version-check.ts
435
- var SDK_VERSION = "1.11.0";
435
+ var SDK_VERSION = "1.13.0";
436
436
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
437
437
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
438
438
  function sdkHeaders() {
@@ -478,6 +478,22 @@ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8
478
478
  var OAUTH_TOKEN_ENDPOINT = "/api/v1/oauth/token";
479
479
  var OAUTH_REVOKE_ENDPOINT = "/api/v1/oauth/revoke";
480
480
  var MS_PER_SECOND = 1e3;
481
+ function validateTokenResponse(tokenResponse) {
482
+ const invalid = [];
483
+ if (!tokenResponse.access_token) {
484
+ invalid.push("access_token");
485
+ }
486
+ if (!tokenResponse.refresh_token) {
487
+ invalid.push("refresh_token");
488
+ }
489
+ if (typeof tokenResponse.expires_in !== "number" || tokenResponse.expires_in <= 0) {
490
+ invalid.push("expires_in");
491
+ }
492
+ if (invalid.length > 0) {
493
+ 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(", ")}`;
494
+ }
495
+ return null;
496
+ }
481
497
  function createOAuthTokenMethods(deps) {
482
498
  const {
483
499
  baseUrl,
@@ -547,6 +563,16 @@ function createOAuthTokenMethods(deps) {
547
563
  }
548
564
  };
549
565
  }
566
+ const validationError = validateTokenResponse(tokenResponse);
567
+ if (validationError) {
568
+ logger.error("Token exchange validation failed:", validationError);
569
+ return {
570
+ error: {
571
+ code: errorCodes.TOKEN_EXCHANGE_ERROR,
572
+ message: validationError
573
+ }
574
+ };
575
+ }
550
576
  tokenStore.setTokens({
551
577
  accessToken: tokenResponse.access_token,
552
578
  refreshToken: tokenResponse.refresh_token,
@@ -615,6 +641,16 @@ function createOAuthTokenMethods(deps) {
615
641
  }
616
642
  };
617
643
  }
644
+ const validationError = validateTokenResponse(tokenResponse);
645
+ if (validationError) {
646
+ logger.error("Token refresh validation failed:", validationError);
647
+ return {
648
+ error: {
649
+ code: errorCodes.TOKEN_REFRESH_ERROR,
650
+ message: validationError
651
+ }
652
+ };
653
+ }
618
654
  tokenStore.setTokens({
619
655
  accessToken: tokenResponse.access_token,
620
656
  refreshToken: tokenResponse.refresh_token,
@@ -868,11 +904,17 @@ async function generateCodeChallenge(verifier) {
868
904
  const digest = await crypto.subtle.digest("SHA-256", data);
869
905
  return base64UrlEncode(digest);
870
906
  }
871
- function storeCodeVerifier(verifier) {
907
+ function storeCodeVerifier(verifier, logger) {
872
908
  try {
873
- if (typeof window === "undefined") return;
909
+ if (typeof window === "undefined") {
910
+ logger?.warn("storeCodeVerifier: sessionStorage is not available in SSR context");
911
+ return false;
912
+ }
874
913
  window.sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
875
- } catch {
914
+ return true;
915
+ } catch (error) {
916
+ logger?.warn("storeCodeVerifier: sessionStorage \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
917
+ return false;
876
918
  }
877
919
  }
878
920
  function base64UrlEncode(buffer) {
@@ -904,32 +946,34 @@ function createRedirectMethods(deps) {
904
946
  } = deps;
905
947
  async function redirectToAuthorize() {
906
948
  const verifier = generateCodeVerifier();
907
- storeCodeVerifier(verifier);
949
+ storeCodeVerifier(verifier, logger);
950
+ let challenge;
908
951
  try {
909
- const challenge = await generateCodeChallenge(verifier);
910
- const state = generateRandomState();
911
- const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
912
- const params = new URLSearchParams({
913
- response_type: "code",
914
- client_id: clientId,
915
- redirect_uri: redirectUri,
916
- state,
917
- code_challenge: challenge,
918
- code_challenge_method: "S256"
919
- });
920
- const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
921
- logger.debug("Redirecting to authorize:", authorizeUrl);
922
- window.location.href = authorizeUrl;
923
- return true;
952
+ challenge = await generateCodeChallenge(verifier);
924
953
  } catch (error) {
954
+ 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";
925
955
  logger.error("PKCE \u30B3\u30FC\u30C9\u30C1\u30E3\u30EC\u30F3\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
926
- return false;
956
+ return { success: false, error: errorMessage };
927
957
  }
958
+ const state = generateRandomState();
959
+ const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
960
+ const params = new URLSearchParams({
961
+ response_type: "code",
962
+ client_id: clientId,
963
+ redirect_uri: redirectUri,
964
+ state,
965
+ code_challenge: challenge,
966
+ code_challenge_method: "S256"
967
+ });
968
+ const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
969
+ logger.debug("Redirecting to authorize:", authorizeUrl);
970
+ window.location.href = authorizeUrl;
971
+ return { success: true };
928
972
  }
929
973
  async function redirectToLogin() {
930
974
  if (typeof window === "undefined") {
931
- logger.debug("Cannot redirect in SSR context");
932
- return false;
975
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
976
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
933
977
  }
934
978
  if (authMode === "token") {
935
979
  return redirectToAuthorize();
@@ -938,19 +982,273 @@ function createRedirectMethods(deps) {
938
982
  const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(serviceCode)}`;
939
983
  logger.debug("Redirecting to login:", loginUrl);
940
984
  window.location.href = loginUrl;
941
- return true;
985
+ return { success: true };
942
986
  }
943
987
  function getLoginUrl(redirectUrl) {
944
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
988
+ let redirect;
989
+ if (redirectUrl != null) {
990
+ redirect = redirectUrl;
991
+ } else if (typeof window !== "undefined") {
992
+ redirect = window.location.href;
993
+ } else {
994
+ 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");
995
+ redirect = "";
996
+ }
945
997
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
946
998
  }
947
999
  function getSignupUrl(redirectUrl) {
948
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1000
+ let redirect;
1001
+ if (redirectUrl != null) {
1002
+ redirect = redirectUrl;
1003
+ } else if (typeof window !== "undefined") {
1004
+ redirect = window.location.href;
1005
+ } else {
1006
+ 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");
1007
+ redirect = "";
1008
+ }
949
1009
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
950
1010
  }
951
1011
  return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
952
1012
  }
953
1013
 
1014
+ // src/client/password-reset.ts
1015
+ var RESET_PASSWORD_BASE = "/api/v1/auth/reset-password";
1016
+ function isBlank(value) {
1017
+ return !value || !value.trim();
1018
+ }
1019
+ function createPasswordResetMethods(deps) {
1020
+ const { baseUrl, logger, createError, fetchWithAuth, errorCodes } = deps;
1021
+ async function fetchPublic(endpoint, options = {}) {
1022
+ const url = `${baseUrl}${endpoint}`;
1023
+ logger.debug("Fetching (public):", url);
1024
+ let response;
1025
+ try {
1026
+ response = await fetch(url, {
1027
+ ...options,
1028
+ credentials: "include",
1029
+ headers: {
1030
+ "Content-Type": "application/json",
1031
+ ...sdkHeaders(),
1032
+ ...options.headers
1033
+ }
1034
+ });
1035
+ } catch (error) {
1036
+ logger.error("Network error:", error);
1037
+ return {
1038
+ error: {
1039
+ code: errorCodes.NETWORK_ERROR,
1040
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1041
+ }
1042
+ };
1043
+ }
1044
+ let raw;
1045
+ try {
1046
+ raw = await response.json();
1047
+ } catch (parseError) {
1048
+ logger.error("Parse error:", parseError, "Status:", response.status);
1049
+ return {
1050
+ error: {
1051
+ code: errorCodes.PARSE_ERROR,
1052
+ 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})`
1053
+ }
1054
+ };
1055
+ }
1056
+ logger.debug("Response (public):", response.status, raw);
1057
+ checkVersionHeader(response, logger);
1058
+ if (!response.ok) {
1059
+ return {
1060
+ error: raw.error ?? {
1061
+ code: errorCodes.UNKNOWN_ERROR,
1062
+ message: "\u30D1\u30B9\u30EF\u30FC\u30C9\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1063
+ }
1064
+ };
1065
+ }
1066
+ if (raw.data === void 0) {
1067
+ return {
1068
+ error: {
1069
+ code: errorCodes.UNKNOWN_ERROR,
1070
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1071
+ }
1072
+ };
1073
+ }
1074
+ return { data: raw.data };
1075
+ }
1076
+ async function requestPasswordReset(email) {
1077
+ if (isBlank(email)) {
1078
+ return {
1079
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1080
+ };
1081
+ }
1082
+ return fetchPublic(
1083
+ RESET_PASSWORD_BASE,
1084
+ {
1085
+ method: "POST",
1086
+ body: JSON.stringify({ email })
1087
+ }
1088
+ );
1089
+ }
1090
+ async function verifyPasswordResetToken(accessToken) {
1091
+ if (isBlank(accessToken)) {
1092
+ return {
1093
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1094
+ };
1095
+ }
1096
+ const query = new URLSearchParams({
1097
+ access_token: accessToken,
1098
+ type: "recovery"
1099
+ });
1100
+ return fetchPublic(
1101
+ `${RESET_PASSWORD_BASE}/verify?${query.toString()}`
1102
+ );
1103
+ }
1104
+ async function establishResetSession(accessToken, refreshToken) {
1105
+ if (isBlank(accessToken)) {
1106
+ return {
1107
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1108
+ };
1109
+ }
1110
+ if (isBlank(refreshToken)) {
1111
+ return {
1112
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1113
+ };
1114
+ }
1115
+ return fetchPublic(
1116
+ `${RESET_PASSWORD_BASE}/session`,
1117
+ {
1118
+ method: "POST",
1119
+ body: JSON.stringify({ accessToken, refreshToken })
1120
+ }
1121
+ );
1122
+ }
1123
+ async function confirmPasswordReset(password) {
1124
+ if (isBlank(password)) {
1125
+ return {
1126
+ error: createError("VALIDATION_ERROR", "\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u5FC5\u9808\u3067\u3059")
1127
+ };
1128
+ }
1129
+ return fetchWithAuth(
1130
+ `${RESET_PASSWORD_BASE}/confirm`,
1131
+ {
1132
+ method: "POST",
1133
+ body: JSON.stringify({ password })
1134
+ }
1135
+ );
1136
+ }
1137
+ return {
1138
+ requestPasswordReset,
1139
+ verifyPasswordResetToken,
1140
+ establishResetSession,
1141
+ confirmPasswordReset
1142
+ };
1143
+ }
1144
+
1145
+ // src/client/otp.ts
1146
+ var OTP_BASE = "/api/v1/auth/otp";
1147
+ function isBlank2(value) {
1148
+ return !value || !value.trim();
1149
+ }
1150
+ function createOtpMethods(deps) {
1151
+ const { baseUrl, logger, createError, errorCodes } = deps;
1152
+ async function fetchPublic(endpoint, options = {}) {
1153
+ const url = `${baseUrl}${endpoint}`;
1154
+ logger.debug("Fetching (public):", url);
1155
+ let response;
1156
+ try {
1157
+ response = await fetch(url, {
1158
+ ...options,
1159
+ credentials: "include",
1160
+ headers: {
1161
+ "Content-Type": "application/json",
1162
+ ...sdkHeaders(),
1163
+ ...options.headers
1164
+ }
1165
+ });
1166
+ } catch (error) {
1167
+ logger.error("Network error:", error);
1168
+ return {
1169
+ error: {
1170
+ code: errorCodes.NETWORK_ERROR,
1171
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1172
+ }
1173
+ };
1174
+ }
1175
+ let raw;
1176
+ try {
1177
+ raw = await response.json();
1178
+ } catch (parseError) {
1179
+ logger.error("Parse error:", parseError, "Status:", response.status);
1180
+ return {
1181
+ error: {
1182
+ code: errorCodes.PARSE_ERROR,
1183
+ 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})`
1184
+ }
1185
+ };
1186
+ }
1187
+ logger.debug("Response (public):", response.status, raw);
1188
+ checkVersionHeader(response, logger);
1189
+ if (!response.ok) {
1190
+ return {
1191
+ error: raw.error ?? {
1192
+ code: errorCodes.UNKNOWN_ERROR,
1193
+ message: "OTP\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1194
+ }
1195
+ };
1196
+ }
1197
+ if (raw.data === void 0) {
1198
+ return {
1199
+ error: {
1200
+ code: errorCodes.UNKNOWN_ERROR,
1201
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1202
+ }
1203
+ };
1204
+ }
1205
+ return { data: raw.data };
1206
+ }
1207
+ async function sendOtp(email, options) {
1208
+ if (isBlank2(email)) {
1209
+ return {
1210
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1211
+ };
1212
+ }
1213
+ return fetchPublic(
1214
+ `${OTP_BASE}/send`,
1215
+ {
1216
+ method: "POST",
1217
+ body: JSON.stringify({
1218
+ email,
1219
+ ...options?.redirectUrl ? { redirectUrl: options.redirectUrl } : {}
1220
+ })
1221
+ }
1222
+ );
1223
+ }
1224
+ async function verifyOtp(params) {
1225
+ if (isBlank2(params.accessToken)) {
1226
+ return {
1227
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1228
+ };
1229
+ }
1230
+ if (isBlank2(params.refreshToken)) {
1231
+ return {
1232
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1233
+ };
1234
+ }
1235
+ return fetchPublic(
1236
+ `${OTP_BASE}/verify`,
1237
+ {
1238
+ method: "POST",
1239
+ body: JSON.stringify({
1240
+ accessToken: params.accessToken,
1241
+ refreshToken: params.refreshToken
1242
+ })
1243
+ }
1244
+ );
1245
+ }
1246
+ return {
1247
+ sendOtp,
1248
+ verifyOtp
1249
+ };
1250
+ }
1251
+
954
1252
  // src/client/ffid-client.ts
955
1253
  var UNAUTHORIZED_STATUS2 = 401;
956
1254
  var SDK_LOG_PREFIX = "[FFID SDK]";
@@ -1083,6 +1381,9 @@ function createFFIDClient(config) {
1083
1381
  }
1084
1382
  };
1085
1383
  }
1384
+ } else {
1385
+ logger.warn("Token refresh failed, returning refresh error:", refreshResult.error);
1386
+ return { error: refreshResult.error };
1086
1387
  }
1087
1388
  }
1088
1389
  let raw;
@@ -1160,6 +1461,24 @@ function createFFIDClient(config) {
1160
1461
  fetchWithAuth,
1161
1462
  createError
1162
1463
  });
1464
+ const {
1465
+ requestPasswordReset,
1466
+ verifyPasswordResetToken,
1467
+ establishResetSession,
1468
+ confirmPasswordReset
1469
+ } = createPasswordResetMethods({
1470
+ baseUrl,
1471
+ logger,
1472
+ createError,
1473
+ fetchWithAuth,
1474
+ errorCodes: FFID_ERROR_CODES
1475
+ });
1476
+ const { sendOtp, verifyOtp } = createOtpMethods({
1477
+ baseUrl,
1478
+ logger,
1479
+ createError,
1480
+ errorCodes: FFID_ERROR_CODES
1481
+ });
1163
1482
  const verifyAccessToken = createVerifyAccessToken({
1164
1483
  authMode,
1165
1484
  baseUrl,
@@ -1185,6 +1504,12 @@ function createFFIDClient(config) {
1185
1504
  createCheckoutSession,
1186
1505
  createPortalSession,
1187
1506
  verifyAccessToken,
1507
+ requestPasswordReset,
1508
+ verifyPasswordResetToken,
1509
+ establishResetSession,
1510
+ confirmPasswordReset,
1511
+ sendOtp,
1512
+ verifyOtp,
1188
1513
  /** Token store (token mode only) */
1189
1514
  tokenStore,
1190
1515
  /** Resolved auth mode */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",