0

I want to use the IdentityModel extension methods (TokenRefresh) added for System.Net.Http in my UWP app which uses Windows.Web.Http. However, I'm currently using version 4.3.1. On this version Windows.Web.Http.HttpClient extensions are NOT available.

Nuget: Install-Package IdentityModel -Version 4.3.1
SurenSaluka
  • 1,534
  • 3
  • 18
  • 36

1 Answers1

2

Creators of the IdentityModel refused to add support to the Windows.Web.Http module. So I implemented the refresh token myself. You'll are welcome to point any issue here.

public static async Task<HttpClient> GetHttpClient()
    {
        var httpBaseProtocolFilter = new HttpBaseProtocolFilter
        {
            MaxVersion = HttpVersion.Http20,
        };
        return await SetHeaders(new HttpClient(httpBaseProtocolFilter));
    }

private static async Task<HttpClient> SetHeaders(HttpClient httpClient)
    {
        try
        {
            httpClient.DefaultRequestHeaders.Add("X-requestIp", DeviceIp);
            httpClient.DefaultRequestHeaders.Add("client", "5");

            if (OAuth2Manager.Token != null && !string.IsNullOrWhiteSpace(OAuth2Manager.Token.AccessToken))
                httpClient.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", OAuth2Manager.Token.AccessToken));

            return httpClient;
        }
        catch (Exception)
        {
            throw;
        }
    }

private static async Task<ResponseDataModel> RequestSender1(object postData, string requestUrl,
            string requestMehtod, Action<HttpProgress> OnSendRequestProgress)
        {
            try
            {
                HttpClient httpClient = await GetHttpClient();
                httpClient = await SetBearerToken(httpClient);

                //Do all the request send related stuff here
            }
            catch (Exception)
            {

            }
        }

static readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
private static async Task<HttpClient> SetBearerToken(HttpClient httpClient)
    {
        try
        {
            if (OAuth2Manager.TokenExpiration < DateTime.Now)
            {
                await semaphoreSlim.WaitAsync();
                try
                {
                    await OAuth2Manager.RequestRefreshTokenAsync();
                }
                finally
                {
                    semaphoreSlim.Release();
                }
            }

            return httpClient;
        }
        catch (Exception)
        {
            throw;
        }
    }

//retryCount = 3
    //tempRetryCount = 1
    public static async Task<TokenResponse> RequestRefreshTokenAsync()
    {
        try
        {
            do
            {
                if (retryCount == tempRetryCount) break;

                Token = await RequestRefreshTokenAsyncInternal();
                tempRetryCount++;

                if (Token.IsError || !string.IsNullOrWhiteSpace(Token.Error))
                {
                    //Log errors
                }

            } while (Token.IsError || !string.IsNullOrWhiteSpace(Token.Error));


            if (Token.IsError || !string.IsNullOrWhiteSpace(Token.Error))
            {
                //Log errors
                return Token;
            }

            //percentage == 80
            //Token expiration set as 80% of the actual expire value so 20% of the remaining time can be
            //used to send a refresh token request.
            TokenExpiration = DateTime.Now.AddSeconds(Token.ExpiresIn * percentage / 100);
            return Token;
        }
        catch (Exception)
        {
            throw;
        }
    }

private static async Task<TokenResponse> RequestRefreshTokenAsyncInternal()
    {
        try
        {
            if (Token == null || Token.RefreshToken != null)
            {
                Token = await httpClient.RequestRefreshTokenAsync(new RefreshTokenRequest()
                {
                    Address = discoveryDocumentResponse.TokenEndpoint,
                    RefreshToken = Token.RefreshToken,
                    ClientId = clientId,
                    ClientSecret = clientSecret,
                    AuthorizationHeaderStyle = BasicAuthenticationHeaderStyle.Rfc6749,
                });

                if (Token.IsError && Token.HttpStatusCode == System.Net.HttpStatusCode.BadRequest && Token.Error.Equals(ApplicationConstants.InvalidGrant))
                {
                    await RequestPasswordTokenAsync(Global.Username, Global.Password);
                }
            }
            else
            {
                await RequestPasswordTokenAsync(Global.Username, Global.Password);
            }

            if (Token.IsError || !string.IsNullOrWhiteSpace(Token.Error))
            {
                //Log errors
            }

            return Token;
        }
        catch (Exception)
        {
            throw;
        }
    }

Hope you'll understand what I did. Cheers!

SurenSaluka
  • 1,534
  • 3
  • 18
  • 36
  • 2
    Thank you! I will check this soon. – ArchieCoder Feb 19 '21 at 14:27
  • 1
    Make sure you reuse your HttpClient instance, in your example above, there is a HttpClient object created for each call, see https://contrivedexample.com/2017/07/01/using-httpclient-as-it-was-intended-because-youre-not/ Thank you for your code! – ArchieCoder Mar 02 '21 at 05:03