diff --git a/src/main/java/net/frozenorb/apiv3/model/User.java b/src/main/java/net/frozenorb/apiv3/model/User.java index e968192..69b711f 100644 --- a/src/main/java/net/frozenorb/apiv3/model/User.java +++ b/src/main/java/net/frozenorb/apiv3/model/User.java @@ -60,6 +60,8 @@ public final class User { @Getter @ExcludeFromReplies private String pendingPhone; @Getter @ExcludeFromReplies private String pendingPhoneToken; @Getter @ExcludeFromReplies private Instant pendingPhoneTokenSetAt; + @Getter @ExcludeFromReplies private Instant pendingPhoneTokenVerificationAttemptedAt; + @Getter @ExcludeFromReplies private Set phoneVerificationFailedAttempts; @Getter private String lastSeenOn; @Getter private Instant lastSeenAt; @Getter private Instant firstSeenAt; @@ -503,12 +505,19 @@ public final class User { this.pendingPhoneTokenSetAt = Instant.now(); } + public void failedPhoneRegistration() { + this.pendingPhoneTokenVerificationAttemptedAt = Instant.now(); + this.phoneVerificationFailedAttempts.add(Instant.now()); + } + public void completePhoneRegistration(String phoneNumber) { this.phone = phoneNumber; this.phoneRegisteredAt = Instant.now(); this.pendingPhone = null; this.pendingPhoneToken = null; this.pendingPhoneTokenSetAt = null; + this.pendingPhoneTokenVerificationAttemptedAt = null; + this.phoneVerificationFailedAttempts = null; } public void hasPermissionAnywhere(String permission, SingleResultCallback callback) { diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java index 9935424..51ada98 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdConfirmPhone.java @@ -12,6 +12,7 @@ import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.util.ErrorUtils; +import java.time.Instant; import java.util.concurrent.TimeUnit; public final class POSTUsersIdConfirmPhone implements Handler { @@ -27,25 +28,40 @@ public final class POSTUsersIdConfirmPhone implements Handler { } if (user.getPhone() != null) { - ErrorUtils.respondInvalidInput(ctx, "User provided already has a confirmed phone number."); + ErrorUtils.respondOther(ctx, 409, "User provided already has a confirmed phone number", "phoneNumberAlreadyConfirmed", ImmutableMap.of()); return; } if (user.getPendingPhoneToken() == null) { - ErrorUtils.respondInvalidInput(ctx, "User provided already hasn't started confirming a phone number."); + ErrorUtils.respondOther(ctx, 409, "User provided already hasn't started confirming a phone number.", "phoneConfirmationNotStarted", ImmutableMap.of()); return; } JsonObject requestBody = ctx.getBodyAsJson(); int phoneCode = requestBody.getInteger("phoneCode"); - if ((System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) > TimeUnit.MINUTES.toMillis(20)) { - ErrorUtils.respondInvalidInput(ctx, "Phone token is expired"); + if ((System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) > TimeUnit.HOURS.toMillis(6)) { + ErrorUtils.respondOther(ctx, 409, "Phone token is expired", "phoneTokenExpired", ImmutableMap.of()); + return; + } + + if ((System.currentTimeMillis() - user.getPendingPhoneTokenVerificationAttemptedAt().toEpochMilli()) < TimeUnit.MINUTES.toMillis(20)) { + ErrorUtils.respondOther(ctx, 409, "Wait before attempting phone verification again.", "waitBeforeAttemptingPhoneVerificationAgain", ImmutableMap.of()); + return; + } + + if (user.getPhoneVerificationFailedAttempts().size() >= 5) { + ErrorUtils.respondOther(ctx, 409, "Too many failed verification attempts", "tooManyFailedPhoneVerifications", ImmutableMap.of()); return; } if (!String.valueOf(phoneCode).equals(user.getPendingPhoneToken())) { - ErrorUtils.respondInvalidInput(ctx, "Phone token doesn't match"); + user.failedPhoneRegistration(); + BlockingCallback callback = new BlockingCallback<>(); + user.save(callback); + callback.get(); + + ErrorUtils.respondOther(ctx, 409, "Phone token doesn't match", "phoneTokenNoMatch", ImmutableMap.of()); return; } diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java index 3a33329..3abc236 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegisterPhone.java @@ -45,7 +45,7 @@ public final class POSTUsersIdRegisterPhone implements Handler { return; } - if (user.getPendingPhoneToken() != null && (System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) < TimeUnit.MINUTES.toMillis(20)) { + if (user.getPendingPhoneToken() != null && (System.currentTimeMillis() - user.getPendingPhoneTokenSetAt().toEpochMilli()) < TimeUnit.HOURS.toMillis(6)) { ErrorUtils.respondOther(ctx, 409, "Confirmation code recently sent.", "confirmationCodeRecentlySent", ImmutableMap.of()); return; }