Skip to content

Commit 743930c

Browse files
merge: Prevent user enumeration
Closes #187 See merge request opensavvy/formulaide!111
2 parents 1b3b042 + ab4814c commit 743930c

File tree

2 files changed

+20
-5
lines changed

2 files changed

+20
-5
lines changed

Diff for: fake/src/commonMain/kotlin/User.kt

+7-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import opensavvy.formulaide.core.data.Email
1515
import opensavvy.formulaide.core.data.Password
1616
import opensavvy.formulaide.core.data.Token
1717
import opensavvy.formulaide.fake.utils.newId
18+
import opensavvy.state.Failure
1819
import opensavvy.state.outcome.*
1920
import kotlin.random.Random
2021
import kotlin.random.nextUInt
@@ -159,10 +160,12 @@ class FakeUsers : User.Service {
159160

160161
override suspend fun logIn(email: Email, password: Password): Outcome<Pair<User.Ref, Token>> = out {
161162
lock.withPermit {
162-
val (id, user) = users.asSequence().first { it.value.email == email }
163-
ensureFound(user.active) { "Could not find user $email" }
164-
ensureAuthenticated(passwords[id] == password) { "Incorrect password" }
165-
ensureAuthenticated(id !in blocked) { "The single-use password has already been used." }
163+
val (id, user) = users.asSequence()
164+
.firstOrNull { it.value.email == email }
165+
?: shift(Failure(Failure.Kind.Unauthenticated, "Invalid credentials"))
166+
ensureAuthenticated(user.active) { "Invalid credentials" }
167+
ensureAuthenticated(passwords[id] == password) { "Invalid credentials" }
168+
ensureAuthenticated(id !in blocked) { "Invalid credentials" }
166169

167170
val token = Token("very-strong-token-${Random.nextUInt()}")
168171
tokens.getOrPut(id) { ArrayList() }

Diff for: test/src/commonMain/kotlin/User.kt

+13-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import kotlinx.coroutines.withContext
66
import opensavvy.backbone.Ref.Companion.now
77
import opensavvy.formulaide.core.Auth
88
import opensavvy.formulaide.core.User
9+
import opensavvy.formulaide.core.data.Email
910
import opensavvy.formulaide.core.data.Email.Companion.asEmail
1011
import opensavvy.formulaide.core.data.Password
1112
import opensavvy.formulaide.core.data.Token
@@ -292,7 +293,8 @@ abstract class UserTestCases : TestCase<User.Service> {
292293
assertSuccess(employee.disable())
293294
val email = employee.now().orThrow().email
294295

295-
assertNotFound(users.logIn(email, singleUse))
296+
// do not return NotFound! -> it would help an attacker enumerate users
297+
assertUnauthenticated(users.logIn(email, singleUse))
296298
}
297299

298300
@Test
@@ -383,4 +385,14 @@ abstract class UserTestCases : TestCase<User.Service> {
383385
assertUnauthenticated(employee.verifyToken(token2))
384386
}
385387

388+
@Test
389+
@JsName("logInFalseUser")
390+
fun `cannot log in as a user that doesn't exist`() = runTest {
391+
val users = new()
392+
393+
// No user was created, this user cannot exist
394+
// do not return a NotFound! it would help an attacker enumerate accounts
395+
assertUnauthenticated(users.logIn(Email("[email protected]"), Password("whatever")))
396+
}
397+
386398
}

0 commit comments

Comments
 (0)