0

This is my first time posting, apologize for the question format, I'm not sure how to edit it.

My question is, is it possible to call a data generated (in this case the random code for OTP) in different class, in this case from otp controller class to the email service class?

I'm generating an otp to be sent to email with ASP.NET Web API. So far, I created a separate class / controller to generate and save the otp, then another class to set up the email service then lastly email controller to send the email.

I'm generating a random code as OTP to be send directly to the database with the post method.

Here's the code in OTP controller

[HttpPost]
public async Task<IActionResult> AddOTPs(Otp otp)
{
    Random rand = new Random();
    randomcode = (rand.Next(9999999)).ToString();

    var otps = new Otp()
        {
            AlamatEmail = otp.AlamatEmail,
            NamaKaryawan = otp.NamaKaryawan,
            OTP_C = randomcode,
        };

    await _context.Otps.AddAsync(otps);
    await _context.SaveChangesAsync();

    return Ok(otps);
}

enter image description here

In a different class, I have set up the email service to be called in email controller. Here's that code:

public void SendEmail(Otp request)
{
    var email = new MimeMessage();
    email.From.Add(MailboxAddress.Parse(_config.GetSection("EmailUsername").Value));
    email.To.Add(MailboxAddress.Parse(request.AlamatEmail));
    email.Subject = "OTP Test";
    email.Body = new TextPart(TextFormat.Html) { 
            Text = request.OTP_C
        };

    using var smtp = new SmtpClient();
    smtp.Connect(_config.GetSection("EmailHost").Value, 587, SecureSocketOptions.StartTls);
    smtp.Authenticate(_config.GetSection("EmailUsername").Value, _config.GetSection("EmailPassword").Value);

    smtp.Send(email);
    smtp.Disconnect(true);
}

enter image description here

Email controller code :

[HttpPost]
public IActionResult SendEmail(Otp request)
{
    _emailService.SendEmail(request);
    return Ok();
}

Now, all of this code works well without any error, but when I have to call separate API to post the otp to the database, and then another API to send the email where I have to input both the email address and otp manually. So now the flow is like this, post otp to database api, get otp from database api, then post send email api, with total of 3 api calls.

My question is: how can I make sure the random code generated in OTP controller is being sent to the email service class? Or how can I make it so when the otp generated its going to be saved to database and then send the email.

Basically I want to make sure there's only 1-2 APIs being called instead of 3 by eliminating the get otp from database API.

Thank you in advance.

All the code is written in C# for the ASP.NET framework

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • What's a _"data generate"_ ? What's an _"OTP"_ (one time pad??) – Flydog57 Nov 04 '22 at 04:15
  • data generate , i meant the random number i generate as the OTP (One time password). and thank you for your help in the formatting my question, appreciate it alot –  Nov 04 '22 at 04:19
  • If you want to create one time passwords, use a cryptographically strong random number generator (call `RandomNumberGenerator.Create()` to get an instance): https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator. Get a random number with as much entropy as you want and then convert it to text - either as hex or base64. What you don't want to do is create a new instance of `Random` every time you are asked for a password, if the requests are close enough, you'll be handing back the same "random" number – Flydog57 Nov 04 '22 at 14:25
  • oh thank you so much for letting me know. i havent used randomnumbergenerator before, could you please point up to the direction where i can learn it, with possible of code samples. Also if send the strong random number to the email - would it be in number format or hex or base64? –  Nov 04 '22 at 15:32

2 Answers2

0

You cannot do it like this. It would be better if you have a Facade Class after the controller. You can call the Facade class, this class will Save the OTP into the database after that will call the email service with the same OTP. Then this Facade class can be called from the controller.

public class OTPFacede{ 
private readonly EmailService _emailService;
private readonly DBContext _context;
public OTPFacede(EmailSevice emailservice,DBContext context){ 
         _emailService = emailService;
         _context = context;
}
public void SaveAndSendOtp(){
        Random rand = new Random();
        randomcode = (rand.Next(9999999)).ToString();
        var otps = new Otp()
        {
            AlamatEmail = otp.AlamatEmail,
            NamaKaryawan = otp.NamaKaryawan,
            OTP_C = randomcode,
        };
        _context.Otps.AddAsync(otps);
        _context.SaveChanges();
         SendEmail(otps)
   }

public void  SendEmail(Otp request){

       
        var email = new MimeMessage();
        email.From.Add(MailboxAddress.Parse(_config.GetSection("EmailUsername").Value));
        email.To.Add(MailboxAddress.Parse(request.AlamatEmail));
        email.Subject = "OTP Test";
        email.Body = new TextPart(TextFormat.Html) { 
            Text = request.OTP_C
        };


        using var smtp = new SmtpClient();
        smtp.Connect(_config.GetSection("EmailHost").Value, 587, SecureSocketOptions.StartTls);
        smtp.Authenticate(_config.GetSection("EmailUsername").Value, _config.GetSection("EmailPassword").Value);
        smtp.Send(email);
        smtp.Disconnect(true);
    }

}

  • hi, thank you so much, could you please help me on how to call in the post method api after this, if its not too much to ask, please and thank you. –  Nov 04 '22 at 07:34
  • You don't have to call it. Right now you are calling two post methods one for saving the OTP and one for Sending the mail. Instead of two calls make one call to both saving and send an email. – Partha Thakura Nov 04 '22 at 07:47
  • thank you. i was wondering, what should i write after the httppost to call the saveandsaveotp. im a little confused there. i made the class as per what you did above, but then im a bit blank on what to write after the httpost one, could you perhaps help me on that please? –  Nov 04 '22 at 07:56
  • create an instance of this class in the controller and call the method. after that return ok – Partha Thakura Nov 04 '22 at 10:36
  • i know im asking a bit lot, but for some reasons its not working on my end, if its not too much to ask, could you please help me with that instance syntax and calling the method, please? again, im so sorry for bothering you so much and thanks again in advance. –  Nov 04 '22 at 11:04
0

This isn't an answer to your question, it's a response to your query in the comments (about some sample code for a cryptographically strong random password)

Here's the code:

public static string GetRandomString(int entropyByteCount)
{
    using (var rng = RandomNumberGenerator.Create())
    {
        var byteArray = new byte[entropyByteCount * 8];
        rng.GetBytes(byteArray);
        return BytesToHexString(byteArray);
    }       
}

private static string BytesToHexString(byte[] bytes)
{
    var buffer = new StringBuilder(bytes.Length * 2);
    foreach (var b in bytes)
    {
        buffer.Append(b.ToString("X2"));
    }
    return buffer.ToString();
}

The result will be a hex string that has 16 times as many characters as the value of entropyByteCount parameter. This is because the number of bytes in the array passed to GetBytes should really be divisible by 8 and it takes two hex digits to render the contents of a byte.

The code expects that you have this (using System.Security.Cryptography;) in your program.

There are better ways to convert a byte array to a hex string, take a look at: How do you convert a byte array to a hexadecimal string, and vice versa?

Flydog57
  • 6,851
  • 2
  • 17
  • 18
  • thank you so much im gonna try it now. i appreciate it so much. thanks once again. –  Nov 05 '22 at 07:28