0

My WebAPI application gets a token from a service on start-up. This token is then to be used in a shared HTTP Client to prevent port exhaustion.

When this token is about to expire, I want to get a new one and save it in my service for re-use.

In my implementation, a token is retrieved - however it has the same expiry as the original token:

    // Retrieve the token and assign to AuthenticationResult
    private async Task GetAPIToken()
    {
        AuthenticationContext authContext = new AuthenticationContext(Authority);
        var clientCredential = new ClientCredential(clientId, clientSecret);

        // Same token after multiple calls
        AuthenticationResult = await authContext.AcquireTokenAsync(resourceId, clientCredential).ConfigureAwait(false);
    }

How can I save the latest authentication token?

TomSelleck
  • 6,706
  • 22
  • 82
  • 151
  • Out of curiosity, what's the reason for doing `Task.Run(async () => await GetAPIToken()).Wait();` instead of `GetAPIToken().Wait();`? – Theodor Zoulias Jul 20 '21 at 18:17
  • Regarding the main question, are you sure that the retrieved value is not saved to the static `AuthenticationResult` property, and not happening something else like the retrieved value being already expired upon arrival? – Theodor Zoulias Jul 20 '21 at 18:27
  • I'm not sure if `.wait()` alone would run synchronously. Yep I'm sure the latest token which is retrieved is correct.. the static token's expiration is the same as when it's first retrieved, even though a new request has come in and renewed it – TomSelleck Jul 20 '21 at 20:49
  • Based on the code you've shown to us, I can see no reason why the retrieved value would not be saved to the static `AuthenticationResult` property. The only worrisome point is that the `AuthenticationResult` is not always accessed while holding the `lock`, so it's not accessed with volatile semantics. But I doubt that converting it to a `volatile` field (`private static volatile AuthenticationResult AuthenticationResult;`) will fix the issue. Honestly there are lots of things that I don't like in your code, but nevertheless it should work as expected. – Theodor Zoulias Jul 20 '21 at 23:02
  • My preferred way to handle the expiration problem would be to do it completely asynchronously, by using something like the `AsyncExpiringLazy` type found in [this](https://github.com/filipw/async-expiring-lazy) package. – Theodor Zoulias Jul 20 '21 at 23:27
  • Does `AuthenticationResult` eventually update? Or are you saying it never updates? How do you know it doesn't? Have you traced your calls to `GetAPIToken`? And `...` isn't valid code - can you show the rest of the method please? – Enigmativity Jul 21 '21 at 00:56
  • 1
    @TheodorZoulias You were correct, my 'latest' token being retrieved was actually a cached version of the original token. The value was being correctly assigned. – TomSelleck Jul 21 '21 at 11:03
  • OK. So this question is probably now falling in the "Not reproducible or was caused by a typo" category. – Theodor Zoulias Jul 21 '21 at 11:33
  • The hint @Enigmativity to share more of `GetAPIToken` made me realise the AuthContext was holding onto the token. I'll update with an answer – TomSelleck Jul 21 '21 at 11:36

1 Answers1

3

The issue here was that AuthenticationContext authContext = new AuthenticationContext(Authority); will cache a token and retrieve it if it hasn't expired.

Disabling the cache and managing the token lifecycle myself works as a solution:

AuthenticationContext authContext = new AuthenticationContext(Authority, null);

TomSelleck
  • 6,706
  • 22
  • 82
  • 151
  • 1
    Thank you! This was the solution for my problem. Basically we updated a package related to Azure API Authentication and for some reason the tokens began being stored, whereas they were not in the past. I had been around this issue for days but finally saw your answer here and everything is working as expected now. Don't know how updating the package changed the caching configuration but that's a whole other story. – Henrique Moisés Oct 25 '22 at 14:07