<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>emial - Inero Software - Software Consulting</title>
	<atom:link href="https://inero-software.com/tag/emial/feed/" rel="self" type="application/rss+xml" />
	<link>https://inero-software.com/tag/emial/</link>
	<description>We unleash innovations using cutting-edge technologies, modern design and AI</description>
	<lastBuildDate>Thu, 13 Feb 2025 11:33:00 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://inero-software.com/wp-content/uploads/2018/11/inero-logo-favicon.png</url>
	<title>emial - Inero Software - Software Consulting</title>
	<link>https://inero-software.com/tag/emial/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">153509928</site>	<item>
		<title>Behind the Scenes #2: Implementing email-based MFA in Keycloak</title>
		<link>https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/</link>
		
		<dc:creator><![CDATA[Marceli Formela]]></dc:creator>
		<pubDate>Thu, 13 Feb 2025 09:50:32 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Company]]></category>
		<category><![CDATA[Keycloak]]></category>
		<category><![CDATA[2FA]]></category>
		<category><![CDATA[emial]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[keycloak]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[Multi-Factor Authentication]]></category>
		<category><![CDATA[OTP]]></category>
		<guid isPermaLink="false">https://inero-software.com/?p=7042</guid>

					<description><![CDATA[<p>In this post, we’ll explore a custom MFA implementation that sends a one-time authentication code to the user’s email.</p>
<p>Artykuł <a href="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/">Behind the Scenes #2: Implementing email-based MFA in Keycloak</a> pochodzi z serwisu <a href="https://inero-software.com">Inero Software - Software Consulting</a>.</p>
]]></description>
										<content:encoded><![CDATA[		<div data-elementor-type="wp-post" data-elementor-id="7042" class="elementor elementor-7042" data-elementor-post-type="post">
				<div class="elementor-element elementor-element-091b893 e-flex e-con-boxed e-con e-parent" data-id="091b893" data-element_type="container">
					<div class="e-con-inner">
		<div class="elementor-element elementor-element-628ea76 e-con-full e-flex e-con e-child" data-id="628ea76" data-element_type="container">
				</div>
		<div class="elementor-element elementor-element-9ca8a9e e-con-full e-flex e-con e-child" data-id="9ca8a9e" data-element_type="container">
				<div class="elementor-element elementor-element-edd1fd0 elementor-widget elementor-widget-html" data-id="edd1fd0" data-element_type="widget" data-widget_type="html.default">
				<div class="elementor-widget-container">
			 		</div>
				</div>
				<div class="elementor-element elementor-element-3e56066 elementor-widget elementor-widget-text-editor" data-id="3e56066" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<h5><strong>Keycloak natively supports many secure login solutions and comes with built-in one-time password (OTP) mechanisms, such as authentication via mobile apps like Google Authenticator or our solution <a href="https://inero-software.com/introducing-authm8-a-free-cross-platform-2fa-solution-tailored-to-your-brand-for-secure-authentication/">AuthM8</a>. However, if we want to use other advanced authentication methods and for example send OTP codes via email, then similar to SMS multi factor authentication (more details <a href="https://inero-software.com/custom-sms-authenticator-with-keycloak/">HERE</a>), we need to implement this functionality ourselves. In this post, we’ll explore a custom MFA implementation that sends a one-time authentication code to the user’s email. </strong></h5>						</div>
				</div>
				<div class="elementor-element elementor-element-0994f82 elementor-widget elementor-widget-heading" data-id="0994f82" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">How does email-based MFA work?
</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-8e8909e elementor-widget elementor-widget-text-editor" data-id="8e8909e" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p>The authentication process consists of two main stages:</p>						</div>
				</div>
				<div class="elementor-element elementor-element-8435873 elementor-widget elementor-widget-text-editor" data-id="8435873" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<ul><li style="list-style-type: none;"><ul><li><b style="color: var( --e-global-color-text ); text-align: var(--text-align);">Generating and sending the MFA code</b></li></ul></li></ul><p><span style="font-weight: 400;">If the user already has an active cookie confirming a previous MFA verification, they should be immediately authenticated. Otherwise, Keycloak creates a new credential for the user and generates a one-time code based on configurable parameters like length or time-to-live.  The code is stored in the user’s credentials and then is emailed using the email provider.</span></p><p> </p><ul><li style="list-style-type: none;"><ul><li aria-level="1"><b>Verifying the entered code</b></li></ul></li></ul><p><span style="font-weight: 400;">When a user submits the code, KC retrieves the stored credential and compares the entered value. If the code is correct and still valid (not expired), authentication is successful, and a cookie is set to remember the verification. If the code is incorrect, the user is prompted to re-enter it and if the code has expired, an error message is shown and the process must be restarted.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-d65460b elementor-widget elementor-widget-image" data-id="d65460b" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img fetchpriority="high" decoding="async" data-attachment-id="7044" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/email-based-two-factor-authentication-flowchart/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART.png" data-orig-size="1920,1080" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="EMAIL-BASED TWO-FACTOR AUTHENTICATION FLOWCHART" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-300x169.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-1030x579.png" tabindex="0" role="button" width="1030" height="579" src="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-1030x579.png" class="attachment-large size-large wp-image-7044" alt="" srcset="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-1030x579.png 1030w, https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-300x169.png 300w, https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-768x432.png 768w, https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-1536x864.png 1536w, https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-533x300.png 533w, https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART.png 1920w" sizes="(max-width: 1030px) 100vw, 1030px" data-attachment-id="7044" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/email-based-two-factor-authentication-flowchart/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART.png" data-orig-size="1920,1080" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="EMAIL-BASED TWO-FACTOR AUTHENTICATION FLOWCHART" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-300x169.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/EMAIL-BASED-TWO-FACTOR-AUTHENTICATION-FLOWCHART-1030x579.png" role="button" />													</div>
				</div>
				<div class="elementor-element elementor-element-5925a75 elementor-widget elementor-widget-heading" data-id="5925a75" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default"><strong data-start="157" data-end="185">Email MFA: Pros and Cons</strong> </h3>		</div>
				</div>
				<div class="elementor-element elementor-element-3c6c4e2 elementor-widget elementor-widget-text-editor" data-id="3c6c4e2" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">Email-based MFA offers additional security when the primary factor, such as a password, has been compromised. This is particularly helpful in cases where passwords are brute-forced or easily guessed, such as with common combinations like 123456. Similarly, this solution offers protection against credential stuffing, where attackers use leaked passwords from other breaches to attempt logging into account.</span></p><p><span style="font-weight: 400;">There are several other benefits to using email as a MFA:</span></p><ul><li style="list-style-type: none;"><ul><li aria-level="1"><span style="font-weight: 400;">Email MFA does not require users to provide additional sensitive information, such as a phone number, reducing concerns about privacy.</span></li></ul></li></ul><ul><li style="list-style-type: none;"><ul><li aria-level="1"><span style="font-weight: 400;">It does not require users to install a separate app or complete a complicated setup, which simplifies the process.</span></li></ul></li></ul><ul><li style="list-style-type: none;"><ul><li aria-level="1"><span style="font-weight: 400;"> </span><span style="font-weight: 400;">Users are accustomed to providing their email for various purposes, such as receiving important account updates or resetting passwords. This familiarity makes it more accessible.</span></li></ul></li></ul><p><span style="font-weight: 400;">However, email as a delivery channel does have some drawbacks. If an attacker compromises your email (gains access to an email account through stolen credentials or by exploiting an active session.), they could potentially reset other accounts’ passwords as well. For users in vulnerable situations, such as those with access to shared devices, email-based MFA can still leave them exposed. As with any security measure, it’s essential to weigh the benefits against the potential risks and mix email MFA with other safeguards, such as strong passwords policy and secure email practices.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-4fd89fe elementor-widget elementor-widget-heading" data-id="4fd89fe" data-element_type="widget" data-widget_type="heading.default">
				<div class="elementor-widget-container">
			<h3 class="elementor-heading-title elementor-size-default">Implementing Email MFA</h3>		</div>
				</div>
				<div class="elementor-element elementor-element-cfc16d2 elementor-widget elementor-widget-image" data-id="cfc16d2" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img decoding="async" data-attachment-id="7045" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-102335/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335.png" data-orig-size="755,508" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 102335" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335-300x202.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335.png" tabindex="0" role="button" width="755" height="508" src="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335.png" class="attachment-large size-large wp-image-7045" alt="" srcset="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335.png 755w, https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335-300x202.png 300w, https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335-446x300.png 446w" sizes="(max-width: 755px) 100vw, 755px" data-attachment-id="7045" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-102335/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335.png" data-orig-size="755,508" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 102335" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335-300x202.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102335.png" role="button" />													</div>
				</div>
				<div class="elementor-element elementor-element-295f2c1 elementor-widget elementor-widget-text-editor" data-id="295f2c1" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">In this modified Browser Authentication Flow, we integrate our custom MFA as an additional authentication method. There are two new steps:</span></p><ul><li style="list-style-type: none;"><ul><li style="font-weight: 400;" aria-level="1"><b>MFA Email setup</b><span style="font-weight: 400;"> – this step ensures that email is set up and verified for the user before proceeding. If the user does not have a custom MFA Credential (which stores OTP codes as secrets), it will be set as well.</span></li></ul></li></ul>						</div>
				</div>
				<div class="elementor-element elementor-element-1cb9fd2 elementor-widget elementor-widget-text-editor" data-id="1cb9fd2" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<pre><span style="font-weight: 400;">public class MfaEmailSetupAuthenticator implements Authenticator, CredentialValidator&lt;MfaEmailCredentialProvider&gt; {</span><br /><span style="font-weight: 400;">@Override</span><br /><span style="font-weight: 400;">public void authenticate(AuthenticationFlowContext context) {</span><br /><span style="font-weight: 400;">[…]</span><br /><span style="font-weight: 400;">// Require email verification</span><br /><span style="font-weight: 400;">if (!userModel.isEmailVerified()) {</span><br /><span style="font-weight: 400;">userModel.addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);</span><br /><span style="font-weight: 400;">}</span><br /><span style="font-weight: 400;">// Add MFA email credential if not present</span><br /><span style="font-weight: 400;">if (!getCredentialProvider(context.getSession()).isConfiguredFor(realmModel, userModel, MfaEmailCredentialModel.TYPE)) {</span><br /><span style="font-weight: 400;">userModel.credentialManager().createStoredCredential(new MfaEmailCredentialModel(new MfaEmailCredentialData()));</span><br /><span style="font-weight: 400;">}</span><br /><span style="font-weight: 400;">[…]</span></pre>						</div>
				</div>
				<div class="elementor-element elementor-element-6908dab elementor-widget__width-initial elementor-widget elementor-widget-image" data-id="6908dab" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img decoding="async" data-attachment-id="7046" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-102520/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520.png" data-orig-size="635,398" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 102520" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520-300x188.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520.png" tabindex="0" role="button" width="635" height="398" src="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520.png" class="attachment-large size-large wp-image-7046" alt="" srcset="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520.png 635w, https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520-300x188.png 300w, https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520-479x300.png 479w" sizes="(max-width: 635px) 100vw, 635px" data-attachment-id="7046" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-102520/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520.png" data-orig-size="635,398" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 102520" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520-300x188.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102520.png" role="button" />													</div>
				</div>
				<div class="elementor-element elementor-element-eafd6c8 elementor-widget elementor-widget-text-editor" data-id="eafd6c8" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<ul><li style="list-style-type: none;"><ul><li style="font-weight: 400;" aria-level="1"><b>MFA Email Authentication</b><span style="font-weight: 400;"> – this is the actual authentication step where a one-time code is sent via email. Marked as Alternative, meaning it can be used instead of other MFA methods like mobile app OTP.</span></li></ul></li></ul><p><span style="font-weight: 400;">Here, you can see how the configuration of this authenticator could look like in the Keycloak authentication flow.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-bdcf27f elementor-widget__width-initial elementor-widget elementor-widget-image" data-id="bdcf27f" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img loading="lazy" decoding="async" data-attachment-id="7047" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-102652/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652.png" data-orig-size="473,622" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 102652" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652-228x300.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652.png" tabindex="0" role="button" width="473" height="622" src="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652.png" class="attachment-large size-large wp-image-7047" alt="" srcset="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652.png 473w, https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652-228x300.png 228w" sizes="(max-width: 473px) 100vw, 473px" data-attachment-id="7047" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-102652/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652.png" data-orig-size="473,622" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 102652" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652-228x300.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-102652.png" role="button" />													</div>
				</div>
				<div class="elementor-element elementor-element-10b800d elementor-widget elementor-widget-text-editor" data-id="10b800d" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<ul><li style="list-style-type: none;"><ul><li style="font-weight: 400;" aria-level="1"><b>Max Cookie Age</b><span style="font-weight: 400;"> this setting determines how long the MFA session (cookie) is valid. If the cookie is still valid, the user won&#8217;t be prompted for MFA. </span></li><li style="font-weight: 400;" aria-level="1"><b>Time-to-live</b><span style="font-weight: 400;"> indicates the lifetime of the MFA code.</span></li></ul></li></ul><p> </p><p><span style="font-weight: 400;">Now let’s take a look at the code. </span></p><p> </p><p><span style="font-weight: 400;">The method below handles the MFA process itself. If a valid cookie exists (indicating that the user has already completed MFA), the method immediately returns success, meaning the authentication flow is complete without requiring additional actions.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-89fa524 elementor-widget elementor-widget-text-editor" data-id="89fa524" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<pre><span style="font-weight: 400;">@Override</span><br /><span style="font-weight: 400;">public void authenticate(AuthenticationFlowContext context) {</span><br /><span style="font-weight: 400;">if (hasValidCookie(context)) {</span><br /><span style="font-weight: 400;">context.success();</span><br /><span style="font-weight: 400;">return;</span><br /><span style="font-weight: 400;">}</span><br /><span style="font-weight: 400;">[…]</span></pre>						</div>
				</div>
				<div class="elementor-element elementor-element-55ff859 elementor-widget elementor-widget-text-editor" data-id="55ff859" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">If there is no cookie, we should try to retrieve the user’s existing MFA credential from the credential provider. If the user doesn’t have one, a new instance is created using the MfaEmailCredentialModel which just extends the built-in CredentialModel:</span><span style="font-weight: 400;"><br /></span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-0af0624 elementor-widget elementor-widget-text-editor" data-id="0af0624" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<pre><span style="font-weight: 400;">[…]</span><br /><span style="font-weight: 400;">// get existing credential or create a new one</span><br /><span style="font-weight: 400;">CredentialModel credentialModel = getCredentialProvider(session)</span><br /><span style="font-weight: 400;">.getDefaultCredential(session, context.getRealm(), user);</span><br /><span style="font-weight: 400;">if (credentialModel == null) {</span><br /><span style="font-weight: 400;">credentialModel = user.credentialManager().createStoredCredential(new MfaEmailCredentialModel(new MfaEmailCredentialData()));</span><br /><span style="font-weight: 400;">}</span><br /><span style="font-weight: 400;">[…]</span></pre>						</div>
				</div>
				<div class="elementor-element elementor-element-c7af14a elementor-widget elementor-widget-text-editor" data-id="c7af14a" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">Then the authenticate method reads configuration properties like code length and TTL (time-to-live). The code itself can be generated using some utils method and will be stored as the secretData in the credential model.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-6551375 elementor-widget elementor-widget-text-editor" data-id="6551375" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<pre><span style="font-weight: 400;">// generate and store code</span><br /><span style="font-weight: 400;">int length = Integer.parseInt(configMap.get(CONFIG_CODE_LENGTH));</span><br /><span style="font-weight: 400;">int ttl = Integer.parseInt(configMap.get(CONFIG_CODE_TTL));</span><br /><span style="font-weight: 400;">String code = MfaEmailCodesUtils.generateCode(length);</span><br /><span style="font-weight: 400;">credentialModel.setSecretData(code);</span><br /><span style="font-weight: 400;">user.credentialManager().updateStoredCredential(credentialModel);</span><br /><span style="font-weight: 400;">AuthenticationSessionModel authSession = context.getAuthenticationSession();</span><br /><span style="font-weight: 400;">authSession.setAuthNote("ttl", Long.toString(System.currentTimeMillis() + (ttl * 1000L)));</span></pre>						</div>
				</div>
				<div class="elementor-element elementor-element-b7f4d62 elementor-widget elementor-widget-text-editor" data-id="b7f4d62" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">In the end the sendCode method is called to send the generated code to the user’s email. If the email is sent successfully, the method presents the form where the user can enter the MFA code.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-dc63501 elementor-widget elementor-widget-text-editor" data-id="dc63501" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<pre><span style="font-weight: 400;">// send email and show input form</span><br /><span style="font-weight: 400;">try {</span><br /><span style="font-weight: 400;">MfaEmailCodesUtils.sendCode(session, user, ttl, code, configMap);</span><br /><span style="font-weight: 400;">context.challenge(context.form().setAttribute("realm", context.getRealm()).createForm(TPL_CODE));</span><br /><span style="font-weight: 400;">} catch (Exception e) {</span><br /><span style="font-weight: 400;">context.failureChallenge(AuthenticationFlowError.INTERNAL_ERROR,</span><br /><span style="font-weight: 400;">context.form().setError("mfaEmailNotSent", e.getMessage())  .createErrorPage(Response.Status.INTERNAL_SERVER_ERROR));</span><br /><span style="font-weight: 400;">}</span></pre>						</div>
				</div>
				<div class="elementor-element elementor-element-b41ad7b elementor-widget elementor-widget-text-editor" data-id="b41ad7b" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">The second major part of our Authenticator is the action method which handles the validation of the code entered by the user. It is invoked when the user submits the input form after receiving the email.  </span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-9551f9f elementor-widget__width-initial elementor-widget elementor-widget-image" data-id="9551f9f" data-element_type="widget" data-widget_type="image.default">
				<div class="elementor-widget-container">
													<img loading="lazy" decoding="async" data-attachment-id="7048" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-103114/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114.png" data-orig-size="663,391" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 103114" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114-300x177.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114.png" tabindex="0" role="button" width="663" height="391" src="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114.png" class="attachment-large size-large wp-image-7048" alt="" srcset="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114.png 663w, https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114-300x177.png 300w, https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114-509x300.png 509w" sizes="(max-width: 663px) 100vw, 663px" data-attachment-id="7048" data-permalink="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/zrzut-ekranu-2025-02-13-103114/" data-orig-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114.png" data-orig-size="663,391" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Zrzut ekranu 2025-02-13 103114" data-image-description="" data-image-caption="" data-medium-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114-300x177.png" data-large-file="https://inero-software.com/wp-content/uploads/2025/02/Zrzut-ekranu-2025-02-13-103114.png" role="button" />													</div>
				</div>
				<div class="elementor-element elementor-element-460cfb7 elementor-widget elementor-widget-text-editor" data-id="460cfb7" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">The method retrieves the user’s credential from the provider and then the code is validated by checking it against the stored credential using the custom isValid method.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-ff19a7e elementor-widget elementor-widget-text-editor" data-id="ff19a7e" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<pre><span style="font-weight: 400;">[…]</span><br /><span style="font-weight: 400;">final MfaEmailCredentialModel credentialModel = getCredentialProvider(session)</span><br /><span style="font-weight: 400;">        .getDefaultCredential(session, context.getRealm(), user);</span><br /><span style="font-weight: 400;">boolean isValid = getCredentialProvider(session).isValid(context.getRealm(), user,</span><br /><span style="font-weight: 400;">    </span> <span style="font-weight: 400;">new UserCredentialModel(credentialModel.getId(), getCredentialProvider(context.getSession()).getType(), enteredCode));</span><br /><span style="font-weight: 400;">[…]</span></pre>						</div>
				</div>
				<div class="elementor-element elementor-element-7b502f8 elementor-widget elementor-widget-text-editor" data-id="7b502f8" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">If the code is valid, the next step is to check if it is expired. We can also set a cookie that stores the MFA session to prevent the user from </span><b>being prompted for MFA again</b><span style="font-weight: 400;"> during the cookie’s validity period.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-3db7437 elementor-widget elementor-widget-text-editor" data-id="3db7437" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<pre><span style="font-weight: 400;">[…]</span><br /><span style="font-weight: 400;">// valid</span><br /><span style="font-weight: 400;">HttpResponse response = context.getSession().getContext().getHttpResponse();</span><br /><span style="font-weight: 400;">response.setCookieIfAbsent(createCookie(context));</span><br /><span style="font-weight: 400;">context.success();</span><br /><span style="font-weight: 400;">[…]</span></pre><p><span style="font-weight: 400;"> </span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-83cf638 elementor-widget elementor-widget-text-editor" data-id="83cf638" data-element_type="widget" data-widget_type="text-editor.default">
				<div class="elementor-widget-container">
							<p><span style="font-weight: 400;">Of course, in this post, we will not cover the entire topic, omitting implementation details such as sending the code, generating the code, validation, and creating our custom cookie.</span></p><p><span style="font-weight: 400;"><br></span></p>
<p><span style="font-weight: 400;">However, we have walked through the major steps of implementing 2FA using email-based codes. On the one hand, this approach offers a simple and accessible solution. Although it has its drawbacks, using it in solutions like Keycloak helps mitigate many of these vulnerabilities. Keycloak also provides the flexibility to combine email-based MFA with other security measures, creating a more layered and resilient authentication process that can help protect against evolving cybersecurity threats.</span></p>						</div>
				</div>
				<div class="elementor-element elementor-element-a27180c elementor-cta--skin-cover elementor-animated-content elementor-bg-transform elementor-bg-transform-zoom-in elementor-widget elementor-widget-call-to-action" data-id="a27180c" data-element_type="widget" data-widget_type="call-to-action.default">
				<div class="elementor-widget-container">
					<div class="elementor-cta">
					<div class="elementor-cta__bg-wrapper">
				<div class="elementor-cta__bg elementor-bg" style="background-image: url(https://inero-software.com/wp-content/uploads/2024/11/tlo-popup-keycloak-1030x731.png);" role="img" aria-label="tło popup keycloak"></div>
				<div class="elementor-cta__bg-overlay"></div>
			</div>
							<div class="elementor-cta__content">
				
									<h3 class="elementor-cta__title elementor-cta__content-item elementor-content-item elementor-animated-item--grow">
						Do you need help configuring multi-factor authentication?					</h3>
				
									<div class="elementor-cta__description elementor-cta__content-item elementor-content-item elementor-animated-item--grow">
						Schedule a meeting to find out how we can help you.					</div>
				
									<div class="elementor-cta__button-wrapper elementor-cta__content-item elementor-content-item elementor-animated-item--grow">
					<a class="elementor-cta__button elementor-button elementor-size-" href="https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ3e3C_1YeBkt1uCr_qfOnG_N298UgLFwORcSTXigrPfOk0ls3ok-Uw_dSeGCoLdtYsN13GMm-n-">
						Schedule a meeting					</a>
					</div>
							</div>
						</div>
				</div>
				</div>
				</div>
		<div class="elementor-element elementor-element-6bc7752 e-con-full e-flex e-con e-child" data-id="6bc7752" data-element_type="container">
				</div>
					</div>
				</div>
		<div class="elementor-element elementor-element-091ddaf e-flex e-con-boxed e-con e-parent" data-id="091ddaf" data-element_type="container">
					<div class="e-con-inner">
					</div>
				</div>
				</div>
		<p>Artykuł <a href="https://inero-software.com/behind-the-scenes-2-implementing-email-based-mfa-in-keycloak/">Behind the Scenes #2: Implementing email-based MFA in Keycloak</a> pochodzi z serwisu <a href="https://inero-software.com">Inero Software - Software Consulting</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">7042</post-id>	</item>
	</channel>
</rss>
