In NAV, OTP authentication can add an extra layer of security to our Environments. The basic idea is that when every user tries to open a company, s/he will receive a OTP on their registered mobile number. They will have to key in this number in NAV to gain access. This is usually known as two factor authentications.
To do this in NAV you need a service to send SMS. There are a lot available on internet. In this demo, I am using SMS service from NEXMO. Do note that when choosing a service, it is crucial to check the limitations. For example, NEXMO can deliver SMS in India only between 9 am to 6 pm. Similar limitations exist for other regions as well.
Once you choose a service you will have to go through their documentation. Most will provide Restful APIs which can be easily consumed in NAV. Lets try to use Nexmo for the time being
- Create a new codeunit in your object range
- You will need a new function to initiliaze and send request to the API.
LOCAL HttpPostSMS() Status:=1; Answer := DIALOG.CONFIRM('Do you want to recieve OTP',FALSE); IF Answer= TRUE THEN BEGIN HttpWebRequestMgt.Initialize(Url+GetParams); HttpWebRequestMgt.DisableUI; HttpWebRequestMgt.SetMethod('POST'); HttpWebRequestMgt.SetReturnType('application/json'); TempBlob.INIT; TempBlob.Blob.CREATEINSTREAM(Instr); IF HttpWebRequestMgt.GetResponse(Instr,HttpStatusCode,ResponseHeaders) THEN BEGIN IF HttpStatusCode.ToString = HttpStatusCode.OK.ToString THEN BEGIN Result := TempBlob.ReadAsText('',TEXTENCODING::UTF8); JObject:= JObject.Parse(Result); temp:=JObject.GetValue('messages').ToString; temp :=DELCHR(temp,'=',''); JObject :=JObject.Parse(temp); EVALUATE(Status,JObject.GetValue('status').ToString); END ELSE MESSAGE('not 200'); END ELSE MESSAGE('no reponse at all'); END ELSE ERROR ('Cannot open company without OTP'); LOCAL GetParams() : Text Params := '?api_key=<your key>&api_secret=<your secret>&to=<number to which sms has to go>&from=Jaspreet NAV&text=%1'; Params := STRSUBSTNO(Params,GetRandom); EXIT(Params); LOCAL GetRandom() : Text OTP := RANDOM(99999); RandomParam := SMStext; RandomParam:=STRSUBSTNO(RandomParam,COMPANYNAME,OTP); EXIT(RandomParam);
You will also need to define local variables and global variables
This function would send a SMS to a hardcoded number (should come from user master in actual implementation). Next, we need to call this function Before a company opens. I will use OnBeforeOpenCompany event from Code unit 1 (ApplicationManagement). For versions where events are not available you can simply call the function in code unit 1.
HttpPostSMS; Count := 0; Success := FALSE; IF Status = 0 THEN REPEAT CLEAR(TwoFactorAuth); IF TwoFactorAuth.RUNMODAL<>ACTION::OK THEN ERROR('Cancelled'); IF TwoFactorAuth.GetEnteredOTP = OTP THEN BEGIN Success:=TRUE; END ELSE BEGIN Count += 1; MESSAGE('Wrong OTP'); END; UNTIL (Count = 3) OR Success; IF NOT Success THEN ERROR('You have entered wrong otp too many times');
Variables need to be defined as well.
You also need to create a new page and declare a single global variable. Then set that variable as a field.
Global variables of page
We also need a function in page to transfer the entered OTP to our code unit
Once everything is done, When a user tries to login s/he will get a OTP on their phone. They will see a dialog in NAV and must enter the OTP. They get three tries to do so.
In actual implementation users phone number should come from the master. Also there can be a option where the end user can either choose SMS or Email to receive the OTP for authentication.