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
10 changes: 8 additions & 2 deletions packages/services/src/Domain/User/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
})

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

this.unlockSyncing()
Expand Down
7 changes: 2 additions & 5 deletions packages/snjs/lib/Services/Session/SessionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
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
Expand Up @@ -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({
Expand Down
20 changes: 17 additions & 3 deletions packages/snjs/mocha/lib/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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({
Expand Down