From 1346a4dacacdb7cbbe9188e2da5c1fc47b358760 Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 12:50:13 -0500 Subject: [PATCH 01/10] Create custom email verification flow --- .../CustomVerifyEmailRequiredAction.java | 133 ++++++++++++++++++ ...cloak.authentication.RequiredActionFactory | 1 + .../ol/email/mjml/email-verification.mjml | 37 ++++- .../theme/ol/login/login-verify-email.ftl | 43 +++++- 4 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java create mode 100644 ol-keycloak/oltheme/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java new file mode 100644 index 0000000..6265f33 --- /dev/null +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -0,0 +1,133 @@ +package edu.mit.ol.keycloak.providers; + +import org.keycloak.authentication.RequiredActionContext; +import org.keycloak.authentication.RequiredActionProvider; +import org.keycloak.authentication.RequiredActionFactory; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.email.EmailException; +import org.keycloak.email.EmailTemplateProvider; +import org.keycloak.common.util.RandomString; +import org.jboss.logging.Logger; + +import jakarta.ws.rs.core.Response; +import java.util.Objects; + +public class CustomVerifyEmailRequiredAction implements RequiredActionProvider { + + private static final Logger logger = Logger.getLogger(CustomVerifyEmailRequiredAction.class); + public static final String PROVIDER_ID = "CUSTOM_VERIFY_EMAIL"; + + @Override + public void evaluateTriggers(RequiredActionContext context) { + if (!context.getUser().isEmailVerified()) { + context.getUser().addRequiredAction(PROVIDER_ID); + } + } + + @Override + public void requiredActionChallenge(RequiredActionContext context) { + // Check if we need to resend code + String resend = context.getHttpRequest().getDecodedFormParameters().getFirst("resend"); + if ("true".equals(resend)) { + sendVerificationCode(context); + } else if (context.getUser().getFirstAttribute("email_verification_code") == null) { + // First time - send code + sendVerificationCode(context); + } + + Response challenge = context.form() + .setAttribute("user", context.getUser()) + .createForm("verify-email.ftl"); + context.challenge(challenge); + } + + @Override + public void processAction(RequiredActionContext context) { + String code = context.getHttpRequest().getDecodedFormParameters().getFirst("email_code"); + String resend = context.getHttpRequest().getDecodedFormParameters().getFirst("resend"); + + if ("true".equals(resend)) { + sendVerificationCode(context); + requiredActionChallenge(context); + return; + } + + if (code != null && isValidVerificationCode(context, code)) { + context.getUser().setEmailVerified(true); + context.getUser().removeAttribute("email_verification_code"); + context.getUser().removeAttribute("email_code_timestamp"); + context.success(); + } else { + Response challenge = context.form() + .setError("Invalid verification code. Please check your email and try again.") + .setAttribute("user", context.getUser()) + .createForm("verify-email.ftl"); + context.challenge(challenge); + } + } + + private void sendVerificationCode(RequiredActionContext context) { + String code = RandomString.randomCode(6); + long timestamp = System.currentTimeMillis(); + + context.getUser().setSingleAttribute("email_verification_code", code); + context.getUser().setSingleAttribute("email_code_timestamp", String.valueOf(timestamp)); + + try { + EmailTemplateProvider emailProvider = context.getSession().getProvider(EmailTemplateProvider.class); + emailProvider.setRealm(context.getRealm()) + .setUser(context.getUser()) + .setAttribute("code", code) + .send("emailVerificationSubject", "email-verification.ftl"); + } catch (EmailException e) { + logger.error("Failed to send verification email", e); + } + } + + private boolean isValidVerificationCode(RequiredActionContext context, String code) { + String storedCode = context.getUser().getFirstAttribute("email_verification_code"); + String timestampStr = context.getUser().getFirstAttribute("email_code_timestamp"); + + if (storedCode == null || timestampStr == null) { + return false; + } + + // Check if code is expired (15 minutes) + try { + long timestamp = Long.parseLong(timestampStr); + long now = System.currentTimeMillis(); + if (now - timestamp > 15 * 60 * 1000) { + context.getUser().removeAttribute("email_verification_code"); + context.getUser().removeAttribute("email_code_timestamp"); + return false; + } + } catch (NumberFormatException e) { + return false; + } + + return Objects.equals(code, storedCode); + } + + @Override + public void close() {} + + public static class Factory implements RequiredActionFactory { + + @Override + public RequiredActionProvider create(KeycloakSession session) { + return new CustomVerifyEmailRequiredAction(); + } + + @Override + public String getDisplayText() { + return "Custom Email Verification with Code"; + } + + @Override + public String getId() { + return PROVIDER_ID; + } + } +} diff --git a/ol-keycloak/oltheme/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory b/ol-keycloak/oltheme/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory new file mode 100644 index 0000000..a256d4b --- /dev/null +++ b/ol-keycloak/oltheme/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory @@ -0,0 +1 @@ +edu.mit.ol.keycloak.providers.CustomVerifyEmailRequiredAction$Factory diff --git a/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml b/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml index 0eedba9..12d3a29 100644 --- a/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml +++ b/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml @@ -3,7 +3,6 @@ - @@ -12,6 +11,39 @@ Thank you for creating an account with ${realmName}. Please complete the account verification process by clicking this link:

+ + <#if code??> + + +

Your Verification Code

+
+ + +
+ + ${code} + +
+
+ + +

+ Enter this code on the verification page to complete your email verification. +

+

+ This code expires in 15 minutes. +

+
+ + + + +

+ Or click the button below to verify automatically: +

+
+ + Verify Your Email @@ -21,8 +53,7 @@
-
- + \ No newline at end of file diff --git a/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl b/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl index bb23f5e..62ed93b 100755 --- a/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl +++ b/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl @@ -6,18 +6,53 @@

${msg("emailVerifyInstruction1", user.email)}

+ + +
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ + +
+
+ + +
+
+ <#elseif section = "info">
-

${msg("emailVerifyInstruction2")}

+

A 6-digit verification code has been sent to your email address.


-

${msg("emailVerifyInstruction3")}

+

Enter the code from your email to verify your account.

- ${msg("emailVerifyInstruction4Bold")} + Code not working? ${msg("emailVerifyInstruction4")} +

+

+ Didn't receive the code? + Check your spam folder or click "Resend Code" above. ${msg("emailVerifySupportLinkTitle")}.

- + \ No newline at end of file From ee16a6cae079aab059cd466ca9855004c6d6de98 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 17:53:25 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../CustomVerifyEmailRequiredAction.java | 20 +++++++++---------- .../ol/email/mjml/email-verification.mjml | 8 ++++---- .../theme/ol/login/login-verify-email.ftl | 14 ++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index 6265f33..267b588 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -18,7 +18,7 @@ public class CustomVerifyEmailRequiredAction implements RequiredActionProvider { private static final Logger logger = Logger.getLogger(CustomVerifyEmailRequiredAction.class); public static final String PROVIDER_ID = "CUSTOM_VERIFY_EMAIL"; - + @Override public void evaluateTriggers(RequiredActionContext context) { if (!context.getUser().isEmailVerified()) { @@ -36,7 +36,7 @@ public void requiredActionChallenge(RequiredActionContext context) { // First time - send code sendVerificationCode(context); } - + Response challenge = context.form() .setAttribute("user", context.getUser()) .createForm("verify-email.ftl"); @@ -47,13 +47,13 @@ public void requiredActionChallenge(RequiredActionContext context) { public void processAction(RequiredActionContext context) { String code = context.getHttpRequest().getDecodedFormParameters().getFirst("email_code"); String resend = context.getHttpRequest().getDecodedFormParameters().getFirst("resend"); - + if ("true".equals(resend)) { sendVerificationCode(context); requiredActionChallenge(context); return; } - + if (code != null && isValidVerificationCode(context, code)) { context.getUser().setEmailVerified(true); context.getUser().removeAttribute("email_verification_code"); @@ -71,10 +71,10 @@ public void processAction(RequiredActionContext context) { private void sendVerificationCode(RequiredActionContext context) { String code = RandomString.randomCode(6); long timestamp = System.currentTimeMillis(); - + context.getUser().setSingleAttribute("email_verification_code", code); context.getUser().setSingleAttribute("email_code_timestamp", String.valueOf(timestamp)); - + try { EmailTemplateProvider emailProvider = context.getSession().getProvider(EmailTemplateProvider.class); emailProvider.setRealm(context.getRealm()) @@ -89,11 +89,11 @@ private void sendVerificationCode(RequiredActionContext context) { private boolean isValidVerificationCode(RequiredActionContext context, String code) { String storedCode = context.getUser().getFirstAttribute("email_verification_code"); String timestampStr = context.getUser().getFirstAttribute("email_code_timestamp"); - + if (storedCode == null || timestampStr == null) { return false; } - + // Check if code is expired (15 minutes) try { long timestamp = Long.parseLong(timestampStr); @@ -106,7 +106,7 @@ private boolean isValidVerificationCode(RequiredActionContext context, String co } catch (NumberFormatException e) { return false; } - + return Objects.equals(code, storedCode); } @@ -114,7 +114,7 @@ private boolean isValidVerificationCode(RequiredActionContext context, String co public void close() {} public static class Factory implements RequiredActionFactory { - + @Override public RequiredActionProvider create(KeycloakSession session) { return new CustomVerifyEmailRequiredAction(); diff --git a/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml b/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml index 12d3a29..6cde617 100644 --- a/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml +++ b/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml @@ -17,7 +17,7 @@

Your Verification Code

- +
@@ -25,7 +25,7 @@
- +

Enter this code on the verification page to complete your email verification. @@ -36,7 +36,7 @@ - +

Or click the button below to verify automatically: @@ -56,4 +56,4 @@ - \ No newline at end of file + diff --git a/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl b/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl index 62ed93b..1cba92f 100755 --- a/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl +++ b/ol-keycloak/oltheme/src/main/resources/theme/ol/login/login-verify-email.ftl @@ -6,7 +6,7 @@

${msg("emailVerifyInstruction1", user.email)}

- +
@@ -14,19 +14,19 @@
-
-
- +
@@ -36,7 +36,7 @@
- + <#elseif section = "info">
@@ -55,4 +55,4 @@
- \ No newline at end of file + From 59a305c19809a1f56d79df866657a942c84d0ad3 Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:00:54 -0500 Subject: [PATCH 03/10] Add dependencies --- ol-keycloak/oltheme/pom.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ol-keycloak/oltheme/pom.xml b/ol-keycloak/oltheme/pom.xml index b0dfa32..dec1fc8 100644 --- a/ol-keycloak/oltheme/pom.xml +++ b/ol-keycloak/oltheme/pom.xml @@ -11,4 +11,25 @@ 1.8 OL theme + + + + org.keycloak + keycloak-server-spi + ${keycloak.version} + provided + + + org.keycloak + keycloak-server-spi-private + ${keycloak.version} + provided + + + org.keycloak + keycloak-services + ${keycloak.version} + provided + + From e4ea854995a855ce1be93070ec877fd4e6549280 Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:08:18 -0500 Subject: [PATCH 04/10] Resolve code review feedback --- ol-keycloak/oltheme/pom.xml | 1 + .../CustomVerifyEmailRequiredAction.java | 72 ++++++++++--------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/ol-keycloak/oltheme/pom.xml b/ol-keycloak/oltheme/pom.xml index dec1fc8..6d0dca0 100644 --- a/ol-keycloak/oltheme/pom.xml +++ b/ol-keycloak/oltheme/pom.xml @@ -9,6 +9,7 @@ UTF-8 1.8 1.8 + 26.3.0 OL theme diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index 267b588..b98b872 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -28,12 +28,8 @@ public void evaluateTriggers(RequiredActionContext context) { @Override public void requiredActionChallenge(RequiredActionContext context) { - // Check if we need to resend code - String resend = context.getHttpRequest().getDecodedFormParameters().getFirst("resend"); - if ("true".equals(resend)) { - sendVerificationCode(context); - } else if (context.getUser().getFirstAttribute("email_verification_code") == null) { - // First time - send code + // Only send code if one doesn't already exist + if (context.getUser().getFirstAttribute("email_verification_code") == null) { sendVerificationCode(context); } @@ -43,6 +39,31 @@ public void requiredActionChallenge(RequiredActionContext context) { context.challenge(challenge); } + private enum VerificationCodeStatus { + VALID, INVALID, EXPIRED + } + + private VerificationCodeStatus checkVerificationCode(RequiredActionContext context, String code) { + String storedCode = context.getUser().getFirstAttribute("email_verification_code"); + String timestampStr = context.getUser().getFirstAttribute("email_code_timestamp"); + + if (storedCode == null || timestampStr == null) { + return VerificationCodeStatus.INVALID; + } + + try { + long timestamp = Long.parseLong(timestampStr); + long now = System.currentTimeMillis(); + if (now - timestamp > 15 * 60 * 1000) { + return VerificationCodeStatus.EXPIRED; + } + } catch (NumberFormatException e) { + return VerificationCodeStatus.INVALID; + } + + return Objects.equals(code, storedCode) ? VerificationCodeStatus.VALID : VerificationCodeStatus.INVALID; + } + @Override public void processAction(RequiredActionContext context) { String code = context.getHttpRequest().getDecodedFormParameters().getFirst("email_code"); @@ -54,11 +75,22 @@ public void processAction(RequiredActionContext context) { return; } - if (code != null && isValidVerificationCode(context, code)) { + VerificationCodeStatus status = checkVerificationCode(context, code); + + if (status == VerificationCodeStatus.VALID) { context.getUser().setEmailVerified(true); context.getUser().removeAttribute("email_verification_code"); context.getUser().removeAttribute("email_code_timestamp"); context.success(); + } else if (status == VerificationCodeStatus.EXPIRED) { + context.getUser().removeAttribute("email_verification_code"); + context.getUser().removeAttribute("email_code_timestamp"); + sendVerificationCode(context); + Response challenge = context.form() + .setError("Your verification code has expired. A new code has been sent to your email.") + .setAttribute("user", context.getUser()) + .createForm("verify-email.ftl"); + context.challenge(challenge); } else { Response challenge = context.form() .setError("Invalid verification code. Please check your email and try again.") @@ -69,7 +101,7 @@ public void processAction(RequiredActionContext context) { } private void sendVerificationCode(RequiredActionContext context) { - String code = RandomString.randomCode(6); + String code = RandomString.randomString(6, RandomString.DIGITS); // 6-digit numeric code long timestamp = System.currentTimeMillis(); context.getUser().setSingleAttribute("email_verification_code", code); @@ -86,30 +118,6 @@ private void sendVerificationCode(RequiredActionContext context) { } } - private boolean isValidVerificationCode(RequiredActionContext context, String code) { - String storedCode = context.getUser().getFirstAttribute("email_verification_code"); - String timestampStr = context.getUser().getFirstAttribute("email_code_timestamp"); - - if (storedCode == null || timestampStr == null) { - return false; - } - - // Check if code is expired (15 minutes) - try { - long timestamp = Long.parseLong(timestampStr); - long now = System.currentTimeMillis(); - if (now - timestamp > 15 * 60 * 1000) { - context.getUser().removeAttribute("email_verification_code"); - context.getUser().removeAttribute("email_code_timestamp"); - return false; - } - } catch (NumberFormatException e) { - return false; - } - - return Objects.equals(code, storedCode); - } - @Override public void close() {} From 233b84c155144f95ecb0e42f0fc761631d1ff772 Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:11:47 -0500 Subject: [PATCH 05/10] Resolve missing randomstring lib --- .../providers/CustomVerifyEmailRequiredAction.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index b98b872..a06c7e3 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -8,7 +8,6 @@ import org.keycloak.models.UserModel; import org.keycloak.email.EmailException; import org.keycloak.email.EmailTemplateProvider; -import org.keycloak.common.util.RandomString; import org.jboss.logging.Logger; import jakarta.ws.rs.core.Response; @@ -101,7 +100,7 @@ public void processAction(RequiredActionContext context) { } private void sendVerificationCode(RequiredActionContext context) { - String code = RandomString.randomString(6, RandomString.DIGITS); // 6-digit numeric code + String code = generateNumericCode(6); // 6-digit numeric code long timestamp = System.currentTimeMillis(); context.getUser().setSingleAttribute("email_verification_code", code); @@ -118,6 +117,15 @@ private void sendVerificationCode(RequiredActionContext context) { } } + private String generateNumericCode(int length) { + StringBuilder sb = new StringBuilder(length); + java.util.Random random = new java.util.Random(); + for (int i = 0; i < length; i++) { + sb.append(random.nextInt(10)); + } + return sb.toString(); + } + @Override public void close() {} From 45bfce2f7f6a74d7afa9cd414686cb3b5c450f1d Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:15:26 -0500 Subject: [PATCH 06/10] Resolve signature mismatch --- .../providers/CustomVerifyEmailRequiredAction.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index a06c7e3..8c08beb 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -12,6 +12,8 @@ import jakarta.ws.rs.core.Response; import java.util.Objects; +import java.util.HashMap; +import java.util.Map; public class CustomVerifyEmailRequiredAction implements RequiredActionProvider { @@ -108,10 +110,11 @@ private void sendVerificationCode(RequiredActionContext context) { try { EmailTemplateProvider emailProvider = context.getSession().getProvider(EmailTemplateProvider.class); + Map attributes = new HashMap<>(); + attributes.put("code", code); emailProvider.setRealm(context.getRealm()) .setUser(context.getUser()) - .setAttribute("code", code) - .send("emailVerificationSubject", "email-verification.ftl"); + .send("emailVerificationSubject", "email-verification.ftl", attributes); } catch (EmailException e) { logger.error("Failed to send verification email", e); } @@ -127,7 +130,8 @@ private String generateNumericCode(int length) { } @Override - public void close() {} + public void close() { + } public static class Factory implements RequiredActionFactory { From b4591cede49a1597eb063d1f50ccf45be65a25c2 Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:19:44 -0500 Subject: [PATCH 07/10] Add a close method --- .../keycloak/providers/CustomVerifyEmailRequiredAction.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index 8c08beb..6d0b55f 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -149,5 +149,10 @@ public String getDisplayText() { public String getId() { return PROVIDER_ID; } + + @Override + public void close() { + // No resources to clean up + } } } From b59308f68d84bc56a240066ae5cbab03d57f9ba3 Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:25:04 -0500 Subject: [PATCH 08/10] postInit method --- .../keycloak/providers/CustomVerifyEmailRequiredAction.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index 6d0b55f..aea5062 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -154,5 +154,10 @@ public String getId() { public void close() { // No resources to clean up } + + @Override + public void postInit(org.keycloak.models.KeycloakSessionFactory factory) { + // No post-initialization needed + } } } From 0d2253332ebd0c3b2dfb5567d23951831bce3a02 Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:27:43 -0500 Subject: [PATCH 09/10] Add init method to Factory class --- .../providers/CustomVerifyEmailRequiredAction.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index aea5062..b30b1c5 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -151,13 +151,18 @@ public String getId() { } @Override - public void close() { - // No resources to clean up + public void init(org.keycloak.Config.Scope config) { + // No initialization needed } @Override public void postInit(org.keycloak.models.KeycloakSessionFactory factory) { // No post-initialization needed } + + @Override + public void close() { + // No resources to clean up + } } } From 6e0c198071e8b2ae7edcef351b4122d7da29a4bc Mon Sep 17 00:00:00 2001 From: sar Date: Tue, 15 Jul 2025 13:44:44 -0500 Subject: [PATCH 10/10] Fixes based on feedback --- .../CustomVerifyEmailRequiredAction.java | 3 +- .../ol/email/mjml/email-verification.mjml | 32 ------------------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java index b30b1c5..07f10ae 100644 --- a/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java +++ b/ol-keycloak/oltheme/src/main/java/edu/mit/ol/keycloak/providers/CustomVerifyEmailRequiredAction.java @@ -14,6 +14,7 @@ import java.util.Objects; import java.util.HashMap; import java.util.Map; +import java.security.SecureRandom; public class CustomVerifyEmailRequiredAction implements RequiredActionProvider { @@ -122,7 +123,7 @@ private void sendVerificationCode(RequiredActionContext context) { private String generateNumericCode(int length) { StringBuilder sb = new StringBuilder(length); - java.util.Random random = new java.util.Random(); + SecureRandom random = new SecureRandom(); for (int i = 0; i < length; i++) { sb.append(random.nextInt(10)); } diff --git a/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml b/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml index 6cde617..ff95ce7 100644 --- a/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml +++ b/ol-keycloak/oltheme/src/main/resources/theme/ol/email/mjml/email-verification.mjml @@ -12,38 +12,6 @@ the account verification process by clicking this link:

- <#if code??> - - -

Your Verification Code

-
- - -
- - ${code} - -
-
- - -

- Enter this code on the verification page to complete your email verification. -

-

- This code expires in 15 minutes. -

-
- - - - -

- Or click the button below to verify automatically: -

-
- - Verify Your Email