@djangocfg/api 2.1.481 → 2.1.483

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.
Files changed (51) hide show
  1. package/README.md +1 -1
  2. package/dist/auth.cjs +64 -25
  3. package/dist/auth.cjs.map +1 -1
  4. package/dist/auth.d.cts +14 -1
  5. package/dist/auth.d.ts +14 -1
  6. package/dist/auth.mjs +64 -25
  7. package/dist/auth.mjs.map +1 -1
  8. package/dist/clients.cjs +29 -8
  9. package/dist/clients.cjs.map +1 -1
  10. package/dist/clients.d.cts +22 -2
  11. package/dist/clients.d.ts +22 -2
  12. package/dist/clients.mjs +29 -8
  13. package/dist/clients.mjs.map +1 -1
  14. package/dist/hooks.cjs +29 -8
  15. package/dist/hooks.cjs.map +1 -1
  16. package/dist/hooks.mjs +29 -8
  17. package/dist/hooks.mjs.map +1 -1
  18. package/dist/index.cjs +31 -9
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +32 -4
  21. package/dist/index.d.ts +32 -4
  22. package/dist/index.mjs +31 -9
  23. package/dist/index.mjs.map +1 -1
  24. package/package.json +2 -2
  25. package/src/_api/generated/_cfg_accounts/api.ts +3 -3
  26. package/src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOtpConsentPolicyRetrieve.ts +2 -2
  27. package/src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOtpRequestCreate.ts +2 -2
  28. package/src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOtpVerifyCreate.ts +2 -2
  29. package/src/_api/generated/_cfg_accounts/index.ts +1 -1
  30. package/src/_api/generated/_cfg_accounts/openapi.json +21 -43
  31. package/src/_api/generated/_cfg_accounts/schemas/OAuthCallbackRequestRequest.ts +1 -0
  32. package/src/_api/generated/_cfg_accounts/schemas/OAuthTokenResponse.ts +1 -0
  33. package/src/_api/generated/_cfg_accounts/schemas/OTPVerifyRequest.ts +1 -0
  34. package/src/_api/generated/_cfg_accounts/schemas/OTPVerifyResponse.ts +1 -0
  35. package/src/_api/generated/_cfg_totp/openapi.json +5 -0
  36. package/src/_api/generated/_cfg_totp/schemas/VerifyResponse.ts +1 -0
  37. package/src/_api/generated/helpers/auth.ts +27 -6
  38. package/src/_api/generated/index.ts +1 -1
  39. package/src/_api/generated/openapi.json +26 -43
  40. package/src/_api/generated/sdk.gen.ts +1 -1
  41. package/src/_api/generated/types.gen.ts +28 -0
  42. package/src/auth/context/AccountsContext.tsx +1 -0
  43. package/src/auth/context/AuthContext.tsx +3 -2
  44. package/src/auth/context/types.ts +1 -1
  45. package/src/auth/hooks/useAuthForm.ts +3 -2
  46. package/src/auth/hooks/useAuthFormState.ts +3 -0
  47. package/src/auth/hooks/useAuthRedirect.ts +2 -2
  48. package/src/auth/hooks/useGithubAuth.ts +34 -21
  49. package/src/auth/hooks/useTwoFactorVerify.ts +5 -3
  50. package/src/auth/types/form.ts +3 -1
  51. package/src/index.ts +5 -1
package/README.md CHANGED
@@ -55,7 +55,7 @@ const tokens = await CfgAccountsAuth.cfgAccountsTokenRefreshCreate({
55
55
  import { AuthProvider, useAuth } from '@djangocfg/api/auth';
56
56
 
57
57
  // Provider
58
- <AuthProvider config={{ routes: { auth: '/auth', defaultCallback: '/dashboard' } }}>
58
+ <AuthProvider config={{ routes: { auth: '/auth', defaultCallback: '/private' } }}>
59
59
  {children}
60
60
  </AuthProvider>
61
61
 
package/dist/auth.cjs CHANGED
@@ -182,6 +182,24 @@ var localStorageBackend = {
182
182
  }
183
183
  }
184
184
  };
185
+ var sessionStorageBackend = {
186
+ get(key) {
187
+ if (!isBrowser) return null;
188
+ try {
189
+ return window.sessionStorage.getItem(key);
190
+ } catch {
191
+ return null;
192
+ }
193
+ },
194
+ set(key, value) {
195
+ if (!isBrowser) return;
196
+ try {
197
+ if (value === null) window.sessionStorage.removeItem(key);
198
+ else window.sessionStorage.setItem(key, value);
199
+ } catch {
200
+ }
201
+ }
202
+ };
185
203
  var COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
186
204
  var cookieBackend = {
187
205
  get(key) {
@@ -189,7 +207,8 @@ var cookieBackend = {
189
207
  try {
190
208
  const re = new RegExp(`(?:^|;\\s*)${encodeURIComponent(key)}=([^;]*)`);
191
209
  const m = document.cookie.match(re);
192
- return m ? decodeURIComponent(m[1]) : null;
210
+ const value = m?.[1];
211
+ return value === void 0 ? null : decodeURIComponent(value);
193
212
  } catch {
194
213
  return null;
195
214
  }
@@ -215,7 +234,8 @@ function detectLocale() {
215
234
  try {
216
235
  if (typeof document !== "undefined") {
217
236
  const m = document.cookie.match(/(?:^|;\s*)NEXT_LOCALE=([^;]*)/);
218
- if (m) return decodeURIComponent(m[1]);
237
+ const locale = m?.[1];
238
+ if (locale !== void 0) return decodeURIComponent(locale);
219
239
  }
220
240
  if (typeof navigator !== "undefined" && navigator.language) {
221
241
  return navigator.language;
@@ -365,7 +385,7 @@ var auth = {
365
385
  },
366
386
  setStorageMode(mode) {
367
387
  _storageMode = mode;
368
- _storage = mode === "cookie" ? cookieBackend : localStorageBackend;
388
+ _storage = mode === "cookie" ? cookieBackend : mode === "sessionStorage" ? sessionStorageBackend : localStorageBackend;
369
389
  notifySessionChanged();
370
390
  },
371
391
  // ── Bearer token ──────────────────────────────────────────────────
@@ -629,7 +649,7 @@ __name(_getDpopKeyPair, "_getDpopKeyPair");
629
649
  function _b64urlFromBytes(bytes) {
630
650
  const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
631
651
  let s = "";
632
- for (let i = 0; i < arr.length; i++) s += String.fromCharCode(arr[i]);
652
+ for (let i = 0; i < arr.length; i++) s += String.fromCharCode(arr[i] ?? 0);
633
653
  return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
634
654
  }
635
655
  __name(_b64urlFromBytes, "_b64urlFromBytes");
@@ -647,7 +667,8 @@ async function _makeDpopProof(method, url) {
647
667
  const pair = await _getDpopKeyPair();
648
668
  const jwk = await _publicJwk(pair.publicKey);
649
669
  const header = { typ: "dpop+jwt", alg: "ES256", jwk };
650
- const htu = url.split("#")[0].split("?")[0];
670
+ const withoutFragment = url.split("#")[0] ?? url;
671
+ const htu = withoutFragment.split("?")[0] ?? withoutFragment;
651
672
  const jti = crypto.randomUUID && crypto.randomUUID() || _b64urlFromBytes(crypto.getRandomValues(new Uint8Array(16)));
652
673
  const payload = { htm: method.toUpperCase(), htu, iat: Math.floor(Date.now() / 1e3), jti };
653
674
  const signingInput = `${_b64urlFromString(JSON.stringify(header))}.${_b64urlFromString(JSON.stringify(payload))}`;
@@ -1019,6 +1040,7 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "") => {
1019
1040
  const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
1020
1041
  const [acceptedTerms, setAcceptedTerms] = (0, import_react4.useState)(true);
1021
1042
  const [marketingConsent, setMarketingConsent] = (0, import_react4.useState)(null);
1043
+ const [rememberMe, setRememberMe] = (0, import_react4.useState)(false);
1022
1044
  const [step, setStep] = (0, import_react4.useState)("identifier");
1023
1045
  const [error, setError] = (0, import_react4.useState)("");
1024
1046
  const [webmail, setWebmail] = (0, import_react4.useState)(null);
@@ -1053,6 +1075,7 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "") => {
1053
1075
  isLoading,
1054
1076
  acceptedTerms,
1055
1077
  marketingConsent,
1078
+ rememberMe,
1056
1079
  step,
1057
1080
  error,
1058
1081
  twoFactorSessionId,
@@ -1068,6 +1091,7 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "") => {
1068
1091
  setOtp,
1069
1092
  setAcceptedTerms,
1070
1093
  setMarketingConsent,
1094
+ setRememberMe,
1071
1095
  setError,
1072
1096
  clearError,
1073
1097
  setStep,
@@ -2061,9 +2085,9 @@ var CfgAccountsOauth = class {
2061
2085
  return (options?.client ?? client).get({ url: "/cfg/accounts/oauth/providers/", ...options });
2062
2086
  }
2063
2087
  };
2064
- var CfgAccounts = class {
2088
+ var CfgAccountsOtp = class {
2065
2089
  static {
2066
- __name(this, "CfgAccounts");
2090
+ __name(this, "CfgAccountsOtp");
2067
2091
  }
2068
2092
  /**
2069
2093
  * Marketing-consent checkbox policy for the caller's jurisdiction.
@@ -2674,7 +2698,7 @@ var consumeSavedRedirect = /* @__PURE__ */ __name(() => {
2674
2698
  return stored || null;
2675
2699
  }, "consumeSavedRedirect");
2676
2700
  var useAuthRedirectManager = /* @__PURE__ */ __name((options = {}) => {
2677
- const { fallbackUrl = "/dashboard", clearOnUse = true } = options;
2701
+ const { fallbackUrl = "/private", clearOnUse = true } = options;
2678
2702
  const [redirectUrl, setRedirectUrl, removeRedirectUrl] = useSessionStorage(AUTH_REDIRECT_KEY, "");
2679
2703
  const setRedirect = /* @__PURE__ */ __name((url) => {
2680
2704
  setRedirectUrl(url);
@@ -2753,6 +2777,7 @@ var useTwoFactorVerify = /* @__PURE__ */ __name((options = {}) => {
2753
2777
  setError(null);
2754
2778
  }, []);
2755
2779
  const handleSuccess = (0, import_react8.useCallback)((response) => {
2780
+ auth.setStorageMode(response.persistent_session ? "localStorage" : "sessionStorage");
2756
2781
  auth.setSession({ access: response.access_token, refresh: response.refresh_token });
2757
2782
  if (response.warning) {
2758
2783
  setWarning(response.warning);
@@ -2769,7 +2794,7 @@ var useTwoFactorVerify = /* @__PURE__ */ __name((options = {}) => {
2769
2794
  }
2770
2795
  onSuccess?.(response.user);
2771
2796
  if (!skipRedirect) {
2772
- const finalRedirectUrl = consumeSavedRedirect() || redirectUrl || "/";
2797
+ const finalRedirectUrl = consumeSavedRedirect() || redirectUrl || "/private";
2773
2798
  authLogger.info("2FA successful, redirecting to:", finalRedirectUrl);
2774
2799
  router.hardPush(finalRedirectUrl);
2775
2800
  }
@@ -2903,6 +2928,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
2903
2928
  isLoading,
2904
2929
  acceptedTerms,
2905
2930
  marketingConsent,
2931
+ rememberMe,
2906
2932
  twoFactorSessionId,
2907
2933
  twoFactorCode,
2908
2934
  useBackupCode,
@@ -3018,7 +3044,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
3018
3044
  setIsLoading(true);
3019
3045
  clearError();
3020
3046
  try {
3021
- const result = await verifyOTP(submitIdentifier, submitOtp, sourceUrl, redirectUrl, true);
3047
+ const result = await verifyOTP(submitIdentifier, submitOtp, sourceUrl, redirectUrl, true, rememberMe);
3022
3048
  if (result.requires_2fa && result.session_id) {
3023
3049
  authLogger.info("2FA required after OTP verification");
3024
3050
  setTwoFactorSessionId(result.session_id);
@@ -3046,7 +3072,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
3046
3072
  } finally {
3047
3073
  setIsLoading(false);
3048
3074
  }
3049
- }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
3075
+ }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, rememberMe, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
3050
3076
  const handleOTPSubmit = (0, import_react9.useCallback)(async (e) => {
3051
3077
  e.preventDefault();
3052
3078
  await submitOTP(identifier, otp);
@@ -3165,13 +3191,14 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
3165
3191
  // src/auth/hooks/useGithubAuth.ts
3166
3192
  var import_react10 = require("react");
3167
3193
  var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
3168
- const { sourceUrl, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
3194
+ const { sourceUrl, rememberMe = false, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
3169
3195
  const router = useCfgRouter();
3170
3196
  const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
3171
3197
  const [error, setError] = (0, import_react10.useState)(null);
3172
3198
  const startGithubAuth = (0, import_react10.useCallback)(async () => {
3173
3199
  setIsLoading(true);
3174
3200
  setError(null);
3201
+ auth.clearSession();
3175
3202
  try {
3176
3203
  authLogger.info("Starting GitHub OAuth flow...");
3177
3204
  Analytics.event("auth_oauth_start" /* AUTH_OAUTH_START */, {
@@ -3192,6 +3219,7 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
3192
3219
  if (typeof window !== "undefined") {
3193
3220
  sessionStorage.setItem("oauth_state", response.state);
3194
3221
  sessionStorage.setItem("oauth_provider", "github");
3222
+ sessionStorage.setItem("cfg.oauth.remember_me", String(rememberMe));
3195
3223
  }
3196
3224
  window.location.href = response.authorization_url;
3197
3225
  } catch (err) {
@@ -3206,22 +3234,25 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
3206
3234
  } finally {
3207
3235
  setIsLoading(false);
3208
3236
  }
3209
- }, [sourceUrl, onError]);
3237
+ }, [sourceUrl, rememberMe, onError]);
3210
3238
  const handleGithubCallback = (0, import_react10.useCallback)(async (code, state) => {
3211
3239
  setIsLoading(true);
3212
3240
  setError(null);
3213
3241
  try {
3214
3242
  authLogger.info("Processing GitHub OAuth callback...");
3243
+ let rememberMeForSession = false;
3215
3244
  if (typeof window !== "undefined") {
3216
3245
  const storedState = sessionStorage.getItem("oauth_state");
3217
3246
  if (storedState && storedState !== state) {
3218
3247
  throw new Error("Invalid OAuth state - possible CSRF attack");
3219
3248
  }
3249
+ rememberMeForSession = sessionStorage.getItem("cfg.oauth.remember_me") === "true";
3220
3250
  sessionStorage.removeItem("oauth_state");
3221
3251
  sessionStorage.removeItem("oauth_provider");
3252
+ sessionStorage.removeItem("cfg.oauth.remember_me");
3222
3253
  }
3223
3254
  const result = await CfgAccountsOauth.cfgAccountsOauthGithubCallbackCreate({
3224
- body: { code, state },
3255
+ body: { code, state, remember_me: rememberMeForSession },
3225
3256
  throwOnError: true
3226
3257
  });
3227
3258
  const response = result.data;
@@ -3238,6 +3269,7 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
3238
3269
  throw new Error("Invalid response from OAuth callback");
3239
3270
  }
3240
3271
  authLogger.info("GitHub OAuth successful, user:", response.user);
3272
+ auth.setStorageMode(response.persistent_session ? "localStorage" : "sessionStorage");
3241
3273
  auth.setSession({ access: response.access, refresh: response.refresh });
3242
3274
  Analytics.event("auth_login_success" /* AUTH_LOGIN_SUCCESS */, {
3243
3275
  category: "auth" /* AUTH */,
@@ -3248,9 +3280,10 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
3248
3280
  }
3249
3281
  onSuccess?.(response.user, response.is_new_user || false);
3250
3282
  if (!skipRedirect) {
3251
- const finalRedirectUrl = redirectUrl || "/dashboard";
3283
+ const finalRedirectUrl = redirectUrl || "/private";
3252
3284
  router.hardPush(finalRedirectUrl);
3253
3285
  }
3286
+ return;
3254
3287
  } catch (err) {
3255
3288
  const errorMessage = err instanceof Error ? err.message : "GitHub authentication failed";
3256
3289
  authLogger.error("GitHub OAuth callback error:", err);
@@ -3263,7 +3296,7 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
3263
3296
  } finally {
3264
3297
  setIsLoading(false);
3265
3298
  }
3266
- }, [onSuccess, onError, redirectUrl, router]);
3299
+ }, [onSuccess, onError, onRequires2FA, redirectUrl, router, skipRedirect]);
3267
3300
  return {
3268
3301
  isLoading,
3269
3302
  error,
@@ -3897,7 +3930,8 @@ var OAuthTokenResponseSchema = import_zod10.z.object({
3897
3930
  user: import_zod10.z.object({}).passthrough().nullable().optional(),
3898
3931
  is_new_user: import_zod10.z.boolean().default(false).optional(),
3899
3932
  is_new_connection: import_zod10.z.boolean().default(false).optional(),
3900
- should_prompt_2fa: import_zod10.z.boolean().optional()
3933
+ should_prompt_2fa: import_zod10.z.boolean().optional(),
3934
+ persistent_session: import_zod10.z.boolean().optional()
3901
3935
  });
3902
3936
 
3903
3937
  // src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOauthProvidersRetrieve.ts
@@ -3957,7 +3991,7 @@ function useCfgAccountsOtpRequestCreate(config) {
3957
3991
  return (0, import_mutation7.default)(
3958
3992
  ["cfg_accounts_otp_request_create"],
3959
3993
  async (_key, { arg }) => {
3960
- const res = await CfgAccounts.cfgAccountsOtpRequestCreate({ ...arg, throwOnError: true });
3994
+ const res = await CfgAccountsOtp.cfgAccountsOtpRequestCreate({ ...arg, throwOnError: true });
3961
3995
  const data = res.data;
3962
3996
  const parsed = OTPRequestResponseSchema.safeParse(data);
3963
3997
  if (!parsed.success) {
@@ -4048,7 +4082,8 @@ var OTPVerifyResponseSchema = import_zod19.z.object({
4048
4082
  refresh: import_zod19.z.string().nullable().optional(),
4049
4083
  access: import_zod19.z.string().nullable().optional(),
4050
4084
  user: UserSchema.nullable().optional(),
4051
- should_prompt_2fa: import_zod19.z.boolean().optional()
4085
+ should_prompt_2fa: import_zod19.z.boolean().optional(),
4086
+ persistent_session: import_zod19.z.boolean().optional()
4052
4087
  });
4053
4088
 
4054
4089
  // src/_api/generated/_cfg_accounts/hooks/useCfgAccountsOtpVerifyCreate.ts
@@ -4056,7 +4091,7 @@ function useCfgAccountsOtpVerifyCreate(config) {
4056
4091
  return (0, import_mutation8.default)(
4057
4092
  ["cfg_accounts_otp_verify_create"],
4058
4093
  async (_key, { arg }) => {
4059
- const res = await CfgAccounts.cfgAccountsOtpVerifyCreate({ ...arg, throwOnError: true });
4094
+ const res = await CfgAccountsOtp.cfgAccountsOtpVerifyCreate({ ...arg, throwOnError: true });
4060
4095
  const data = res.data;
4061
4096
  const parsed = OTPVerifyResponseSchema.safeParse(data);
4062
4097
  if (!parsed.success) {
@@ -4312,7 +4347,8 @@ var import_zod26 = require("zod");
4312
4347
  var OAuthCallbackRequestRequestSchema = import_zod26.z.object({
4313
4348
  code: import_zod26.z.string().min(10).max(500),
4314
4349
  state: import_zod26.z.string().min(20).max(100),
4315
- redirect_uri: import_zod26.z.string().optional()
4350
+ redirect_uri: import_zod26.z.string().optional(),
4351
+ remember_me: import_zod26.z.boolean().default(false).optional()
4316
4352
  });
4317
4353
 
4318
4354
  // src/_api/generated/_cfg_accounts/schemas/OAuthDisconnectRequestRequest.ts
@@ -4350,7 +4386,8 @@ var import_zod31 = require("zod");
4350
4386
  var OTPVerifyRequestSchema = import_zod31.z.object({
4351
4387
  identifier: import_zod31.z.string().min(1),
4352
4388
  otp: import_zod31.z.string().min(4).max(4),
4353
- source_url: import_zod31.z.string().optional()
4389
+ source_url: import_zod31.z.string().optional(),
4390
+ remember_me: import_zod31.z.boolean().default(false).optional()
4354
4391
  });
4355
4392
 
4356
4393
  // src/_api/generated/_cfg_accounts/schemas/PatchedCfgUserUpdateRequest.ts
@@ -4452,6 +4489,7 @@ function AccountsProvider({ children }) {
4452
4489
  return result;
4453
4490
  }
4454
4491
  if (result.access && result.refresh) {
4492
+ auth.setStorageMode(result.persistent_session ? "localStorage" : "sessionStorage");
4455
4493
  auth.setSession({ access: result.access, refresh: result.refresh });
4456
4494
  try {
4457
4495
  await refreshProfile({ callerId: "verifyOTP", force: true });
@@ -4512,7 +4550,7 @@ __name(useAccountsContext, "useAccountsContext");
4512
4550
  var import_jsx_runtime2 = require("react/jsx-runtime");
4513
4551
  var defaultRoutes = {
4514
4552
  auth: "/auth",
4515
- defaultCallback: "/",
4553
+ defaultCallback: "/private",
4516
4554
  defaultAuthCallback: "/auth"
4517
4555
  };
4518
4556
  var resolveAuthRoutes = /* @__PURE__ */ __name((routes) => ({
@@ -4670,13 +4708,14 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4670
4708
  []
4671
4709
  );
4672
4710
  const verifyOTP = (0, import_react16.useCallback)(
4673
- async (identifier, otpCode, sourceUrl, redirectUrl, skipRedirect) => {
4711
+ async (identifier, otpCode, sourceUrl, redirectUrl, skipRedirect, rememberMe = false) => {
4674
4712
  try {
4675
4713
  pendingManualNavRef.current = Boolean(skipRedirect);
4676
4714
  const result = await accountsRef.current.verifyOTP({
4677
4715
  identifier,
4678
4716
  otp: otpCode,
4679
- source_url: sourceUrl
4717
+ source_url: sourceUrl,
4718
+ remember_me: rememberMe
4680
4719
  });
4681
4720
  if (result.requires_2fa && result.session_id) {
4682
4721
  authLogger.info("2FA required, session:", result.session_id);