@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.
@@ -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.14.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
  }
@@ -903,6 +946,7 @@ function base64UrlEncode(buffer) {
903
946
 
904
947
  // src/client/redirect.ts
905
948
  var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
949
+ var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
906
950
  var STATE_RANDOM_BYTES = 16;
907
951
  var HEX_BASE2 = 16;
908
952
  function generateRandomState() {
@@ -921,32 +965,34 @@ function createRedirectMethods(deps) {
921
965
  } = deps;
922
966
  async function redirectToAuthorize() {
923
967
  const verifier = generateCodeVerifier();
924
- storeCodeVerifier(verifier);
968
+ storeCodeVerifier(verifier, logger);
969
+ let challenge;
925
970
  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;
971
+ challenge = await generateCodeChallenge(verifier);
941
972
  } catch (error) {
973
+ 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
974
  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;
975
+ return { success: false, error: errorMessage };
944
976
  }
977
+ const state = generateRandomState();
978
+ const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
979
+ const params = new URLSearchParams({
980
+ response_type: "code",
981
+ client_id: clientId,
982
+ redirect_uri: redirectUri,
983
+ state,
984
+ code_challenge: challenge,
985
+ code_challenge_method: "S256"
986
+ });
987
+ const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
988
+ logger.debug("Redirecting to authorize:", authorizeUrl);
989
+ window.location.href = authorizeUrl;
990
+ return { success: true };
945
991
  }
946
992
  async function redirectToLogin() {
947
993
  if (typeof window === "undefined") {
948
- logger.debug("Cannot redirect in SSR context");
949
- return false;
994
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
995
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
950
996
  }
951
997
  if (authMode === "token") {
952
998
  return redirectToAuthorize();
@@ -955,17 +1001,289 @@ function createRedirectMethods(deps) {
955
1001
  const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(serviceCode)}`;
956
1002
  logger.debug("Redirecting to login:", loginUrl);
957
1003
  window.location.href = loginUrl;
958
- return true;
1004
+ return { success: true };
959
1005
  }
960
1006
  function getLoginUrl(redirectUrl) {
961
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1007
+ let redirect;
1008
+ if (redirectUrl != null) {
1009
+ redirect = redirectUrl;
1010
+ } else if (typeof window !== "undefined") {
1011
+ redirect = window.location.href;
1012
+ } else {
1013
+ 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");
1014
+ redirect = "";
1015
+ }
962
1016
  return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
963
1017
  }
964
1018
  function getSignupUrl(redirectUrl) {
965
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1019
+ let redirect;
1020
+ if (redirectUrl != null) {
1021
+ redirect = redirectUrl;
1022
+ } else if (typeof window !== "undefined") {
1023
+ redirect = window.location.href;
1024
+ } else {
1025
+ 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");
1026
+ redirect = "";
1027
+ }
966
1028
  return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
967
1029
  }
968
- return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
1030
+ function getLogoutUrl(postLogoutRedirectUri) {
1031
+ const url = new URL(`${baseUrl}${AUTH_LOGOUT_ENDPOINT}`);
1032
+ url.searchParams.set("client_id", clientId);
1033
+ if (postLogoutRedirectUri != null) {
1034
+ url.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUri);
1035
+ }
1036
+ return url.toString();
1037
+ }
1038
+ function redirectToLogout(postLogoutRedirectUri) {
1039
+ if (typeof window === "undefined") {
1040
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
1041
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
1042
+ }
1043
+ const logoutUrl = getLogoutUrl(postLogoutRedirectUri);
1044
+ logger.debug("Redirecting to logout:", logoutUrl);
1045
+ window.location.href = logoutUrl;
1046
+ return { success: true };
1047
+ }
1048
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout };
1049
+ }
1050
+
1051
+ // src/client/password-reset.ts
1052
+ var RESET_PASSWORD_BASE = "/api/v1/auth/reset-password";
1053
+ function isBlank(value) {
1054
+ return !value || !value.trim();
1055
+ }
1056
+ function createPasswordResetMethods(deps) {
1057
+ const { baseUrl, logger, createError, fetchWithAuth, errorCodes } = deps;
1058
+ async function fetchPublic(endpoint, options = {}) {
1059
+ const url = `${baseUrl}${endpoint}`;
1060
+ logger.debug("Fetching (public):", url);
1061
+ let response;
1062
+ try {
1063
+ response = await fetch(url, {
1064
+ ...options,
1065
+ credentials: "include",
1066
+ headers: {
1067
+ "Content-Type": "application/json",
1068
+ ...sdkHeaders(),
1069
+ ...options.headers
1070
+ }
1071
+ });
1072
+ } catch (error) {
1073
+ logger.error("Network error:", error);
1074
+ return {
1075
+ error: {
1076
+ code: errorCodes.NETWORK_ERROR,
1077
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1078
+ }
1079
+ };
1080
+ }
1081
+ let raw;
1082
+ try {
1083
+ raw = await response.json();
1084
+ } catch (parseError) {
1085
+ logger.error("Parse error:", parseError, "Status:", response.status);
1086
+ return {
1087
+ error: {
1088
+ code: errorCodes.PARSE_ERROR,
1089
+ 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})`
1090
+ }
1091
+ };
1092
+ }
1093
+ logger.debug("Response (public):", response.status, raw);
1094
+ checkVersionHeader(response, logger);
1095
+ if (!response.ok) {
1096
+ return {
1097
+ error: raw.error ?? {
1098
+ code: errorCodes.UNKNOWN_ERROR,
1099
+ message: "\u30D1\u30B9\u30EF\u30FC\u30C9\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1100
+ }
1101
+ };
1102
+ }
1103
+ if (raw.data === void 0) {
1104
+ return {
1105
+ error: {
1106
+ code: errorCodes.UNKNOWN_ERROR,
1107
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1108
+ }
1109
+ };
1110
+ }
1111
+ return { data: raw.data };
1112
+ }
1113
+ async function requestPasswordReset(email) {
1114
+ if (isBlank(email)) {
1115
+ return {
1116
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1117
+ };
1118
+ }
1119
+ return fetchPublic(
1120
+ RESET_PASSWORD_BASE,
1121
+ {
1122
+ method: "POST",
1123
+ body: JSON.stringify({ email })
1124
+ }
1125
+ );
1126
+ }
1127
+ async function verifyPasswordResetToken(accessToken) {
1128
+ if (isBlank(accessToken)) {
1129
+ return {
1130
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1131
+ };
1132
+ }
1133
+ const query = new URLSearchParams({
1134
+ access_token: accessToken,
1135
+ type: "recovery"
1136
+ });
1137
+ return fetchPublic(
1138
+ `${RESET_PASSWORD_BASE}/verify?${query.toString()}`
1139
+ );
1140
+ }
1141
+ async function establishResetSession(accessToken, refreshToken) {
1142
+ if (isBlank(accessToken)) {
1143
+ return {
1144
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1145
+ };
1146
+ }
1147
+ if (isBlank(refreshToken)) {
1148
+ return {
1149
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1150
+ };
1151
+ }
1152
+ return fetchPublic(
1153
+ `${RESET_PASSWORD_BASE}/session`,
1154
+ {
1155
+ method: "POST",
1156
+ body: JSON.stringify({ accessToken, refreshToken })
1157
+ }
1158
+ );
1159
+ }
1160
+ async function confirmPasswordReset(password) {
1161
+ if (isBlank(password)) {
1162
+ return {
1163
+ error: createError("VALIDATION_ERROR", "\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u5FC5\u9808\u3067\u3059")
1164
+ };
1165
+ }
1166
+ return fetchWithAuth(
1167
+ `${RESET_PASSWORD_BASE}/confirm`,
1168
+ {
1169
+ method: "POST",
1170
+ body: JSON.stringify({ password })
1171
+ }
1172
+ );
1173
+ }
1174
+ return {
1175
+ requestPasswordReset,
1176
+ verifyPasswordResetToken,
1177
+ establishResetSession,
1178
+ confirmPasswordReset
1179
+ };
1180
+ }
1181
+
1182
+ // src/client/otp.ts
1183
+ var OTP_BASE = "/api/v1/auth/otp";
1184
+ function isBlank2(value) {
1185
+ return !value || !value.trim();
1186
+ }
1187
+ function createOtpMethods(deps) {
1188
+ const { baseUrl, logger, createError, errorCodes } = deps;
1189
+ async function fetchPublic(endpoint, options = {}) {
1190
+ const url = `${baseUrl}${endpoint}`;
1191
+ logger.debug("Fetching (public):", url);
1192
+ let response;
1193
+ try {
1194
+ response = await fetch(url, {
1195
+ ...options,
1196
+ credentials: "include",
1197
+ headers: {
1198
+ "Content-Type": "application/json",
1199
+ ...sdkHeaders(),
1200
+ ...options.headers
1201
+ }
1202
+ });
1203
+ } catch (error) {
1204
+ logger.error("Network error:", error);
1205
+ return {
1206
+ error: {
1207
+ code: errorCodes.NETWORK_ERROR,
1208
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1209
+ }
1210
+ };
1211
+ }
1212
+ let raw;
1213
+ try {
1214
+ raw = await response.json();
1215
+ } catch (parseError) {
1216
+ logger.error("Parse error:", parseError, "Status:", response.status);
1217
+ return {
1218
+ error: {
1219
+ code: errorCodes.PARSE_ERROR,
1220
+ 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})`
1221
+ }
1222
+ };
1223
+ }
1224
+ logger.debug("Response (public):", response.status, raw);
1225
+ checkVersionHeader(response, logger);
1226
+ if (!response.ok) {
1227
+ return {
1228
+ error: raw.error ?? {
1229
+ code: errorCodes.UNKNOWN_ERROR,
1230
+ message: "OTP\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1231
+ }
1232
+ };
1233
+ }
1234
+ if (raw.data === void 0) {
1235
+ return {
1236
+ error: {
1237
+ code: errorCodes.UNKNOWN_ERROR,
1238
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1239
+ }
1240
+ };
1241
+ }
1242
+ return { data: raw.data };
1243
+ }
1244
+ async function sendOtp(email, options) {
1245
+ if (isBlank2(email)) {
1246
+ return {
1247
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1248
+ };
1249
+ }
1250
+ return fetchPublic(
1251
+ `${OTP_BASE}/send`,
1252
+ {
1253
+ method: "POST",
1254
+ body: JSON.stringify({
1255
+ email,
1256
+ ...options?.redirectUrl ? { redirectUrl: options.redirectUrl } : {}
1257
+ })
1258
+ }
1259
+ );
1260
+ }
1261
+ async function verifyOtp(params) {
1262
+ if (isBlank2(params.accessToken)) {
1263
+ return {
1264
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1265
+ };
1266
+ }
1267
+ if (isBlank2(params.refreshToken)) {
1268
+ return {
1269
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1270
+ };
1271
+ }
1272
+ return fetchPublic(
1273
+ `${OTP_BASE}/verify`,
1274
+ {
1275
+ method: "POST",
1276
+ body: JSON.stringify({
1277
+ accessToken: params.accessToken,
1278
+ refreshToken: params.refreshToken
1279
+ })
1280
+ }
1281
+ );
1282
+ }
1283
+ return {
1284
+ sendOtp,
1285
+ verifyOtp
1286
+ };
969
1287
  }
970
1288
 
971
1289
  // src/client/ffid-client.ts
@@ -1100,6 +1418,9 @@ function createFFIDClient(config) {
1100
1418
  }
1101
1419
  };
1102
1420
  }
1421
+ } else {
1422
+ logger.warn("Token refresh failed, returning refresh error:", refreshResult.error);
1423
+ return { error: refreshResult.error };
1103
1424
  }
1104
1425
  }
1105
1426
  let raw;
@@ -1150,7 +1471,7 @@ function createFFIDClient(config) {
1150
1471
  }
1151
1472
  return signOutCookie();
1152
1473
  }
1153
- const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1474
+ const { redirectToLogin, getLoginUrl, getSignupUrl, getLogoutUrl, redirectToLogout } = createRedirectMethods({
1154
1475
  authMode,
1155
1476
  baseUrl,
1156
1477
  clientId,
@@ -1177,6 +1498,24 @@ function createFFIDClient(config) {
1177
1498
  fetchWithAuth,
1178
1499
  createError
1179
1500
  });
1501
+ const {
1502
+ requestPasswordReset,
1503
+ verifyPasswordResetToken,
1504
+ establishResetSession,
1505
+ confirmPasswordReset
1506
+ } = createPasswordResetMethods({
1507
+ baseUrl,
1508
+ logger,
1509
+ createError,
1510
+ fetchWithAuth,
1511
+ errorCodes: FFID_ERROR_CODES
1512
+ });
1513
+ const { sendOtp, verifyOtp } = createOtpMethods({
1514
+ baseUrl,
1515
+ logger,
1516
+ createError,
1517
+ errorCodes: FFID_ERROR_CODES
1518
+ });
1180
1519
  const verifyAccessToken = createVerifyAccessToken({
1181
1520
  authMode,
1182
1521
  baseUrl,
@@ -1193,7 +1532,9 @@ function createFFIDClient(config) {
1193
1532
  getSession,
1194
1533
  signOut,
1195
1534
  redirectToLogin,
1535
+ redirectToLogout,
1196
1536
  getLoginUrl,
1537
+ getLogoutUrl,
1197
1538
  getSignupUrl,
1198
1539
  createError,
1199
1540
  exchangeCodeForTokens,
@@ -1202,6 +1543,12 @@ function createFFIDClient(config) {
1202
1543
  createCheckoutSession,
1203
1544
  createPortalSession,
1204
1545
  verifyAccessToken,
1546
+ requestPasswordReset,
1547
+ verifyPasswordResetToken,
1548
+ establishResetSession,
1549
+ confirmPasswordReset,
1550
+ sendOtp,
1551
+ verifyOtp,
1205
1552
  /** Token store (token mode only) */
1206
1553
  tokenStore,
1207
1554
  /** Resolved auth mode */