public async Task AuthenticateForLimitedPublicAsync(bool forCartManagement)
        {
            if (forCartManagement && string.IsNullOrEmpty(SessionToken))
            {
                var sessionTokenUri      = Billboard.GetSessionTokenUri();
                var sessionTokenResponse = await GetAsync <SessionToken>(sessionTokenUri).ConfigureAwait(false);

                if (sessionTokenResponse != null)
                {
                    SessionToken = sessionTokenResponse.session_token;
                }
            }
            var billboard = await Billboard.GetResourceAccessAsync(this).ConfigureAwait(false);

            var tokenUri = FormLimitedPublicTokenUri(billboard);
            var log      = BeginLog("POST", tokenUri);
            var response = await PostLimitedPublicTokenRequestAsync(tokenUri).ConfigureAwait(false);

            LogResponse(log, response);
            await ThrowIfFailAsync(response, log).ConfigureAwait(false);

            EndLog(log, String.Empty);
            await SetTokensAsync(response).ConfigureAwait(false);
        }
        private async Task <HttpResponseMessage> RetryIfTokenExpired(Func <Task <HttpResponseMessage> > req)
        {
            // BEWARE: Don't call any higher-level Get or Post here which might themselves do a retry.
            var response = await req().ConfigureAwait(false);

            if (response.StatusCode != HttpStatusCode.Unauthorized ||
                _httpClient.DefaultRequestHeaders.Authorization == null ||
                string.IsNullOrEmpty(RefreshToken))
            {
                return(response);
            }

            // perhaps bearer token is expired
            var billboard = await Billboard.GetResourceAccessAsync(this).ConfigureAwait(false);

            if (SessionToken == null)
            {
                // since no session token is involved, try to use refresh token to get new bearer token
                var refreshUri = Billboard.ResolveTemplate(billboard.Token.Uri, Billboard.Templates.RefreshTokenQuery,
                                                           new { client_id = ApiKey, refresh_token = RefreshToken });
                _httpClient.DefaultRequestHeaders.Authorization = null;
                response = await _httpClient.PostAsync(refreshUri, null).ConfigureAwait(false);

                if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Unauthorized)
                {
                    return(response);
                }
                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    // refresh token might be expired; start over
                    response = await PostLimitedPublicTokenRequestAsync(FormLimitedPublicTokenUri(billboard)).ConfigureAwait(false);

                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        return(response);
                    }
                }
            }
            else
            {
                // todo: when we implement shopper authentication, if we have a shopper, this needs to be the shopper token request, not limited public
                // if used a session token to get bearer token, unfortunately refresh token is doo doo
                // start over with existing session token if have one
                if (!string.IsNullOrEmpty(SessionToken))
                {
                    response = await PostLimitedPublicTokenRequestAsync(FormLimitedPublicTokenUri(billboard)).ConfigureAwait(false);
                }
                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    // perhaps session token is expired; need to get a new session token
                    // our thoughts and prayers go out to the poor shopper who is now going to lose their cart contents
                    var sessionTokenUri = Billboard.GetSessionTokenUri();
                    var json            = await GetStringAsync(sessionTokenUri, true /*no retry!*/).ConfigureAwait(false);

                    if (!string.IsNullOrEmpty(json))
                    {
                        var sessionTokenResponse = JsonConvert.DeserializeObject <SessionToken>(json);
                        if (sessionTokenResponse != null)
                        {
                            SessionToken = sessionTokenResponse.session_token;
                        }
                    }
                    response = await PostLimitedPublicTokenRequestAsync(FormLimitedPublicTokenUri(billboard)).ConfigureAwait(false);

                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        return(response);
                    }
                }
            }

            // whew!!! we got reauthorized
            await SetTokensAsync(response).ConfigureAwait(false);

            // replay request
            return(await req().ConfigureAwait(false));
        }