diff --git a/.env.sample b/.env.sample
index 97ba36978d92576c2497e8527c02eb83234bba8f..f7856c2a57d0b3de1da61357ead48f7ed47469cc 100644
--- a/.env.sample
+++ b/.env.sample
@@ -7,6 +7,16 @@ GUPSHUP_USERNAME="2000xxxxxxx"
 GUPSHUP_PASSWORD="password"
 GUPSHUP_OTP_TEMPLATE="Hi User! The OTP to reset password for Samagra Shiksha App is %code%."
 
+# CDAC
+CDAC_SERVICE_URL=
+CDAC_OTP_TEMPLATE_ID="123456"
+CDAC_OTP_TEMPLATE="Respected User, The OTP to reset password for %phone% is %code%."
+
+# SMS Adapter
+SMS_ADAPTER_TYPE=    # CDAC or GUPSHUP
+SMS_TOTP_SECRET=     # any random string, needed for CDAC
+SMS_TOTP_EXPIRY=600  # in seconds, needed for CDAC
+
 # Fusionauth
 FUSIONAUTH_APPLICATION_ID="f0ddb3f6-091b-45e4-8c0f-889f89d4f5da"
 FUSIONAUTH_SAMARTH_HP_APPLICATION_ID=f18c3f6f-45b8-4928-b978-a9906fd03f22
@@ -14,6 +24,6 @@ FUSIONAUTH_HP_ADMIN_CONSOLE_APPLICATION_ID=
 FUSIONAUTH_BASE_URL="https://auth.samarth.samagra.io"
 FUSIONAUTH_API_KEY="bla"
 ENCRYPTION_KEY="bla"
-FUSIONAUTH_ADMIN_SEARCH_APPLICATION_IDS=["1", "2"]  # JSON array of application IDs
+FUSIONAUTH_ADMIN_SEARCH_APPLICATION_IDS=["1","2"]  # JSON array of application IDs
 
 # APP_abcd3f6f_45b8_4928_b978_a9906fd03f22={"host": "dummy.com", "encryption": {"enabled": true, "key": "veryhardkey"}, "salt": "sampl-salt-for-otp-encrypption"}   # the key "application_id" must be underscore(_) separated instead of hyphen(-). Also it must be prefixed with <APP_>
\ No newline at end of file
diff --git a/package.json b/package.json
index e9e2361ffedfc5430d63cebb561dc9152f8ec06d..5b18f7bb9c7f14b95614bf617f74437b29c8d1b3 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
     "reflect-metadata": "^0.1.13",
     "rimraf": "^3.0.2",
     "rxjs": "^7.8.0",
+    "speakeasy": "^2.0.0",
     "swagger-ui-express": "^4.1.6",
     "uuid": "^8.3.2"
   },
diff --git a/src/api/api.module.ts b/src/api/api.module.ts
index ea5ec0b3ede7084d5b45b59abcb901f17593b28c..7c4276d95a0b4e318142d16cb14197b42e62a281 100644
--- a/src/api/api.module.ts
+++ b/src/api/api.module.ts
@@ -10,24 +10,35 @@ import { OtpService } from './otp/otp.service';
 import { GupshupService } from './sms/gupshup/gupshup.service';
 import { SmsService } from './sms/sms.service';
 import got from 'got/dist/source';
-
-const gupshupFactory = {
-  provide: 'OtpService',
-  useFactory: (username, password, baseUrl) => {
-    return new GupshupService(
-      username,
-      password,
-      baseUrl,
-      got,
-    );
-  },
-  inject: [],
-};
+import { CdacService } from './sms/cdac/cdac.service';
 
 const otpServiceFactory = {
   provide: OtpService,
   useFactory: (config: ConfigService) => {
-    return new OtpService(gupshupFactory.useFactory(config.get('GUPSHUP_USERNAME'), config.get('GUPSHUP_PASSWORD'), config.get('GUPSHUP_BASEURL'),));
+    let factory;
+    if (config.get<string>('SMS_ADAPTER_TYPE') == 'CDAC') {
+      factory = {
+        provide: 'OtpService',
+        useFactory: () => {
+          return new CdacService(config);
+        },
+        inject: [],
+      }.useFactory();
+    } else {
+      factory = {
+        provide: 'OtpService',
+        useFactory: (username, password, baseUrl) => {
+          return new GupshupService(
+            username,
+            password,
+            baseUrl,
+            got,
+          );
+        },
+        inject: [],
+      }.useFactory(config.get('GUPSHUP_USERNAME'), config.get('GUPSHUP_PASSWORD'), config.get('GUPSHUP_BASEURL'));
+    }
+    return new OtpService(factory);
   },
   inject: [ConfigService],
 };
@@ -35,6 +46,7 @@ const otpServiceFactory = {
 @Module({
   imports: [HttpModule, ConfigModule],
   controllers: [ApiController],
-  providers: [ApiService, FusionauthService, SmsService, otpServiceFactory, QueryGeneratorService, ConfigResolverService]
+  providers: [ApiService, FusionauthService, SmsService, otpServiceFactory, QueryGeneratorService, ConfigResolverService],
 })
-export class ApiModule {}
+export class ApiModule {
+}
diff --git a/src/api/sms/cdac/cdac.service.spec.ts b/src/api/sms/cdac/cdac.service.spec.ts
index 103ede18e58cabbfed6fa774a591afc5bcadb478..8baae1d6f8315e0fec81ddb661a023bd5b50e039 100644
--- a/src/api/sms/cdac/cdac.service.spec.ts
+++ b/src/api/sms/cdac/cdac.service.spec.ts
@@ -1,12 +1,30 @@
 import { Test, TestingModule } from '@nestjs/testing';
 import { CdacService } from './cdac.service';
+import { ConfigService } from '@nestjs/config';
+import { OtpService } from '../../otp/otp.service';
+
+const otpServiceFactory = {
+  provide: OtpService,
+  useFactory: (config: ConfigService) => {
+    let factory;
+    factory = {
+      provide: 'OtpService',
+      useFactory: () => {
+        return new CdacService(config);
+      },
+      inject: [],
+    }.useFactory();
+    return new OtpService(factory);
+  },
+  inject: [ConfigService],
+};
 
 describe('CdacService', () => {
   let service: CdacService;
 
   beforeEach(async () => {
     const module: TestingModule = await Test.createTestingModule({
-      providers: [CdacService],
+      providers: [ConfigService, CdacService, otpServiceFactory],
     }).compile();
 
     service = module.get<CdacService>(CdacService);
@@ -14,5 +32,6 @@ describe('CdacService', () => {
 
   it('should be defined', () => {
     expect(service).toBeDefined();
+    expect(service).toBeDefined();
   });
 });
diff --git a/src/api/sms/cdac/cdac.service.ts b/src/api/sms/cdac/cdac.service.ts
index 02edba95c9bb3ffc9086c75ebb0505c20cc9f0ef..2c1ee26710b860a3c81200f3b6f2c35b0d919faf 100644
--- a/src/api/sms/cdac/cdac.service.ts
+++ b/src/api/sms/cdac/cdac.service.ts
@@ -1,82 +1,196 @@
-import { SMS, SMSResponse } from '../sms.interface';
+import {
+  OTPResponse,
+  SMS,
+  SMSData,
+  SMSError,
+  SMSProvider,
+  SMSResponse,
+  SMSResponseStatus,
+  SMSType,
+  TrackResponse,
+} from '../sms.interface';
 
-import { Injectable } from '@nestjs/common';
+import { HttpException, Injectable } from '@nestjs/common';
 import { SmsService } from '../sms.service';
-import fetch from 'node-fetch';
+import { ConfigService } from '@nestjs/config';
+import got, {Got} from 'got';
+import * as speakeasy from 'speakeasy';
 
 @Injectable()
 export class CdacService extends SmsService implements SMS {
-  send(id: any): Promise<SMSResponse> {
-    console.log(id);
-    throw new Error('Method not implemented.');
+  baseURL: string;
+  path = '';
+  data: SMSData;
+  httpClient: Got;
+
+  constructor(
+    private configService: ConfigService,
+  ) {
+    super();
+    this.baseURL = configService.get<string>('CDAC_SERVICE_URL');
+    this.httpClient = got;
   }
 
-  track(id: any): Promise<SMSResponse> {
-    console.log(id);
-    throw new Error('Method not implemented.');
+  send(data: SMSData): Promise<SMSResponse> {
+    if (!data) {
+      throw new Error('Data cannot be empty');
+    }
+    this.data = data;
+    if (this.data.type === SMSType.otp) return this.doOTPRequest(data);
+    else return this.doRequest();
+  }
+
+  track(data: SMSData): Promise<SMSResponse> {
+    if (!data) {
+      throw new Error('Data cannot be null');
+    }
+    this.data = data;
+    if (this.data.type === SMSType.otp) return this.verifyOTP(data);
+    else return this.doRequest();
   }
 
-  convertFromUnicodeToText = (message) => {
-    let finalMessage = '';
-    console.log(message);
-    for (let i = 0; i < message.length; i++) {
-      const ch = message.charCodeAt(i);
-      const j = ch;
-      const sss = '&#' + j + ';';
-      finalMessage = finalMessage + sss;
+  private getTotpSecret(phone): string {
+    return `${this.configService.get<string>('SMS_TOTP_SECRET')}${phone}`
+  }
+
+  private doOTPRequest(data: SMSData): Promise<any> {
+    let otp = '';
+    try {
+      otp = speakeasy.totp({
+        secret: this.getTotpSecret(data.phone),
+        encoding: 'base32',
+        step: this.configService.get<string>('SMS_TOTP_EXPIRY'),
+      });
+    } catch (error) {
+      console.log(error);
+      throw new HttpException('TOTP generation failed!', 500);
     }
-    return finalMessage;
-  };
 
-  sendSingleSMS = async (params) => {
-    const url = `https://msdgweb.mgov.gov.in/esms/sendsmsrequest?`;
-    const esc = encodeURIComponent;
-    const query = Object.keys(params)
-      .map((k) => esc(k) + '=' + esc(params[k]))
-      .join('&');
+    const payload = this.configService.get<string>('CDAC_OTP_TEMPLATE')
+      .replace('%phone%', data.phone)
+      .replace('%code%', otp + '');
+    const params = new URLSearchParams({
+      message: payload,
+      mobileNumber: data.phone,
+      templateid: this.configService.get<string>('CDAC_OTP_TEMPLATE_ID'),
+    });
+    this.path = '/api/send_otp_sms'
+    const url = `${this.baseURL}${this.path}?${params.toString()}`;
 
-    // console.log('Sending SMS now');
-    // console.log(query);
-    return fetch(url + query, {
-      method: 'POST',
-      timeout: 15000,
-    })
-      .then((r) => {
-        // console.log('Request finished for: ');
-        const text = r.text();
-        return text;
+    const status: OTPResponse = {} as OTPResponse;
+    status.provider = SMSProvider.cdac;
+    status.phone = data.phone;
+
+    // noinspection DuplicatedCode
+    return this.httpClient.get(url, {})
+      .then((response): OTPResponse => {
+        status.networkResponseCode = 200;
+        const r = this.parseResponse(response.body);
+        status.messageID = r.messageID;
+        status.error = r.error;
+        status.providerResponseCode = r.providerResponseCode;
+        status.providerSuccessResponse = r.providerSuccessResponse;
+        status.status = r.status;
+        return status;
       })
-      .catch((e) => {
-        console.log(e);
-        return '498, ';
+      .catch((e: Error): OTPResponse => {
+        const error: SMSError = {
+          errorText: `Uncaught Exception :: ${e.message}`,
+          errorCode: 'CUSTOM ERROR',
+        };
+        status.networkResponseCode = 200;
+        status.messageID = null;
+        status.error = error;
+        status.providerResponseCode = null;
+        status.providerSuccessResponse = null;
+        status.status = SMSResponseStatus.failure;
+        return status;
       });
-  };
+  }
 
-  parseSMSResponse = (response) => {
-    // console.log(response);
-    let s;
-    // Case for error => 'ERROR : 406 Invalid mobile number\r\n'
-    try {
-      s = response.split(':');
-      return {
-        responseCode: parseInt(s[1].trim().split(' ')[0]),
-        messageID: undefined,
-      };
-    } catch (e) {}
+  doRequest(): Promise<SMSResponse> {
+    throw new Error('Method not implemented.');
+  }
 
-    // Case for normal response => '402,MsgID = 070820191565174573847hpgovt-hpssa\r\n'
-    s = response.split(',');
+  parseResponse(response: string) {
     try {
-      const messageID = s[1].split('=')[1].trim();
-      return {
-        responseCode: parseInt(s[0]),
-        messageID: messageID,
-      };
+      const responseCode: string = response.slice(0, 3);
+      if (responseCode === '402') {
+        return {
+          providerResponseCode: null,
+          status: SMSResponseStatus.success,
+          messageID: response.slice(12, -1),
+          error: null,
+          providerSuccessResponse: null,
+        };
+      } else {
+        const error: SMSError = {
+          errorText: 'CDAC Error',
+          errorCode: responseCode,
+        };
+        return {
+          providerResponseCode: responseCode,
+          status: SMSResponseStatus.failure,
+          messageID: null,
+          error,
+          providerSuccessResponse: null,
+        };
+      }
     } catch (e) {
+      const error: SMSError = {
+        errorText: `CDAC response could not be parsed :: ${e.message}; Provider Response - ${response}`,
+        errorCode: 'CUSTOM ERROR',
+      };
       return {
-        responseCode: parseInt(s[0]),
-        messageID: undefined,
+        providerResponseCode: null,
+        status: SMSResponseStatus.failure,
+        messageID: null,
+        error,
+        providerSuccessResponse: null,
       };
     }
-  };
+  }
+
+  verifyOTP(data: SMSData): Promise<TrackResponse> {
+    let verified = false;
+    try {
+      verified = speakeasy.totp.verify({
+        secret: this.getTotpSecret(data.phone),
+        encoding: 'base32',
+        token: data.params.otp,
+        step: this.configService.get<string>('SMS_TOTP_EXPIRY'),
+      });
+      if (verified) {
+        return new Promise(resolve => {
+          const status: TrackResponse = {} as TrackResponse;
+          status.provider = SMSProvider.cdac;
+          status.phone = data.phone;
+          status.messageID = '';
+          status.error = null;
+          status.providerResponseCode = null;
+          status.providerSuccessResponse = null;
+          status.status = SMSResponseStatus.success;
+          resolve(status);
+        });
+      } else {
+        return new Promise(resolve => {
+          const status: TrackResponse = {} as TrackResponse;
+          status.provider = SMSProvider.cdac;
+          status.phone = data.phone;
+          status.networkResponseCode = 200;
+          status.messageID = '';
+          status.error = {
+            errorText: 'Invalid or expired OTP.',
+            errorCode: '400'
+          };
+          status.providerResponseCode = '400';
+          status.providerSuccessResponse = null;
+          status.status = SMSResponseStatus.failure;
+          resolve(status);
+        });
+      }
+    } catch(error) {
+      throw new HttpException(error, 500);
+    }
+  }
 }
diff --git a/src/auth/auth-jwt.guard.ts b/src/auth/auth-jwt.guard.ts
index 09ba72b56f3668108b09d4dc78ff6ff00bb614da..dd26ff8227ba194557737e8e56611f793204b5ee 100644
--- a/src/auth/auth-jwt.guard.ts
+++ b/src/auth/auth-jwt.guard.ts
@@ -1,10 +1,8 @@
 import {
     ExecutionContext,
     Injectable,
-    UnauthorizedException,
   } from '@nestjs/common';
   import { AuthGuard, IAuthGuard } from '@nestjs/passport';
-  import { JwtService } from '@nestjs/jwt'
   import { Reflector } from '@nestjs/core';
   
   @Injectable()
@@ -36,8 +34,8 @@ import {
         return isAllowed;
     }
   
+    // noinspection JSUnusedLocalSymbols
     handleRequest(err, user, info) {
-        console.log({handleRequest: info, error: err, user: user})
         return user;
     }
   }
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index c3bba2902f3bee2830618d60691c35aedd807e0b..e433aa364b873dde8b86a23bd5ed9f659e2e49e2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2040,6 +2040,11 @@ balanced-match@^1.0.0:
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
+base32.js@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/base32.js/-/base32.js-0.0.1.tgz#d045736a57b1f6c139f0c7df42518a84e91bb2ba"
+  integrity sha512-EGHIRiegFa62/SsA1J+Xs2tIzludPdzM064N9wjbiEgHnGnJ1V0WEpA4pEwCYT5nDvZk3ubf0shqaCS7k6xeUQ==
+
 base64-js@^1.3.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@@ -6086,6 +6091,13 @@ spdx-license-ids@^3.0.0:
   resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz"
   integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==
 
+speakeasy@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/speakeasy/-/speakeasy-2.0.0.tgz#85c91a071b09a5cb8642590d983566165f57613a"
+  integrity sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==
+  dependencies:
+    base32.js "0.0.1"
+
 sprintf-js@~1.0.2:
   version "1.0.3"
   resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"