Skip to content

Commit 944d357

Browse files
authored
Merge pull request mesutpiskin#103 from ktiass/main
Fix CVE vulnerabilities, enforce OTP verification during setup, and add max attempts protection
2 parents d44d5ae + 526048e commit 944d357

19 files changed

+543
-41
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ COPY src ./src
99
RUN mvn clean package -DskipTests
1010

1111
# Stage 2: Create the Keycloak image
12-
FROM quay.io/keycloak/keycloak:26.0.0
12+
FROM quay.io/keycloak/keycloak:26.5.3
1313

1414
# Copy the provider JAR from the builder stage
1515
COPY --from=builder /app/target/keycloak-2fa-email-authenticator-*.jar /opt/keycloak/providers/

pom.xml

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<artifactId>keycloak-2fa-email-authenticator</artifactId>
77
<groupId>com.mesutpiskin.keycloak</groupId>
8-
<version>26.1.1</version>
8+
<version>26.2.0</version>
99
<modelVersion>4.0.0</modelVersion>
1010
<packaging>jar</packaging>
1111

@@ -14,10 +14,27 @@
1414
<java.version>21</java.version>
1515
<maven.compiler.source>${java.version}</maven.compiler.source>
1616
<maven.compiler.target>${java.version}</maven.compiler.target>
17-
<keycloak.version>26.0.0</keycloak.version>
17+
<keycloak.version>26.5.3</keycloak.version>
18+
<sendgrid.version>4.10.3</sendgrid.version>
19+
<aws-sdk.version>2.30.31</aws-sdk.version>
20+
<junit-jupiter.version>5.11.4</junit-jupiter.version>
21+
<mockito.version>5.15.2</mockito.version>
22+
<maven-surefire.plugin.version>3.5.2</maven-surefire.plugin.version>
1823
<maven-jar.plugin.version>3.4.2</maven-jar.plugin.version>
1924
</properties>
2025

26+
<dependencyManagement>
27+
<dependencies>
28+
<dependency>
29+
<groupId>software.amazon.awssdk</groupId>
30+
<artifactId>bom</artifactId>
31+
<version>${aws-sdk.version}</version>
32+
<type>pom</type>
33+
<scope>import</scope>
34+
</dependency>
35+
</dependencies>
36+
</dependencyManagement>
37+
2138
<dependencies>
2239
<dependency>
2340
<groupId>org.keycloak</groupId>
@@ -43,39 +60,43 @@
4360
<dependency>
4461
<groupId>org.junit.jupiter</groupId>
4562
<artifactId>junit-jupiter</artifactId>
46-
<version>5.10.0</version>
63+
<version>${junit-jupiter.version}</version>
4764
<scope>test</scope>
4865
</dependency>
4966
<dependency>
5067
<groupId>org.mockito</groupId>
5168
<artifactId>mockito-core</artifactId>
52-
<version>5.5.0</version>
69+
<version>${mockito.version}</version>
5370
<scope>test</scope>
5471
</dependency>
5572
<dependency>
5673
<groupId>org.mockito</groupId>
5774
<artifactId>mockito-junit-jupiter</artifactId>
58-
<version>5.5.0</version>
75+
<version>${mockito.version}</version>
5976
<scope>test</scope>
6077
</dependency>
6178

6279
<!-- SendGrid Email Service Provider -->
6380
<dependency>
6481
<groupId>com.sendgrid</groupId>
6582
<artifactId>sendgrid-java</artifactId>
66-
<version>4.10.2</version>
83+
<version>${sendgrid.version}</version>
6784
</dependency>
6885

6986
<!-- AWS SES Email Service Provider -->
7087
<dependency>
7188
<groupId>software.amazon.awssdk</groupId>
7289
<artifactId>ses</artifactId>
73-
<version>2.20.26</version>
7490
</dependency>
7591
</dependencies>
7692

7793
<build>
7894
<plugins>
95+
<plugin>
96+
<groupId>org.apache.maven.plugins</groupId>
97+
<artifactId>maven-surefire-plugin</artifactId>
98+
<version>${maven-surefire.plugin.version}</version>
99+
</plugin>
79100
<plugin>
80101
<groupId>org.apache.maven.plugins</groupId>
81102
<artifactId>maven-jar-plugin</artifactId>

src/main/java/com/mesutpiskin/keycloak/auth/email/EmailAuthenticatorForm.java

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class EmailAuthenticatorForm extends AbstractUsernameFormAuthenticator
5555
implements CredentialValidator<EmailAuthenticatorCredentialProvider> {
5656

5757
protected static final Logger logger = Logger.getLogger(EmailAuthenticatorForm.class);
58+
private static final String CODE_ATTEMPTS = "emailCodeAttempts";
5859

5960
/**
6061
* Initiates the authentication process by presenting the email OTP challenge to
@@ -277,14 +278,29 @@ private boolean isValidCodeContext(AuthenticationFlowContext context, UserModel
277278
if (codeContext.submittedCode().equals(codeContext.storedCode()))
278279
return true;
279280

280-
// AuthenticationExecutionModel execution = context.getExecution();
281-
// if (execution.isRequired()) {
282281
context.getEvent().user(user).error(Errors.INVALID_USER_CREDENTIALS);
283-
Response challengeResponse = challenge(context, Messages.INVALID_ACCESS_CODE, EmailConstants.CODE);
284-
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
285-
// } else if (execution.isConditional() || execution.isAlternative()) {
286-
// context.attempted();
287-
// }
282+
283+
AuthenticationSessionModel session = context.getAuthenticationSession();
284+
int attempts = incrementAttempts(session);
285+
286+
AuthenticatorConfigModel config = context.getAuthenticatorConfig();
287+
Map<String, String> configValues = config != null && config.getConfig() != null
288+
? config.getConfig()
289+
: Map.of();
290+
int maxAttempts = resolvePositiveInt(configValues, EmailConstants.MAX_ATTEMPTS,
291+
EmailConstants.DEFAULT_MAX_ATTEMPTS);
292+
293+
if (attempts >= maxAttempts) {
294+
resetEmailCode(context);
295+
LoginFormsProvider form = prepareForm(context, null);
296+
form.setAttribute("maxAttemptsReached", true);
297+
applyFormMessage(form, "email-authenticator-too-many-attempts", EmailConstants.CODE);
298+
Response challengeResponse = form.createForm("email-code-form.ftl");
299+
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
300+
} else {
301+
Response challengeResponse = challenge(context, Messages.INVALID_ACCESS_CODE, EmailConstants.CODE);
302+
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
303+
}
288304
return false;
289305
}
290306

@@ -327,7 +343,6 @@ private void applyFormMessage(LoginFormsProvider form, String messageKey, String
327343
}
328344
}
329345

330-
@Override
331346
protected String disabledByBruteForceError() {
332347
return Messages.INVALID_ACCESS_CODE;
333348
}
@@ -337,6 +352,21 @@ private void resetEmailCode(AuthenticationFlowContext context) {
337352
session.removeAuthNote(EmailConstants.CODE);
338353
session.removeAuthNote(EmailConstants.CODE_TTL);
339354
session.removeAuthNote(EmailConstants.CODE_RESEND_AVAILABLE_AFTER);
355+
session.removeAuthNote(CODE_ATTEMPTS);
356+
}
357+
358+
private int incrementAttempts(AuthenticationSessionModel session) {
359+
String raw = session.getAuthNote(CODE_ATTEMPTS);
360+
int attempts = 1;
361+
if (raw != null) {
362+
try {
363+
attempts = Integer.parseInt(raw) + 1;
364+
} catch (NumberFormatException ignored) {
365+
// corrupt value, start over
366+
}
367+
}
368+
session.setAuthNote(CODE_ATTEMPTS, Integer.toString(attempts));
369+
return attempts;
340370
}
341371

342372
@Override

src/main/java/com/mesutpiskin/keycloak/auth/email/EmailAuthenticatorFormFactory.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,10 @@ public List<ProviderConfigProperty> getConfigProperties() {
103103
ProviderConfigProperty.BOOLEAN_TYPE, String.valueOf(EmailConstants.DEFAULT_SIMULATION_MODE)),
104104
new ProviderConfigProperty(EmailConstants.RESEND_COOLDOWN, "Resend Cooldown (seconds)",
105105
"The minimum number of seconds a user must wait before requesting a new code.",
106-
ProviderConfigProperty.STRING_TYPE, String.valueOf(EmailConstants.DEFAULT_RESEND_COOLDOWN)));
106+
ProviderConfigProperty.STRING_TYPE, String.valueOf(EmailConstants.DEFAULT_RESEND_COOLDOWN)),
107+
new ProviderConfigProperty(EmailConstants.MAX_ATTEMPTS, "Max Code Attempts",
108+
"The maximum number of invalid code attempts before the code is invalidated and a new one must be requested.",
109+
ProviderConfigProperty.STRING_TYPE, String.valueOf(EmailConstants.DEFAULT_MAX_ATTEMPTS)));
107110
}
108111

109112
@Override

0 commit comments

Comments
 (0)