public string ConstructRedirectUrl(DiscourseSsoReturnPayload payload)
        {
            string signature;
            string discoursePayloadUrlEncoded = CreateSsoPayload(payload, out signature);

            if (DiscourseServerUrl.EndsWith("/"))
            {
                DiscourseServerUrl = DiscourseServerUrl.Substring(0, DiscourseServerUrl.Length - 1);
            }
            //Don't use AddQueryParam as sso payload is already URL encoded and that will invalidate signature.
            return(DiscourseServerUrl + "/session/sso_login?sso=" + discoursePayloadUrlEncoded + "&sig=" + signature);
        }
        private string CreateSsoPayload(DiscourseSsoReturnPayload payload, out string signature)
        {
            var discoursePayloadRawString = "nonce={0}&name={1}&username={2}&email={3}&external_id={4}".Fmt(
                payload.Nonce,
                payload.Name,
                payload.UserName,
                payload.Email,
                payload.ExternalId);
            var discoursePayloadBase64     = Convert.ToBase64String(Encoding.UTF8.GetBytes(discoursePayloadRawString));
            var discoursePayloadUrlEncoded = discoursePayloadBase64.UrlEncode();
            var sha256 = new HMACSHA256(Encoding.UTF8.GetBytes(DiscourseSsoSecret));

            //signature needs to be generated off the base64 payload, NOT the url ended base64 payload...
            //https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045/71
            signature =
                HashEncode(sha256.ComputeHash(Encoding.UTF8.GetBytes(discoursePayloadBase64)));
            return(discoursePayloadUrlEncoded);
        }