Example #1
0
        /// <summary>
        /// Console Loop
        /// </summary>
        static void ConsoleLoop()
        {
            const int MOD_PAYLOAD_OPTION = 99;

            // load the list of available users
            var users = UserController.LoadUsers();

            // Build a menu of Scenarions that can used to test
            StringBuilder sb      = new StringBuilder();
            int           userIdx = 1;

            sb.AppendLine();
            sb.AppendLine("DataCap POC for Auth via JWT Headers");
            sb.AppendLine();
            sb.AppendLine(" List of Test Users");
            sb.AppendLine("====================");
            sb.AppendLine($" [0] Anonymous (Do not send an Auth Header)");
            foreach (var user in users)
            {
                sb.AppendLine($" [{userIdx}] {user.User} " + (user.IsEnabled ? string.Empty : "(Disabled)"));

                userIdx++;
            }

            // pick a user to use for the Modified Payload
            var goodTestUser = users.Where(u => u.IsEnabled == true).FirstOrDefault();

            sb.AppendLine($" [{MOD_PAYLOAD_OPTION}] {goodTestUser.User} (Modified Body)");
            sb.AppendLine(" <enter> to exit");
            sb.AppendLine();

            // define variables outside the while loop to reduce GC pressure
            string         choiceText  = string.Empty;
            int            choiceIndex = 0;
            UserIdentityBE selectedUser;
            bool           shouldContinue      = true;
            AuthCRequestBE authcRequest        = null;
            string         httpBody            = string.Empty;
            Stopwatch      stw                 = null;
            string         jwtToken            = string.Empty;
            long           create1stJWTElapsed = 0;

            // =========================
            // start the message loop
            // =========================
            while (shouldContinue)
            {
                // reset variables
                choiceIndex  = -1;
                selectedUser = null;

                // clear the screen
                Console.Clear();

                // display the menu
                Console.WriteLine(sb.ToString());
                Console.Write("Choice: > ");
                choiceText = Console.ReadLine();

                if (string.IsNullOrEmpty(choiceText))
                {
                    // blank == exit
                    shouldContinue = false;
                }
                else
                {
                    // if the entry is a number and within the list, find the associated user
                    Int32.TryParse(choiceText, out choiceIndex);
                    if (choiceIndex == 0)
                    {
                        selectedUser = new UserIdentityBE()
                        {
                            User = @"Anonymous"
                        };
                        Console.WriteLine($" ==> Ready to send anonymous request");
                    }
                    else if (choiceIndex == MOD_PAYLOAD_OPTION)
                    {
                        selectedUser = goodTestUser;
                        Console.WriteLine($" ==> Ready to send request with tampered body");
                    }
                    else if (choiceIndex > 0 && choiceIndex <= users.Count)
                    {
                        selectedUser = users[choiceIndex - 1];
                        Console.WriteLine($" ==> Ready to send request for: {selectedUser.User} [{selectedUser.Company}].");
                    }

                    if (selectedUser == null)
                    {
                        Console.WriteLine($" [{choiceText}] is not at valid selection.");
                        Console.WriteLine($" ==> Press <enter> to continue");
                    }
                    else
                    {
                        // ==================================================
                        // this should be as short as possible to limit ability to fradualently reuse,
                        //  but long enough to tolerate clock skew between client and service
                        // ==================================================
                        int ttlMinutes = 5;

                        authcRequest = IsoMsgBuilder.GetAuthMsg();
                        httpBody     = authcRequest.ToString();

                        stw = new Stopwatch();
                        stw.Start();
                        jwtToken = (choiceIndex > 0) ? JwtController.CreateJWTToken(selectedUser.User, selectedUser.Company, JwtController.AUDIENCE, ttlMinutes, httpBody) : string.Empty;
                        stw.Stop();
                        create1stJWTElapsed = stw.ElapsedMilliseconds;

                        // test ben token
                        //string benToken = JwtController.CreateBENJWTToken(selectedUser.User, selectedUser.Company, JwtController.AUDIENCE, ttlMinutes);

                        //stw.Reset();
                        //stw.Start();
                        //jwtToken = (choiceIndex > 0) ? JwtController.CreateJWTToken(selectedUser.User, selectedUser.Company, JwtController.AUDIENCE, ttlMinutes, httpBody) : string.Empty;
                        //stw.Stop();
                        //var create2ndJWTElapsed = stw.ElapsedMilliseconds;

                        //string downstreamJwtToken = (choiceIndex > 0) ? JwtController.CreateDownstreamJWTToken(selectedUser.User, selectedUser.Company, JwtController.AUDIENCE, ttlMinutes, jwtToken) : string.Empty;
                        //stw.Reset();
                        //stw.Start();
                        //var dsT = JwtController.ValidateJWTToken(downstreamJwtToken, JwtController.AUDIENCE);
                        //stw.Stop();
                        //var validate1stJWTElapsed = stw.ElapsedMilliseconds;

                        //stw.Reset();
                        //stw.Start();
                        //dsT = JwtController.ValidateJWTToken(downstreamJwtToken, JwtController.AUDIENCE);
                        //stw.Stop();
                        //var validate2ndJWTElapsed = stw.ElapsedMilliseconds;

                        if (choiceIndex == MOD_PAYLOAD_OPTION)
                        {
                            // simulate man-in-the middle tampering with the payload
                            httpBody = httpBody + "123";
                        }

                        // make the call
                        (HttpStatusCode responseCode, IList <Parameter> responseHeaders, string responseContent) = CallWebApiPost(jwtToken, httpBody);

                        // evaluate the response
                        if (responseCode == HttpStatusCode.OK)
                        {
                            Console.ForegroundColor = ConsoleColor.Green;
                        }
                        else if (responseCode == HttpStatusCode.Unauthorized)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                        }
                        else
                        {
                            Console.ForegroundColor = ConsoleColor.Cyan;
                        }

                        Console.WriteLine();
                        Console.WriteLine($"responseCode: [{responseCode.ToString()}]");
                        Console.WriteLine();
                        Console.WriteLine($"headers");
                        foreach (var responseHeader in responseHeaders)
                        {
                            Console.WriteLine($"   {responseHeader.Name} : {responseHeader.Value}");
                        }
                        Console.WriteLine();
                        Console.WriteLine($"content: [{responseContent}]");

                        Console.ResetColor();

                        Console.WriteLine($"Create 1st JWT: [{create1stJWTElapsed} mSec]");
                        //Console.WriteLine($"Create 2nd JWT: [{create2ndJWTElapsed} mSec]");
                        //Console.WriteLine($"Valdiate 1st JWT: [{validate1stJWTElapsed} mSec]");
                        //Console.WriteLine($"Valdiate 2nd JWT: [{validate2ndJWTElapsed} mSec]");

                        Console.WriteLine();
                        Console.WriteLine($" ==> Press <enter> to continue");
                    }

                    Console.ReadLine();
                }
            }
        }
        //public ActionResult<string> Post([FromBody] string rawRequest)
        public async Task <ActionResult <string> > Post()
        {
            // note: I specficially choose NOT to use the [FromBody] approach to make debugging easier
            //          if we use the [FromBody] approach this method never get control if the body cannot be correctly deserialized
            //          this approach lets us capture the raw body content passed
            string httpBody = Request.GetRawBodyString();

            AuthCRequestBE authRequest = null;

            try
            {
                authRequest = JsonConvert.DeserializeObject <AuthCRequestBE>(httpBody);
            }
            catch (Exception ex)
            {
                // you could do addl logging here to support debugging
                return(BadRequest());
            }

            // look for an Auth Header
            // we expect it to look like:
            // Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFERTJERjQ2NkQyMTg4RDMyRjc0ODdCMjlCQzc2OTExNURDNTM0NzIiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJodHRwczovL3d3dy5kYXRhY2Fwc3lzdGVtcy5jb20vIiwiYXVkIjoiaHR0cHM6Ly93d3cud29ybGRwYXkuY29tLyIsImV4cCI6MTU2NDk0MjEwN30.mwwaFczSPP1_EMDcgAdXIbf3hwHw26nTv-kG4b1_EH9q8TFrNMmPMjayyWzHDizbwF-As-6AppaNlMbEQFp-ilXLCx_MAgvff1vNA_qA_wh_t0rcsUO_Evbn5lapoDOCom97cddSIywUnb4zA14TRlrttfuOnpkj08WaR2WM38unpKjBpIHYZJYrrG5Gzyyjs2uzPfCydOCcXVuv3xcVTbmgDGVraDswDMF0xVKHwrFNG9HLfCsJhgA14_puVELPRceuXa_o-u9o05U8-BRrzvyEOxobpXc_z6c0FlnA5OcTGbVDChCASal-8kXjaZYzk1dF-FBQxK3Sj75wCi3IYg
            string authHeader = Request.Headers["Authorization"];

            if (string.IsNullOrEmpty(authHeader))
            {
                return(Unauthorized());
            }
            else
            {
                // parse the header
                var    parts          = authHeader.Split(" ");
                string jwtTokenString = parts[1];

                (bool isJwtTokenValid, string username, JwtSecurityToken jwtToken) = JwtController.ValidateJWTToken(jwtTokenString, JwtController.AUDIENCE);

                if (!isJwtTokenValid)
                {
                    return(Unauthorized());
                }
                else
                {
                    // at this point this controller would add the identity of the authenticated caller as a new HTTP header and call a downstream webapi
                    // this part would typically be in the downstream client and called using await

                    // validate that the body has not body modified
                    string hash = httpBody.SHA256Hash();
                    (string hashType, string bodyHash) = JwtController.GetBodyHashInfo(jwtToken.Payload);
                    if (!string.IsNullOrEmpty(hashType))
                    {
                        if (hashType.ToLower() != @"sha256")
                        {
                            return(BadRequest($"hash type: [{hashType}] is NOT supported, use sha256 instead!"));
                        }

                        if (httpBody.SHA256Hash() != bodyHash)
                        {
                            return(BadRequest($"Post Body has been modified"));
                        }
                    }


                    // in this POC just return the identity that we derived from the JWT
                    var authCResult = new AuthCResultBE()
                    {
                        IsValid = isJwtTokenValid, User = username, JwtToken = jwtTokenString
                    };
                    //return Ok(JsonConvert.SerializeObject(authCResult, Formatting.Indented));
                    return(Ok(authCResult));
                }
            }
        }