diff --git a/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230919-add-otp-fake.xml b/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230919-add-otp-fake.xml
new file mode 100644
index 000000000..edc0bdf3d
--- /dev/null
+++ b/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230919-add-otp-fake.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+ Add fake column
+
+
+
+
+
+
+
diff --git a/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230824-add-tag-1.5.0.xml b/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230919-add-tag-1.5.0.xml
similarity index 88%
rename from docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230824-add-tag-1.5.0.xml
rename to docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230919-add-tag-1.5.0.xml
index 02ccab024..c7c3a870d 100644
--- a/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230824-add-tag-1.5.0.xml
+++ b/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/20230919-add-tag-1.5.0.xml
@@ -3,7 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
-
+
diff --git a/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/db.changelog-version.xml b/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/db.changelog-version.xml
index 1a6797e7c..2cc5996bf 100644
--- a/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/db.changelog-version.xml
+++ b/docs/db/changelog/changesets/enrollment-server-onboarding/1.5.x/db.changelog-version.xml
@@ -6,6 +6,7 @@
-
+
+
\ No newline at end of file
diff --git a/docs/onboarding/Database-Structure.md b/docs/onboarding/Database-Structure.md
index f5c6632ca..977fafe71 100644
--- a/docs/onboarding/Database-Structure.md
+++ b/docs/onboarding/Database-Structure.md
@@ -69,22 +69,23 @@ Stores onboarding OTP codes used during activation and user verification.
#### Schema
-| Name | Type | Info | Note |
-|---|---|---|---|
-| `id` | `VARCHAR(36)` | `NOT NULL PRIMARY KEY` | Autogenerated record identifier (UUID). |
-| `process_id` | `VARCHAR(36)` | `NOT NULL` | Process identifier (UUID). |
-| `otp_code` | `VARCHAR(32)` | `NOT NULL` | Generated OTP code. |
-| `status` | `VARCHAR(36)` | | Generated OTP code. |
-| `type` | `VARCHAR(32)` | `NOT NULL` | OTP code type (`ACTIVATION`, `USER_VERIFICATION`). |
-| `error_detail` | `VARCHAR(256)` | | Detail of error (e.g. information about timeout or exceeded number of failed attempts). |
-| `error_origin` | `VARCHAR(256)` | | Origin of the error (`DOCUMENT_VERIFICATION`, `PRESENCE_CHECK`, `CLIENT_EVALUATION`, `OTP_VERIFICATION`, `PROCESS_LIMIT_CHECK`, `USER_REQUEST`). |
-| `failed_attempts` | `INTEGER` | | Number of failed attempts for verification. |
-| `total_attempts` | `INTEGER` | | Number of total attempts for verification. |
-| `timestamp_created` | `TIMESTAMP` | `NOT NULL DEFAULT CURRENT_TIMESTAMP` | Timestamp when process was started. |
-| `timestamp_expiration` | `TIMESTAMP` | `NOT NULL` | Timestamp when the OTP expires. |
-| `timestamp_last_updated` | `TIMESTAMP` | | Timestamp when record was last updated. |
-| `timestamp_verified` | `TIMESTAMP` | | Timestamp when OTP was verified. |
-| `timestamp_failed` | `TIMESTAMP` | | Timestamp when OTP failed. |
+| Name | Type | Info | Note |
+|--------------------------|----------------|--------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | `VARCHAR(36)` | `NOT NULL PRIMARY KEY` | Autogenerated record identifier (UUID). |
+| `process_id` | `VARCHAR(36)` | `NOT NULL` | Process identifier (UUID). |
+| `otp_code` | `VARCHAR(32)` | `NOT NULL` | Generated OTP code. |
+| `status` | `VARCHAR(36)` | | Generated OTP code. |
+| `type` | `VARCHAR(32)` | `NOT NULL` | OTP code type (`ACTIVATION`, `USER_VERIFICATION`). |
+| `error_detail` | `VARCHAR(256)` | | Detail of error (e.g. information about timeout or exceeded number of failed attempts). |
+| `error_origin` | `VARCHAR(256)` | | Origin of the error (`DOCUMENT_VERIFICATION`, `PRESENCE_CHECK`, `CLIENT_EVALUATION`, `OTP_VERIFICATION`, `PROCESS_LIMIT_CHECK`, `USER_REQUEST`). |
+| `failed_attempts` | `INTEGER` | | Number of failed attempts for verification. |
+| `total_attempts` | `INTEGER` | | Number of total attempts for verification. |
+| `fake` | `BOOLEAN` | `NOT NULL DEFAULT FALSE` | Whether is a fake entry not supposed to be sent. |
+| `timestamp_created` | `TIMESTAMP` | `NOT NULL DEFAULT CURRENT_TIMESTAMP` | Timestamp when process was started. |
+| `timestamp_expiration` | `TIMESTAMP` | `NOT NULL` | Timestamp when the OTP expires. |
+| `timestamp_last_updated` | `TIMESTAMP` | | Timestamp when record was last updated. |
+| `timestamp_verified` | `TIMESTAMP` | | Timestamp when OTP was verified. |
+| `timestamp_failed` | `TIMESTAMP` | | Timestamp when OTP failed. |
diff --git a/docs/onboarding/PowerAuth-Enrollment-Onboarding-Server-1.5.0.md b/docs/onboarding/PowerAuth-Enrollment-Onboarding-Server-1.5.0.md
index e50ca51ab..d30c20fcf 100644
--- a/docs/onboarding/PowerAuth-Enrollment-Onboarding-Server-1.5.0.md
+++ b/docs/onboarding/PowerAuth-Enrollment-Onboarding-Server-1.5.0.md
@@ -128,6 +128,27 @@ CREATE INDEX PROCESS_ID ON ES_SCA_RESULT (PROCESS_ID);
```
+### OTP Fake Marker
+
+A new column `fake` has been added to the table `es_onboarding_otp`.
+
+
+#### PostgreSQL
+
+```sql
+ALTER TABLE es_onboarding_otp
+ ADD COLUMN fake BOOLEAN NOT NULL DEFAULT FALSE;
+```
+
+
+#### Oracle
+
+```sql
+ALTER TABLE es_onboarding_OTP
+ ADD fake NUMBER(1) NOT NULL DEFAULT 0;
+```
+
+
## Dependencies
PostgreSQL JDBC driver is already included in the WAR file.
diff --git a/docs/sql/oracle/onboarding/create-schema.sql b/docs/sql/oracle/onboarding/create-schema.sql
index 53f6d4c27..7000e1498 100644
--- a/docs/sql/oracle/onboarding/create-schema.sql
+++ b/docs/sql/oracle/onboarding/create-schema.sql
@@ -52,6 +52,7 @@ CREATE TABLE ES_ONBOARDING_OTP (
ERROR_ORIGIN VARCHAR2(256 CHAR),
FAILED_ATTEMPTS INTEGER,
TOTAL_ATTEMPTS INTEGER DEFAULT 0,
+ FAKE NUMBER(1) NOT NULL DEFAULT 0,
TIMESTAMP_CREATED TIMESTAMP(6) NOT NULL,
TIMESTAMP_EXPIRATION TIMESTAMP(6) NOT NULL,
TIMESTAMP_LAST_UPDATED TIMESTAMP(6),
diff --git a/docs/sql/postgresql/onboarding/create-schema.sql b/docs/sql/postgresql/onboarding/create-schema.sql
index 1defbb886..a91ebe7c2 100644
--- a/docs/sql/postgresql/onboarding/create-schema.sql
+++ b/docs/sql/postgresql/onboarding/create-schema.sql
@@ -56,6 +56,7 @@ CREATE TABLE es_onboarding_otp (
error_origin VARCHAR(256),
failed_attempts INTEGER,
total_attempts INTEGER DEFAULT 0,
+ fake BOOLEAN NOT NULL DEFAULT false,
timestamp_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
timestamp_expiration TIMESTAMP NOT NULL,
timestamp_last_updated TIMESTAMP,
diff --git a/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingOtpEntity.java b/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingOtpEntity.java
index 3b4c97830..5137e65b7 100644
--- a/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingOtpEntity.java
+++ b/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingOtpEntity.java
@@ -26,7 +26,6 @@
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
-import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.UuidGenerator;
import java.io.Serial;
@@ -96,6 +95,12 @@ public class OnboardingOtpEntity implements Serializable {
@Column(name = "total_attempts")
private int totalAttempts;
+ /**
+ * Whether is a fake entry not supposed to be sent.
+ */
+ @Column(name = "fake")
+ private boolean fake;
+
@Column(name = "timestamp_created", nullable = false)
private Date timestampCreated;
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java
index 3e6a89079..9f3639627 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java
@@ -173,11 +173,12 @@ public OnboardingStartResponse startOnboarding(
throw new TooManyProcessesException("Maximum number of processes per day reached for user: " + userId);
}
- final String otpCode = otpService.createOtpCode(process, OtpType.ACTIVATION);
if (userId == null) {
logger.debug("User ID is null, OTP is not sent");
+ otpService.createFakeOtpCode(process, OtpType.ACTIVATION);
} else {
logger.debug("Sending OTP for user ID: {}", userId);
+ final String otpCode = otpService.createOtpCode(process, OtpType.ACTIVATION);
sendOtp(process, otpCode);
}
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OtpServiceImpl.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OtpServiceImpl.java
index 8ba0e08eb..2a00aeddc 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OtpServiceImpl.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OtpServiceImpl.java
@@ -95,6 +95,18 @@ public String createOtpCode(OnboardingProcessEntity process, OtpType otpType) th
return generateOtpCode(process, otpType);
}
+ /**
+ * Create a fake OTP code for onboarding process.
+ *
+ * @param process Onboarding process.
+ * @param otpType OTP type.
+ * @throws OnboardingProcessException Thrown in case OTP code could not be generated.
+ * @see #createOtpCode(OnboardingProcessEntity, OtpType)
+ */
+ public void createFakeOtpCode(OnboardingProcessEntity process, OtpType otpType) throws OnboardingProcessException {
+ generateFakeOtpCode(process, otpType);
+ }
+
/**
* Create an OTP code for onboarding process for resend.
* @param process Onboarding process.
@@ -155,6 +167,31 @@ public void cancelOtp(OnboardingProcessEntity process, OtpType otpType) {
* @throws OnboardingProcessException Thrown in case OTP code could not be generated.
*/
private String generateOtpCode(OnboardingProcessEntity process, OtpType otpType) throws OnboardingProcessException {
+ return generateOtpCode(process, otpType, false);
+ }
+
+ /**
+ * Generate a fake OTP code for an onboarding process.
+ *
+ * @param process Onboarding process.
+ * @param otpType OTP type.
+ * @return OTP code.
+ * @throws OnboardingProcessException Thrown in case OTP code could not be generated.
+ */
+ private void generateFakeOtpCode(OnboardingProcessEntity process, OtpType otpType) throws OnboardingProcessException {
+ generateOtpCode(process, otpType, true);
+ }
+
+ /**
+ * Generate an OTP code for an onboarding process.
+ *
+ * @param process Onboarding process.
+ * @param otpType OTP type.
+ * @param fake Whether OTP should be marked as a fake.
+ * @return OTP code.
+ * @throws OnboardingProcessException Thrown in case OTP code could not be generated.
+ */
+ private String generateOtpCode(OnboardingProcessEntity process, OtpType otpType, boolean fake) throws OnboardingProcessException {
int otpLength = onboardingConfig.getOtpLength();
String otpCode = otpGeneratorService.generateOtpCode(otpLength);
@@ -173,6 +210,7 @@ private String generateOtpCode(OnboardingProcessEntity process, OtpType otpType)
otp.setTimestampExpiration(timestampExpiration);
otp.setFailedAttempts(0);
otp.setTotalAttempts(0);
+ otp.setFake(fake);
if (otpType == OtpType.USER_VERIFICATION) {
final String activationId = process.getActivationId();
diff --git a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql
index fa258467f..c1307d611 100644
--- a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql
+++ b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql
@@ -1,7 +1,7 @@
INSERT INTO es_onboarding_process(id, identification_data, status, error_score, custom_data, timestamp_created) VALUES
('b4662611-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, '{}', now());
-INSERT INTO es_onboarding_otp(id, process_id, otp_code, failed_attempts, total_attempts, status, type, timestamp_created, timestamp_expiration) VALUES
- ('f50b8c04-649d-43a7-8079-4dbf9b0bbc72', 'b4662611-df91-4053-bb3d-3970979baf5d', '123', 0, 0, 'ACTIVE', 'USER_VERIFICATION', now(), now()),
- ('6560a85d-7d97-44c0-bd29-04c57051aa57', 'b4662611-df91-4053-bb3d-3970979baf5d', '123', 0, 0, 'ACTIVE', 'USER_VERIFICATION', now() - interval '301' second, now()), -- to be failed
- ('e4974ef6-135a-4ae1-be91-a2c0f674c8fd', 'b4662611-df91-4053-bb3d-3970979baf5d', '123', 0, 0, 'VERIFIED', 'USER_VERIFICATION', now() - interval '301' second, now());
+INSERT INTO es_onboarding_otp(id, process_id, otp_code, failed_attempts, total_attempts, status, type, fake, timestamp_created, timestamp_expiration) VALUES
+ ('f50b8c04-649d-43a7-8079-4dbf9b0bbc72', 'b4662611-df91-4053-bb3d-3970979baf5d', '123', 0, 0, 'ACTIVE', 'USER_VERIFICATION', false, now(), now()),
+ ('6560a85d-7d97-44c0-bd29-04c57051aa57', 'b4662611-df91-4053-bb3d-3970979baf5d', '123', 0, 0, 'ACTIVE', 'USER_VERIFICATION', false, now() - interval '301' second, now()), -- to be failed
+ ('e4974ef6-135a-4ae1-be91-a2c0f674c8fd', 'b4662611-df91-4053-bb3d-3970979baf5d', '123', 0, 0, 'VERIFIED', 'USER_VERIFICATION', false, now() - interval '301' second, now());