diff --git a/mock-db-plugin/README.md b/mock-db-plugin/README.md
new file mode 100644
index 00000000..67b9aeb9
--- /dev/null
+++ b/mock-db-plugin/README.md
@@ -0,0 +1,51 @@
+# Mock DB plugin
+
+## About
+
+Implementation for all the interfaces defined in esignet-integration-api. Mock DB plugin is built to use eSignet with any database
+
+This library should be added as a runtime dependency to [esignet-service](https://github.com/mosip/esignet) for development purpose only.
+
+**Note**: This is not production use implementation.
+
+## Configurations
+
+Refer [application.properties](src/main/resources/application.properties) for all the configurations required to use this plugin implementation. All the properties
+are set with default values. If required values can be overridden in the host application by setting them as environment variable. Refer [esignet-service](https://github.com/mosip/esignet)
+docker-compose file to see how the configuration property values can be changed.
+
+Add "bindingtransaction" cache name in "mosip.esignet.cache.names" property.
+
+## Databases
+You have to create a new database, table and some user details entries as well.
+
+```
+-- Step 1: Create Database
+CREATE DATABASE IF NOT EXISTS mock_db;
+
+-- Step 2: Create User and Grant Privileges
+CREATE USER IF NOT EXISTS 'mock_user'@'localhost' IDENTIFIED BY 'SecureP@ss123';
+GRANT ALL PRIVILEGES ON mock_db.* TO 'mock_user'@'localhost';
+FLUSH PRIVILEGES;
+
+-- Step 3: Use the Database
+USE mock_db;
+
+-- Step 4: Create Table user_detail
+CREATE TABLE IF NOT EXISTS user_detail (
+ id VARCHAR(12) PRIMARY KEY,
+ name VARCHAR(100) NOT NULL,
+ dob DATE NOT NULL,
+ email VARCHAR(100) UNIQUE NOT NULL
+);
+
+-- Step 5: Insert Sample Data
+INSERT INTO user_detail (id, name, dob, email) VALUES
+('3453434553', 'Alice Johnson', '1990-05-14', 'alice@example.com'),
+('2583148061', 'Bob Smith', '1985-09-23', 'bob@example.com'),
+('9834544352', 'Charlie Brown', '1992-07-11', 'charlie@example.com'),
+('5236574533', 'Diana Ross', '1988-12-30', 'diana@example.com');
+```
+
+## License
+This project is licensed under the terms of [Mozilla Public License 2.0](LICENSE).
diff --git a/mock-db-plugin/pom.xml b/mock-db-plugin/pom.xml
new file mode 100644
index 00000000..22b24b2f
--- /dev/null
+++ b/mock-db-plugin/pom.xml
@@ -0,0 +1,79 @@
+
+
+ 4.0.0
+
+ org.mock.esignet.plugin
+ mock-db-plugin
+ 1.0-SNAPSHOT
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+ io.mosip.esignet
+ esignet-integration-api
+ 1.5.1
+ provided
+
+
+
+ io.mosip.esignet
+ esignet-core
+ 1.5.1
+ provided
+
+
+
+ com.mysql
+ mysql-connector-j
+ 8.0.33
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.36
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+ jar-with-dependencies
+
+ false
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 9
+ 9
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mock-db-plugin/src/main/java/org/mock/esignet/plugin/config/MockDBConfig.java b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/config/MockDBConfig.java
new file mode 100644
index 00000000..2dd142dc
--- /dev/null
+++ b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/config/MockDBConfig.java
@@ -0,0 +1,35 @@
+package org.mock.esignet.plugin.config;
+
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class MockDBConfig {
+ @Value("${org.mock.esignet.plugin.db-url}")
+ private String dbURL;
+
+ @Value("${org.mock.esignet.plugin.db-username}")
+ private String dbUsername;
+
+ @Value("${org.mock.esignet.plugin.db-password}")
+ private String dbPassword;
+
+ public DataSource dataSource() {
+ HikariDataSource hikariDataSource = new HikariDataSource();
+ hikariDataSource.setJdbcUrl(dbURL);
+ hikariDataSource.setUsername(dbUsername);
+ hikariDataSource.setPassword(dbPassword);
+ hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
+ return hikariDataSource;
+ }
+
+ @Bean("mockPluginJdbcTemplate")
+ public JdbcTemplate mockPluginJdbcTemplate() {
+ return new JdbcTemplate(dataSource());
+ }
+}
diff --git a/mock-db-plugin/src/main/java/org/mock/esignet/plugin/dto/UserDetail.java b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/dto/UserDetail.java
new file mode 100644
index 00000000..a9152cf0
--- /dev/null
+++ b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/dto/UserDetail.java
@@ -0,0 +1,13 @@
+package org.mock.esignet.plugin.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@AllArgsConstructor
+@Data
+public class UserDetail {
+ private String id;
+ private String name;
+ private String dob;
+ private String email;
+}
diff --git a/mock-db-plugin/src/main/java/org/mock/esignet/plugin/repositories/UserDetailRepository.java b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/repositories/UserDetailRepository.java
new file mode 100644
index 00000000..5b5a0161
--- /dev/null
+++ b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/repositories/UserDetailRepository.java
@@ -0,0 +1,34 @@
+package org.mock.esignet.plugin.repositories;
+
+import org.mock.esignet.plugin.dto.UserDetail;
+import org.springframework.stereotype.Repository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+@Repository
+public class UserDetailRepository {
+ private static final String QUERY = "select * from user_detail where id=?";
+
+ @Autowired
+ @Qualifier("mockPluginJdbcTemplate")
+ private JdbcTemplate mockPluginJdbcTemplate;
+
+ public UserDetail findUserById(String individualId) {
+ List list = mockPluginJdbcTemplate.query(QUERY, new Object[]{individualId}, new RowMapper() {
+ @Override
+ public UserDetail mapRow(ResultSet resultSet, int i) throws SQLException {
+ return new UserDetail(resultSet.getString(1),resultSet.getString(2), resultSet.getString(3), resultSet.getString(4));
+ }
+ });
+
+ if(!list.isEmpty()) {
+ return list.get(0);
+ }
+ return null;
+ }
+}
diff --git a/mock-db-plugin/src/main/java/org/mock/esignet/plugin/service/MockDBAuthenticatorImpl.java b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/service/MockDBAuthenticatorImpl.java
new file mode 100644
index 00000000..60c04267
--- /dev/null
+++ b/mock-db-plugin/src/main/java/org/mock/esignet/plugin/service/MockDBAuthenticatorImpl.java
@@ -0,0 +1,178 @@
+package org.mock.esignet.plugin.service;
+
+import io.mosip.esignet.api.dto.*;
+import io.mosip.esignet.api.exception.KycAuthException;
+import io.mosip.esignet.api.exception.KycExchangeException;
+import io.mosip.esignet.api.exception.KycSigningCertificateException;
+import io.mosip.esignet.api.exception.SendOtpException;
+import io.mosip.esignet.api.spi.Authenticator;
+
+import java.util.List;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.mock.esignet.plugin.dto.UserDetail;
+import org.mock.esignet.plugin.repositories.UserDetailRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.security.auth.x500.X500Principal;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.time.ZoneOffset;
+import java.util.*;
+
+@Component
+public class MockDBAuthenticatorImpl implements Authenticator {
+
+ private Map localMap = new HashMap<>();
+
+ @Autowired
+ private UserDetailRepository userDetailRepository;
+ private X509Certificate keyCertificate;
+ private KeyPair localKey;
+
+ @Override
+ public KycAuthResult doKycAuth(String relyingPartyId, String clientId, KycAuthDto kycAuthDto) throws KycAuthException {
+ UserDetail userDetail = userDetailRepository.findUserById(kycAuthDto.getIndividualId());
+
+ if (userDetail == null)
+ throw new KycAuthException("user_not_found");
+
+ boolean authStatus = false;
+ AuthChallenge authChallenge = kycAuthDto.getChallengeList().get(0);
+ switch (authChallenge.getAuthFactorType()) {
+ case "OTP":
+ authStatus = authChallenge.getChallenge().equals("111111");
+ break;
+ default:
+ throw new KycAuthException("invalid_auth_factor");
+ }
+
+ if(!authStatus)
+ throw new KycAuthException("auth_failed");
+
+ String token = UUID.randomUUID().toString();
+ localMap.put(token, kycAuthDto.getIndividualId());
+ KycAuthResult kycAuthResult = new KycAuthResult();
+ kycAuthResult.setKycToken(token);
+ kycAuthResult.setPartnerSpecificUserToken(token);
+ return kycAuthResult;
+ }
+
+ @Override
+ public KycExchangeResult doKycExchange(String relyingPartyId, String clientId, KycExchangeDto kycExchangeDto) throws KycExchangeException {
+ String storedKycToken = localMap.get(kycExchangeDto.getKycToken());
+ if (storedKycToken == null)
+ throw new KycExchangeException("invalid_kyc_token");
+
+ UserDetail userDetail = userDetailRepository.findUserById(storedKycToken);
+
+ JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder()
+ .subject(storedKycToken)
+ .issuer("eSignet")
+ .expirationTime(new Date(System.currentTimeMillis() + 3600 * 1000));
+
+ for (String claim : kycExchangeDto.getAcceptedClaims()) {
+ switch (claim) {
+ case "name":
+ builder.claim("name", userDetail.getName());
+ break;
+ case "birthdate":
+ builder.claim("birthDate", userDetail.getDob());
+ break;
+ case "email":
+ builder.claim("email", userDetail.getEmail());
+ break;
+ }
+ }
+
+ JWTClaimsSet claimsSet = builder.build();
+ String signedJWT = signJWT(claimsSet); //additionally can be encrypted with public key of relying party
+
+ KycExchangeResult kycExchangeResult = new KycExchangeResult();
+ kycExchangeResult.setEncryptedKyc(signedJWT);
+ return kycExchangeResult;
+ }
+
+
+
+ @Override
+ public SendOtpResult sendOtp(String relyingPartyId, String clientId, SendOtpDto sendOtpDto) throws SendOtpException {
+ SendOtpResult sendOtpResult = new SendOtpResult();
+ sendOtpResult.setTransactionId(sendOtpDto.getTransactionId());
+ sendOtpResult.setMaskedMobile("");
+ return sendOtpResult;
+ }
+
+ @Override
+ public boolean isSupportedOtpChannel(String channel) {
+ return true;
+ }
+
+ @Override
+ public List getAllKycSigningCertificates() throws KycSigningCertificateException {
+ KycSigningCertificateData kycSigningCertificateData = new KycSigningCertificateData();
+ try {
+
+ Base64.Encoder encoder = Base64.getMimeEncoder(64, "\n".getBytes());
+ String encodedCert = encoder.encodeToString(keyCertificate.getEncoded());
+ kycSigningCertificateData.setCertificateData("-----BEGIN CERTIFICATE-----\n" + encodedCert + "\n-----END CERTIFICATE-----");
+ kycSigningCertificateData.setExpiryAt(keyCertificate.getNotAfter().toInstant().atZone(ZoneOffset.UTC).toLocalDateTime());
+ kycSigningCertificateData.setIssuedAt(keyCertificate.getNotBefore().toInstant().atZone(ZoneOffset.UTC).toLocalDateTime());
+
+ } catch (CertificateEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ //return public key from this method
+ return List.of(kycSigningCertificateData);
+ }
+
+ public void generateKeyCertificate() {
+ if (localKey == null || keyCertificate == null) {
+ try {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ localKey = keyPairGenerator.generateKeyPair();
+
+ X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+ X500Principal dnName = new X500Principal("CN=Self-Signed, O=Example Org, C=US");
+ certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+ certGen.setSubjectDN(dnName);
+ certGen.setIssuerDN(dnName); // Self-signed
+ certGen.setNotBefore(new Date(System.currentTimeMillis()));
+ certGen.setNotAfter(new Date(System.currentTimeMillis() + (365 * 24 * 60 * 60 * 1000L))); // 1 year validity
+ certGen.setPublicKey(localKey.getPublic());
+ certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+ keyCertificate = certGen.generate(localKey.getPrivate(), "BC");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private String signJWT(JWTClaimsSet claimsSet) throws KycExchangeException {
+ if(localKey == null || keyCertificate == null) {
+ generateKeyCertificate();
+ }
+
+ JWSHeader header = new JWSHeader(JWSAlgorithm.RS256);
+ SignedJWT signedJWT = new SignedJWT(header, claimsSet);
+ JWSSigner signer = new RSASSASigner((RSAPrivateKey)localKey.getPrivate());
+ try {
+ signedJWT.sign(signer);
+ } catch (JOSEException e) {
+ throw new KycExchangeException("signing_failed");
+ }
+ return signedJWT.serialize();
+ }
+}
diff --git a/mock-db-plugin/src/main/resources/application.properties b/mock-db-plugin/src/main/resources/application.properties
new file mode 100644
index 00000000..ba03f24c
--- /dev/null
+++ b/mock-db-plugin/src/main/resources/application.properties
@@ -0,0 +1,15 @@
+## eSignet mock plugin configuration
+mosip.esignet.integration.scan-base-package=org.mock.esignet.plugin
+mosip.esignet.integration.authenticator=MockDBAuthenticatorImpl
+mosip.esignet.integration.key-binder=NoOpKeyBinder
+
+# MySQL (Plugin DataSource)
+org.mock.esignet.plugin.db-url=jdbc:mysql://localhost:3306/mock_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
+org.mock.esignet.plugin.db-username=mock_user
+org.mock.esignet.plugin.db-password=SecureP@ss123
+spring.datasource.plugin.driver-class-name=com.mysql.cj.jdbc.Driver
+
+## Disable authz & authn
+mosip.esignet.security.auth.post-urls={}
+mosip.esignet.security.auth.put-urls={}
+mosip.esignet.security.auth.get-urls={}
\ No newline at end of file