@feelflow/ffid-sdk 1.11.0 → 1.14.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.
@@ -433,7 +433,7 @@ function createBillingMethods(deps) {
433
433
  }
434
434
 
435
435
  // src/client/version-check.ts
436
- var SDK_VERSION = "1.11.0";
436
+ var SDK_VERSION = "1.14.0";
437
437
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
438
438
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
439
439
  function sdkHeaders() {
@@ -479,6 +479,22 @@ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8
479
479
  var OAUTH_TOKEN_ENDPOINT = "/api/v1/oauth/token";
480
480
  var OAUTH_REVOKE_ENDPOINT = "/api/v1/oauth/revoke";
481
481
  var MS_PER_SECOND = 1e3;
482
+ function validateTokenResponse(tokenResponse) {
483
+ const invalid = [];
484
+ if (!tokenResponse.access_token) {
485
+ invalid.push("access_token");
486
+ }
487
+ if (!tokenResponse.refresh_token) {
488
+ invalid.push("refresh_token");
489
+ }
490
+ if (typeof tokenResponse.expires_in !== "number" || tokenResponse.expires_in <= 0) {
491
+ invalid.push("expires_in");
492
+ }
493
+ if (invalid.length > 0) {
494
+ 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(", ")}`;
495
+ }
496
+ return null;
497
+ }
482
498
  function createOAuthTokenMethods(deps) {
483
499
  const {
484
500
  baseUrl,
@@ -548,6 +564,16 @@ function createOAuthTokenMethods(deps) {
548
564
  }
549
565
  };
550
566
  }
567
+ const validationError = validateTokenResponse(tokenResponse);
568
+ if (validationError) {
569
+ logger.error("Token exchange validation failed:", validationError);
570
+ return {
571
+ error: {
572
+ code: errorCodes.TOKEN_EXCHANGE_ERROR,
573
+ message: validationError
574
+ }
575
+ };
576
+ }
551
577
  tokenStore.setTokens({
552
578
  accessToken: tokenResponse.access_token,
553
579
  refreshToken: tokenResponse.refresh_token,
@@ -616,6 +642,16 @@ function createOAuthTokenMethods(deps) {
616
642
  }
617
643
  };
618
644
  }
645
+ const validationError = validateTokenResponse(tokenResponse);
646
+ if (validationError) {
647
+ logger.error("Token refresh validation failed:", validationError);
648
+ return {
649
+ error: {
650
+ code: errorCodes.TOKEN_REFRESH_ERROR,
651
+ message: validationError
652
+ }
653
+ };
654
+ }
619
655
  tokenStore.setTokens({
620
656
  accessToken: tokenResponse.access_token,
621
657
  refreshToken: tokenResponse.refresh_token,
@@ -869,11 +905,17 @@ async function generateCodeChallenge(verifier) {
869
905
  const digest = await crypto.subtle.digest("SHA-256", data);
870
906
  return base64UrlEncode(digest);
871
907
  }
872
- function storeCodeVerifier(verifier) {
908
+ function storeCodeVerifier(verifier, logger) {
873
909
  try {
874
- if (typeof window === "undefined") return;
910
+ if (typeof window === "undefined") {
911
+ logger?.warn("storeCodeVerifier: sessionStorage is not available in SSR context");
912
+ return false;
913
+ }
875
914
  window.sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
876
- } catch {
915
+ return true;
916
+ } catch (error) {
917
+ logger?.warn("storeCodeVerifier: sessionStorage \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
918
+ return false;
877
919
  }
878
920
  }
879
921
  function base64UrlEncode(buffer) {
@@ -887,6 +929,7 @@ function base64UrlEncode(buffer) {
887
929
 
888
930
  // src/client/redirect.ts
889
931
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
932
+ var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
890
933
  var STATE_RANDOM_BYTES = 16;
891
934
  var HEX_BASE2 = 16;
892
935
  function generateRandomState() {
@@ -905,32 +948,34 @@ function createRedirectMethods(deps) {
905
948
  } = deps;
906
949
  async function redirectToAuthorize() {
907
950
  const verifier = generateCodeVerifier();
908
- storeCodeVerifier(verifier);
951
+ storeCodeVerifier(verifier, logger);
952
+ let challenge;
909
953
  try {
910
- const challenge = await generateCodeChallenge(verifier);
911
- const state = generateRandomState();
912
- const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
913
- const params = new URLSearchParams({
914
- response_type: "code",
915
- client_id: clientId,
916
- redirect_uri: redirectUri,
917
- state,
918
- code_challenge: challenge,
919
- code_challenge_method: "S256"
920
- });
921
- const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
922
- logger.debug("Redirecting to authorize:", authorizeUrl);
923
- window.location.href = authorizeUrl;
924
- return true;
954
+ challenge = await generateCodeChallenge(verifier);
925
955
  } catch (error) {
956
+ 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";
926
957
  logger.error("PKCE \u30B3\u30FC\u30C9\u30C1\u30E3\u30EC\u30F3\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
927
- return false;
958
+ return { success: false, error: errorMessage };
928
959
  }
960
+ const state = generateRandomState();
961
+ const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
962
+ const params = new URLSearchParams({
963
+ response_type: "code",
964
+ client_id: clientId,
965
+ redirect_uri: redirectUri,
966
+ state,
967
+ code_challenge: challenge,
968
+ code_challenge_method: "S256"
969
+ });
970
+ const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
971
+ logger.debug("Redirecting to authorize:", authorizeUrl);
972
+ window.location.href = authorizeUrl;
973
+ return { success: true };
929
974
  }
930
975
  async function redirectToLogin() {
931
976
  if (typeof window === "undefined") {
932
- logger.debug("Cannot redirect in SSR context");
933
- return false;
977
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
978
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
934
979
  }
935
980
  if (authMode === "token") {
936
981
  return redirectToAuthorize();
@@ -939,17 +984,289 @@ function createRedirectMethods(deps) {
939
984
  const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(serviceCode)}`;
940
985
  logger.debug("Redirecting to login:", loginUrl);
941
986
  window.location.href = loginUrl;
942
- return true;
987
+ return { success: true };
943
988
  }
944
989
  function getLoginUrl(redirectUrl) {
945
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
990
+ let redirect;
991
+ if (redirectUrl != null) {
992
+ redirect = redirectUrl;
993
+ } else if (typeof window !== "undefined") {
994
+ redirect = window.location.href;
995
+ } else {
996
+ 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");
997
+ redirect = "";
998
+ }
946
999
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
947
1000
  }
948
1001
  function getSignupUrl(redirectUrl) {
949
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1002
+ let redirect;
1003
+ if (redirectUrl != null) {
1004
+ redirect = redirectUrl;
1005
+ } else if (typeof window !== "undefined") {
1006
+ redirect = window.location.href;
1007
+ } else {
1008
+ 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");
1009
+ redirect = "";
1010
+ }
950
1011
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
951
1012
  }
952
- return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
1013
+ function getLogoutUrl(postLogoutRedirectUri) {
1014
+ const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
1015
+ url.searchParams.set("client_id", clientId);
1016
+ if (postLogoutRedirectUri != null) {
1017
+ url.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUri);
1018
+ }
1019
+ return url.toString();
1020
+ }
1021
+ function redirectToLogout(postLogoutRedirectUri) {
1022
+ if (typeof window === "undefined") {
1023
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1024
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1025
+ }
1026
+ const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
1027
+ logger.debug("Redirecting to logout:", logoutUrl);
1028
+ window.location.href = logoutUrl;
1029
+ return { success: true };
1030
+ }
1031
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout };
1032
+ }
1033
+
1034
+ // src/client/password-reset.ts
1035
+ var RESET_PASSWORD_BASE = "/api/v1/auth/reset-password";
1036
+ function isBlank(value) {
1037
+ return !value || !value.trim();
1038
+ }
1039
+ function createPasswordResetMethods(deps) {
1040
+ const { baseUrl, logger, createError, fetchWithAuth, errorCodes } = deps;
1041
+ async function fetchPublic(endpoint, options = {}) {
1042
+ const url = `${baseUrl}${endpoint}`;
1043
+ logger.debug("Fetching (public):", url);
1044
+ let response;
1045
+ try {
1046
+ response = await fetch(url, {
1047
+ ...options,
1048
+ credentials: "include",
1049
+ headers: {
1050
+ "Content-Type": "application/json",
1051
+ ...sdkHeaders(),
1052
+ ...options.headers
1053
+ }
1054
+ });
1055
+ } catch (error) {
1056
+ logger.error("Network error:", error);
1057
+ return {
1058
+ error: {
1059
+ code: errorCodes.NETWORK_ERROR,
1060
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1061
+ }
1062
+ };
1063
+ }
1064
+ let raw;
1065
+ try {
1066
+ raw = await response.json();
1067
+ } catch (parseError) {
1068
+ logger.error("Parse error:", parseError, "Status:", response.status);
1069
+ return {
1070
+ error: {
1071
+ code: errorCodes.PARSE_ERROR,
1072
+ 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})`
1073
+ }
1074
+ };
1075
+ }
1076
+ logger.debug("Response (public):", response.status, raw);
1077
+ checkVersionHeader(response, logger);
1078
+ if (!response.ok) {
1079
+ return {
1080
+ error: raw.error ?? {
1081
+ code: errorCodes.UNKNOWN_ERROR,
1082
+ message: "\u30D1\u30B9\u30EF\u30FC\u30C9\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1083
+ }
1084
+ };
1085
+ }
1086
+ if (raw.data === void 0) {
1087
+ return {
1088
+ error: {
1089
+ code: errorCodes.UNKNOWN_ERROR,
1090
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1091
+ }
1092
+ };
1093
+ }
1094
+ return { data: raw.data };
1095
+ }
1096
+ async function requestPasswordReset(email) {
1097
+ if (isBlank(email)) {
1098
+ return {
1099
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1100
+ };
1101
+ }
1102
+ return fetchPublic(
1103
+ RESET_PASSWORD_BASE,
1104
+ {
1105
+ method: "POST",
1106
+ body: JSON.stringify({ email })
1107
+ }
1108
+ );
1109
+ }
1110
+ async function verifyPasswordResetToken(accessToken) {
1111
+ if (isBlank(accessToken)) {
1112
+ return {
1113
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1114
+ };
1115
+ }
1116
+ const query = new URLSearchParams({
1117
+ access_token: accessToken,
1118
+ type: "recovery"
1119
+ });
1120
+ return fetchPublic(
1121
+ `${RESET_PASSWORD_BASE}/verify?${query.toString()}`
1122
+ );
1123
+ }
1124
+ async function establishResetSession(accessToken, refreshToken) {
1125
+ if (isBlank(accessToken)) {
1126
+ return {
1127
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1128
+ };
1129
+ }
1130
+ if (isBlank(refreshToken)) {
1131
+ return {
1132
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1133
+ };
1134
+ }
1135
+ return fetchPublic(
1136
+ `${RESET_PASSWORD_BASE}/session`,
1137
+ {
1138
+ method: "POST",
1139
+ body: JSON.stringify({ accessToken, refreshToken })
1140
+ }
1141
+ );
1142
+ }
1143
+ async function confirmPasswordReset(password) {
1144
+ if (isBlank(password)) {
1145
+ return {
1146
+ error: createError("VALIDATION_ERROR", "\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u5FC5\u9808\u3067\u3059")
1147
+ };
1148
+ }
1149
+ return fetchWithAuth(
1150
+ `${RESET_PASSWORD_BASE}/confirm`,
1151
+ {
1152
+ method: "POST",
1153
+ body: JSON.stringify({ password })
1154
+ }
1155
+ );
1156
+ }
1157
+ return {
1158
+ requestPasswordReset,
1159
+ verifyPasswordResetToken,
1160
+ establishResetSession,
1161
+ confirmPasswordReset
1162
+ };
1163
+ }
1164
+
1165
+ // src/client/otp.ts
1166
+ var OTP_BASE = "/api/v1/auth/otp";
1167
+ function isBlank2(value) {
1168
+ return !value || !value.trim();
1169
+ }
1170
+ function createOtpMethods(deps) {
1171
+ const { baseUrl, logger, createError, errorCodes } = deps;
1172
+ async function fetchPublic(endpoint, options = {}) {
1173
+ const url = `${baseUrl}${endpoint}`;
1174
+ logger.debug("Fetching (public):", url);
1175
+ let response;
1176
+ try {
1177
+ response = await fetch(url, {
1178
+ ...options,
1179
+ credentials: "include",
1180
+ headers: {
1181
+ "Content-Type": "application/json",
1182
+ ...sdkHeaders(),
1183
+ ...options.headers
1184
+ }
1185
+ });
1186
+ } catch (error) {
1187
+ logger.error("Network error:", error);
1188
+ return {
1189
+ error: {
1190
+ code: errorCodes.NETWORK_ERROR,
1191
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1192
+ }
1193
+ };
1194
+ }
1195
+ let raw;
1196
+ try {
1197
+ raw = await response.json();
1198
+ } catch (parseError) {
1199
+ logger.error("Parse error:", parseError, "Status:", response.status);
1200
+ return {
1201
+ error: {
1202
+ code: errorCodes.PARSE_ERROR,
1203
+ 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})`
1204
+ }
1205
+ };
1206
+ }
1207
+ logger.debug("Response (public):", response.status, raw);
1208
+ checkVersionHeader(response, logger);
1209
+ if (!response.ok) {
1210
+ return {
1211
+ error: raw.error ?? {
1212
+ code: errorCodes.UNKNOWN_ERROR,
1213
+ message: "OTP\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1214
+ }
1215
+ };
1216
+ }
1217
+ if (raw.data === void 0) {
1218
+ return {
1219
+ error: {
1220
+ code: errorCodes.UNKNOWN_ERROR,
1221
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1222
+ }
1223
+ };
1224
+ }
1225
+ return { data: raw.data };
1226
+ }
1227
+ async function sendOtp(email, options) {
1228
+ if (isBlank2(email)) {
1229
+ return {
1230
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1231
+ };
1232
+ }
1233
+ return fetchPublic(
1234
+ `${OTP_BASE}/send`,
1235
+ {
1236
+ method: "POST",
1237
+ body: JSON.stringify({
1238
+ email,
1239
+ ...options?.redirectUrl ? { redirectUrl: options.redirectUrl } : {}
1240
+ })
1241
+ }
1242
+ );
1243
+ }
1244
+ async function verifyOtp(params) {
1245
+ if (isBlank2(params.accessToken)) {
1246
+ return {
1247
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1248
+ };
1249
+ }
1250
+ if (isBlank2(params.refreshToken)) {
1251
+ return {
1252
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1253
+ };
1254
+ }
1255
+ return fetchPublic(
1256
+ `${OTP_BASE}/verify`,
1257
+ {
1258
+ method: "POST",
1259
+ body: JSON.stringify({
1260
+ accessToken: params.accessToken,
1261
+ refreshToken: params.refreshToken
1262
+ })
1263
+ }
1264
+ );
1265
+ }
1266
+ return {
1267
+ sendOtp,
1268
+ verifyOtp
1269
+ };
953
1270
  }
954
1271
 
955
1272
  // src/client/ffid-client.ts
@@ -1084,6 +1401,9 @@ function createFFIDClient(config) {
1084
1401
  }
1085
1402
  };
1086
1403
  }
1404
+ } else {
1405
+ logger.warn("Token refresh failed, returning refresh error:", refreshResult.error);
1406
+ return { error: refreshResult.error };
1087
1407
  }
1088
1408
  }
1089
1409
  let raw;
@@ -1134,7 +1454,7 @@ function createFFIDClient(config) {
1134
1454
  }
1135
1455
  return signOutCookie();
1136
1456
  }
1137
- const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1457
+ const { redirectToLogin, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout } = createRedirectMethods({
1138
1458
  authMode,
1139
1459
  baseUrl,
1140
1460
  clientId,
@@ -1161,6 +1481,24 @@ function createFFIDClient(config) {
1161
1481
  fetchWithAuth,
1162
1482
  createError
1163
1483
  });
1484
+ const {
1485
+ requestPasswordReset,
1486
+ verifyPasswordResetToken,
1487
+ establishResetSession,
1488
+ confirmPasswordReset
1489
+ } = createPasswordResetMethods({
1490
+ baseUrl,
1491
+ logger,
1492
+ createError,
1493
+ fetchWithAuth,
1494
+ errorCodes: FFID_ERROR_CODES
1495
+ });
1496
+ const { sendOtp, verifyOtp } = createOtpMethods({
1497
+ baseUrl,
1498
+ logger,
1499
+ createError,
1500
+ errorCodes: FFID_ERROR_CODES
1501
+ });
1164
1502
  const verifyAccessToken = createVerifyAccessToken({
1165
1503
  authMode,
1166
1504
  baseUrl,
@@ -1177,7 +1515,9 @@ function createFFIDClient(config) {
1177
1515
  getSession,
1178
1516
  signOut,
1179
1517
  redirectToLogin,
1518
+ redirectToLogout,
1180
1519
  getLoginUrl,
1520
+ getLogoutUrl,
1181
1521
  getSignupUrl,
1182
1522
  createError,
1183
1523
  exchangeCodeForTokens,
@@ -1186,6 +1526,12 @@ function createFFIDClient(config) {
1186
1526
  createCheckoutSession,
1187
1527
  createPortalSession,
1188
1528
  verifyAccessToken,
1529
+ requestPasswordReset,
1530
+ verifyPasswordResetToken,
1531
+ establishResetSession,
1532
+ confirmPasswordReset,
1533
+ sendOtp,
1534
+ verifyOtp,
1189
1535
  /** Token store (token mode only) */
1190
1536
  tokenStore,
1191
1537
  /** Resolved auth mode */
@@ -301,6 +301,55 @@ interface FFIDCreatePortalParams {
301
301
  /** URL to redirect when user exits the portal */
302
302
  returnUrl: string;
303
303
  }
304
+ /**
305
+ * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
306
+ *
307
+ * Structured return type so callers can inspect failure reasons
308
+ * instead of receiving a bare `false`.
309
+ */
310
+ type FFIDRedirectResult = {
311
+ success: true;
312
+ } | {
313
+ success: false;
314
+ error: string;
315
+ };
316
+
317
+ /** OTP / magic link methods - sendOtp / verifyOtp */
318
+
319
+ /** Response from sendOtp */
320
+ interface FFIDOtpSendResponse {
321
+ message: string;
322
+ }
323
+ /** Response from verifyOtp */
324
+ interface FFIDOtpVerifyResponse {
325
+ user: {
326
+ id: string;
327
+ email: string;
328
+ displayName: string | null;
329
+ avatarUrl: string | null;
330
+ };
331
+ session: {
332
+ accessToken: string;
333
+ refreshToken: string;
334
+ expiresAt: number;
335
+ expiresIn: number;
336
+ };
337
+ }
338
+
339
+ /** Password reset methods - requestPasswordReset / verifyPasswordResetToken / establishResetSession / confirmPasswordReset */
340
+
341
+ /** Response from requestPasswordReset */
342
+ interface FFIDPasswordResetResponse {
343
+ message: string;
344
+ }
345
+ /** Response from verifyPasswordResetToken */
346
+ interface FFIDPasswordResetVerifyResponse {
347
+ valid: boolean;
348
+ }
349
+ /** Response from establishResetSession */
350
+ type FFIDResetSessionResponse = FFIDPasswordResetResponse;
351
+ /** Response from confirmPasswordReset */
352
+ type FFIDPasswordResetConfirmResponse = FFIDPasswordResetResponse;
304
353
 
305
354
  /**
306
355
  * Token Store
@@ -347,8 +396,10 @@ declare function createTokenStore(storageType?: 'localStorage' | 'memory'): Toke
347
396
  declare function createFFIDClient(config: FFIDConfig): {
348
397
  getSession: () => Promise<FFIDApiResponse<FFIDSessionResponse>>;
349
398
  signOut: () => Promise<FFIDApiResponse<void>>;
350
- redirectToLogin: () => Promise<boolean>;
399
+ redirectToLogin: () => Promise<FFIDRedirectResult>;
400
+ redirectToLogout: (postLogoutRedirectUri?: string) => FFIDRedirectResult;
351
401
  getLoginUrl: (redirectUrl?: string) => string;
402
+ getLogoutUrl: (postLogoutRedirectUri?: string) => string;
352
403
  getSignupUrl: (redirectUrl?: string) => string;
353
404
  createError: (code: string, message: string) => FFIDError;
354
405
  exchangeCodeForTokens: (code: string, codeVerifier?: string) => Promise<FFIDApiResponse<void>>;
@@ -360,6 +411,17 @@ declare function createFFIDClient(config: FFIDConfig): {
360
411
  createCheckoutSession: (params: FFIDCreateCheckoutParams) => Promise<FFIDApiResponse<FFIDCheckoutSessionResponse>>;
361
412
  createPortalSession: (params: FFIDCreatePortalParams) => Promise<FFIDApiResponse<FFIDPortalSessionResponse>>;
362
413
  verifyAccessToken: (accessToken: string, options?: FFIDVerifyAccessTokenOptions) => Promise<FFIDApiResponse<FFIDOAuthUserInfo>>;
414
+ requestPasswordReset: (email: string) => Promise<FFIDApiResponse<FFIDPasswordResetResponse>>;
415
+ verifyPasswordResetToken: (accessToken: string) => Promise<FFIDApiResponse<FFIDPasswordResetVerifyResponse>>;
416
+ establishResetSession: (accessToken: string, refreshToken: string) => Promise<FFIDApiResponse<FFIDResetSessionResponse>>;
417
+ confirmPasswordReset: (password: string) => Promise<FFIDApiResponse<FFIDPasswordResetConfirmResponse>>;
418
+ sendOtp: (email: string, options?: {
419
+ redirectUrl?: string;
420
+ }) => Promise<FFIDApiResponse<FFIDOtpSendResponse>>;
421
+ verifyOtp: (params: {
422
+ accessToken: string;
423
+ refreshToken: string;
424
+ }) => Promise<FFIDApiResponse<FFIDOtpVerifyResponse>>;
363
425
  /** Token store (token mode only) */
364
426
  tokenStore: TokenStore;
365
427
  /** Resolved auth mode */
@@ -428,4 +490,4 @@ interface KVNamespaceLike {
428
490
  */
429
491
  declare function createKVCacheAdapter(kv: KVNamespaceLike): FFIDCacheAdapter;
430
492
 
431
- export { type FFIDCacheAdapter, type FFIDCacheConfig, type FFIDClient, type FFIDConfig, type FFIDOAuthUserInfo, type FFIDOrganization, type FFIDSubscription, type FFIDUser, type FFIDVerifyAccessTokenOptions, type KVNamespaceLike, type TokenData, type TokenStore, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, createVerifyAccessToken };
493
+ export { type FFIDCacheAdapter, type FFIDCacheConfig, type FFIDClient, type FFIDConfig, type FFIDOAuthUserInfo, type FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, type FFIDResetSessionResponse, type FFIDSubscription, type FFIDUser, type FFIDVerifyAccessTokenOptions, type KVNamespaceLike, type TokenData, type TokenStore, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, createVerifyAccessToken };
@@ -301,6 +301,55 @@ interface FFIDCreatePortalParams {
301
301
  /** URL to redirect when user exits the portal */
302
302
  returnUrl: string;
303
303
  }
304
+ /**
305
+ * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
306
+ *
307
+ * Structured return type so callers can inspect failure reasons
308
+ * instead of receiving a bare `false`.
309
+ */
310
+ type FFIDRedirectResult = {
311
+ success: true;
312
+ } | {
313
+ success: false;
314
+ error: string;
315
+ };
316
+
317
+ /** OTP / magic link methods - sendOtp / verifyOtp */
318
+
319
+ /** Response from sendOtp */
320
+ interface FFIDOtpSendResponse {
321
+ message: string;
322
+ }
323
+ /** Response from verifyOtp */
324
+ interface FFIDOtpVerifyResponse {
325
+ user: {
326
+ id: string;
327
+ email: string;
328
+ displayName: string | null;
329
+ avatarUrl: string | null;
330
+ };
331
+ session: {
332
+ accessToken: string;
333
+ refreshToken: string;
334
+ expiresAt: number;
335
+ expiresIn: number;
336
+ };
337
+ }
338
+
339
+ /** Password reset methods - requestPasswordReset / verifyPasswordResetToken / establishResetSession / confirmPasswordReset */
340
+
341
+ /** Response from requestPasswordReset */
342
+ interface FFIDPasswordResetResponse {
343
+ message: string;
344
+ }
345
+ /** Response from verifyPasswordResetToken */
346
+ interface FFIDPasswordResetVerifyResponse {
347
+ valid: boolean;
348
+ }
349
+ /** Response from establishResetSession */
350
+ type FFIDResetSessionResponse = FFIDPasswordResetResponse;
351
+ /** Response from confirmPasswordReset */
352
+ type FFIDPasswordResetConfirmResponse = FFIDPasswordResetResponse;
304
353
 
305
354
  /**
306
355
  * Token Store
@@ -347,8 +396,10 @@ declare function createTokenStore(storageType?: 'localStorage' | 'memory'): Toke
347
396
  declare function createFFIDClient(config: FFIDConfig): {
348
397
  getSession: () => Promise<FFIDApiResponse<FFIDSessionResponse>>;
349
398
  signOut: () => Promise<FFIDApiResponse<void>>;
350
- redirectToLogin: () => Promise<boolean>;
399
+ redirectToLogin: () => Promise<FFIDRedirectResult>;
400
+ redirectToLogout: (postLogoutRedirectUri?: string) => FFIDRedirectResult;
351
401
  getLoginUrl: (redirectUrl?: string) => string;
402
+ getLogoutUrl: (postLogoutRedirectUri?: string) => string;
352
403
  getSignupUrl: (redirectUrl?: string) => string;
353
404
  createError: (code: string, message: string) => FFIDError;
354
405
  exchangeCodeForTokens: (code: string, codeVerifier?: string) => Promise<FFIDApiResponse<void>>;
@@ -360,6 +411,17 @@ declare function createFFIDClient(config: FFIDConfig): {
360
411
  createCheckoutSession: (params: FFIDCreateCheckoutParams) => Promise<FFIDApiResponse<FFIDCheckoutSessionResponse>>;
361
412
  createPortalSession: (params: FFIDCreatePortalParams) => Promise<FFIDApiResponse<FFIDPortalSessionResponse>>;
362
413
  verifyAccessToken: (accessToken: string, options?: FFIDVerifyAccessTokenOptions) => Promise<FFIDApiResponse<FFIDOAuthUserInfo>>;
414
+ requestPasswordReset: (email: string) => Promise<FFIDApiResponse<FFIDPasswordResetResponse>>;
415
+ verifyPasswordResetToken: (accessToken: string) => Promise<FFIDApiResponse<FFIDPasswordResetVerifyResponse>>;
416
+ establishResetSession: (accessToken: string, refreshToken: string) => Promise<FFIDApiResponse<FFIDResetSessionResponse>>;
417
+ confirmPasswordReset: (password: string) => Promise<FFIDApiResponse<FFIDPasswordResetConfirmResponse>>;
418
+ sendOtp: (email: string, options?: {
419
+ redirectUrl?: string;
420
+ }) => Promise<FFIDApiResponse<FFIDOtpSendResponse>>;
421
+ verifyOtp: (params: {
422
+ accessToken: string;
423
+ refreshToken: string;
424
+ }) => Promise<FFIDApiResponse<FFIDOtpVerifyResponse>>;
363
425
  /** Token store (token mode only) */
364
426
  tokenStore: TokenStore;
365
427
  /** Resolved auth mode */
@@ -428,4 +490,4 @@ interface KVNamespaceLike {
428
490
  */
429
491
  declare function createKVCacheAdapter(kv: KVNamespaceLike): FFIDCacheAdapter;
430
492
 
431
- export { type FFIDCacheAdapter, type FFIDCacheConfig, type FFIDClient, type FFIDConfig, type FFIDOAuthUserInfo, type FFIDOrganization, type FFIDSubscription, type FFIDUser, type FFIDVerifyAccessTokenOptions, type KVNamespaceLike, type TokenData, type TokenStore, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, createVerifyAccessToken };
493
+ export { type FFIDCacheAdapter, type FFIDCacheConfig, type FFIDClient, type FFIDConfig, type FFIDOAuthUserInfo, type FFIDOrganization, type FFIDOtpSendResponse, type FFIDOtpVerifyResponse, type FFIDPasswordResetConfirmResponse, type FFIDPasswordResetResponse, type FFIDPasswordResetVerifyResponse, type FFIDResetSessionResponse, type FFIDSubscription, type FFIDUser, type FFIDVerifyAccessTokenOptions, type KVNamespaceLike, type TokenData, type TokenStore, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createTokenStore, createVerifyAccessToken };