public async Task <ActionResult> Generate()
        {
            string input = null;

            // If not data came in, then return
            if (this.Request.Body == null)
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("Request content is null", HttpStatusCode.Conflict)));
            }

            // Read the input claims from the request body
            using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
            {
                input = await reader.ReadToEndAsync();
            }

            // Check input content value
            if (string.IsNullOrEmpty(input))
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("Request content is empty", HttpStatusCode.Conflict)));
            }

            // Convert the input string into InputClaimsModel object
            TotpInputClaims inputClaims = TotpInputClaims.Parse(input);

            if (inputClaims == null)
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("Can not deserialize input claims", HttpStatusCode.Conflict)));
            }

            try
            {
                // Define the URL for the QR code. When user scan this URL, it opens one of the
                // authentication apps running on the mobile device
                byte[] secretKey = KeyGeneration.GenerateRandomKey(20);

                string TOTPUrl = KeyUrl.GetTotpUrl(secretKey, $"{AppSettings.TOTPAccountPrefix}:{inputClaims.UserName}",
                                                   AppSettings.TOTPTimestep);

                TOTPUrl = $"{TOTPUrl}&issuer={AppSettings.TOTPIssuer.Replace(" ", "%20")}";

                // Generate QR code for the above URL
                var              qrCodeGenerator = new QRCodeGenerator();
                QRCodeData       qrCodeData      = qrCodeGenerator.CreateQrCode(TOTPUrl, QRCodeGenerator.ECCLevel.L);
                BitmapByteQRCode qrCode          = new BitmapByteQRCode(qrCodeData);
                byte[]           qrCodeBitmap    = qrCode.GetGraphic(4);

                var output = new B2CResponseContent(string.Empty, HttpStatusCode.OK)
                {
                    QrCodeBitmap = Convert.ToBase64String(qrCodeBitmap),
                    SecretKey    = this.EncryptAndBase64(Convert.ToBase64String(secretKey))
                };

                return(Ok(output));
            }
            catch (Exception ex)
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent($"General error (REST API): {ex.Message}", HttpStatusCode.Conflict)));
            }
        }
        public async Task <ActionResult> Verify()
        {
            string input = null;

            // If not data came in, then return
            if (this.Request.Body == null)
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("Request content is null", HttpStatusCode.Conflict)));
            }

            // Read the input claims from the request body
            using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
            {
                input = await reader.ReadToEndAsync();
            }

            // Check input content value
            if (string.IsNullOrEmpty(input))
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("Request content is empty", HttpStatusCode.Conflict)));
            }

            // Convert the input string into InputClaimsModel object
            var inputClaims = TotpInputClaims.Parse(input);

            if (inputClaims == null)
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("Can not deserialize input claims", HttpStatusCode.Conflict)));
            }

            try
            {
                byte[] secretKey = Convert.FromBase64String(this.DecryptAndBase64(inputClaims.SecretKey));

                Totp totp = new Totp(secretKey);
                long timeStepMatched;

                // Verify the TOTP code provided by the users
                bool verificationResult = totp.VerifyTotp(
                    inputClaims.TotpCode,
                    out timeStepMatched,
                    VerificationWindow.RfcSpecifiedNetworkDelay);

                if (!verificationResult)
                {
                    return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("The verification code is invalid.", HttpStatusCode.Conflict)));
                }

                // Using the input claim 'timeStepMatched', we check whether the verification code has already been used.
                // For sign-up, the 'timeStepMatched' input claim is null and should not be evaluated
                // For sign-in, the 'timeStepMatched' input claim contains the last time last matched (from the user profile), and evaluated with
                // the value of the result of the TOTP out 'timeStepMatched' variable
                if (!string.IsNullOrEmpty(inputClaims.TimeStepMatched) && (inputClaims.TimeStepMatched).Equals(timeStepMatched.ToString()))
                {
                    return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent("The verification code has already been used.", HttpStatusCode.Conflict)));
                }

                var output = new B2CResponseContent(string.Empty, HttpStatusCode.OK)
                {
                    TimeStepMatched = timeStepMatched.ToString()
                };

                return(Ok(output));
            }
            catch (Exception ex)
            {
                return(StatusCode((int)HttpStatusCode.Conflict, new B2CResponseContent($"General error (REST API): {ex.Message}", HttpStatusCode.Conflict)));
            }
        }