Config name | Section | Type | Description |
---|---|---|---|
secret | Both | string | |
length | Generation | int | 6,7,8 digits of OTP |
expiry-duration | Generation | duration | 1m, 30s |
otp-buffer-window | Validation | int | expiry period multiplier |
generation-limit | Generation | int | Consecutive generation limit. After a successful validation, this resets to zero |
resend-interval | Generation | duration | can re-send only after this duration of last send |
validation-limit | Validation | int | Consecutive validate limit when validate keeps failing. if a validates succeeds, this counter resets to zero |
cool-off-period | Both | duration | Validation and generation are blocked for this period when user is blocked |
Last active
April 25, 2024 21:40
-
-
Save bilal-fazlani/593c833bb057c1365baa5747a9f135fa to your computer and use it in GitHub Desktop.
OTP generation and validation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//OTP generation | |
//services | |
trait NotificationService: | |
def sendOTP(otp: String): Unit | |
//config values | |
val generationLimit: Int = ??? | |
val resendInterval: Duration = ??? | |
val cooldown: Duration = ??? | |
//api input | |
enum UserIdentifier: | |
case Email(email: String) | |
case Phone(phone: String) | |
enum OTPType: | |
case Transaction(tranasctionId: String) | |
case Login(id: UserIdentifier) | |
case class GenerateOTP(otpType: OTPType) | |
//api output | |
enum Response: | |
//OTP generated successfully | |
case Success(id: UUID, attemptsRemaining: Int) | |
//OTP generation failed because of resend interval | |
case TooSoon(timeRemaining: Duration) | |
//OTP generation failed because account is locked for some time | |
case TooManyAttempts(timeRemaining: Duration) | |
//db record | |
case class OTPRecord( | |
id: UUID, | |
generationCounter: Int, | |
allowGenerationAfter: DateTime, | |
ttl: DateTime, | |
otpType: OTPType | |
) | |
//current time | |
val currentTime: DateTime = ??? | |
val record: OTPRecord = ??? | |
//check if OTP can be generated | |
val canGenerate = | |
currentTime.isAfter(record.allowGenerationAfter) && record.generationCounter < generationLimit | |
if (canGenerate) | |
val newOTP = generateOTP() | |
NotificationService.sendOTP(newOTP) | |
val newGenerationCounter = generationCounter + 1 | |
if newGenerationCounter == generationLimit | |
val newAllowGenerationAfter = currentTime.plus(cooldown) | |
updateDB(newGenerationCounter, newAllowGenerationAfter, TTL(newAllowGenerationAfter)) | |
else | |
val newAllowGenerationAfter = currentTime.plus(resendInterval) //1 min | |
val ttl = currentTime.plus(cooldown) // 1 day | |
updateDB(newGenerationCounter, newAllowGenerationAfter, ttl) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment