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,585
  • 19
  • 76
  • 138
  • 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

2

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,585
  • 19
  • 76
  • 138