Skip to content
Merged
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
1 change: 0 additions & 1 deletion packages/services/src/Domain/Mfa/MfaServiceInterface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export interface MfaServiceInterface {
isMfaActivated(): Promise<boolean>
generateMfaSecret(): Promise<string>
getOtpToken(secret: string): Promise<string>
enableMfa(secret: string, otpToken: string): Promise<void>
disableMfa(): Promise<void>
}
1 change: 0 additions & 1 deletion packages/snjs/lib/Application/Dependencies/Dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,6 @@ export class Dependencies {
this.factory.set(TYPES.MfaService, () => {
return new MfaService(
this.get<SettingsService>(TYPES.SettingsService),
this.get<PureCryptoInterface>(TYPES.Crypto),
this.get<FeaturesService>(TYPES.FeaturesService),
this.get<ProtectionsClientInterface>(TYPES.ProtectionService),
this.get<EncryptionService>(TYPES.EncryptionService),
Expand Down
13 changes: 12 additions & 1 deletion packages/snjs/lib/Services/Api/ApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { Paths } from './Paths'
import { DiskStorageService } from '../Storage/DiskStorageService'
import { UuidString } from '../../Types/UuidString'
import { SettingsServerInterface } from '../Settings/SettingsServerInterface'
import { SettingsServerInterface, MfaSecretResponse } from '../Settings/SettingsServerInterface'
import { Strings } from '@Lib/Strings'
import { AnyFeatureDescription } from '@standardnotes/features'

Expand Down Expand Up @@ -563,11 +563,13 @@ export class LegacyApiService
settingName: string,
settingValue: string | null,
sensitive: boolean,
totpToken?: string,
): Promise<HttpResponse<UpdateSettingResponse>> {
const params = {
name: settingName,
value: settingValue,
sensitive: sensitive,
...(totpToken && { totpToken }),
}
return this.tokenRefreshableRequest<UpdateSettingResponse>({
verb: HttpVerb.Put,
Expand Down Expand Up @@ -637,6 +639,15 @@ export class LegacyApiService
})
}

async getMfaSecret(userUuid: UuidString): Promise<HttpResponse<MfaSecretResponse>> {
return this.tokenRefreshableRequest<MfaSecretResponse>({
verb: HttpVerb.Get,
url: joinPaths(this.host, Paths.v1.mfaSecret(userUuid)),
authentication: this.getSessionAccessToken(),
fallbackErrorMessage: 'Failed to get MFA secret.',
})
}

public downloadFeatureUrl(url: string): Promise<HttpResponse> {
return this.request({
verb: HttpVerb.Get,
Expand Down
1 change: 1 addition & 0 deletions packages/snjs/lib/Services/Api/Paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const ItemsPaths = {
const SettingsPaths = {
settings: (userUuid: string) => `/v1/users/${userUuid}/settings`,
setting: (userUuid: string, settingName: string) => `/v1/users/${userUuid}/settings/${settingName}`,
mfaSecret: (userUuid: string) => `/v1/users/${userUuid}/mfa-secret`,
subscriptionSetting: (userUuid: string, settingName: string) =>
`/v1/users/${userUuid}/subscription-settings/${settingName}`,
subscriptionSettings: (userUuid: string) => `/v1/users/${userUuid}/subscription-settings`,
Expand Down
26 changes: 2 additions & 24 deletions packages/snjs/lib/Services/Mfa/MfaService.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
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'
Expand All @@ -16,7 +14,6 @@ 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,
Expand All @@ -25,14 +22,6 @@ export class MfaService extends AbstractService implements MfaServiceInterface {
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(),
Expand All @@ -41,21 +30,11 @@ export class MfaService extends AbstractService implements MfaServiceInterface {
}

async generateMfaSecret(): Promise<string> {
return this.crypto.generateOtpSecret()
}

async getOtpToken(secret: string): Promise<string> {
return this.crypto.totpToken(secret, Date.now(), 6, 30)
return this.settingsService.generateMfaSecret()
}

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)
return this.settingsService.updateMfaSetting(secret, otpToken)
}

async disableMfa(): Promise<void> {
Expand All @@ -80,7 +59,6 @@ export class MfaService extends AbstractService implements MfaServiceInterface {

override deinit(): void {
;(this.settingsService as unknown) = undefined
;(this.crypto as unknown) = undefined
;(this.featuresService as unknown) = undefined
super.deinit()
}
Expand Down
17 changes: 15 additions & 2 deletions packages/snjs/lib/Services/Settings/SNSettingsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export class SettingsService extends AbstractService implements SettingsClientIn
return this.provider.updateSubscriptionSetting(name, payload, sensitive)
}

async updateSetting(name: SettingName, payload: string, sensitive = false) {
return this.provider.updateSetting(name, payload, sensitive)
async updateSetting(name: SettingName, payload: string, sensitive = false, totpToken?: string) {
return this.provider.updateSetting(name, payload, sensitive, totpToken)
}

async getDoesSensitiveSettingExist(name: SettingName) {
Expand All @@ -54,6 +54,19 @@ export class SettingsService extends AbstractService implements SettingsClientIn
return this.provider.deleteSetting(name, serverPassword)
}

async generateMfaSecret(): Promise<string> {
return this.provider.getMfaSecret()
}

async updateMfaSetting(secret: string, totpToken: string): Promise<void> {
return this.provider.updateSetting(
SettingName.create(SettingName.NAMES.MfaSecret).getValue(),
secret,
true,
totpToken,
)
}

getEmailBackupFrequencyOptionLabel(frequency: EmailBackupFrequency): string {
return this.frequencyOptionsLabels[frequency]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ export interface SettingsClientInterface {

getDoesSensitiveSettingExist(name: SettingName): Promise<boolean>

updateSetting(name: SettingName, payload: string, sensitive?: boolean): Promise<void>
updateSetting(name: SettingName, payload: string, sensitive?: boolean, totpToken?: string): Promise<void>

deleteSetting(name: SettingName, serverPassword?: string): Promise<void>

generateMfaSecret(): Promise<string>

updateMfaSetting(secret: string, totpToken: string): Promise<void>

getEmailBackupFrequencyOptionLabel(frequency: EmailBackupFrequency): string
}
12 changes: 10 additions & 2 deletions packages/snjs/lib/Services/Settings/SettingsGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ export class SettingsGateway {
return response.data?.success ?? false
}

async updateSetting(name: SettingName, payload: string, sensitive: boolean): Promise<void> {
const response = await this.settingsApi.updateSetting(this.userUuid, name.value, payload, sensitive)
async updateSetting(name: SettingName, payload: string, sensitive: boolean, totpToken?: string): Promise<void> {
const response = await this.settingsApi.updateSetting(this.userUuid, name.value, payload, sensitive, totpToken)
if (isErrorResponse(response)) {
throw new Error(getErrorFromErrorResponse(response).message)
}
Expand All @@ -116,6 +116,14 @@ export class SettingsGateway {
}
}

async getMfaSecret(): Promise<string> {
const response = await this.settingsApi.getMfaSecret(this.userUuid)
if (isErrorResponse(response)) {
throw new Error(getErrorFromErrorResponse(response).message)
}
return response.data.secret
}

deinit() {
;(this.settingsApi as unknown) = undefined
;(this.userProvider as unknown) = undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import {
} from '@standardnotes/responses'
import { UuidString } from '@Lib/Types/UuidString'

export interface MfaSecretResponse {
secret: string
}

export interface SettingsServerInterface {
listSettings(userUuid: UuidString): Promise<HttpResponse<ListSettingsResponse>>

Expand All @@ -15,6 +19,7 @@ export interface SettingsServerInterface {
settingName: string,
settingValue: string,
sensitive: boolean,
totpToken?: string,
): Promise<HttpResponse<UpdateSettingResponse>>

getSetting(
Expand All @@ -32,6 +37,8 @@ export interface SettingsServerInterface {
sensitive: boolean,
): Promise<HttpResponse<UpdateSettingResponse>>

getMfaSecret(userUuid: UuidString): Promise<HttpResponse<MfaSecretResponse>>

deleteSetting(
userUuid: UuidString,
settingName: string,
Expand Down
Loading