From 365b5753419238bb96bc3f9b744d380ff20cbafc Mon Sep 17 00:00:00 2001
From: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Mon, 7 Apr 2025 16:14:41 +0200
Subject: [feature] add TOTP two-factor authentication (2FA) (#3960)

* [feature] add TOTP two-factor authentication (2FA)

* use byteutil.S2B to avoid allocations when comparing + generating password hashes

* don't bother with string conversion  for consts

* use io.ReadFull

* use MustGenerateSecret for backup codes

* rename util functions
---
 .../settings/views/user/account/password.tsx       | 103 +++++++++++++++++++++
 1 file changed, 103 insertions(+)
 create mode 100644 web/source/settings/views/user/account/password.tsx

(limited to 'web/source/settings/views/user/account/password.tsx')

diff --git a/web/source/settings/views/user/account/password.tsx b/web/source/settings/views/user/account/password.tsx
new file mode 100644
index 000000000..a2f0eeb3b
--- /dev/null
+++ b/web/source/settings/views/user/account/password.tsx
@@ -0,0 +1,103 @@
+/*
+	GoToSocial
+	Copyright (C) GoToSocial Authors admin@gotosocial.org
+	SPDX-License-Identifier: AGPL-3.0-or-later
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU Affero General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU Affero General Public License for more details.
+
+	You should have received a copy of the GNU Affero General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import React from "react";
+import { useTextInput } from "../../../lib/form";
+import useFormSubmit from "../../../lib/form/submit";
+import { TextInput } from "../../../components/form/inputs";
+import MutationButton from "../../../components/form/mutation-button";
+import { usePasswordChangeMutation } from "../../../lib/query/user";
+
+export default function PasswordChange({ oidcEnabled }: { oidcEnabled?: boolean }) {
+	const form = {
+		oldPassword: useTextInput("old_password"),
+		newPassword: useTextInput("new_password", {
+			validator(val) {
+				if (val != "" && val == form.oldPassword.value) {
+					return "New password same as old password";
+				}
+				return "";
+			}
+		})
+	};
+
+	const verifyNewPassword = useTextInput("verifyNewPassword", {
+		validator(val) {
+			if (val != "" && val != form.newPassword.value) {
+				return "Passwords do not match";
+			}
+			return "";
+		}
+	});
+
+	const [submitForm, result] = useFormSubmit(form, usePasswordChangeMutation());
+
+	return (
+		<form className="change-password" onSubmit={submitForm}>
+			<div className="form-section-docs">
+				<h3>Change Password</h3>
+				{ oidcEnabled && <p>
+					This instance is running with OIDC as its authorization + identity provider.
+					<br/>
+					This means <strong>you cannot change your password using this settings panel</strong>.
+					<br/>
+					To change your password, you should instead contact your OIDC provider.
+				</p> }
+				<a
+					href="https://docs.gotosocial.org/en/latest/user_guide/settings/#password-change"
+					target="_blank"
+					className="docslink"
+					rel="noreferrer"
+				>
+					Learn more about this (opens in a new tab)
+				</a>
+			</div>
+			
+			<TextInput
+				type="password"
+				name="password"
+				field={form.oldPassword}
+				label="Current password"
+				autoComplete="current-password"
+				disabled={oidcEnabled}
+			/>
+			<TextInput
+				type="password"
+				name="newPassword"
+				field={form.newPassword}
+				label="New password"
+				autoComplete="new-password"
+				disabled={oidcEnabled}
+			/>
+			<TextInput
+				type="password"
+				name="confirmNewPassword"
+				field={verifyNewPassword}
+				label="Confirm new password"
+				autoComplete="new-password"
+				disabled={oidcEnabled}
+			/>
+			<MutationButton
+				label="Change password"
+				result={result}
+				disabled={oidcEnabled ?? false}
+			/>
+		</form>
+	);
+}
-- 
cgit v1.2.3

