0

I have the following code to generate token.

How can I ensure to re-use the token if it is already generated. The token is used for subsequent API calls.

 using (var client = new HttpClient())
{
    var postData = new List<KeyValuePair<string, string>>();
    postData.Add(new KeyValuePair<string, string>("username", _user));
    postData.Add(new KeyValuePair<string, string>("password", _pwd));
    postData.Add(new KeyValuePair<string, string>("grant_type", "password"));
    postData.Add(new KeyValuePair<string, string>("client_id", _clientId));
    postData.Add(new KeyValuePair<string, string>("client_secret", _clientSecret));

    HttpContent content = new FormUrlEncodedContent(postData);
    content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

    var responseResult = client.PostAsync(_tokenUrl, content).Result;

    return responseResult.Content.ReadAsStringAsync().Result;
}
user2281858
  • 1,957
  • 10
  • 41
  • 82
  • Does this answer your question? [Setting Authorization Header of HttpClient](https://stackoverflow.com/questions/14627399/setting-authorization-header-of-httpclient) – YK1 Nov 02 '21 at 22:46
  • I want to check the validity of a token. – user2281858 Nov 02 '21 at 22:50
  • https://stackoverflow.com/questions/50204844/how-to-validate-a-jwt-token – YK1 Nov 02 '21 at 22:53
  • that does not answer the OP – user2281858 Nov 02 '21 at 23:01
  • ok, then you have to clarify what is the question exactly? Issued token is JWT or not? What have you tried? – YK1 Nov 02 '21 at 23:03
  • once the token is issued, I want to ensure that the same token is re-used for API calls with same client. I do not want to generate the token everytime. – user2281858 Nov 02 '21 at 23:05
  • 1
    Create another `HttpClient` instance to make API calls and set `httpClient.DefaultRequestHeaders.Authorization` as shown in my first comment. All subsequent API calls made through that httpclient will contain the token. – YK1 Nov 02 '21 at 23:09
  • can you please post that in the answer. – user2281858 Nov 02 '21 at 23:10
  • Your question's title and the body ask different things. One is to check if you have a valid token, the other asks how to reuse the token. Which are you intending to ask? – mason Nov 02 '21 at 23:27
  • @mason sorry about that, I want to reuse the token. – user2281858 Nov 02 '21 at 23:42
  • 1
    We'd need more details in order to guide you to a proper solution. What's the lifetime of the token? How long is it good for? – mason Nov 02 '21 at 23:43
  • it is 3600 seconds – user2281858 Nov 02 '21 at 23:44
  • @mason my point is, if i already generated the token, every subsequent call for the same client should re-use the token instead of generating it again. looks like the code above might just re-generate it again. – user2281858 Nov 02 '21 at 23:47
  • 1
    Every time the above code is called, it would obtain a new token. What you choose to do with the token once obtained is what determines whether it gets reused or not. You haven't shown that. So 3600 seconds - that's 1 hour. So what I'd do is store the token and the DateTime that the token is expected to expire. On each call, I would check that stored DateTime. If it's 2ish minutes before expiration time, then I'd go ahead and obtain a new token. You'd need to store the token and expiration time in some singleton class most likely. Do you have a Dependency Injection system? – mason Nov 02 '21 at 23:59
  • Let me add the remaining code, I am new to .net but yea dependency injection system exists. – user2281858 Nov 03 '21 at 00:07
  • @mason if you can elaborate on the singletopn class/dependency injection on a answer, it will be helpful. I have update the code. – user2281858 Nov 03 '21 at 00:13

1 Answers1

2

The token is valid for 1 hour. So you just need to store it for 1 hour before obtaining a new token. Ideally you'd get a token a few minutes before the time limit is up, to decrease the chance that you'll use an invalid token.

So, let's create a class responsible for obtaining and caching a token. This class needs to be registered with your Dependency Injection container as a singleton, so that there's only a single instance. Otherwise you'll needlessly get new tokens all the time. I don't know what Dependency Injection container you're using, so you'll have to research how to register our TokenClient class as a singleton with it.

We'll capture the time we obtained the token in a DateTime field, and the token itself in a field as well.

public class TokenClient
{
    readonly IHttpClientFactory _httpClientFactory;

    string _cachedToken;

    DateTime _tokenRetrievalTime;
    

    public TokenClient(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<string> GetToken()
    {
        if (!string.IsNullOrEmpty(_cachedToken) && _tokenRetrievalTime.AddMinutes(55) > DateTime.Now)
        {
            _cachedToken = await GetTokenFromServer();
            _tokenRetrievalTime = DateTime.Now;
            return _cachedToken;
        }
        else
        {
            return _cachedToken;
        }
    }

    async Task<string> GetTokenFromServer()
    {
        var postData = new List<KeyValuePair<string, string>>();
 
        postData.Add(new KeyValuePair<string, string>("grant_type", "password"));
        postData.Add(new KeyValuePair<string, string>("client_id", _clientId));
        postData.Add(new KeyValuePair<string, string>("client_secret", _clientSecret));

        HttpContent content = new FormUrlEncodedContent(postData);
        content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
        var client = _httpClientFactory.CreateClient();
        var responseResult = await client.PostAsync(_tokenUrl, content);

        string token = await responseResult.Content.ReadAsStringAsync();
        return token;
    }
}

There's a couple of things I'm doing differently here besides just caching the token. I'm using IHttpClientFactory to get an instance of an HttpClient, to avoid the issues detailed in You're Using HttpClient Wrong And It's Destablizing Your Software and You're probably still using HttpClient Wrong And It's Destabilizing Your Software. An even better solution would be to use Flurl, which takes care of those details for you.

The other thing I've changed compared to your code is that I'm properly awaiting the calls that return tasks. That keeps the code from deadlocking and helps keep it running efficiently. For more info on that, see Async Await Best Practices and 8 Await Async Mistakes You Should Avoid.

mason
  • 31,774
  • 10
  • 77
  • 121