diff --git a/frontend/locales/en.json b/frontend/locales/en.json
index 0d17abab2..0300d63ea 100644
--- a/frontend/locales/en.json
+++ b/frontend/locales/en.json
@@ -94,6 +94,19 @@
"pagination_controls": {
"total": "Total: {{totalCount}}"
},
+ "reset_cross_signing": {
+ "button": "Allow cross-signing reset",
+ "description": "If you have lost access to all your verified devices and your security key, you can reset your cross-signing keys. Deleting cross-signing keys is permanent, but will allow you to go through the verification process again and set up new keys.",
+ "failure": {
+ "description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.",
+ "title": "Failed to allow cross-signing"
+ },
+ "heading": "Cross-signing reset",
+ "success": {
+ "description": "A client can now temporarily reset your account cross-signing keys. Follow the instructions in your client to complete the process.",
+ "title": "Cross-signing reset temporarily allowed"
+ }
+ },
"selectable_session": {
"label": "Select session"
},
diff --git a/frontend/src/components/UserProfile/CrossSigningReset.tsx b/frontend/src/components/UserProfile/CrossSigningReset.tsx
new file mode 100644
index 000000000..40ad17e3a
--- /dev/null
+++ b/frontend/src/components/UserProfile/CrossSigningReset.tsx
@@ -0,0 +1,95 @@
+// Copyright 2023 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Alert, Button, H3, Text } from "@vector-im/compound-web";
+import { atom, useAtom } from "jotai";
+import { atomFamily } from "jotai/utils";
+import { atomWithMutation } from "jotai-urql";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import { graphql } from "../../gql";
+import BlockList from "../BlockList";
+import LoadingSpinner from "../LoadingSpinner";
+
+const ALLOW_CROSS_SIGING_RESET_MUTATION = graphql(/* GraphQL */ `
+ mutation AllowCrossSigningReset($userId: ID!) {
+ allowUserCrossSigningReset(input: { userId: $userId }) {
+ user {
+ id
+ }
+ }
+ }
+`);
+
+const allowCrossSigningResetFamily = atomFamily((id: string) => {
+ const allowCrossSigingReset = atomWithMutation(
+ ALLOW_CROSS_SIGING_RESET_MUTATION,
+ );
+
+ // A proxy atom which pre-sets the id variable in the mutation
+ const allowCrossSigningResetAtom = atom(
+ (get) => get(allowCrossSigingReset),
+ (_get, set) => set(allowCrossSigingReset, { userId: id }),
+ );
+
+ return allowCrossSigningResetAtom;
+});
+
+const CrossSigningReset: React.FC<{ userId: string }> = ({ userId }) => {
+ const { t } = useTranslation();
+ const [result, allowReset] = useAtom(allowCrossSigningResetFamily(userId));
+ const [inProgress, setInProgress] = useState(false);
+
+ const onClick = (): void => {
+ if (inProgress) return;
+ setInProgress(true);
+ allowReset().finally(() => setInProgress(false));
+ };
+
+ return (
+
+