/// <summary>
        /// Retrieves a set of OAuth tokens using the shortcode.
        /// </summary>
        /// <param name="cancellationToken">Cancellation for the operation</param>
        /// <returns>The created set of OAuth tokens.</returns>
        /// <exception cref="InvalidOperationException">If called when the
        /// <see cref="ShortcodeState"/> is not <code>Accepted</code>.</exception>
        public async Task <OAuthTokens> GetTokensAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            if (this.readCode == null)
            {
                var state = await this.PollAsync(cancellationToken);

                if (state != ShortcodeState.Accepted)
                {
                    throw new InvalidOperationException(
                              "GetTokensAsync may only be called once a shortcode "
                              + $"is {ShortcodeState.Accepted},  but it was {state}.");
                }
            }

            var content = HttpHelpers.JsonContent(
                new
            {
                client_id  = this.options.ClientId,
                grant_type = "authorization_code",
                code       = this.readCode
            });

            using (var response = await this.options.Client.PostAsync(
                       $"{this.options.Host}/api/v1/oauth/token",
                       content,
                       cancellationToken))
            {
                await HttpHelpers.ThrowInvalidContentsAsync(response);

                return(new OAuthTokens(
                           JsonConvert.DeserializeObject <OAuthTokenResponse>(await response.Content.ReadAsStringAsync()),
                           this.options.Scopes));
            }
        }
        /// <summary>
        /// Attempts to retrieve a single OAuth shortcode from the server.
        /// </summary>
        /// <param name="cancellationToken">Optional cancellation for the operation</param>
        /// <returns>A task that resolves to the retrieved code.</returns>
        /// <exception cref="OAuthException">If there was an error granting
        /// access. This can occur if your client ID is incorrect, for instance,
        /// or if the user denies access. The <code>Response.Error</code>
        /// property can tell you more about what went wrong.</exception>
        /// <exception cref="UnexpectedHttpException">If some unknown
        /// error occurred.</exception>
        public async Task <OAuthShortcode> GetSingleCodeAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            var body = HttpHelpers.JsonContent(
                new
            {
                scope         = string.Join(" ", this.options.Scopes),
                client_secret = this.options.ClientSecret,
                client_id     = this.options.ClientId
            });

            var response = await this.options.Client.PostAsync(
                $"{this.options.Host}/api/v1/oauth/shortcode",
                body,
                cancellationToken);

            using (response)
            {
                await HttpHelpers.ThrowInvalidContentsAsync(response);

                var parsed = JsonConvert.DeserializeObject <ShortcodeCreateResponse>(
                    await response.Content.ReadAsStringAsync());
                return(new OAuthShortcode(this.options, parsed));
            }
        }
        /// <summary>
        /// Refreshes the set of OAuth tokens.
        /// </summary>
        /// <param name="tokens">Previously granted tokens</param>
        /// <param name="cancellationToken">Cancellation token for the operation</param>
        /// <returns>A new set of tokens.</returns>
        /// <exception cref="OAuthException">If there was an error granting
        /// access. This can occur if your client ID is incorrect, for instance,
        /// or your refresh token is invalid. The <code>Response.Error</code>
        /// property can tell you more about what went wrong.</exception>
        /// <exception cref="UnexpectedHttpException">If some unknown
        /// error occurred.</exception>
        /// <exception cref="TaskCanceledException">If the cancellation
        /// token fires.</exception>
        public async Task <OAuthTokens> RefreshAsync(OAuthTokens tokens, CancellationToken cancellationToken = default(CancellationToken))
        {
            var content = HttpHelpers.JsonContent(
                new
            {
                grant_type    = "refresh_token",
                refresh_token = tokens.RefreshToken,
                client_id     = this.options.ClientId
            });

            var response = await this.options.Client.PostAsync(
                $"{this.options.Host}/api/v1/oauth/token",
                content,
                cancellationToken);

            using (response)
            {
                await HttpHelpers.ThrowInvalidContentsAsync(response);

                return(new OAuthTokens(
                           JsonConvert.DeserializeObject <OAuthTokenResponse>(await response.Content.ReadAsStringAsync()),
                           tokens.Scopes));
            }
        }