Files
standardnotes-app-web/packages/snjs/lib/Services/Mfa/MfaService.ts
Antonella Sgarlatta 54af28aa04 chore: Add serverPassword param to endpoints (#2919) [skip e2e]
* chore: send server password param to delete account endpoint

* chore: send server password param to disable mfa endpoint

* chore: modify tests

* chore: force challenge prompt for mfa disable

* chore: fix eslint errors

* chore: add server passsword to get recovery codes

* chore: fix tests

* chore: pass server password as header
2025-08-26 09:04:03 -03:00

88 lines
2.7 KiB
TypeScript

import { SettingsService } from '../Settings'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { FeaturesService } from '../Features/FeaturesService'
import {
AbstractService,
InternalEventBusInterface,
MfaServiceInterface,
ProtectionsClientInterface,
EncryptionService,
SignInStrings,
ChallengeValidation,
} from '@standardnotes/services'
import { SettingName } from '@standardnotes/domain-core'
import { SNRootKeyParams } from '@standardnotes/encryption'
export class MfaService extends AbstractService implements MfaServiceInterface {
constructor(
private settingsService: SettingsService,
private crypto: PureCryptoInterface,
private featuresService: FeaturesService,
private protections: ProtectionsClientInterface,
private encryption: EncryptionService,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
}
private async saveMfaSetting(secret: string): Promise<void> {
return await this.settingsService.updateSetting(
SettingName.create(SettingName.NAMES.MfaSecret).getValue(),
secret,
true,
)
}
async isMfaActivated(): Promise<boolean> {
const mfaSetting = await this.settingsService.getDoesSensitiveSettingExist(
SettingName.create(SettingName.NAMES.MfaSecret).getValue(),
)
return mfaSetting != false
}
async generateMfaSecret(): Promise<string> {
return this.crypto.generateOtpSecret()
}
async getOtpToken(secret: string): Promise<string> {
return this.crypto.totpToken(secret, Date.now(), 6, 30)
}
async enableMfa(secret: string, otpToken: string): Promise<void> {
const otpTokenValid = otpToken != undefined && otpToken === (await this.getOtpToken(secret))
if (!otpTokenValid) {
throw new Error(SignInStrings.IncorrectMfa)
}
return this.saveMfaSetting(secret)
}
async disableMfa(): Promise<void> {
const { success, challengeResponse } = await this.protections.authorizeMfaDisable()
if (!success) {
return
}
const password = challengeResponse?.getValueForType(ChallengeValidation.AccountPassword).value as string
const currentRootKey = await this.encryption.computeRootKey(
password,
this.encryption.getRootKeyParams() as SNRootKeyParams,
)
const serverPassword = currentRootKey.serverPassword
return await this.settingsService.deleteSetting(
SettingName.create(SettingName.NAMES.MfaSecret).getValue(),
serverPassword,
)
}
override deinit(): void {
;(this.settingsService as unknown) = undefined
;(this.crypto as unknown) = undefined
;(this.featuresService as unknown) = undefined
super.deinit()
}
}