For the past few days I have been regularly working on integrating API’s
and most of us developers do that on regular basis.
The most common
thing in all these API’s is (not bad documentation 😅) getting
an access token to access protected API’s which expires in a certain period of time.
Problem Statement I came across:
Generate an access token after some interval and store it
Should be easily accessible as it will be required again and again
Most suggested way to go about this based on discussion with my team was :
Push it to redis and add some TTL(time to live), if the token’s there use it or generate it
Being a gopher for a long time now. Whenever I think of an async task, I think about go routines
So I came up with an idea of a simple token manager.
The TokenManager has a few key responsibilities:
Token Generation: It takes a TokenGeneratorFunc as an input, which is a function that generates a new token. This function could be making an API call, performing some cryptographic operation, or any other logic required to generate a token.
Token Refresh: The TokenManager periodically calls the TokenGeneratorFunc to refresh the token. The refresh interval is determined by the duration field, which specifies the time between token refreshes.
Token Storage: The TokenManager stores the current token in an atomic variable, ensuring thread-safety when multiple goroutines try to access or update the token concurrently.
Token Distribution: Other parts of the system can retrieve the current token by calling the GetToken method on the TokenManager.
Let’s get into the code now
Constructor - instantiates the token manager
This is a constructor function that creates a new TokenManager instance. It takes three arguments:
name: The name of the resource or service for which the token is being managed.
duration: A pointer to a time.Duration that specifies the interval between token refreshes.
generatorFunc: The TokenGeneratorFunc that will be used to generate new tokens.
The function initializes the TokenManager struct with the provided values, creates a new channel blockFirstTime, and stores an empty string as the initial token value.
GetToken - retrieves token
The GetToken method is used to retrieve the current token from the TokenManager,.
If the first token hasn’t been generated yet, it blocks until the blockFirstTime, channel receives a value (which happens after the first token is generated).
Then, it returns the current token by loading it from the token field using the atomic.Value.Load, method.
RunTokenGenerator - runs an async task to generate tokens
The RunTokenGenerator method is responsible for periodically refreshing the token.
It creates a new time.Ticker based on the duration field, which sends a value on the ticker.C channel at the specified interval.
The method immediately generates the first token by calling tm.UpdateToken(). Then, it enters a loop that blocks until a value is received on the ticker.C channel, at which point it calls tm.UpdateToken() again to refresh the token.
The UpdateToken method is responsible for generating a new token and storing it in the TokenManager. It first prints a log message indicating that it’s generating a new token for the specified name.
Then, it calls the generatorFunc to generate a new token. If an error occurs during token generation, it prints the error and returns without updating the token. If the token is generated successfully, it stores the new token in the token field using the atomic.Value.Store method.
If this is the first token being generated, it sets the hasGeneratedFirstToken flag to true and sends a value on the blockFirstTime channel, allowing the GetToken method to unblock and return the newly generated token.