Skip to content

Convert MFA to Glimmer Component #1558

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

Merged
merged 1 commit into from
May 16, 2025
Merged

Conversation

SmitGala
Copy link
Contributor

No description provided.

Copy link

coderabbitai bot commented Apr 16, 2025

Walkthrough

This update introduces a comprehensive refactor and modularization of the multi-factor authentication (MFA) management system within account settings. Several new Glimmer components and their templates are added to handle enabling, disabling, and switching MFA methods (email and app-based) via dedicated modals. The main MFA management component is simplified, delegating detailed flows and asynchronous logic to these new components. Testability is enhanced with additional data-test attributes and improved form handling. The update also introduces new SCSS files for modal-specific styling and removes redundant styles from the main MFA SCSS. A new integration test suite is included to thoroughly verify all MFA flows and UI states.

Changes

Files/Groups Change Summary
app/components/account-settings/security/mfa-app-enable/index.hbs,
mfa-app-enable/index.ts
Added new component and template for enabling MFA via authenticator app, including modal UI, OTP verification, and async flows.
app/components/account-settings/security/mfa-appverify/index.hbs,
mfa-appverify/index.ts
Enhanced app OTP verification component: added loading skeleton UI, improved testability, and updated method signatures for form handling and loading state.
app/components/account-settings/security/mfa-disable/index.hbs,
mfa-disable/index.ts,
mfa-disable/index.scss
Added new component, template, and styles for disabling MFA, supporting both email and app OTP flows, conditional UI, and async verification.
app/components/account-settings/security/mfa-email-enable/index.hbs,
mfa-email-enable/index.ts,
mfa-email-enable/index.scss
Added new component, template, and styles for enabling MFA via email, including confirmation, OTP entry, async flows, and notification handling.
app/components/account-settings/security/mfa-switch-to-app/index.hbs,
mfa-switch-to-app/index.ts,
mfa-switch-to-app/index.scss
Added new component, template, and styles for switching MFA to app-based, with multi-step modal UI, email and app OTP verification, and async state management.
app/components/account-settings/security/mfa-switch-to-email/index.hbs,
mfa-switch-to-email/index.ts,
mfa-switch-to-email/index.scss
Added new component, template, and styles for switching MFA to email-based, supporting confirmation, app OTP, and email OTP flows in a modal.
app/components/account-settings/security/multi-factor-auth/index.hbs Refactored template to delegate modal flows to new dedicated components, removed inline modal code, added data-test attributes, and improved testability.
app/components/account-settings/security/multi-factor-auth/index.ts Refactored from classic to Glimmer component; removed all OTP verification and evented logic, delegating flows to new modular components; simplified state management.
app/components/account-settings/security/multi-factor-auth/index.scss Removed .alert-success and .info-box styles, now handled in new modal-specific SCSS files.
tests/integration/components/account-setting/security/multi-factor-auth-test.js Added comprehensive integration test suite covering all MFA enable, disable, and switch flows, including UI state assertions and error handling.

Suggested reviewers

  • avzz-19
  • future-pirate-king

Poem

🐇✨
In the warren of code, MFA grows anew,
With modals that shimmer and OTPs to view.
Switch, enable, disable—each flow stands alone,
Components now guide you, no more on your own!
Tests hop along, ensuring all's right,
Security’s stronger, and UI’s light.
—A rabbit’s delight in modular might!
🥕

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm warn config production Use --omit=dev instead.
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: @storybook/[email protected]
npm error Found: [email protected]
npm error node_modules/ember-source
npm error dev ember-source@"~5.12.0" from the root project
npm error peer ember-source@">= 3.28.12" from @ember-data/[email protected]
npm error node_modules/@ember-data/tracking
npm error peer @ember-data/tracking@"5.3.9" from @ember-data/[email protected]
npm error node_modules/@ember-data/model
npm error peer @ember-data/model@"5.3.9" from @ember-data/[email protected]
npm error node_modules/@ember-data/debug
npm error @ember-data/debug@"5.3.9" from [email protected]
npm error node_modules/ember-data
npm error 2 more (ember-cli-mirage, ember-data)
npm error peer @ember-data/tracking@"5.3.9" from @ember-data/[email protected]
npm error node_modules/@ember-data/store
npm error peer @ember-data/store@"5.3.9" from @ember-data/[email protected]
npm error node_modules/@ember-data/adapter
npm error @ember-data/adapter@"5.3.9" from [email protected]
npm error node_modules/ember-data
npm error 8 more (@ember-data/debug, @ember-data/graph, ...)
npm error 1 more (ember-data)
npm error 24 more (@ember/render-modifiers, @ember/test-helpers, ...)
npm error
npm error Could not resolve dependency:
npm error peer ember-source@"~3.28.1 || ^4.0.0" from @storybook/[email protected]
npm error node_modules/@storybook/ember
npm error dev @storybook/ember@"8.4.6" from the root project
npm error
npm error Conflicting peer dependency: [email protected]
npm error node_modules/ember-source
npm error peer ember-source@"~3.28.1 || ^4.0.0" from @storybook/[email protected]
npm error node_modules/@storybook/ember
npm error dev @storybook/ember@"8.4.6" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /.npm/_logs/2025-05-16T11_02_51_350Z-eresolve-report.txt
npm error A complete log of this run can be found in: /.npm/_logs/2025-05-16T11_02_51_350Z-debug-0.log

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

cloudflare-workers-and-pages bot commented Apr 16, 2025

Deploying irenestaging with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0e30001
Status: ✅  Deploy successful!
Preview URL: https://68192957.irenestaging.pages.dev
Branch Preview URL: https://pd-1654-convert-mfa-to-glimm.irenestaging.pages.dev

View logs

Copy link

cypress bot commented Apr 16, 2025

Irene    Run #646

Run Properties:  status check failed Failed #646  •  git commit bc5fd7244e ℹ️: Merge d440685901aa809e5c79b24e2a98e6393be85bb5 into c8e48c44c0581ba39e474bb73f44...
Project Irene
Branch Review PD-1654-Convert-MFA-to-Glimmer
Run status status check failed Failed #646
Run duration 06m 21s
Commit git commit bc5fd7244e ℹ️: Merge d440685901aa809e5c79b24e2a98e6393be85bb5 into c8e48c44c0581ba39e474bb73f44...
Committer SmitGala
View all properties for this run ↗︎

Test results
Tests that failed  Failures 1
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 31
View all changes introduced in this branch ↗︎

Tests for review

Failed  cypress/tests/dynamic-scan.spec.ts • 1 failed test

View Output

Test Artifacts
Dynamic Scan > it tests dynamic scan for an ipa file: 58061 Test Replay Screenshots

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🧹 Nitpick comments (28)
app/components/account-settings/security/mfa-app-enable/index.hbs (1)

1-15: Structure looks good, but add accessibility attributes

The modal structure is well-organized, but consider adding accessibility attributes like data-test-* selectors for testing and aria-label for the modal to improve accessibility.

<AkModal
  @showHeader={{true}}
  @headerTitle={{t 'modalCard.mfaTitle.enable'}}
  @onClose={{this.closeAppEnable}}
+ data-test-mfa-app-enable-modal
+ aria-label={{t 'modalCard.mfaTitle.enable'}}
>
  <AccountSettings::Security::MfaAppverify
    @secret={{this.mfaAppSecret}}
    @email={{this.user.email}}
    @otp={{this.appOTP}}
    @onContinue={{perform this.verifyAppOTP}}
    @onCancel={{this.closeAppEnable}}
    @waiting={{this.verifyAppOTP.isRunning}}
    @loading={{this.noMFAEnableApp.isRunning}}
+   data-test-mfa-app-verify
  />
</AkModal>
app/components/account-settings/security/mfa-appverify/index.hbs (1)

63-84: Add data-test attribute for the cancel button

The continue button has a data-test attribute, but the cancel button is missing one. For consistent testability, add a data-test attribute to the cancel button as well.

<AkButton
  @variant='outlined'
  @color='neutral'
  {{on 'click' this.cancel}}
+ data-test-mfa-appverify-cancel-button
>
  {{t 'cancel'}}
</AkButton>
app/components/account-settings/security/mfa-switch-to-email/index.hbs (1)

1-106: Consider adding OTP resend functionality for email verification

The modal handles the OTP verification flow well, but there's no way for users to request a new OTP if they don't receive the email or if the code expires. Consider adding a "Resend Code" button during the email verification step.

{{#if this.showSwitchTOEmailEmailVerify}}
  <div local-class='info-box' class='px-3 py-2'>
    <AkTypography data-test-switch-to-email-enable-email>
      {{t 'modalCard.enableMFAEmail.description'}}
      <strong>{{this.user.email}}</strong>
    </AkTypography>
+   <AkButton
+     @variant='text'
+     @color='primary'
+     @size='small'
+     {{on 'click' (perform this.resendEmailOTP)}}
+     class='mt-2'
+     data-test-switch-to-email-resend-code
+   >
+     {{t 'resendCode'}}
+   </AkButton>
  </div>
app/components/account-settings/security/mfa-email-enable/index.hbs (1)

16-21: Consider adding OTP resend functionality

Similar to the switch-to-email component, consider adding a "Resend Code" button to allow users to request a new OTP if needed.

<div local-class='info-box' class='px-3 py-2'>
  <AkTypography data-test-mfa-email-enable-email-desc>
    {{t 'modalCard.enableMFAEmail.description'}}
    <strong>{{this.user.email}}</strong>
  </AkTypography>
+ <AkButton
+   @variant='text'
+   @color='primary'
+   @size='small'
+   {{on 'click' (perform this.resendEmailToken)}}
+   class='mt-2'
+   data-test-mfa-email-resend-code
+ >
+   {{t 'resendCode'}}
+ </AkButton>
</div>
app/components/account-settings/security/mfa-switch-to-app/index.hbs (2)

71-79: Consider adding a “Cancel” button to the email‑OTP state for UX parity

In the email‑verification state the footer exposes only a “Confirm” button. Every other state offers both “Confirm/Continue” and “Cancel”, so users lose a quick escape route once they begin entering the OTP.

{{#if this.staEmailVerifyActive}}
  …
  <AkButton …> {{t "confirm"}} </AkButton>
+  <AkButton
+    @variant="outlined"
+    @color="neutral"
+    {{on "click" this.staClose}}
+  >{{t "cancel"}}</AkButton>
{{/if}}

Small change, but it keeps the modal behaviour consistent.


26-31: Accessibility nit – missing explicit label/id association for OTP field

AkTextField will render a visually hidden <label> only if @label is supplied.
The label is important for screen‑readers in an OTP form:

<AkTextField
-  @label={{t 'modalCard.enableMFAEmail.enterCode'}}
+  @label={{t 'modalCard.enableMFAEmail.enterCode'}}
   @value={{this.emailOTP}}
   data-test-switch-to-app-disable-email-field
/>

If AkTextField already links the label internally you can ignore this comment, otherwise please keep accessibility in mind.

app/components/account-settings/security/mfa-disable/index.hbs (2)

12-17: Visual semantics – using “success” styling for a denial alert

alert-success is semantically misleading when the alert message is an error (“disableMFADenied”).
Consider changing the modifier to an error/warning variant (e.g. alert-error) or removing the success class.


46-49: Accessibility – provide a label for the OTP text field

The OTP <AkTextField> lacks an explicit @label, which makes the form harder to use with assistive technologies.

<AkTextField
+  @label={{t 'modalCard.disableMFA.enterOTPLabel'}}
   @value={{this.disableOTP}}
   data-test-mfa-disable-textfield
/>

This also helps automated a11y tests pass.

tests/integration/components/account-setting/security/multi-factor-auth-test.js (2)

37-40: Minor: unnecessary async call may slow tests

await this.owner.lookup('service:organization').load(); is executed for every test although the organization record is not used afterwards. Dropping the await (or the entire call) can shave a few hundred ms off the suite.

Not critical, but worth keeping in mind as the test matrix grows.


45-63: Repeated server.get('/v2/mfa' …) stubs may leak across tests

this.stubMfaState re‑registers the same route on each invocation. Mirage keeps the last handler, so earlier stubs are shadowed but still accumulate. Over many tests this can make debugging difficult.

Consider:

this.server.get('/v2/mfa', (schema, request) => {
  
}, { timing: 0, overwrite: true }); // or this.server.passthrough().get…

or use this.server.get('/v2/mfa').timing = 0 to reset between tests.

app/components/account-settings/security/multi-factor-auth/index.hbs (1)

111-116: Left‑over test hook looks accidental

data-test-button-aaa is not self‑descriptive and does not follow the convention used elsewhere (e.g. data-test-mfa‑*). Consider renaming or removing to avoid brittle tests and future confusion.

app/components/account-settings/security/mfa-switch-to-email/index.ts (3)

32-35: Boolean flag naming is inconsistent and hard to scan

showSwitchTOEmailAppVerify / showSwitchTOEmailEmailVerify mix the capital “TO” and read awkwardly. Prefer concise, homogeneous names such as isAppStep, isEmailStep, etc., to improve readability and avoid typo‑prone repetitions.


104-106: Leverage optional chaining for clarity

const otpMsg = errorObj.otp && errorObj.otp[0]; can be simplified:

-const otpMsg = errorObj.otp && errorObj.otp[0];
+const otpMsg = errorObj.otp?.[0];

Produces the same result while reducing cognitive load.

🧰 Tools
🪛 Biome (1.9.4)

[error] 105-105: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


141-147: Duplicate error‑handling block – factor it out

The invalid‑OTP vs generic error logic is repeated in both verification tasks. Consider extracting a small helper to DRY the code and keep both tasks minimal.

🧰 Tools
🪛 Biome (1.9.4)

[error] 142-142: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-app-enable/index.ts (2)

64-66: noMFAEnableApp – misleading method name

The prefix “no” implies a negative action; the task actually enables MFA. Rename to enableMfaAppTask (or similar) for clarity and future maintainability.


110-118: Optional chaining + shared error helper

Same comments as in the switch‑to‑email component—errorObj.otp?.[0] and a shared “showOtpError” helper would cut duplication.

🧰 Tools
🪛 Biome (1.9.4)

[error] 112-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-email-enable/index.ts (3)

78-79: Localise the “failed to send OTP” message
All other user‑facing strings come from intl, but this one is hard‑coded English. Users on non‑English locales will see a mixed UI.

-      this.notify.error('Failed to send OTP. Please try again.');
+      this.notify.error(this.intl.t('mfaEmailEnable.failedToSendOtp'));

107-109: Use optional chaining to simplify error parsing

Static analysis already flagged this. Optional chaining is shorter and avoids the manual fallback object.

-      const errorObj = (error as AjaxError).payload || {};
-      const otpMsg   = errorObj.otp && errorObj.otp[0];
+      const otpMsg = (error as AjaxError).payload?.otp?.[0];
🧰 Tools
🪛 Biome (1.9.4)

[error] 108-108: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


39-42: Naming consistency
tsomethingWentWrong breaks camel‑case used by tEnterOTP/tInvalidOTP. Rename to tSomethingWentWrong for readability.

app/components/account-settings/security/mfa-switch-to-app/index.ts (2)

91-92: Reuse optional‑chaining pattern for brevity

-      const errorObj = (error as AjaxError).payload || {};
-      const otpMsg   = errorObj.otp && errorObj.otp[0];
+      const otpMsg = (error as AjaxError).payload?.otp?.[0];

Apply to all three duplicated locations.

Also applies to: 129-130, 165-166

🧰 Tools
🪛 Biome (1.9.4)

[error] 91-91: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


48-57: Minor typo: capitalise translation helper
tsomethingWentWrongtSomethingWentWrong for consistency.

app/components/account-settings/security/mfa-disable/index.ts (2)

110-114: Adopt optional chaining in error handling

Cleaner & matches project‑wide style.

-      if (payload.otp && payload.otp.length) {
+      if (payload?.otp?.length) {

and

-      const errorObj = (error as AjaxError).payload || {};
-      const otpMsg   = errorObj.otp && errorObj.otp[0];
+      const otpMsg = (error as AjaxError).payload?.otp?.[0];

Also applies to: 145-146

🧰 Tools
🪛 Biome (1.9.4)

[error] 111-111: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


35-38: Consistent camel‑casing for translation vars
Same casing nit as the other components: tsomethingWentWrongtSomethingWentWrong.

app/components/account-settings/security/multi-factor-auth/index.ts (5)

20-24: Service declarations are correct, but consider “private” visibility
Adding the private keyword (or readonly) to injected services can prevent accidental reassignment.


26-31: Many tracked booleans – consider consolidating modal state
A single enum/union discriminated union will scale better than six individual flags:

-@tracked showEmailEnableModal = false;
-@tracked showAppEnableModal = false;
-@tracked showSwitchToEmailModal = false;
-@tracked showSwitchToAppModal = false;
-@tracked showMFADisableModal = false;
+@tracked activeModal: 'email-enable' | 'app-enable' |
+                     'switch-email' | 'switch-app' |
+                     'disable' | null = null;

This removes the need to manually reset competing flags.


116-118: Minor naming nit
noMFAEnableEmailDialog() could be shortened to openEmailEnable() for parity with the other helpers.


125-128: Typo: “Model” vs “Modal”

-@action
-showAppOTPModel() {
+@action
+showAppOTPModal() {

Rename for clarity; same applies to the template invocation.


155-163: Rename ambiguous helpers staShow/staClose

-@action
-staShow() {
-  this.showSwitchToAppModal = true;
-}
-
-@action
-staClose() {
-  this.showSwitchToAppModal = false;
-}
+@action
+showSwitchToApp() {
+  this.showSwitchToAppModal = true;
+}
+
+@action
+closeSwitchToApp() {
+  this.showSwitchToAppModal = false;
+}

Clear names self‑document the intent and ease template usage.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8e48c4 and d440685.

📒 Files selected for processing (20)
  • app/components/account-settings/security/mfa-app-enable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-app-enable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-appverify/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-appverify/index.ts (2 hunks)
  • app/components/account-settings/security/mfa-disable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-disable/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-disable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.ts (1 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.hbs (6 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.scss (0 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.ts (2 hunks)
  • tests/integration/components/account-setting/security/multi-factor-auth-test.js (1 hunks)
💤 Files with no reviewable changes (1)
  • app/components/account-settings/security/multi-factor-auth/index.scss
🧰 Additional context used
🧬 Code Graph Analysis (3)
app/components/account-settings/security/mfa-switch-to-email/index.ts (2)
app/models/user.ts (1)
  • UserModel (14-198)
app/services/ajax.ts (2)
  • IreneAjaxService (143-385)
  • AjaxError (33-41)
app/components/account-settings/security/mfa-email-enable/index.ts (2)
app/models/user.ts (1)
  • UserModel (14-198)
app/services/ajax.ts (2)
  • IreneAjaxService (143-385)
  • AjaxError (33-41)
tests/integration/components/account-setting/security/multi-factor-auth-test.js (6)
app/components/account-settings/security/mfa-app-enable/index.ts (1)
  • user (55-57)
app/components/account-settings/security/mfa-email-enable/index.ts (1)
  • user (54-56)
app/components/account-settings/security/mfa-disable/index.ts (1)
  • user (50-52)
app/components/account-settings/security/multi-factor-auth/index.ts (1)
  • user (70-72)
app/components/account-settings/security/mfa-switch-to-app/index.ts (1)
  • user (59-61)
app/components/account-settings/security/mfa-switch-to-email/index.ts (1)
  • user (58-60)
🪛 Biome (1.9.4)
app/components/account-settings/security/mfa-switch-to-email/index.ts

[error] 105-105: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 142-142: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-app-enable/index.ts

[error] 112-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-switch-to-app/index.ts

[error] 91-91: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 129-129: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 165-165: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-disable/index.ts

[error] 111-111: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 145-145: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-email-enable/index.ts

[error] 108-108: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Run E2E Tests (5)
  • GitHub Check: Run E2E Tests (4)
  • GitHub Check: Run E2E Tests (3)
  • GitHub Check: Run E2E Tests (2)
  • GitHub Check: Run E2E Tests (1)
  • GitHub Check: Publish to Cloudflare Pages
  • GitHub Check: Setup & Build Application
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (25)
app/components/account-settings/security/mfa-email-enable/index.scss (1)

1-3: Clean and effective styling for the info box.

The style is properly implemented using CSS variables for theming, which follows good practices for maintaining consistent UI appearance across the application.

app/components/account-settings/security/mfa-switch-to-email/index.scss (1)

1-3: Consistent styling with other MFA components.

This styling matches the pattern used in other MFA components, ensuring visual consistency across the MFA flow.

app/components/account-settings/security/mfa-disable/index.scss (1)

1-8: Well-structured alert styling with appropriate theming variables.

The success alert styling is correctly implemented with separate variables for border, background, and text colors, allowing for consistent theming and potential dark mode support.

app/components/account-settings/security/mfa-switch-to-app/index.scss (1)

1-3: Consistent info box styling across MFA components.

The styling maintains visual consistency with other MFA components that use the same info box pattern.

app/components/account-settings/security/mfa-appverify/index.ts (2)

18-18: Good addition of optional loading state.

The optional loading property is properly typed with ? modifier, following TypeScript best practices for optional properties.


49-51: Improved form handling with proper event prevention.

The change correctly updates the continue method to handle the event object and prevent default form submission behavior. This is an important enhancement for proper form handling and prevents page reloads.

app/components/account-settings/security/mfa-appverify/index.hbs (3)

2-31: Great addition of loading state skeleton UI

The skeleton UI implementation is well-structured and provides a good visual representation of the loading state. The use of AkStack for layout and AkSkeleton components matches the actual content structure.


41-42: Good use of data-test attributes for testing

Adding data-test attributes improves testability of the component. This makes it easier to write and maintain integration tests.

Also applies to: 48-49, 60-61, 72-73


52-85: Well-structured form with proper event handling

The form is well-organized with proper event handling. The submit event is bound to the continue action, and buttons are properly grouped in a flex container with appropriate spacing and alignment.

app/components/account-settings/security/mfa-switch-to-email/index.hbs (2)

31-50: Good use of info-box for email verification instructions

The info-box styling for email verification instructions provides clear visual separation. The form structure with proper aria-label and test attributes is well-implemented.


54-104: Well-structured conditional footer with appropriate button states

The footer section with conditional rendering of different button sets based on the current step is well-implemented. Loading states are properly bound to the running state of async tasks.

app/components/account-settings/security/mfa-email-enable/index.hbs (4)

1-6: Good use of modal properties and test attributes

The modal setup with header, title, and close action is well-implemented. The data-test attribute on the modal helps with testing.


8-13: Well-structured conditional content based on flow state

The conditional rendering based on showEmailSendConfirm and showEmailOTPEnter states provides a clear multi-step UX. The use of strong tags for the email helps with visual emphasis.

Also applies to: 15-34


23-33: Form handling with proper event binding and accessibility

The form is well-structured with proper event binding to the verifyEmailOTP action. The aria-label and test attributes improve accessibility and testability.


38-72: Well-structured footer with appropriate button states

The footer implementation with conditional buttons based on the current step is clean. The loading states are properly bound to the running state of async tasks.

app/components/account-settings/security/multi-factor-auth/index.hbs (1)

88-90: showMFADisable handler: double‑check that the action still exists

The template wires the “Disable” button to this.showMFADisable, but the backing Glimmer class was massively simplified in this PR. Please verify that a method with that exact name is still exported; otherwise the click will throw at run‑time.

app/components/account-settings/security/mfa-email-enable/index.ts (1)

34-36: Unused secret in TokenData
emailTokenData.secret is never referenced. If the template doesn’t use it, consider pruning the field to avoid mental overhead.

app/components/account-settings/security/mfa-disable/index.ts (1)

62-73: enabledMethod can resolve to NONE

If both isEmailMFAEnabled and isAppMFAEnabled are false we send method: NONE, which the API likely rejects. Consider guarding:

   if (this.enabledMethod === ENUMS.MFA_METHOD.NONE) {
     this.notify.error(this.intl.t('mfaDisable.noMethodEnabled'));
     return false;
   }
app/components/account-settings/security/multi-factor-auth/index.ts (7)

1-8: Imports look good and are minimal
The imported symbols are necessary and used; no issues spotted here.


14-18: Well‑typed public signature
Defining an explicit signature object keeps the component API clear. 👍


43-47: findAll('mfa') + slice() converts to a plain array but loses live updates
If other parts of the app push new MFA records into the store, this array won’t react.
Consider:

this.mfas = [...mfaResponse.toArray()];

or keep the ManyArray and access .slice() only in the getter.


70-71: Simple getter – no issues


74-75: isMFAEnabled – concise & readable


78-81: Email‑MFA check is fine


84-87: App‑MFA check is fine

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (7)
app/components/account-settings/security/mfa-switch-to-email/index.ts (1)

1-11: NotificationService import still missing – TS will fail to compile
The class injects @service('notifications') declare notify: NotificationService; but the symbol has no import. Re-add the import to satisfy the TS compiler.

 import { service } from '@ember/service';
 ...
+import NotificationService from 'ember-cli-notifications/services/notifications';
app/components/account-settings/security/mfa-app-enable/index.ts (2)

1-11: Missing NotificationService import

Same compilation issue as previously highlighted; bring the type into scope:

 import { service } from '@ember/service';
 ...
+import NotificationService from 'ember-cli-notifications/services/notifications';

52-53: Triggering an async task in the constructor may run before DOM is ready

Firing this.noMFAEnableApp.perform() inside constructor risks racing against
did-insert timing, potentially leading to state updates before the template
renders. Move the invocation to an @action wired to
{{did-insert}}, or call it from willDestroy safe lifecycle hook instead.

app/components/account-settings/security/mfa-email-enable/index.ts (1)

85-100: ⚠️ Potential issue

Guard against missing token before verifying OTP

emailTokenData can still be null when the previous request failed or was cancelled.
Attempting to hit /v2/mfa with an empty token causes an unnecessary extra round-trip and a confusing 400 for the user.

- const token = this.emailTokenData?.token;
+ const token = this.emailTokenData?.token;
+ if (!token) {
+   this.notify.error(this.tsomethingWentWrong);
+   return false;
+ }
app/components/account-settings/security/mfa-switch-to-app/index.ts (1)

82-99: Success path leaves the modal in “waiting” state
No change since the last review

When the initial staInitialEmail request succeeds, no UI flag is flipped, so the user sees the same confirm step indefinitely. Forward them to staShowVerifyApp() (or close the modal) just like the failure-to-OTP branch.

🧰 Tools
🪛 Biome (1.9.4)

[error] 91-91: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/multi-factor-auth/index.ts (2)

34-41: Fire-and-forget task started in constructor still lacks error handling

If store.findAll('mfa') rejects, the exception is swallowed and the component renders an empty state silently. Catch and surface the error (toast / logger) or expose the task’s lastErrored state to the template.


90-98: Type-safety: accept ENUMS.MFA_METHOD instead of parsing strings

The switch statement still coerces method with parseInt, re-introducing the enum → string mismatch flagged earlier. Pass the enum directly from the template and drop the coercion.

-enableMFA = task(async (method: string) => {
-  switch (parseInt(method)) {
+enableMFA = task(async (method: ENUMS.MFA_METHOD) => {
+  switch (method) {
🧹 Nitpick comments (12)
tests/integration/components/account-setting/security/multi-factor-auth-test.js (2)

84-84: Remove vestigial debugger statement

Even though the line is commented-out, leaving breadcrumb debugger; statements in committed code makes grepping noisy and may be re-enabled accidentally. Please delete the line.


130-136: Consider lifting magic OTP value into a constant/helper

The literal '000000' appears many times across the test suite. Defining const MOCK_OTP = '000000'; at the top (or a dedicated helper) will improve readability and allow easier future changes.

app/components/account-settings/security/mfa-switch-to-email/index.ts (2)

100-108: Prefer optional chaining to silence lint warning & avoid undefined access

Static analysis suggests replacing the verbose guard with optional chaining:

-      const errorObj = (error as AjaxError).payload || {};
-      const otpMsg = errorObj.otp && errorObj.otp[0];
+      const otpMsg = (error as AjaxError).payload?.otp?.[0];

Same applies to the analogous block at lines 141-147.

🧰 Tools
🪛 Biome (1.9.4)

[error] 105-105: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


32-35: Naming consistency: showSwitchTOEmailEmailVerify looks like a typo

The TO in the middle breaks camel-casing and may confuse template authors.
Recommend renaming to showSwitchToEmailEmailVerify for consistency (update all references).

app/components/account-settings/security/mfa-app-enable/index.ts (2)

110-116: Replace verbose null-check with optional chaining

-      const errorObj = (error as AjaxError).payload || {};
-      const otpMsg = errorObj.otp && errorObj.otp[0];
+      const otpMsg = (error as AjaxError).payload?.otp?.[0];

This matches the Biome hint and slightly reduces complexity.

🧰 Tools
🪛 Biome (1.9.4)

[error] 112-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


38-41: Typo in property name tsomethingWentWrong

Rename to tSomethingWentWrong to follow camel-case convention used by the other translation fields.

app/components/account-settings/security/mfa-email-enable/index.ts (2)

74-79: Internationalise & surface server-side error details

The end-user won’t see a translated message here, while every other toast relies on intl.
Prefer using an i18n key and, if available, show error.payload.message to help support diagnose issues quicker.

- this.notify.success(`OTP sent to ${this.user?.email}`);
+ this.notify.success(
+  this.intl.t('mfa.email.otpSent', { email: this.user?.email })
+);
...
- this.notify.error('Failed to send OTP. Please try again.');
+ const msg =
+   (error as AjaxError).payload?.message ??
+   this.tsomethingWentWrong;
+ this.notify.error(msg);

107-115: DRY-up error branching with optional chaining

Repeated const otpMsg = errorObj.otp && errorObj.otp[0]; can be simplified and made safer:

- const otpMsg = errorObj.otp && errorObj.otp[0];
+ const otpMsg = errorObj?.otp?.[0];

Purely cosmetic but improves readability.

🧰 Tools
🪛 Biome (1.9.4)

[error] 108-108: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-switch-to-app/index.ts (1)

128-133: Minor: optional chaining simplifies payload lookup

- const otpMsg = errorObj.otp && errorObj.otp[0];
+ const otpMsg = errorObj?.otp?.[0];
🧰 Tools
🪛 Biome (1.9.4)

[error] 129-129: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-disable/index.ts (2)

109-115: Use optional chaining for terser payload checks

- if (payload.otp && payload.otp.length) {
+ if (payload?.otp?.length) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 111-111: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


145-150: Simplify OTP extraction

- const otpMsg = errorObj.otp && errorObj.otp[0];
+ const otpMsg = errorObj?.otp?.[0];
🧰 Tools
🪛 Biome (1.9.4)

[error] 145-145: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/multi-factor-auth/index.ts (1)

32-32: mfaEndpoint is declared but never used

Unused members add maintenance burden and can confuse future readers—consider removing it.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d440685 and 295c7ac.

📒 Files selected for processing (20)
  • app/components/account-settings/security/mfa-app-enable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-app-enable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-appverify/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-appverify/index.ts (2 hunks)
  • app/components/account-settings/security/mfa-disable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-disable/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-disable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.ts (1 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.hbs (6 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.scss (0 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.ts (2 hunks)
  • tests/integration/components/account-setting/security/multi-factor-auth-test.js (1 hunks)
💤 Files with no reviewable changes (1)
  • app/components/account-settings/security/multi-factor-auth/index.scss
✅ Files skipped from review due to trivial changes (2)
  • app/components/account-settings/security/mfa-switch-to-app/index.scss
  • app/components/account-settings/security/mfa-app-enable/index.hbs
🚧 Files skipped from review as they are similar to previous changes (10)
  • app/components/account-settings/security/mfa-email-enable/index.scss
  • app/components/account-settings/security/mfa-switch-to-email/index.scss
  • app/components/account-settings/security/mfa-disable/index.scss
  • app/components/account-settings/security/mfa-appverify/index.ts
  • app/components/account-settings/security/mfa-switch-to-app/index.hbs
  • app/components/account-settings/security/mfa-appverify/index.hbs
  • app/components/account-settings/security/mfa-email-enable/index.hbs
  • app/components/account-settings/security/mfa-switch-to-email/index.hbs
  • app/components/account-settings/security/multi-factor-auth/index.hbs
  • app/components/account-settings/security/mfa-disable/index.hbs
🧰 Additional context used
🧬 Code Graph Analysis (2)
app/components/account-settings/security/mfa-disable/index.ts (2)
app/models/user.ts (1)
  • UserModel (14-198)
app/services/ajax.ts (2)
  • IreneAjaxService (143-385)
  • AjaxError (33-41)
app/components/account-settings/security/mfa-switch-to-app/index.ts (2)
app/models/user.ts (1)
  • UserModel (14-198)
app/services/ajax.ts (2)
  • IreneAjaxService (143-385)
  • AjaxError (33-41)
🪛 Biome (1.9.4)
app/components/account-settings/security/mfa-app-enable/index.ts

[error] 112-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-disable/index.ts

[error] 111-111: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 145-145: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-email-enable/index.ts

[error] 108-108: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-switch-to-app/index.ts

[error] 91-91: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 129-129: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 165-165: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-switch-to-email/index.ts

[error] 105-105: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 142-142: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Publish to Cloudflare Pages
  • GitHub Check: Setup & Build Application
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
tests/integration/components/account-setting/security/multi-factor-auth-test.js (1)

167-176: Add await settled() after OTP confirmation to avoid race conditions

The component triggers ember-concurrency tasks and Mirage requests once the confirm button is clicked. Immediately asserting for DOM updates can yield flakiness because the promises may not have resolved yet. Insert await settled(); (or await waitUntil(...)) after the click to guarantee the UI has re-rendered.

       await click('[data-test-mfa-email-enable-confirm-button]');
+      await settled();

@SmitGala SmitGala force-pushed the PD-1654-Convert-MFA-to-Glimmer branch from 295c7ac to 0e30001 Compare May 16, 2025 10:58
@SmitGala SmitGala force-pushed the PD-1654-Convert-MFA-to-Glimmer branch from 0e30001 to 908889b Compare May 16, 2025 10:59
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (11)
app/components/account-settings/security/mfa-switch-to-email/index.ts (2)

27-30: ⚠️ Potential issue

Missing NotificationService import breaks TS compilation.

@service('notifications') declare notify: NotificationService; requires NotificationService to be in scope.

Add an explicit import:

import type IntlService from 'ember-intl/services/intl';
+import type NotificationService from 'ember-cli-notifications/services/notifications';

import ENUMS from 'irene/enums';

116-134: ⚠️ Potential issue

Guard against missing token before verifying e-mail OTP.

token can be undefined if the first step never succeeded or was cancelled.

    const otp = this.emailOTP;
    const token = this.emailTokenData?.token;

+   if (!token) {
+     this.notify.error(this.tsomethingWentWrong);
+     return false;
+   }

    if (!otp) {
      this.notify.error(this.tEnterOTP);

      return false;
    }
app/components/account-settings/security/mfa-app-enable/index.ts (2)

27-30: ⚠️ Potential issue

Missing NotificationService import.

Same missing import as in other components.

import type IntlService from 'ember-intl/services/intl';
+import type NotificationService from 'ember-cli-notifications/services/notifications';

import ENUMS from 'irene/enums';

52-53: 🛠️ Refactor suggestion

Running an async task in the constructor can cause race conditions.

Calling this.noMFAEnableApp.perform() inside the constructor fires the task before the component's lifecycle hooks are ready.

Move the invocation to a lifecycle hook or did-insert modifier in the template:

  constructor(
    owner: Owner,
    args: AccountSettingsSecurityMfaAppEnableSignature['Args']
  ) {
    super(owner, args);

    this.tEnterOTP = this.intl.t('enterOTP');
    this.tInvalidOTP = this.intl.t('invalidOTP');
    this.tsomethingWentWrong = this.intl.t('somethingWentWrong');
-
-   this.noMFAEnableApp.perform();
  }

+  @action
+  didInsert() {
+    this.noMFAEnableApp.perform();
+  }

Then in your template, add:

<div {{did-insert this.didInsert}} ...>
app/components/account-settings/security/mfa-email-enable/index.ts (2)

27-30: ⚠️ Potential issue

Missing NotificationService import.

Same missing import as in other components.

import type IntlService from 'ember-intl/services/intl';
+import type NotificationService from 'ember-cli-notifications/services/notifications';

import ENUMS from 'irene/enums';

86-93: ⚠️ Potential issue

Guard against missing token before verifying email OTP.

Add a check for the token's existence before attempting to use it in the API call.

const otp = this.emailOTP;
const token = this.emailTokenData?.token;

+if (!token) {
+  this.notify.error(this.tsomethingWentWrong);
+  return false;
+}

if (!otp) {
  this.notify.error(this.tEnterOTP);

  return false;
}
app/components/account-settings/security/mfa-switch-to-app/index.ts (2)

82-100: Success path handling is missing in staInitialEmail task

When the POST request succeeds, the UI state flags remain at their initial values, causing the modal to stay in limbo with no follow-up steps shown to the user.

Apply this fix to transition to the app verification screen after successful initial request:

      await this.ajax.post(this.mfaEndpoint, {
        data: {
          method: ENUMS.MFA_METHOD.TOTP,
        },
      });
+      
+      // Proceed to app verification when server accepts without OTP
+      this.staShowVerifyApp();
🧰 Tools
🪛 Biome (1.9.4)

[error] 91-91: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


141-149: Token presence not validated before final OTP submission

The code doesn't validate if this.mfaAppToken exists before submitting it, which could cause a hard-to-explain backend 400 error.

Add validation to check for token presence:

    const otp = this.appOTP;
    const token = this.mfaAppToken;

+   if (!token) {
+     this.notify.error(this.tsomethingWentWrong);
+     return false;
+   }
    
    if (!otp) {
      this.notify.error(this.tEnterOTP);
app/components/account-settings/security/mfa-disable/index.ts (1)

85-97: continueDisableMFA should abort when no MFA method is enabled

The method doesn't check if both MFA methods are disabled before proceeding, which could lead to backend validation errors.

Add validation to abort early if no MFA method is enabled:

  continueDisableMFA = task(async () => {
    try {
+     if (!this.isEmailMFAEnabled && !this.isAppMFAEnabled) {
+       this.notify.error(this.tsomethingWentWrong);
+       return;
+     }
+     
      if (this.isEmailMFAEnabled) {
        await this.sendDisableMFAOTPEmail.perform();
      }
app/components/account-settings/security/multi-factor-auth/index.ts (2)

34-41: Add error handling to loadMfaData task in constructor

The task is fire-and-forget with no error handling, which could result in a silent failure where the UI may stay blank.

Add error handling to provide feedback when data loading fails:

  constructor(
    owner: Owner,
    args: AccountSettingsSecurityMultiFactorAuthSignature['Args']
  ) {
    super(owner, args);

-   this.loadMfaData.perform();
+   this.loadMfaData.perform().catch((e) => {
+     console.error('Could not load MFA records', e);
+     // Consider adding notification service call here
+   });
  }

90-112: Improve type safety and method naming in enableMFA task

The method has two issues:

  1. method is typed as string and immediately coerced with parseInt()
  2. staShow() is not descriptive enough

Apply these improvements:

- enableMFA = task(async (method: string) => {
-   switch (parseInt(method)) {
+ enableMFA = task(async (method: ENUMS.MFA_METHOD) => {
+   switch (method) {
      case ENUMS.MFA_METHOD.HOTP:
        if (this.isMFAEnabled) {
          this.showSwitchToEmail();
        } else {
          this.noMFAEnableEmailDialog();
        }

        break;

      case ENUMS.MFA_METHOD.TOTP:
        if (this.isMFAEnabled) {
-         this.staShow();
+         this.showSwitchToApp();
        } else {
          this.showAppOTPModel();
        }

        break;

      default:
        break;

Also, rename the method from staShow to showSwitchToApp for consistency and clarity:

  @action
- staShow() {
+ showSwitchToApp() {
    this.showSwitchToAppModal = true;
  }
🧹 Nitpick comments (12)
app/components/account-settings/security/mfa-switch-to-email/index.ts (3)

32-34: Fix inconsistent variable naming case.

The property showSwitchTOEmailEmailVerify has inconsistent capitalization in "TO" which should be "To" to match the naming pattern of other variables.

@tracked showSwitchToEmailConfirm = true;
@tracked showSwitchToEmailAppVerify = false;
-@tracked showSwitchTOEmailEmailVerify = false;
+@tracked showSwitchToEmailEmailVerify = false;

81-114: Refactor to use optional chaining for error handling.

In the error handling logic, use optional chaining to simplify the code and avoid potential null references.

try {
  this.emailTokenData = await this.ajax.post<TokenData>(this.mfaEndpoint, {
    data: {
      method: ENUMS.MFA_METHOD.HOTP,
      otp: otp || '',
    },
  });

  this.showSwitchToEmailVerifyEmail();

  return true;
} catch (error) {
  const errorObj = (error as AjaxError).payload || {};
-  const otpMsg = errorObj.otp && errorObj.otp[0];
+  const otpMsg = errorObj?.otp?.[0];
  if (otpMsg) {
    this.notify.error(this.tInvalidOTP);

    return;
  }

  this.notify.error(this.tsomethingWentWrong);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 105-105: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


141-148: Refactor to use optional chaining for error handling.

Similar to the earlier task, use optional chaining for cleaner error handling.

catch (error) {
  const errorObj = (error as AjaxError).payload || {};
-  const otpMsg = errorObj.otp && errorObj.otp[0];
+  const otpMsg = errorObj?.otp?.[0];

  if (otpMsg) {
    this.notify.error(this.tInvalidOTP);
  } else {
    this.notify.error(this.tsomethingWentWrong);
  }
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 142-142: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-app-enable/index.ts (1)

111-118: Refactor to use optional chaining for error handling.

Simplify error handling with optional chaining.

catch (error) {
  const errorObj = (error as AjaxError).payload || {};
-  const otpMsg = errorObj.otp && errorObj.otp[0];
+  const otpMsg = errorObj?.otp?.[0];

  if (otpMsg) {
    this.notify.error(this.tInvalidOTP);
  } else {
    this.notify.error(this.tsomethingWentWrong);
  }
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 112-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-email-enable/index.ts (2)

63-66: Add @action decorator to event handler method.

The showEmailOTPDialog method appears to be an action handler but is missing the @action decorator.

+@action
showEmailOTPDialog() {
  this.showEmailSendConfirm = false;
  this.showEmailOTPEnter = true;
}

107-114: Refactor to use optional chaining for error handling.

Use optional chaining for more concise and safer error handling.

catch (error) {
  const errorObj = (error as AjaxError).payload || {};
-  const otpMsg = errorObj.otp && errorObj.otp[0];
+  const otpMsg = errorObj?.otp?.[0];

  if (otpMsg) {
    this.notify.error(this.tInvalidOTP);
  } else {
    this.notify.error(this.tsomethingWentWrong);
  }
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 108-108: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-switch-to-app/index.ts (3)

91-91: Use optional chaining for safer property access

The code should use optional chaining to safely access nested properties.

- const otpMsg = errorObj.otp && errorObj.otp[0];
+ const otpMsg = errorObj.otp?.[0];
🧰 Tools
🪛 Biome (1.9.4)

[error] 91-91: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


129-129: Use optional chaining for safer property access

The code should use optional chaining to safely access nested properties.

- const otpMsg = errorObj.otp && errorObj.otp[0];
+ const otpMsg = errorObj.otp?.[0];
🧰 Tools
🪛 Biome (1.9.4)

[error] 129-129: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


165-165: Use optional chaining for safer property access

The code should use optional chaining to safely access nested properties.

- const otpMsg = errorObj.otp && errorObj.otp[0];
+ const otpMsg = errorObj.otp?.[0];
🧰 Tools
🪛 Biome (1.9.4)

[error] 165-165: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-disable/index.ts (2)

111-111: Use optional chaining for safer property access

The code should use optional chaining to safely access nested properties.

- if (payload.otp && payload.otp.length) {
+ if (payload.otp?.length) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 111-111: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


145-145: Use optional chaining for safer property access

The code should use optional chaining to safely access nested properties.

- const otpMsg = errorObj.otp && errorObj.otp[0];
+ const otpMsg = errorObj.otp?.[0];
🧰 Tools
🪛 Biome (1.9.4)

[error] 145-145: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/multi-factor-auth/index.ts (1)

156-158: Rename method for better clarity and consistency

The method name staShow is not descriptive. It should follow the naming pattern of other show methods.

  @action
- staShow() {
+ showSwitchToApp() {
    this.showSwitchToAppModal = true;
  }

Also update any references to this method throughout the codebase.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 295c7ac and 908889b.

📒 Files selected for processing (20)
  • app/components/account-settings/security/mfa-app-enable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-app-enable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-appverify/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-appverify/index.ts (2 hunks)
  • app/components/account-settings/security/mfa-disable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-disable/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-disable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-email-enable/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-app/index.ts (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.hbs (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.scss (1 hunks)
  • app/components/account-settings/security/mfa-switch-to-email/index.ts (1 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.hbs (6 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.scss (0 hunks)
  • app/components/account-settings/security/multi-factor-auth/index.ts (2 hunks)
  • tests/integration/components/account-setting/security/multi-factor-auth-test.js (1 hunks)
💤 Files with no reviewable changes (1)
  • app/components/account-settings/security/multi-factor-auth/index.scss
🚧 Files skipped from review as they are similar to previous changes (12)
  • app/components/account-settings/security/mfa-switch-to-email/index.scss
  • app/components/account-settings/security/mfa-switch-to-app/index.scss
  • app/components/account-settings/security/mfa-email-enable/index.scss
  • app/components/account-settings/security/mfa-app-enable/index.hbs
  • app/components/account-settings/security/mfa-disable/index.scss
  • app/components/account-settings/security/mfa-switch-to-app/index.hbs
  • app/components/account-settings/security/mfa-appverify/index.ts
  • app/components/account-settings/security/mfa-appverify/index.hbs
  • app/components/account-settings/security/mfa-email-enable/index.hbs
  • app/components/account-settings/security/mfa-switch-to-email/index.hbs
  • app/components/account-settings/security/multi-factor-auth/index.hbs
  • app/components/account-settings/security/mfa-disable/index.hbs
🧰 Additional context used
🧬 Code Graph Analysis (2)
tests/integration/components/account-setting/security/multi-factor-auth-test.js (6)
app/components/account-settings/security/mfa-app-enable/index.ts (1)
  • user (55-57)
app/components/account-settings/security/mfa-email-enable/index.ts (1)
  • user (54-56)
app/components/account-settings/security/mfa-disable/index.ts (1)
  • user (50-52)
app/components/account-settings/security/mfa-switch-to-app/index.ts (1)
  • user (59-61)
app/components/account-settings/security/multi-factor-auth/index.ts (1)
  • user (70-72)
app/components/account-settings/security/mfa-switch-to-email/index.ts (1)
  • user (58-60)
app/components/account-settings/security/mfa-switch-to-app/index.ts (2)
app/models/user.ts (1)
  • UserModel (14-198)
app/services/ajax.ts (2)
  • IreneAjaxService (143-385)
  • AjaxError (33-41)
🪛 Biome (1.9.4)
app/components/account-settings/security/mfa-switch-to-app/index.ts

[error] 91-91: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 129-129: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 165-165: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-app-enable/index.ts

[error] 112-112: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-disable/index.ts

[error] 111-111: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 145-145: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-email-enable/index.ts

[error] 108-108: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

app/components/account-settings/security/mfa-switch-to-email/index.ts

[error] 105-105: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 142-142: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run Integration Tests - Part 8
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (11)
tests/integration/components/account-setting/security/multi-factor-auth-test.js (11)

1-9: Well-organized test setup with appropriate imports.

The test file includes all necessary imports for Ember testing, Mirage for API mocking, and internationalization support. The structure follows best practices for integration tests.


10-20: Good use of service stubs for testing isolation.

The NotificationsStub and OrganizationMeStub services effectively isolate the component tests from external dependencies, allowing for controlled testing of notification messages and permission checks.

Also applies to: 22-27


45-63: Well-implemented MFA state stubbing.

The stubMfaState helper function provides a flexible way to configure different MFA states for testing various scenarios. The implementation correctly mocks the API response structure.


79-97: Thorough basic rendering test.

This test verifies all essential UI elements are present in their initial state, with appropriate data test selectors and text content verification.


99-126: Properly tests permission-based UI variations.

The test correctly verifies that UI elements adapt when the user doesn't have permission to disable MFA, showing appropriate messaging.


128-174: Comprehensive email MFA enablement test.

The test successfully verifies the complete flow of enabling email-based MFA, including modal transitions, form interactions, and final UI state updates.


176-227: Complete email MFA disablement test.

This test covers the full disablement flow for email-based MFA, including the confirmation step, OTP verification, and final UI state validation.


229-258: Thorough app-based MFA enablement test.

The test properly validates the authenticator app MFA enablement flow, including QR code presence, OTP verification, and UI state transitions.


260-311: Complete app-based MFA disablement test.

This test thoroughly covers disabling app-based MFA, including permission checks, OTP verification, and UI state validation.


313-383: Comprehensive testing of MFA method switching from app to email.

The test effectively verifies the complete flow of switching from app-based to email-based MFA, including confirmation, app verification, email verification, and final state validation.


385-466: Thorough testing of MFA method switching from email to app.

This test covers the complete flow of switching from email-based to app-based MFA, including error handling for invalid OTP responses.

@Yibaebi Yibaebi merged commit 47cda09 into develop May 16, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants