Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SMS Authenticator - Resend code flow and avoid sending with refresh page button. #157

Open
Mizar01 opened this issue Nov 14, 2024 · 4 comments

Comments

@Mizar01
Copy link

Mizar01 commented Nov 14, 2024

Sometimes the users do not receive the text in their phone and they need to have a resend code button.

I discovered that this is simply done by refreshing the page (well, if no other 2fa method takes priority).

But this also raise a problem. We pay for the sms texts, and we don't want people to click compulsively on the refresh button or the 'resend code' button.

What could be the best approach to allow resending without this risk?

I thought about counting the retries and store this number in session. After a successful code matching, the counter is reset to zero. If the counter reaches the limit, I have to show a message telling the user he cannot retry further.

@RaveNN-0
Copy link

Were you able to find a solution for the SMS "DDoS" situation (the refreshing to keep getting SMS') ?

@Mizar01
Copy link
Author

Mizar01 commented Dec 26, 2024

I still use the method explained at the end of the opening post.
Every time a code is generated without a succesful otp verification, a counter (stored in session) is incremented. No further code will be generated after some threshold value.
Furthermore the resend button is shown with some delay in the page, so, unless hitting refresh on the browser, the user has to wait.
It is some kind of workaround.

One can also store the last time a code has ben generated, or use other strategies, but I wish an official method could be implemented.

@RaveNN-0
Copy link

RaveNN-0 commented Dec 26, 2024

Can you send your used code/the parts that you modified ? @Mizar01

@Mizar01
Copy link
Author

Mizar01 commented Jan 17, 2025

Sorry, I switched to another project...

PhoneValidationRequiredAction.java
`
on method requiredActionChallenge() ...
...
String mobileNumber = authSession.getAuthNote("mobile_number");
// logger.infof("Validating phone number: %s of user: %s", mobileNumber, user.getUsername());

		var maxAttemptsReached = SmsAuthenticator.checkAndSetResendCodeMaxAttempts(authSession);
		if (maxAttemptsReached) {
			handleMaxAttemptsReached(context);
			return;
		}

		int length = Integer.parseInt(config.getConfig().get("length"));
		int ttl = Integer.parseInt(config.getConfig().get("ttl"));
		int resendCodeWaitTime = Integer.parseInt(config.getConfig().getOrDefault("resendCodeWaitTime", "120"));

		String code = SecretGenerator.getInstance().randomString(length, SecretGenerator.DIGITS);
		authSession.setAuthNote("code", code);
		authSession.setAuthNote("ttl", Long.toString(System.currentTimeMillis() + (ttl * 1000L)));

		Theme theme = context.getSession().theme().getTheme(Theme.Type.LOGIN);
		Locale locale = context.getSession().getContext().resolveLocale(user);
		String smsAuthText = theme.getEnhancedMessages(realm,locale).getProperty("smsAuthText");
		String smsText = String.format(smsAuthText, code, Math.floorDiv(ttl, 60));

		SmsServiceFactory.get(config.getConfig()).send(mobileNumber, smsText);

		Response challenge = context.form()
			.setAttribute("realm", realm)
			.setAttribute("resendCodeWaitTime", resendCodeWaitTime)

                      .....

                     // In processAction before context.success insert this: 
                     SmsAuthenticator.resetResendCodeAttempts(authSession);

....

public void handleMaxAttemptsReached(RequiredActionContext context) {
	Response challenge = context
		.form()
		.setAttribute("realm", context.getRealm())
		.setError("resendCodeMaxAttemptsReached")
		.createForm("login-sms.ftl");
	context.challenge(challenge);
}

`

On SmsAuthenticator

public static boolean checkAndSetResendCodeMaxAttempts(AuthenticationSessionModel authSession) { var config = G4Utils.getSmsConfig(authSession.getRealm()); int max = Integer.parseInt(config.get(configResendCodeMaxAttempts)); if (max == -1) { return false; } int currentAttempts = Integer.parseInt((authSession.getAuthNote(sessionVarResendCodeAttempts) != null ? authSession.getAuthNote(sessionVarResendCodeAttempts) : "0")); if (currentAttempts >= max) { return true; } authSession.setAuthNote(sessionVarResendCodeAttempts, Integer.toString(currentAttempts + 1)); return false; }

This is the essential parts I modified. You need to add further configuration variables in order to manage them from the realm console, and add the relevant part of the timed resend button in login-sms.ftl.

PS : I don't know why SmsAuthenticator code is mangled when rendered in this editor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants