Skip to content

Commit

Permalink
fix: trim/lowercase email on email change (#2886)
Browse files Browse the repository at this point in the history
moughxyz authored Sep 20, 2024
1 parent ed0d98a commit 98304cc
Showing 5 changed files with 61 additions and 10 deletions.
10 changes: 8 additions & 2 deletions packages/services/src/Domain/User/UserService.ts
Original file line number Diff line number Diff line change
@@ -40,6 +40,10 @@ import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInt
import { ReencryptTypeAItems } from '../Encryption/UseCase/TypeA/ReencryptTypeAItems'
import { DecryptErroredPayloads } from '../Encryption/UseCase/DecryptErroredPayloads'

const cleanedEmailString = (email: string) => {
return email.trim().toLowerCase()
}

export class UserService
extends AbstractService<AccountEvent, AccountEventData>
implements UserServiceInterface, InternalEventHandlerInterface
@@ -593,13 +597,15 @@ export class UserService
}
}

const newEmail = parameters.newEmail ? cleanedEmailString(parameters.newEmail) : undefined

const user = this.sessions.getUser() as User
const currentEmail = user.email
const { currentRootKey, newRootKey } = await this.recomputeRootKeysForCredentialChange({
currentPassword: parameters.currentPassword,
currentEmail,
origination: parameters.origination,
newEmail: parameters.newEmail,
newEmail: newEmail,
newPassword: parameters.newPassword,
})

@@ -609,7 +615,7 @@ export class UserService
currentServerPassword: currentRootKey.serverPassword as string,
newRootKey: newRootKey,
wrappingKey,
newEmail: parameters.newEmail,
newEmail: newEmail,
})

this.unlockSyncing()
7 changes: 2 additions & 5 deletions packages/snjs/lib/Services/Session/SessionManager.ts
Original file line number Diff line number Diff line change
@@ -70,15 +70,12 @@ import {
UserApiServiceInterface,
UserRegistrationResponseBody,
} from '@standardnotes/api'
import { cleanedEmailString } from './cleanedEmailString'

export const MINIMUM_PASSWORD_LENGTH = 8
export const MissingAccountParams = 'missing-params'
const ThirtyMinutes = 30 * 60 * 1000

const cleanedEmailString = (email: string) => {
return email.trim().toLowerCase()
}

/**
* The session manager is responsible for loading initial user state, and any relevant
* server credentials, such as the session token. It also exposes methods for registering
@@ -659,7 +656,7 @@ export class SessionManager
currentServerPassword: parameters.currentServerPassword,
newServerPassword: parameters.newRootKey.serverPassword as string,
newKeyParams: parameters.newRootKey.keyParams,
newEmail: parameters.newEmail,
newEmail: parameters.newEmail ? cleanedEmailString(parameters.newEmail) : undefined,
})

const oldKeys = this._getKeyPairs.execute()
3 changes: 3 additions & 0 deletions packages/snjs/lib/Services/Session/cleanedEmailString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const cleanedEmailString = (email: string) => {
return email.trim().toLowerCase()
}
31 changes: 31 additions & 0 deletions packages/snjs/mocha/keys.test.js
Original file line number Diff line number Diff line change
@@ -453,6 +453,37 @@ describe('keys', function () {
await Factory.safeDeinit(application)
})

it('changing email to all uppercase should allow sign in with lowercase', async function () {
await Factory.safeDeinit(application)
const realCryptoContext = await Factory.createAppContextWithRealCrypto()
await realCryptoContext.launch()

application = realCryptoContext.application

await Factory.registerUserToApplication({
application: application,
email: email,
password: password,
})
const mixedCaseEmail = `TheFooBar@bar.${UuidGenerator.GenerateUuid()}`

const changeEmailResponse = await application.changeEmail(mixedCaseEmail, password)

expect(changeEmailResponse.error).to.not.be.ok

application = await Factory.signOutApplicationAndReturnNew(application)
const loginResponse = await Factory.loginToApplication({
application: application,
email: mixedCaseEmail.toLowerCase(),
password: password,
})

expect(loginResponse).to.be.ok
expect(loginResponse.status).to.equal(200)

await Factory.safeDeinit(application)
}).timeout(Factory.TwentySecondTimeout)

it('compares root keys', async function () {
const keyParams = {}
const a1 = await CreateNewRootKey({
20 changes: 17 additions & 3 deletions packages/snjs/mocha/lib/factory.js
Original file line number Diff line number Diff line change
@@ -51,7 +51,14 @@ export async function createAppContextWithRealCrypto(identifier) {
return createAppContext({ identifier, crypto: new SNWebCrypto() })
}

export async function createAppContext({ identifier, crypto, email, password, host, syncCallsThresholdPerMinute } = {}) {
export async function createAppContext({
identifier,
crypto,
email,
password,
host,
syncCallsThresholdPerMinute,
} = {}) {
const context = new AppContext({ identifier, crypto, email, password, host, syncCallsThresholdPerMinute })
await context.initialize()
return context
@@ -250,7 +257,14 @@ export async function awaitFunctionInvokation(object, functionName) {
* A new one must be created.
*/
export async function signOutApplicationAndReturnNew(application) {
const isRealCrypto = application.crypto instanceof SNWebCrypto
if (!application) {
throw Error('[signOutApplicationAndReturnNew] Application is undefined')
}
if (!application.options.crypto) {
throw Error('[signOutApplicationAndReturnNew] Application.options.crypto is undefined')
}

const isRealCrypto = application.options.crypto instanceof SNWebCrypto
await application.user.signOut()
if (isRealCrypto) {
return createInitAppWithRealCrypto()
@@ -260,7 +274,7 @@ export async function signOutApplicationAndReturnNew(application) {
}

export async function signOutAndBackIn(application, email, password) {
const isRealCrypto = application.crypto instanceof SNWebCrypto
const isRealCrypto = application.options.crypto instanceof SNWebCrypto
await application.user.signOut()
const newApplication = isRealCrypto ? await createInitAppWithRealCrypto() : await createInitAppWithFakeCrypto()
await this.loginToApplication({

0 comments on commit 98304cc

Please sign in to comment.