private TResponseType AuthenticatedRestRequest <TResponseType>(IRestRequest request)
            where TResponseType : LaunchKeyResponse, new()
        {
            request.AddParameter("app_key", this.appKey);
            var secretInfo         = string.Format("{{\"secret\": '{0}', \"stamped\": '{1}'}}", this.secretKey, this.apiTime.Value.Add(DateTime.Now.Subtract(this.lastPing.Value)).ToString("yyyy-MM-dd HH:mm:ss"));
            var encryptionProvider = new LaunchKeyEncryptionProvider();
            var encryptedSecretKey = encryptionProvider.Encrypt(this.apiPublicKey, Encoding.ASCII.GetBytes(secretInfo));

            request.AddParameter("secret_key", Convert.ToBase64String(encryptedSecretKey));
            request.AddParameter("signature", Convert.ToBase64String(encryptionProvider.Sign(this.appPrivateKey.Private, encryptedSecretKey)));

            return(this.RestRequest <TResponseType>(request));
        }
        /// <summary>
        /// Attempts to decode a deorbit request delivered via callback from the LaunchKey API. When a request is received, pass the request parameters to this method. If a user hash cannot be decoded for any reason, an exception will be thrown
        /// </summary>
        /// <param name="orbitData">The orbit data from the 'deorbit' GET parameter</param>
        /// <param name="signature">The signature which verifies the data in <paramref name="orbitData"/>, from the 'signature' GET parameter</param>
        /// <returns>The user hash being deorbited</returns>
        public string Deorbit(string orbitData, string signature)
        {
            var pingResponse = this.Ping();

            if (!pingResponse.Successful)
            {
                throw new LaunchKeyApiException("Could not verify valid deorbit request, was unable ping API to get server time.");
            }

            var launchKeyEncryptionProvider = new LaunchKeyEncryptionProvider();
            var signatureValid = launchKeyEncryptionProvider.VerifySignature(
                this.apiPublicKey,
                Encoding.ASCII.GetBytes(orbitData),
                Convert.FromBase64String(signature)
                );

            // signature checks out
            if (signatureValid)
            {
                var jobj = JObject.Parse(orbitData);

                if (jobj.Property("launchkey_time") == null)
                {
                    throw new LaunchKeyApiException("Invalid request parameters: launchkey_time was not present");
                }

                if (jobj.Property("user_hash") == null)
                {
                    throw new LaunchKeyApiException("Invalid request parameters: user_hash was not present");
                }


                var requestLaunchKeyTime = (DateTime)jobj.Property("launchkey_time").Value;

                if (this.apiTime.Value.Subtract(requestLaunchKeyTime).TotalMinutes < 5.0)
                {
                    return((string)jobj.Property("user_hash"));
                }
                else
                {
                    throw new LaunchKeyApiException("Request is older than 5 minutes. Possible replay attack?");
                }
            }
            else
            {
                throw new LaunchKeyApiException("Signature validation failed!");
            }
        }
        /// <summary>
        /// Poll the status of an authentication request using the reference id supplied in <paramref name="authRequest"/>. Implicitly calls Ping().
        /// </summary>
        /// <param name="authRequest">unique authentication request reference ID to check</param>
        /// <returns><see cref="PollResponse"/> object including status of request</returns>
        public PollResponse Poll(string authRequest)
        {
            if (string.IsNullOrWhiteSpace(authRequest))
            {
                throw new ArgumentException("authRequest must be provided");
            }

            this.EnsureAuthorizationRecent();

            RestRequest request = new RestRequest("poll", Method.GET);

            request.AddParameter("auth_request", authRequest);
            request.AddParameter("SUPPRESS_RESPONSE_CODES", "TRUE");

            var response = this.AuthenticatedRestRequest <PollResponse>(request);

            if (response.Auth != null)
            {
                var encryptionProvider = new LaunchKeyEncryptionProvider();
                var authPayload        = Convert.FromBase64String(response.Auth.Replace("\\/", "/"));
                var decryptedPayload   = encryptionProvider.Decrypt(this.appPrivateKey.Private, authPayload);
                var decryptedText      = Encoding.ASCII.GetString(decryptedPayload);

                var jobj = JObject.Parse(decryptedText);

                response.DecryptedAuth = new DecryptedPollResponse()
                {
                    AuthRequest = jobj.Property("auth_request") != null?jobj.Property("auth_request").Value.ToString() : null,
                                      DeviceId = jobj.Property("device_id") != null?jobj.Property("device_id").Value.ToString() : null,
                                                     AppPins = jobj.Property("app_pins") != null?jobj.Property("app_pins").Value.ToString().Split(',') : null,
                                                                   Response = jobj.Property("response") != null?bool.Parse(jobj.Property("response").Value.ToString()) : false
                };
            }

            return(response);
        }