diff --git a/smp-angular/src/app/app.component.html b/smp-angular/src/app/app.component.html
index ebb1cdde949786dc81e246f964bf41bd9defdc4f..afc31f99138a397a8d9e66d9b17cd20751a88db5 100644
--- a/smp-angular/src/app/app.component.html
+++ b/smp-angular/src/app/app.component.html
@@ -1,4 +1,5 @@
 <alert #alertMessage ></alert>
+<spinner [show]="showSpinner" [size]="150"></spinner>
 <div style="display:flex; flex-direction: column;height: 100%">
   <window-toolbar #windowToolbar/>
   <mat-drawer-container id="smp-window" [hasBackdrop]="false" style="flex: 1 1 auto;">
diff --git a/smp-angular/src/app/app.component.ts b/smp-angular/src/app/app.component.ts
index 36da13e9659461511a11a7d8da112905b13ca766..dd6f64b5d557c7ad908de5dd8308b486728b7af5 100644
--- a/smp-angular/src/app/app.component.ts
+++ b/smp-angular/src/app/app.component.ts
@@ -11,6 +11,7 @@ import {ToolbarComponent} from "./window/toolbar/toolbar.component";
 import {ThemeService} from "./common/theme-service/theme.service";
 import {UserController} from "./common/services/user-controller";
 import {TranslateService} from "@ngx-translate/core";
+import {WindowSpinnerService} from "./common/services/window-spinner.service";
 
 
 @Component({
@@ -31,6 +32,7 @@ export class AppComponent {
   constructor(
     private alertService: AlertMessageService,
     private securityService: SecurityService,
+    private windowSpinnerService: WindowSpinnerService,
     private router: Router,
     private http: HttpClient,
     private dialog: MatDialog,
@@ -95,4 +97,7 @@ export class AppComponent {
     this.alertService.setKeepAfterNavigationChange(scrollTop > 0)
   }
 
+  get showSpinner(): boolean {
+    return this.windowSpinnerService.showSpinner
+  }
 }
diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts
index 4456a5531890d13590fb4b43257121086f318a38..d075ee2c8c27845337c2f8c3acb1faa5dcabcca8 100644
--- a/smp-angular/src/app/app.module.ts
+++ b/smp-angular/src/app/app.module.ts
@@ -165,6 +165,7 @@ import {
   DocumentPropertyDialogComponent
 } from "./common/dialogs/document-property-dialog/document-property-dialog.component";
 import {NgxTranslateModule} from "./translate/translate.module";
+import {WindowSpinnerService} from "./common/services/window-spinner.service";
 
 
 @NgModule({
@@ -328,6 +329,7 @@ import {NgxTranslateModule} from "./translate/translate.module";
     ThemeService,
     UserDetailsService,
     UserService,
+    WindowSpinnerService,
     {
       provide: ExtendedHttpClient,
       useFactory: extendedHttpClientCreator,
diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss
index 9f1ad60fe59831908890ef23303b065d3471e78d..626a02625bda49fbeb176e78dccb0f02e781909a 100644
--- a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss
+++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss
@@ -12,7 +12,7 @@
   bottom: 0;
   left: 0;
   right: 0;
-  verflow-y: scroll;
+  overflow-y: scroll;
 }
 
 #property-table-container table {
diff --git a/smp-angular/src/app/common/services/window-spinner.service.ts b/smp-angular/src/app/common/services/window-spinner.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7e9a5280621765840745eb21c9fecdee203c6106
--- /dev/null
+++ b/smp-angular/src/app/common/services/window-spinner.service.ts
@@ -0,0 +1,14 @@
+import {Injectable} from "@angular/core";
+
+@Injectable()
+export class WindowSpinnerService {
+  private _showSpinner: boolean = false;
+
+  get showSpinner(): boolean {
+    return this._showSpinner;
+  }
+
+  set showSpinner(value: boolean) {
+    this._showSpinner = value;
+  }
+}
diff --git a/smp-angular/src/app/security/reset-credential/reset-credential.component.ts b/smp-angular/src/app/security/reset-credential/reset-credential.component.ts
index 0956a149b1b4da4649bdbeb688dbb7a99ae35d97..9c08e3158f1d2bd64241ec9bd580466d5085d89f 100644
--- a/smp-angular/src/app/security/reset-credential/reset-credential.component.ts
+++ b/smp-angular/src/app/security/reset-credential/reset-credential.component.ts
@@ -33,6 +33,9 @@ export class ResetCredentialComponent implements OnInit {
       this.resetPasswordValidator(data);
     })
     this.resetToken = this.activatedRoute.snapshot.params['resetToken'];
+
+    // check if reset token is valid
+    this.securityService.validateCredentialReset(this.resetToken);
   }
 
   /**
diff --git a/smp-angular/src/app/security/security.service.ts b/smp-angular/src/app/security/security.service.ts
index ea25f79bd6cb271619d31cbb846dbfaebccddc43..4e8d97adba684e3ee75bdc559bd8157eb8b45385 100644
--- a/smp-angular/src/app/security/security.service.ts
+++ b/smp-angular/src/app/security/security.service.ts
@@ -14,6 +14,7 @@ import {
 import {MatDialog} from "@angular/material/dialog";
 import {Router} from "@angular/router";
 import {TranslateService} from "@ngx-translate/core";
+import {WindowSpinnerService} from "../common/services/window-spinner.service";
 
 @Injectable()
 export class SecurityService {
@@ -26,7 +27,8 @@ export class SecurityService {
     private securityEventService: SecurityEventService,
     private dialog: MatDialog,
     private router: Router,
-    private translateService: TranslateService
+    private translateService: TranslateService,
+    private windowSpinnerService: WindowSpinnerService
   ) {
     this.securityEventService.onLogoutSuccessEvent().subscribe(() => {
       this.dialog.closeAll();
@@ -56,6 +58,7 @@ export class SecurityService {
   }
 
   requestCredentialReset(userid: string) {
+    this.windowSpinnerService.showSpinner = true;
     let headers: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'});
     return this.http.post<User>(SmpConstants.REST_PUBLIC_SECURITY_RESET_CREDENTIALS_REQUEST,
       JSON.stringify({
@@ -65,8 +68,10 @@ export class SecurityService {
       {headers})
       .subscribe({
         complete: () => {
+          this.windowSpinnerService.showSpinner = false;
         }, // completeHandler
         error: (error: any) => {
+          this.windowSpinnerService.showSpinner = false;
           this.alertService.error(error)
         },    // errorHandler
         next: async () => {
@@ -77,8 +82,31 @@ export class SecurityService {
       });
   }
 
+  validateCredentialReset(token: string) {
+    let headers: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'});
+    this.windowSpinnerService.showSpinner = true;
+    return this.http.post<User>(SmpConstants.REST_PUBLIC_SECURITY_RESET_CREDENTIALS_VALIDATE,
+      JSON.stringify({
+        credentialType: 'USERNAME_PASSWORD',
+        resetToken: token,
+      }),
+      {headers})
+      .subscribe({
+        complete: () => {
+          this.windowSpinnerService.showSpinner = false;
+        }, // completeHandler
+        error: (error: any) => {
+          this.windowSpinnerService.showSpinner = false;
+          this.router.navigate(['/search']);
+          this.alertService.error(error);
+        }
+        // errorHandler
+      });
+  }
+
   credentialReset(userid: string, token: string, newPassword: string) {
     let headers: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'});
+    this.windowSpinnerService.showSpinner = true;
     return this.http.post<User>(SmpConstants.REST_PUBLIC_SECURITY_RESET_CREDENTIALS,
       JSON.stringify({
         credentialName: userid,
@@ -89,12 +117,14 @@ export class SecurityService {
       {headers})
       .subscribe({
         complete: () => {
+          this.windowSpinnerService.showSpinner = false;
           this.router.navigate(['/login']);
-        }, // completeHandler
+          }, // completeHandler
         error: (error: any) => {
-          this.alertService.error(error);
+          this.windowSpinnerService.showSpinner = false;
           this.router.navigate(['/login']);
-        },    // errorHandler
+          this.alertService.error(error);
+          },    // errorHandler
         next: async () => {
           this.alertService.success(await lastValueFrom(this.translateService.get("reset.credentials.success.password.reset")), true, -1);
         }
diff --git a/smp-angular/src/app/smp.constants.ts b/smp-angular/src/app/smp.constants.ts
index 1cd984cfc84483aeb05915432bf6824e265bb58a..1581c54ebc485d3abf2ae5f48cb8008cb533efb8 100644
--- a/smp-angular/src/app/smp.constants.ts
+++ b/smp-angular/src/app/smp.constants.ts
@@ -164,8 +164,10 @@ export class SmpConstants {
   public static readonly REST_PUBLIC_SECURITY = SmpConstants.REST_PUBLIC + 'security/';
   public static readonly REST_PUBLIC_SECURITY_AUTHENTICATION = SmpConstants.REST_PUBLIC_SECURITY + 'authentication'
   public static readonly REST_PUBLIC_SECURITY_RESET_CREDENTIALS_REQUEST = SmpConstants.REST_PUBLIC_SECURITY + 'request-reset-credential';
+  public static readonly REST_PUBLIC_SECURITY_RESET_CREDENTIALS_VALIDATE = SmpConstants.REST_PUBLIC_SECURITY + 'validate-reset-credential';
   public static readonly REST_PUBLIC_SECURITY_RESET_CREDENTIALS = SmpConstants.REST_PUBLIC_SECURITY + 'reset-credential';
 
+
   public static readonly REST_PUBLIC_SECURITY_USER = SmpConstants.REST_PUBLIC_SECURITY + 'user';
 
   //------------------------------
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/CredentialDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/CredentialDao.java
index d709fdb81e43e7a9c2b7225fd2326884dc3f9a60..befbf646294f12b4279584a046543eea687bfca7 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/CredentialDao.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/CredentialDao.java
@@ -23,7 +23,6 @@ import eu.europa.ec.edelivery.smp.data.enums.CredentialTargetType;
 import eu.europa.ec.edelivery.smp.data.enums.CredentialType;
 import eu.europa.ec.edelivery.smp.data.model.DBUserDeleteValidation;
 import eu.europa.ec.edelivery.smp.data.model.user.DBCredential;
-import eu.europa.ec.edelivery.smp.data.model.user.DBUser;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
@@ -78,15 +77,16 @@ public class CredentialDao extends BaseDao<DBCredential> {
     }
 
     /**
-     * Method finds user by username.If user does not exist
+     * Method finds credentials by username. If credential does not exist
      * Optional  with isPresent - false is returned.
      *
      * @param username
-     * @return returns Optional DBUser for username
+     * @return returns Optional DBCredential for username
      */
     public Optional<DBCredential> findUsernamePasswordCredentialForUsernameAndUI(String username) {
         // check if blank
         if (StringUtils.isBlank(username)) {
+            LOG.debug("Username is blank! Return empty optional");
             return Optional.empty();
         }
         try {
@@ -97,12 +97,41 @@ public class CredentialDao extends BaseDao<DBCredential> {
 
             return Optional.of(query.getSingleResult());
         } catch (NoResultException e) {
+            LOG.debug("No results: Return empty optional");
             return Optional.empty();
         } catch (NonUniqueResultException e) {
             throw new SMPRuntimeException(ILLEGAL_STATE_USERNAME_MULTIPLE_ENTRY, username);
         }
     }
 
+    /**
+     * Method finds credential entity by reset token. If credential does not exist
+     * Optional  with isPresent - false is returned.
+     *
+     * @param resetTokenIdentifier
+     * @return returns Optional DBCredential for reset Token Identifier
+     * @throws SMPRuntimeException if more than one credential is found!
+     */
+    public Optional<DBCredential> findUCredentialForUsernamePasswordTypeAndResetToken(String resetTokenIdentifier) {
+        // check if blank
+        if (StringUtils.isBlank(resetTokenIdentifier)) {
+            return Optional.empty();
+        }
+        try {
+            TypedQuery<DBCredential> query = memEManager.createNamedQuery(QUERY_CREDENTIALS_BY_TYPE_RESET_TOKEN, DBCredential.class);
+            query.setParameter(PARAM_CREDENTIAL_RESET_TOKEN, StringUtils.trim(resetTokenIdentifier));
+            query.setParameter(PARAM_CREDENTIAL_TYPE, CredentialType.USERNAME_PASSWORD);
+            query.setParameter(PARAM_CREDENTIAL_TARGET, CredentialTargetType.UI);
+
+            return Optional.of(query.getSingleResult());
+        } catch (NoResultException e) {
+            LOG.debug("No results: Return empty optional for reset token: [{}]", resetTokenIdentifier);
+            return Optional.empty();
+        } catch (NonUniqueResultException e) {
+            throw new SMPRuntimeException(ILLEGAL_STATE_USERNAME_MULTIPLE_ENTRY, resetTokenIdentifier);
+        }
+    }
+
     /**
      * Method finds username/password credential for user id.If user does not exist
      * an empty Optional is returned. If there are more than one credential the SMPRuntimeException is thrown
@@ -114,6 +143,7 @@ public class CredentialDao extends BaseDao<DBCredential> {
     public Optional<DBCredential> findUsernamePasswordCredentialForUserIdAndUI(Long userId) {
         // check if blank
         if (userId == null) {
+            LOG.debug("User ID is null! Return empty optional");
             return Optional.empty();
         }
         List<DBCredential> list = findUserCredentialForByUserIdTypeAndTarget(userId,
@@ -121,6 +151,7 @@ public class CredentialDao extends BaseDao<DBCredential> {
                 CredentialTargetType.UI);
 
         if (list.isEmpty()) {
+            LOG.debug("No results: Return empty optional for user ID: [{}]", userId);
             return Optional.empty();
         } else if (list.size() > 1) {
             throw new SMPRuntimeException(ILLEGAL_STATE_USERNAME_MULTIPLE_ENTRY, userId);
@@ -138,6 +169,7 @@ public class CredentialDao extends BaseDao<DBCredential> {
     public Optional<DBCredential> findAccessTokenCredentialForAPI(String accessToken) {
         // check if blank
         if (StringUtils.isBlank(accessToken)) {
+            LOG.debug("Access token is blank! Return empty optional");
             return Optional.empty();
         }
         try {
@@ -148,6 +180,7 @@ public class CredentialDao extends BaseDao<DBCredential> {
 
             return Optional.of(query.getSingleResult());
         } catch (NoResultException e) {
+            LOG.debug("No results: Return empty optional for access token: [{}]", accessToken);
             return Optional.empty();
         } catch (NonUniqueResultException e) {
             throw new SMPRuntimeException(ILLEGAL_STATE_USERNAME_MULTIPLE_ENTRY, accessToken);
@@ -171,29 +204,6 @@ public class CredentialDao extends BaseDao<DBCredential> {
         return query.getResultList();
     }
 
-    /**
-     * Method finds user by user authentication token identifier. If user identity token not exist
-     * Optional  with isPresent - false is returned.
-     *
-     * @param tokeIdentifier
-     * @return returns Optional DBUser for username
-     */
-    public Optional<DBUser> findUserByAuthenticationToken(String tokeIdentifier) {
-        // check if blank
-        if (StringUtils.isBlank(tokeIdentifier)) {
-            return Optional.empty();
-        }
-        try {
-            TypedQuery<DBUser> query = memEManager.createNamedQuery("DBUser.getUserByPatId", DBUser.class);
-            query.setParameter("patId", tokeIdentifier.trim());
-            return Optional.of(query.getSingleResult());
-        } catch (NoResultException e) {
-            return Optional.empty();
-        } catch (NonUniqueResultException e) {
-            throw new SMPRuntimeException(ILLEGAL_STATE_USERNAME_MULTIPLE_ENTRY, tokeIdentifier);
-        }
-    }
-
     /**
      * Get users with credentials which are about to expire, and they were not yet notified in alertInterval period
      * @param credentialType - the credential type to send alert
@@ -290,6 +300,7 @@ public class CredentialDao extends BaseDao<DBCredential> {
             query.setParameter(PARAM_CERTIFICATE_IDENTIFIER, certificateId);
             return Optional.of(query.getSingleResult());
         } catch (NoResultException e) {
+            LOG.debug("No results: Return empty optional for certificate ID: [{}]", certificateId);
             return Optional.empty();
         } catch (NonUniqueResultException e) {
             throw new SMPRuntimeException(ILLEGAL_STATE_CERT_ID_MULTIPLE_ENTRY, certificateId);
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
index 12ef52e08969945dd2283de8df03cada89e15714..5b77e0bfa65e78a49bf3c67a275d9d3de33da7c4 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java
@@ -23,6 +23,8 @@ public class QueryNames {
     public static final String QUERY_CREDENTIAL_BY_CREDENTIAL_NAME_TYPE_TARGET = "DBCredential.getUserByCredentialNameTypeAndTarget";
     public static final String QUERY_CREDENTIALS_BY_CI_USERNAME_CREDENTIAL_TYPE_TARGET = "DBCredential.getUserByUsernameCredentialTypeAndTarget";
     public static final String QUERY_CREDENTIALS_BY_USERID_CREDENTIAL_TYPE_TARGET = "DBCredential.getUserByUserIdCredentialTypeAndTarget";
+    public static final String QUERY_CREDENTIALS_BY_TYPE_RESET_TOKEN = "DBCredential.getCredentialTypeAndResetToken";
+
 
     public static final String QUERY_CREDENTIAL_ALL = "DBCredential.getAll";
     public static final String QUERY_CREDENTIAL_BY_CERTIFICATE_ID = "DBCredential.getCredentialByCertificateId";
@@ -204,10 +206,7 @@ public class QueryNames {
     public static final String PARAM_CREDENTIAL_NAME = "credential_name";
     public static final String PARAM_CREDENTIAL_TYPE = "credential_type";
     public static final String PARAM_CREDENTIAL_TARGET = "credential_target";
-
-
-
-
+    public static final String PARAM_CREDENTIAL_RESET_TOKEN = "reset_token";
 
     private QueryNames() {
     }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBCredential.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBCredential.java
index 9b7fb456088dafaf30bfc5ea9a23473d324c10c1..41db69e01bca7763cdbe5fcad4207ac7501b0ab6 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBCredential.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBCredential.java
@@ -37,11 +37,15 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*;
 @Table(name = "SMP_CREDENTIAL",
         indexes = {
             @Index(name = "SMP_CRD_USER_NAME_TYPE_IDX", columnList = "CREDENTIAL_NAME, CREDENTIAL_TYPE, CREDENTIAL_TARGET",  unique = true),
+            @Index(name = "SMP_CRD_USER_NAME_RESET_IDX", columnList = "RESET_TOKEN, CREDENTIAL_TYPE, CREDENTIAL_TARGET",  unique = true)
         })
 @org.hibernate.annotations.Table(appliesTo = "SMP_CREDENTIAL", comment = "Credentials for the users")
 @NamedQuery(name = QUERY_CREDENTIAL_ALL, query = "SELECT u FROM DBCredential u")
 @NamedQuery(name = QUERY_CREDENTIALS_BY_CI_USERNAME_CREDENTIAL_TYPE_TARGET, query = "SELECT c FROM DBCredential c " +
         "WHERE upper(c.user.username) = upper(:username) and c.credentialType = :credential_type and c.credentialTarget = :credential_target")
+@NamedQuery(name = QUERY_CREDENTIALS_BY_TYPE_RESET_TOKEN, query = "SELECT c FROM DBCredential c " +
+        "WHERE c.credentialType = :credential_type and c.credentialTarget = :credential_target and c.resetToken=:reset_token")
+
 @NamedQuery(name = QUERY_CREDENTIALS_BY_USERID_CREDENTIAL_TYPE_TARGET, query = "SELECT c FROM DBCredential c " +
         "WHERE c.user.id = :user_id and c.credentialType = :credential_type and c.credentialTarget = :credential_target order by c.id")
 // case-insensitive search
@@ -64,18 +68,16 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*;
                 " AND (c.expireAlertOn IS NULL " +
                 "   OR c.expireAlertOn <= c.expireOn " +
                 "   OR c.expireAlertOn < :lastSendAlertDate )")
+// native queries to validate if user is owner of the credential
+@NamedNativeQuery(name = "DBCredentialDeleteValidation.validateUsersForOwnership",
+        resultSetMapping = "DBCredentialDeleteValidationMapping",
+        query = "SELECT S.ID as ID, S.USERNAME as USERNAME, " +
+                "    C.CERTIFICATE_ID as certificateId, COUNT(S.ID) as  ownedCount  FROM " +
+                " SMP_USER S LEFT JOIN SMP_CERTIFICATE C ON (S.ID=C.ID) " +
+                " INNER JOIN SMP_RESOURCE_MEMBER SG ON (S.ID = SG.FK_USER_ID) " +
+                " WHERE S.ID IN (:idList)" +
+                " GROUP BY S.ID, S.USERNAME, C.CERTIFICATE_ID")
 
-
-@NamedNativeQueries({
-        @NamedNativeQuery(name = "DBCredentialDeleteValidation.validateUsersForOwnership",
-                resultSetMapping = "DBCredentialDeleteValidationMapping",
-                query = "SELECT S.ID as ID, S.USERNAME as USERNAME, " +
-                        "    C.CERTIFICATE_ID as certificateId, COUNT(S.ID) as  ownedCount  FROM " +
-                        " SMP_USER S LEFT JOIN SMP_CERTIFICATE C ON (S.ID=C.ID) " +
-                        " INNER JOIN SMP_RESOURCE_MEMBER SG ON (S.ID = SG.FK_USER_ID) " +
-                        " WHERE S.ID IN (:idList)" +
-                        " GROUP BY S.ID, S.USERNAME, C.CERTIFICATE_ID"),
-})
 @SqlResultSetMapping(name = "DBCredentialDeleteValidationMapping", classes = {
         @ConstructorResult(targetClass = DBUserDeleteValidation.class,
                 columns = {@ColumnResult(name = "id", type = Long.class),
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CredentialResetRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CredentialResetRO.java
index 66b5f074be349a0dbcf97c7a6cea59e04f860ca3..d3ff4c66bb864993d3510f461322431296e9151d 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CredentialResetRO.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CredentialResetRO.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -22,9 +22,12 @@ import eu.europa.ec.edelivery.smp.data.enums.CredentialType;
 
 import java.util.StringJoiner;
 
+import static eu.europa.ec.edelivery.smp.utils.PropertyUtils.getMaskedData;
+
 
 /**
  * Credential request reset object
+ *
  * @author Joze Rihtarsic
  * @since 5.1
  */
@@ -74,8 +77,10 @@ public class CredentialResetRO extends BaseRO {
     public String toString() {
         return new StringJoiner(", ", CredentialResetRO.class.getSimpleName() + "[", "]")
                 .add("credentialName='" + credentialName + "'")
-                .add("credentialValue='" + credentialValue + "'")
                 .add("credentialType='" + credentialType + "'")
+                .add("credentialValue='" + getMaskedData(credentialValue) + "'")
+                .add("resetToken='" + getMaskedData(credentialValue) + "'")
                 .toString();
     }
+
 }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java
index 02bb0fc1dcea2bd65cb3ef76e6f40bd4177ed3be..77f5a3dd9f385e5195f72187b6aa6e0238262ca8 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java
@@ -32,6 +32,7 @@ public enum ErrorCode {
     UNAUTHORIZED(401, "SMP:003",ErrorBusinessCode.UNAUTHORIZED, "User not authorized!"),
     UNAUTHORIZED_INVALID_USER_IDENTIFIER(401, "SMP:004",ErrorBusinessCode.UNAUTHORIZED, "Invalid user identifier! User not authorized."),
     UNAUTHORIZED_INVALID_IDENTIFIER(401, "SMP:005",ErrorBusinessCode.UNAUTHORIZED, "Invalid entity identifier!  User not authorized to access the entity data"),
+    UNAUTHORIZED_INVALID_RESET_TOKEN(401, "SMP:006",ErrorBusinessCode.UNAUTHORIZED, "Reset token; Invalid or not active reset token!"),
 
     INVALID_ENCODING (500, "SMP:100",ErrorBusinessCode.TECHNICAL, "Unsupported or invalid encoding for %s!"),
     SML_INVALID_IDENTIFIER (400,"SMP:101",ErrorBusinessCode.FORMAT_ERROR,"Malformed identifier, scheme and id should be delimited by double colon: %s "),
@@ -80,6 +81,7 @@ public enum ErrorCode {
     JAXB_INITIALIZATION (500,"SMP:511",ErrorBusinessCode.TECHNICAL, "Could not create Unmarshaller for class [%s]!"),
     XML_PARSE_EXCEPTION (500,"SMP:512",ErrorBusinessCode.TECHNICAL, "Error occurred while parsing input stream for [%s].  Error: %s!"),
     INVALID_REQUEST(400,"SMP:513",ErrorBusinessCode.TECHNICAL, "Invalid request [%s]. Error: %s!"),
+    INVALID_REQUEST_NO_DETAILS(400,"SMP:513",ErrorBusinessCode.TECHNICAL, "Invalid request"),
     INTERNAL_ERROR (500,"SMP:514",ErrorBusinessCode.TECHNICAL, "Internal error [%s]. Error: %s!"),
     CERTIFICATE_ERROR (500,"SMP:515",ErrorBusinessCode.TECHNICAL, "Certificate error [%s]. Error: %s!"),
     CONFIGURATION_ERROR (500,"SMP:516",ErrorBusinessCode.TECHNICAL, "Configuration error: [%s]!"),
@@ -112,6 +114,9 @@ public enum ErrorCode {
         return messageTemplate;
     }
     public String getMessage(Object ... args) {
+        if (args == null || args.length == 0) {
+            return messageTemplate;
+        }
         return String.format(messageTemplate, args);
     }
 
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java
index 533ab1b82e1ec0818758e116cd01c2875d87a9fe..c121a3c28b5c36f571c279cd19ab57d1008f8576 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java
@@ -70,6 +70,8 @@ public enum SMPMessageCode implements MessageCode {
     SEC_USER_SUSPENDED("SEC-008", "User [{}] is temporarily suspended."),
     SEC_INVALID_TOKEN("SEC-009", "User [{}] has invalid token value for token id: [{}]."),
     SEC_TRUSTSTORE_CERT_INVALID("SEC-010", "Truststore certificate with alias [{}] is invalid: [{}]."),
+    SEC_RESET_TOKEN_NOT_EXISTS("SEC-011", "Reset token [{}] for type [{}] not exists."),
+    SEC_RESET_TOKEN_INVALID("SEC-012", "Reset token for credential [{}] for type [{}] is invalid."),
     ;
 
     String code;
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java
index d3c85c44d4929e42d5caf51e83263b1bbf454ca7..cb02726b6f6494a9973bf56a7fd23bdbb4de0af7 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java
@@ -23,6 +23,7 @@ import eu.europa.ec.edelivery.security.utils.SecurityUtils;
 import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken;
 import eu.europa.ec.edelivery.smp.auth.SMPUserDetails;
 import eu.europa.ec.edelivery.smp.auth.UILoginAuthenticationToken;
+import eu.europa.ec.edelivery.smp.config.SMPEnvironmentProperties;
 import eu.europa.ec.edelivery.smp.data.dao.CredentialDao;
 import eu.europa.ec.edelivery.smp.data.dao.UserDao;
 import eu.europa.ec.edelivery.smp.data.enums.CredentialType;
@@ -71,7 +72,10 @@ import static java.util.Locale.US;
 public class CredentialService {
     protected static final SMPLogger LOG = SMPLoggerFactory.getLogger(CredentialService.class);
     protected static final BadCredentialsException BAD_CREDENTIALS_EXCEPTION = new BadCredentialsException(ErrorCode.UNAUTHORIZED_INVALID_USERNAME_PASSWORD.getMessage());
+    protected static final BadCredentialsException UNAUTHORIZED_INVALID_RESET_TOKEN = new BadCredentialsException(ErrorCode.UNAUTHORIZED_INVALID_RESET_TOKEN.getMessage());
     protected static final BadCredentialsException SUSPENDED_CREDENTIALS_EXCEPTION = new BadCredentialsException(ErrorCode.UNAUTHORIZED_CREDENTIAL_SUSPENDED.getMessage());
+    protected static final int RESET_TOKEN_LENGTH = 64;
+
     final UserDao userDao;
     final CredentialDao credentialDao;
     final ConversionService conversionService;
@@ -135,7 +139,7 @@ public class CredentialService {
                 LOG.securityWarn(SMPMessageCode.SEC_INVALID_USER_CREDENTIALS, username, credential.getName(), credential.getCredentialType(), credential.getCredentialTarget());
                 loginAttemptFailedAndThrowError(credential, true, startTime);
             }
-            LOG.debug("authenticateByUsernamePassword: reset failed attempts for user token [{}]", username);
+            LOG.debug("authenticateByUsernamePassword: clear failed attempts for user token [{}]", username);
             credential.setSequentialLoginFailureCount(0);
             credential.setLastFailedLoginAttempt(null);
         } catch (IllegalArgumentException ex) {
@@ -344,15 +348,22 @@ public class CredentialService {
         generateResetTokenAndSubmitMail(dbCredential);
     }
 
+    /**
+     * Method validates if the reset token is valid (exists and is not expired)
+     * and updates the password.
+     *
+     * @param username of the user
+     * @param resetToken active reset token
+     * @param newPassword new password
+     * @throws AuthenticationServiceException if the reset token is invalid
+     */
     @Transactional
     public void resetUsernamePassword(String username, String resetToken, String newPassword) {
-        // retrieve user Optional credentials by username
         LOG.debug("resetUsernamePassword [{}]", username);
         // retrieve user Optional credentials by username
         Optional<DBCredential> optCredential = getActiveCredentialsForUsernameToReset(username, false);
         if (!optCredential.isPresent()) {
-            LOG.info("Skip generating reset token for username [{}]!", username);
-            return;
+            throw UNAUTHORIZED_INVALID_RESET_TOKEN;
         }
         DBCredential dbCredential = optCredential.get();
         if (!resetToken.equals(dbCredential.getResetToken())) {
@@ -367,22 +378,27 @@ public class CredentialService {
 
         OffsetDateTime now = OffsetDateTime.now();
         dbCredential.setValue(BCrypt.hashpw(newPassword, BCrypt.gensalt()));
-        dbCredential.setResetToken(null);
-        dbCredential.setResetExpireOn(null);
+
         dbCredential.setExpireAlertOn(null);
         dbCredential.setSequentialLoginFailureCount(0);
         dbCredential.setLastFailedLoginAttempt(null);
         dbCredential.setChangedOn(now);
         dbCredential.setExpireOn(now.plusDays(configurationService.getPasswordPolicyValidDays()));
+
+        dbCredential.setResetToken(null);
+        dbCredential.setResetExpireOn(null);
+
         // submit mail with reset token
         alertService.alertCredentialChanged(dbCredential);
     }
 
+
     /**
-     * Method gets credentials for active user and validates if not expired reset token exists .
+     * Method gets User password credential entity for active user and validates
+     * resent token exists and if active.
      *
-     * @param username
-     * @return
+     * @param username of the user
+     * @return Optional of DBCredential: if the user has active credentials and active else empty is returned
      */
     private Optional<DBCredential> getActiveCredentialsForUsernameToReset(String username, boolean toGenerateResetToken) {
         // retrieve user Optional credentials by username
@@ -401,19 +417,36 @@ public class CredentialService {
         // When toGenerateResetToken check if the user has already active reset token
         boolean hasValidResetToken = hasValidResetToken(dbCredential);
         if (toGenerateResetToken && hasValidResetToken) {
-            LOG.info("User [{}] has already active reset token. Skip generating new reset token!", username);
-            return Optional.empty();
+            LOG.warn("User [{}] has already active reset token. Generate new reset token!", username);
         }
         // If action is reset then check if the user has active reset token
         if (!toGenerateResetToken && !hasValidResetToken) {
-            LOG.warn("User [{}] does not have active reset token. The reset token is expired or does not exists!", username);
-            throw new AuthenticationServiceException("User [" + username
-                    + "] does not have active reset token. Please request new reset token for the user!");
+            LOG.securityWarn(SMPMessageCode.SEC_RESET_TOKEN_INVALID, dbCredential.getName(), CredentialType.USERNAME_PASSWORD);
+            throw UNAUTHORIZED_INVALID_RESET_TOKEN;
         }
 
         return optCredential;
     }
 
+    public void validatePasswordResetToken(String resetToken){
+        Optional<DBCredential> optCredential = credentialDao.findUCredentialForUsernamePasswordTypeAndResetToken(resetToken);
+        if (!optCredential.isPresent()) {
+            LOG.securityWarn(SMPMessageCode.SEC_RESET_TOKEN_NOT_EXISTS, resetToken, CredentialType.USERNAME_PASSWORD);
+            throw UNAUTHORIZED_INVALID_RESET_TOKEN;
+        }
+        DBCredential dbCredential = optCredential.get();
+        if (!hasValidResetToken(dbCredential)) {
+            LOG.securityWarn(SMPMessageCode.SEC_RESET_TOKEN_INVALID, dbCredential.getName(), CredentialType.USERNAME_PASSWORD);
+            throw UNAUTHORIZED_INVALID_RESET_TOKEN;
+        }
+    }
+
+    /**
+     * Method validates if the user has valid reset token. The token is valid if it is not empty
+     * and the expiry date is after the current date.
+     * @param dbCredential
+     * @return true if the reset token is valid, else false
+     */
     private boolean hasValidResetToken(DBCredential dbCredential) {
         return StringUtils.isNotBlank(dbCredential.getResetToken())
                 && dbCredential.getResetExpireOn() != null
@@ -427,7 +460,8 @@ public class CredentialService {
      * @param dbCredential credential for which the reset token is generated.
      */
     private void generateResetTokenAndSubmitMail(DBCredential dbCredential) {
-        dbCredential.setResetToken(UUID.randomUUID().toString());
+        boolean isDevMode = SMPEnvironmentProperties.getInstance().isSMPStartupInDevMode();
+        dbCredential.setResetToken(SecurityUtils.generateAuthenticationTokenIdentifier(isDevMode, RESET_TOKEN_LENGTH));
         dbCredential.setResetExpireOn(OffsetDateTime.now().plusMinutes(configurationService.getCredentialsResetPolicyValidMinutes()));
         // submit mail with reset token
         dbCredential.getUser().getEmailAddress();
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java
index d1531230fb4ef668b34741817f371db5e7c03adf..77887976fea0d623dc8f8763ae2043721bdb66d6 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java
@@ -261,11 +261,14 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> {
         dbCredential.setActiveFrom(currentTime);
         dbCredential.setExpireOn(adminUpdate ? null :
                 currentTime.plusDays(configurationService.getPasswordPolicyValidDays()));
+        // clear reset token if exists
+        dbCredential.setResetToken(null);
+        dbCredential.setResetExpireOn(null);
 
         // clear failed attempts
         dbCredential.setLastFailedLoginAttempt(null);
         dbCredential.setSequentialLoginFailureCount(null);
-        // if the credentials are not managed by the session , e.g. new  - the persist it
+        // if the credentials are not managed by the session , e.g. "new" then persist it
         if (dbCredential.getId() == null) {
             credentialDao.persist(dbCredential);
         }
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/PropertyUtils.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/PropertyUtils.java
index 34d545f8b0e6a3e2f9302e27e47622e52778d9c1..d8ca9d339538b3d0ceb5baae69bf5c5b965e678a 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/PropertyUtils.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/PropertyUtils.java
@@ -208,6 +208,10 @@ public class PropertyUtils {
      * @return masked value for sensitive properties. Else it returns value!
      */
     public static String getMaskedData(String property, String value) {
-        return isSensitiveData(property) ? MASKED_VALUE : value;
+        return isSensitiveData(property) ? getMaskedData(value) : value;
+    }
+
+    public static String getMaskedData(String value) {
+        return isNotBlank(value) ? MASKED_VALUE : "Null/Empty/Blank";
     }
 }
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java
index 34c2208df9f7529ef6f6e8779344fda0f26b1045..ef65571b9dad35753b0143a427647eb613da80a7 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java
@@ -20,6 +20,7 @@ package eu.europa.ec.edelivery.smp.auth;
 
 import eu.europa.ec.edelivery.smp.config.SMPSecurityConstants;
 import eu.europa.ec.edelivery.smp.data.enums.CredentialType;
+import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.CredentialService;
@@ -89,10 +90,33 @@ public class SMPAuthenticationService {
         LOG.info("resetUsernamePassword  [{}]", username);
         // retrieve user Optional credentials by username
         long startTime = Calendar.getInstance().getTimeInMillis();
-        credentialService.resetUsernamePassword(username, resetToken, newPassword);
+        try {
+            credentialService.resetUsernamePassword(username, resetToken, newPassword);
+        } catch (SMPRuntimeException | AuthenticationException e) {
+            // delay response to prevent timing attack
+            credentialService.delayResponse(CredentialType.USERNAME_PASSWORD, startTime);
+            throw e;
+        }
         credentialService.delayResponse(CredentialType.USERNAME_PASSWORD, startTime);
     }
 
+    /**
+     * Method validates reset token exists and is valid! If not exception is thrown.
+     * @param resetToken
+     * @throws AuthenticationException if token is not exists or is not valid
+     */
+    public void validateUsernamePasswordResetToken(String resetToken) {
+        long startTime = Calendar.getInstance().getTimeInMillis();
+        try {
+            credentialService.validatePasswordResetToken(resetToken);
+        } catch (SMPRuntimeException | AuthenticationException e) {
+            // delay response to prevent timing attack
+            credentialService.delayResponse(CredentialType.USERNAME_PASSWORD, startTime);
+            throw e;
+        }
+        credentialService.validatePasswordResetToken(resetToken);
+    }
+
     public void logout(HttpServletRequest request, HttpServletResponse response) {
         Authentication auth = SecurityContextHolder.getContext().getAuthentication();
         if (auth == null) {
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationController.java
index 30624687921ff44265270682a92c6579d70a3642..747e827a7021572576cec8d4d9417d7fd9454eb0 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationController.java
@@ -8,9 +8,9 @@
  * versions of the EUPL (the "Licence");
  * You may not use this work except in compliance with the Licence.
  * You may obtain a copy of the Licence at:
- * 
+ *
  * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
  * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the Licence for the specific language governing permissions and limitations under the Licence.
@@ -28,11 +28,13 @@ import eu.europa.ec.edelivery.smp.data.ui.CredentialRequestResetRO;
 import eu.europa.ec.edelivery.smp.data.ui.CredentialResetRO;
 import eu.europa.ec.edelivery.smp.data.ui.LoginRO;
 import eu.europa.ec.edelivery.smp.data.ui.UserRO;
+import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.ConfigurationService;
 import eu.europa.ec.edelivery.smp.services.ui.UIUserService;
 import eu.europa.ec.edelivery.smp.utils.SMPCookieWriter;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.annotation.Secured;
 import org.springframework.security.authentication.BadCredentialsException;
@@ -108,7 +110,7 @@ public class AuthenticationController {
      *
      * @param requestResetRO - the request object containing the credential name and type
      */
-    @PostMapping(value = ResourceConstants.PATH_ACTION_RESET_CREDENTIAL_REQUEST )
+    @PostMapping(value = ResourceConstants.PATH_ACTION_RESET_CREDENTIAL_REQUEST)
     public void requestResetCredentials(@RequestBody CredentialRequestResetRO requestResetRO) {
         LOG.debug("credentialRequestResetRO  [{}]", requestResetRO.getCredentialName());
         if (requestResetRO.getCredentialType() == CredentialType.USERNAME_PASSWORD) {
@@ -116,7 +118,7 @@ public class AuthenticationController {
         } else {
             LOG.warn("Invalid or null credential type [{}] not supported for reset!",
                     requestResetRO.getCredentialType());
-            throw new IllegalArgumentException("Invalid request!");
+            throw new IllegalArgumentException(ErrorCode.INVALID_REQUEST_NO_DETAILS.getMessage());
 
         }
     }
@@ -126,20 +128,41 @@ public class AuthenticationController {
      *
      * @param resetRO - the reset object containing the credential name, type, reset token and new credential value
      */
-    @PostMapping(value = ResourceConstants.PATH_ACTION_RESET_CREDENTIAL )
+    @PostMapping(value = ResourceConstants.PATH_ACTION_RESET_CREDENTIAL)
     public void resetCredentials(@RequestBody CredentialResetRO resetRO) {
-        LOG.debug("credentialResetRO  [{}]", resetRO.getCredentialName());
-        if (resetRO.getCredentialType() == CredentialType.USERNAME_PASSWORD) {
+        LOG.debug("resetCredentials [{}]", resetRO);
+        if (resetRO == null
+                || resetRO.getResetToken() == null
+                || StringUtils.isBlank(resetRO.getResetToken())
+                || StringUtils.isBlank(resetRO.getCredentialName())
+                || resetRO.getCredentialType() != CredentialType.USERNAME_PASSWORD) {
+            LOG.warn("Invalid or incomplete reset token!");
+            throw new IllegalArgumentException(ErrorCode.INVALID_REQUEST_NO_DETAILS.getMessage());
+        }
+        authenticationService.resetUsernamePassword(resetRO.getCredentialName(),
+                resetRO.getResetToken(),
+                resetRO.getCredentialValue());
 
-            authenticationService.resetUsernamePassword(resetRO.getCredentialName(),
-                    resetRO.getResetToken(),
-                    resetRO.getCredentialValue());
-        } else {
-            LOG.warn("Invalid or null credential type [{}] not supported for reset!",
-                    resetRO.getCredentialType());
-            throw new IllegalArgumentException("Invalid request!");
+    }
 
+    /**
+     * Method validates if the reset token is valid (exists and is not expired)
+     * for the given credential type.
+     *
+     * @param resetRO - the reset object containing the credential name, type, reset token and new credential value
+     *                Return 200 if token is valid, 401 if token is invalid
+     */
+    @PostMapping(value = ResourceConstants.PATH_ACTION_VALIDATE_RESET_TOKEN)
+    public void validateResetToken(@RequestBody CredentialResetRO resetRO) {
+        LOG.debug("validateResetToken [{}]", resetRO);
+        if (resetRO == null
+                || StringUtils.isBlank(resetRO.getResetToken())
+                || resetRO.getCredentialType() != CredentialType.USERNAME_PASSWORD) {
+            LOG.warn("Invalid or null reset token or invalid reset token type!");
+            throw new IllegalArgumentException(ErrorCode.INVALID_REQUEST_NO_DETAILS.getMessage());
         }
+
+        authenticationService.validateUsernamePasswordResetToken(resetRO.getResetToken());
     }
 
     @DeleteMapping(value = ResourceConstants.PATH_ACTION_AUTHENTICATION)
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java
index 9aebb52a5e98f27c5edf235517912d2f8e35a6e5..6c94d2e7507d049d850f4d0635dd60d13eb983dc 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java
@@ -73,6 +73,7 @@ public class ResourceConstants {
     public static final String PATH_ACTION_UPDATE_RESOURCE_TYPES = "update-resource-types";
     public static final String PATH_ACTION_UPDATE_SML_DATA = "update-sml-integration-data";
     public static final String PATH_ACTION_RESET_CREDENTIAL_REQUEST = "request-reset-credential";
+    public static final String PATH_ACTION_VALIDATE_RESET_TOKEN = "validate-reset-credential";
     public static final String PATH_ACTION_RESET_CREDENTIAL = "reset-credential";
     public static final String PATH_ACTION_AUTHENTICATION = "authentication";
     public static final String PATH_ACTION_GENERATE_DNS_QUERY = "generate-dns-query";
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
index 421be51f4488369b97b2d55b72acc73cddd08320..2b82adba38fd9a546778ad24572f81c8c26605f4 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl
@@ -587,6 +587,9 @@
     alter table SMP_CREDENTIAL 
        add constraint SMP_CRD_USER_NAME_TYPE_IDX unique (CREDENTIAL_NAME, CREDENTIAL_TYPE, CREDENTIAL_TARGET);
 
+    alter table SMP_CREDENTIAL 
+       add constraint SMP_CRD_USER_NAME_RESET_IDX unique (RESET_TOKEN, CREDENTIAL_TYPE, CREDENTIAL_TARGET);
+
     alter table SMP_DOCUMENT_PROPERTY 
        add constraint SMP_DOC_PROP_IDX unique (FK_DOCUMENT_ID, PROPERTY_NAME);
 create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID);
diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
index c3460e7dd0c793b4acd46f763e89fca37b602e59..953bb1be081432cc584862d50ad970fbebfd56f4 100644
--- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
+++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl
@@ -859,6 +859,9 @@ create sequence SMP_USER_SEQ start with 1 increment by  1;
     alter table SMP_CREDENTIAL 
        add constraint SMP_CRD_USER_NAME_TYPE_IDX unique (CREDENTIAL_NAME, CREDENTIAL_TYPE, CREDENTIAL_TARGET);
 
+    alter table SMP_CREDENTIAL 
+       add constraint SMP_CRD_USER_NAME_RESET_IDX unique (RESET_TOKEN, CREDENTIAL_TYPE, CREDENTIAL_TARGET);
+
     alter table SMP_DOCUMENT_PROPERTY 
        add constraint SMP_DOC_PROP_IDX unique (FK_DOCUMENT_ID, PROPERTY_NAME);
 create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID);