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

fix: Allow disabling MFA using recovery code #11908

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

RicardoE105
Copy link
Contributor

@RicardoE105 RicardoE105 commented Nov 26, 2024

Summary

1 - Allow users to disable MFA also using the recovery code
2 - Standardize how we refer to MFA code and recovery code

Before

image

Now

CleanShot 2024-11-27 at 11 16 51@2x

Related Linear tickets, Github issues, and Community forum posts

https://linear.app/n8n/issue/ADO-2936/can-not-disable-mfa-with-recovery-code

Review / Merge checklist

  • PR title and summary are descriptive. (conventions)
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with release/backport (if the PR is an urgent fix that needs to be backported)

@RicardoE105 RicardoE105 changed the title Allow disabling MFA using recovery code fix: Allow disabling MFA using recovery code Nov 26, 2024
@n8n-assistant n8n-assistant bot added core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team ui Enhancement in /editor-ui or /design-system labels Nov 26, 2024
@@ -2,6 +2,6 @@ import { ForbiddenError } from './forbidden.error';

export class InvalidMfaCodeError extends ForbiddenError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should change the name of this error class as well, if it's not only about mfa code anymore. Is this used in other places where the error might now be incorrect?

@@ -331,9 +331,10 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
}
};

const disableMfa = async (mfaCode: string) => {
const disableMfa = async (mfaCode: string, recoveryCode: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be:

Suggested change
const disableMfa = async (mfaCode: string, recoveryCode: string) => {
const disableMfa = async (mfaCode?: string, recoveryCode?: string) => {

?

@@ -2608,7 +2609,8 @@
"mfa.setup.step2.toast.setupFinished.message": "Two-factor authentication enabled",
"mfa.setup.step2.toast.setupFinished.error.message": "Error enabling two-factor authentication",
"mfa.setup.step2.toast.tokenExpired.error.message": "MFA token expired. Close the modal and enable MFA again",
"mfa.prompt.code.modal.title": "Two-factor code required",
"mfa.prompt.code.modal.title": "Two-factor code or recovery code required",
"mfa.prompt.code.modal.divider": "Or",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"mfa.prompt.code.modal.divider": "Or",
"mfa.prompt.code.modal.divider": "or",

Comment on lines 68 to 71
promptMfaCodeBus.emit('close', {
mfaCode: mfaCode.value,
recoveryCode: recoveryCode.value,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should map this to an object that only has one or the other, not both.

Comment on lines 25 to 28
export type DisableMfaParams = {
token: string;
recoveryCode: string;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type is incorrect, as only one or the other is required. Would this work?

Suggested change
export type DisableMfaParams = {
token: string;
recoveryCode: string;
};
export type DisableMfaParams = { token: string; } | { recoveryCode: string; }

or this?

Suggested change
export type DisableMfaParams = {
token: string;
recoveryCode: string;
};
export type DisableMfaParams = {
token?: string;
recoveryCode?: string;
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always send both because in the backend we have a function that check both and return true/false depending on whether one of the them is valid. It's the same function to login with MFA. The alternative would have been to check in the FE, and in the BE for which one we are sending and validate always only the one we send, and that would have been some IF's conditions that I wanted to avoid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But yeah, I see where you are coming from that is kind of consuming that you need to send both. Will address

@tomi
Copy link
Contributor

tomi commented Nov 27, 2024

The form now allows entering both MFA code and recovery code. I think it prefers the MFA code in that case. Would probably make more sense to allow user to input only one or the other, and have the TS types match that

@RicardoE105
Copy link
Contributor Author

RicardoE105 commented Nov 27, 2024

The form now allows entering both MFA code and recovery code. I think it prefers the MFA code in that case. Would probably make more sense to allow user to input only one or the other, and have the TS types match that

We could:

1 - Add an info box saying that if you send both, the token takes priority.
2 - Disable the save button if both are set, and show an error messages "you can only send one"
3 - Add a select which two options -> MFA token and recovery code, and show the correct field depending on the that.
4 - Suggested by Adi -> maybe we should combine the two input boxes in the future, and figure out based on the string format if it's a TOTP code, or a recovery code.

@tomi
Copy link
Contributor

tomi commented Nov 27, 2024

We could:

1 - Add an info box saying that if you send both, the token takes priority. 2 - Disable the save button if both are set, and show an error messages "you can only send one" 3 - Add a select which two options -> MFA token and recovery code, and show the correct field depending on the that. 4 - Suggested by Adi -> maybe we should combine the two input boxes in the future, and figure out based on the string format if it's a TOTP code, or a recovery code.

Yeah I think option #4 would be the best one 👍

Also updates the UI to allow  the use of recovery codes when disabling MFA
Copy link
Contributor

@tomi tomi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work 👌 Few comments. Also I would prefer if the separated the renaming refactor into a separate PR. That way the changes stay separate and are easier to for example revert if ever needed

packages/cli/src/controllers/auth.controller.ts Outdated Show resolved Hide resolved
packages/cli/src/mfa/mfa.service.ts Outdated Show resolved Hide resolved
packages/cli/src/mfa/mfa.service.ts Outdated Show resolved Hide resolved
packages/editor-ui/src/views/MfaView.vue Outdated Show resolved Hide resolved
packages/editor-ui/src/views/MfaView.vue Outdated Show resolved Hide resolved
@RicardoE105
Copy link
Contributor Author

@tomi 🙏 Thanks for the feedback. Applied it all and whenever I have time I will split this PR into two as you suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team ui Enhancement in /editor-ui or /design-system
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants