From 0f0290132ff52325351b2f567be5241c378e6d4b Mon Sep 17 00:00:00 2001
From: viown <48097677+viown@users.noreply.github.com>
Date: Sat, 8 Aug 2026 11:54:53 +0300
Subject: [PATCH 1/2] Redirect to external password reset link
---
.../routes/session/forgotPassword/index.tsx | 21 ++++++++++++-------
src/strings/en-us.json | 1 +
src/utils/url.ts | 17 +++++++++++++++
3 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/src/apps/legacy/routes/session/forgotPassword/index.tsx b/src/apps/legacy/routes/session/forgotPassword/index.tsx
index 8861a288f3..bf1fe21d3f 100644
--- a/src/apps/legacy/routes/session/forgotPassword/index.tsx
+++ b/src/apps/legacy/routes/session/forgotPassword/index.tsx
@@ -10,6 +10,7 @@ import Input from 'elements/emby-input/Input';
import globalize from 'lib/globalize';
import ServerConnections from 'lib/jellyfin-apiclient/ServerConnections';
import { getAuthenticationApi } from 'utils/sdk/authentication-api';
+import { isValidUrl } from 'utils/url';
export const ForgotPasswordPage = () => {
const navigate = useNavigate();
@@ -41,13 +42,19 @@ export const ForgotPasswordPage = () => {
msg = globalize.translate('MessageForgotPasswordInNetworkRequired');
break;
case ForgotPasswordAction.PinCode:
- msg = globalize.translate('MessageForgotPasswordFileCreated');
- msg += '
';
- msg += globalize.translate('MessageForgotPasswordPinReset');
- msg += '
';
- msg += result.PinFile;
- msg += '
';
- callback = () => navigate('/forgotpasswordpin');
+ if (result.PinFile && isValidUrl(result.PinFile)) {
+ msg += globalize.translate('MessageForgotPasswordRedirect');
+ window.open(result.PinFile, '_blank', 'noopener,noreferrer');
+ callback = () => navigate('/login');
+ } else {
+ msg = globalize.translate('MessageForgotPasswordFileCreated');
+ msg += '
';
+ msg += globalize.translate('MessageForgotPasswordPinReset');
+ msg += '
';
+ msg += result.PinFile;
+ msg += '
';
+ callback = () => navigate('/forgotpasswordpin');
+ }
break;
default:
return;
diff --git a/src/strings/en-us.json b/src/strings/en-us.json
index a6b093a5fd..d93db3d201 100755
--- a/src/strings/en-us.json
+++ b/src/strings/en-us.json
@@ -1186,6 +1186,7 @@
"MessageFileReadError": "There was an error reading the file. Please try again.",
"MessageForgotPasswordFileCreated": "The following file has been created on your server and contains instructions on how to proceed",
"MessageForgotPasswordPinReset": "Enter PIN here to finish Password reset",
+ "MessageForgotPasswordRedirect": "You've been redirected to an external provider for the password reset.",
"MessageForgotPasswordInNetworkRequired": "Please try again within your home network to initiate the password reset process.",
"MessageGetInstalledPluginsError": "An error occurred while getting the list of currently installed plugins.",
"MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.",
diff --git a/src/utils/url.ts b/src/utils/url.ts
index 7e3f4a84c6..3798122200 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -53,3 +53,20 @@ export const safeDecodeURIComponent = (value: string) => {
return value;
}
};
+
+/**
+ * Test if a string is a valid http or https URL.
+ * @param value The value to test.
+ * @returns Whether the value is a URL or not.
+ */
+export const isValidUrl = (value: string) => {
+ let url;
+
+ try {
+ url = new URL(value);
+ } catch {
+ return false;
+ }
+
+ return url.protocol === 'http:' || url.protocol === 'https:';
+};
From cfc98cbb2cbf6b6c61a96e1790ed4375887a977d Mon Sep 17 00:00:00 2001
From: viown <48097677+viown@users.noreply.github.com>
Date: Sat, 8 Aug 2026 19:42:13 +0300
Subject: [PATCH 2/2] Use shell.openUrl
---
src/apps/legacy/routes/session/forgotPassword/index.tsx | 5 +++--
src/strings/en-us.json | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/apps/legacy/routes/session/forgotPassword/index.tsx b/src/apps/legacy/routes/session/forgotPassword/index.tsx
index bf1fe21d3f..3f4f7f9b37 100644
--- a/src/apps/legacy/routes/session/forgotPassword/index.tsx
+++ b/src/apps/legacy/routes/session/forgotPassword/index.tsx
@@ -11,6 +11,7 @@ import globalize from 'lib/globalize';
import ServerConnections from 'lib/jellyfin-apiclient/ServerConnections';
import { getAuthenticationApi } from 'utils/sdk/authentication-api';
import { isValidUrl } from 'utils/url';
+import shell from 'scripts/shell';
export const ForgotPasswordPage = () => {
const navigate = useNavigate();
@@ -43,8 +44,8 @@ export const ForgotPasswordPage = () => {
break;
case ForgotPasswordAction.PinCode:
if (result.PinFile && isValidUrl(result.PinFile)) {
- msg += globalize.translate('MessageForgotPasswordRedirect');
- window.open(result.PinFile, '_blank', 'noopener,noreferrer');
+ msg = globalize.translate('MessageForgotPasswordRedirect');
+ shell.openUrl(result.PinFile);
callback = () => navigate('/login');
} else {
msg = globalize.translate('MessageForgotPasswordFileCreated');
diff --git a/src/strings/en-us.json b/src/strings/en-us.json
index d93db3d201..358b318363 100755
--- a/src/strings/en-us.json
+++ b/src/strings/en-us.json
@@ -1186,7 +1186,7 @@
"MessageFileReadError": "There was an error reading the file. Please try again.",
"MessageForgotPasswordFileCreated": "The following file has been created on your server and contains instructions on how to proceed",
"MessageForgotPasswordPinReset": "Enter PIN here to finish Password reset",
- "MessageForgotPasswordRedirect": "You've been redirected to an external provider for the password reset.",
+ "MessageForgotPasswordRedirect": "You have been redirected to an external provider for the password reset.",
"MessageForgotPasswordInNetworkRequired": "Please try again within your home network to initiate the password reset process.",
"MessageGetInstalledPluginsError": "An error occurred while getting the list of currently installed plugins.",
"MessageImageFileTypeAllowed": "Only JPEG and PNG files are supported.",