Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pdeexp 1914 feat disable mfa recovery #13

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Footer from "./components/Footer.js";
import ShowSettings from "./components/MFA/ShowSettings.js";
import EnableMFA from "./components/MFA/EnableMFA.js";
import UserContext from "./context/UserContext.js";
import DisableMFA from "./components/MFA/DisableMFA.js";

function App() {
const { user, setUser } = useContext(UserContext);
Expand Down Expand Up @@ -60,10 +61,10 @@ function App() {
)}

{showMFASettings === true ? (
!user.ismfaEnabled ? (
<EnableMFA setShowMFASettings={setShowMFASettings} />
user.isMfaEnabled ? (
<DisableMFA setShowMFASettings={setShowMFASettings} />
) : (
"DISABLE MFA PLACEHOLDER"
<EnableMFA setShowMFASettings={setShowMFASettings} />
)
) : (
""
Expand Down
120 changes: 120 additions & 0 deletions src/components/MFA/DisableMFA.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, { useCallback, useContext, useState } from "react";
import { magic } from "../../lib/magic";
import UserContext from "../../context/UserContext";
import MFAOTPModal from "./MFAOTPModal";
import authy from "../../SVG/Authy.svg";
import googleAuth from "../../SVG/GoogleAuthenticator.svg";

export default function DisableMFA({ setShowMFASettings }) {
const { user, setUser } = useContext(UserContext);
const [disabled, setDisabled] = useState(false);
const [mfaHandle, setMFAHandle] = useState(undefined);
const [mfaPage, setMFAPage] = useState(0);

const handleCancel = useCallback(() => {
try {
mfaHandle && mfaHandle.emit("cancel-mfa-disable");
setDisabled(false);
setShowMFASettings(false);
setMFAHandle(undefined);
console.log("%cUser canceled MFA setup", "color: orange");
} catch (err) {
console.log("Error canceling MFA setup");
console.error(err);
}
}, [mfaHandle, setShowMFASettings]);

const handleNext = async () => {
if (mfaPage === 0) {
handleDisableMFA();
setMFAPage((current) => current + 1);
} else {
setShowMFASettings(false);
setMFAHandle(undefined);

// update user info now that user.isMFAEnabled is true
const updatedUserInfo = await magic.user.getInfo();
setUser(updatedUserInfo);
}
};

const handleDisableMFA = () => {
const mfaHandle = magic.user.disableMFA({ showUI: false });
setMFAHandle(mfaHandle);
mfaHandle
.on("mfa-code-requested", () => {
// Dispatched when the user starts the disable MFA process.
console.log("mfa-code-requested");
})
.on("done", async () => {
// MFA disabled successfully.
console.log("MFA disabled successfully");
setMFAPage((currentPage) => currentPage + 1);
})
.on("error", (error) => {
console.log("error configuring MFA");
console.error(error);
setMFAHandle(undefined);
setShowMFASettings(false);
});
};

return (
<div className="overlay-container">
<div className="content-wrapper">
<div className="enable-mfa">
{mfaPage === 0 && (
<>
<div className="mfa-page0-header">
<div className="authenticator-svg-logos-wrapper">
<img src={authy} alt="Authy logo" className="svg-logo" />
<img
src={googleAuth}
alt="Google Authenticator logo"
className="svg-logo"
/>
</div>
<h1>You'll need your authenticator app</h1>
</div>

<div className="mfa-message">
To disable multi-factor authentication, you will need to use the
authenticator app you used to enable MFA, such as{" "}
<a href="https://authy.com/">Authy</a> or{" "}
<a href="https://apps.apple.com/us/app/google-authenticator/id388497605">
Google Authenticator
</a>
.
</div>
</>
)}

{mfaPage === 1 && (
<MFAOTPModal handle={mfaHandle} handleCancel={handleCancel} />
)}

{mfaPage === 2 && (
<>
<div className="mfa-page0-header">
<h1>You disabled MFA!</h1>
<div className="emoji-confirm">👍</div>
</div>
</>
)}

{mfaPage !== 1 && (
<div className="mfa-buttons">
<button
className="mfa-next-button ok-button"
disabled={disabled}
onClick={handleNext}
>
{mfaPage === 2 ? "Finish" : "Next"}
</button>
</div>
)}
</div>
</div>
</div>
);
}
26 changes: 11 additions & 15 deletions src/components/MFA/EnableMFA.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useContext, useState } from "react";
import React, { useContext, useState } from "react";
import { magic } from "../../lib/magic";
import { QRCode } from "react-qrcode-logo";
import authy from "../../SVG/Authy.svg";
Expand All @@ -10,7 +10,6 @@ import MFAOTPModal from "./MFAOTPModal";

export default function EnableMFA({ setShowMFASettings }) {
const { user, setUser } = useContext(UserContext);
const [passcode, setPasscode] = useState("");
const [disabled, setDisabled] = useState(false);
const [mfaHandle, setMFAHandle] = useState(undefined);
const [mfaKey, setMFAKey] = useState();
Expand All @@ -20,7 +19,7 @@ export default function EnableMFA({ setShowMFASettings }) {

const handleCancel = () => {
try {
mfaHandle.emit("cancel-mfa-setup");
mfaHandle && mfaHandle.emit("cancel-mfa-setup");

setDisabled(false);
setShowMFASettings(false);
Expand All @@ -33,24 +32,24 @@ export default function EnableMFA({ setShowMFASettings }) {
}
};

const handleNext = () => {
const handleNext = async () => {
if (mfaPage === 0) {
handleEnableMFA();
setMFAPage((current) => current + 1);
} else if (mfaPage === 1) {
setMFAPage((current) => current + 1);
} else if (mfaPage === 2) {
mfaHandle.emit("verify-mfa-code", Number(passcode));
} else {
setMFAHandle(null);
setShowMFASettings(false);

// update user info now that user.isMFAEnabled is true
const updatedUserInfo = await magic.user.getInfo();
setUser(updatedUserInfo);
}
};

const handleEnableMFA = useCallback(async () => {
const handleEnableMFA = async () => {
try {
setPasscode("");

const mfaHandle = magic.user.enableMFA({ showUI: false });
setMFAHandle(mfaHandle);

Expand All @@ -67,10 +66,6 @@ export default function EnableMFA({ setShowMFASettings }) {

setRecoveryCode(recoveryCode);
setMFAPage((currentPage) => currentPage + 1);

// update user info now that user.isMFAEnabled is true
const updatedUserInfo = await magic.user.getInfo();
setUser(updatedUserInfo);
})
.on("error", (error) => {
console.log("error configuring MFA");
Expand All @@ -80,7 +75,7 @@ export default function EnableMFA({ setShowMFASettings }) {
console.error(error);
setShowMFASettings(false);
}
}, [setShowMFASettings]);
};

const copyToClipboard = (text) => {
navigator.clipboard
Expand Down Expand Up @@ -184,6 +179,8 @@ export default function EnableMFA({ setShowMFASettings }) {
</div>

<div className="key-wrapper">
<span className="span-recovery">Your recovery code</span>

<code
className="key-code"
onClick={() => copyToClipboard(recoveryCode)}
Expand All @@ -195,7 +192,6 @@ export default function EnableMFA({ setShowMFASettings }) {
className="copy-symbol-svg"
/>
</code>
<span>Your recovery code</span>
</div>
</div>
)}
Expand Down
59 changes: 33 additions & 26 deletions src/components/MFA/MFAOTPModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,45 @@ export default function MFAOTPModal({ handle, handleCancel }) {
const [disabled, setDisabled] = useState(false);

const handleSubmit = async (e) => {
e.preventDefault();
try {
e.preventDefault();

setDisabled(true);
setRetries((r) => r - 1);
setPasscode("");
setDisabled(true);
setRetries((r) => r - 1);
setPasscode("");

// Send MFA OTP for verification
handle.emit("verify-mfa-code", passcode);
// Send MFA OTP for verification
handle.emit("verify-mfa-code", passcode);

handle.on("invalid-mfa-otp", (res) => {
console.log("invalid-mfa-otp");
handle
.on("invalid-mfa-otp", (res) => {
if (res && res.errorCode) {
console.log("Error code:", res.errorCode);
}

if (res && res.errorCode) {
console.log("Error code:", res.errorCode);
}
// User entered invalid MFA OTP
setDisabled(false);

// User entered invalid MFA OTP
setDisabled(false);
if (!retries) {
setMessage("No more retries. Please try again later.");

if (!retries) {
setMessage("No more retries. Please try again later.");

handleCancel();
} else {
// Prompt the user again for the MFA OTP
setMessage(
`Incorrect code. Please enter MFA OTP again. ${retries} ${
retries === 1 ? "retry" : "retries"
} left.`
);
}
});
handleCancel();
} else {
// Prompt the user again for the MFA OTP
setMessage(
`Incorrect code. Please enter MFA OTP again. ${retries} ${
retries === 1 ? "retry" : "retries"
} left.`
);
}
})
.on("error", (err) => {
console.error(err);
});
} catch (err) {
console.log("Error submitting MFA OTP");
console.error(err);
}
};

return (
Expand Down
23 changes: 22 additions & 1 deletion src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,6 @@ td {

.key-wrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 0.5em;
margin: auto;
Expand All @@ -606,13 +605,35 @@ td {
background-color: #f7f7ff;
cursor: pointer;
}
.span-recovery {
display: flex;
flex-direction: column;
justify-content: space-around;
color: var(--color-palette-4);
}
.span-key-wrapper {
display: flex;
flex-direction: column;
justify-content: space-around;
color: var(--color-palette-1);
}

.emoji-confirm {
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
justify-content: space-around;
color: var(--color-palette-3);

margin: auto;
border-radius: 10px;
padding: 1em;

background-color: #495867;
font-size: xx-large;
}

.span-page3-wrapper {
display: flex;
flex-direction: column;
Expand Down