Manage Authentication Tokens with a Fixed Time to Live
IBMSoftware Engineer · Online AssessmentLast updated
Design an authentication token manager that handles token generation, renewal, and active token counting based on a fixed time-to-live.
Problem Statement
Design an authentication manager that manages tokens with a fixed time-to-live (TTL). Each token has a unique identifier token_id and an expiration timestamp. When a token is generated or renewed at timestamp current_time, its expiration time is set to current_time + time_to_live.
A token is considered unexpired at current_time if and only if its expiration timestamp is strictly greater than current_time (expiry_time > current_time). At current_time == expiry_time, the token is considered expired.
Implement the AuthenticationManager class:
AuthenticationManager(time_to_live: int): Initializes the manager with a fixedtime_to_live(in seconds).generate(token_id: str, current_time: int) -> None: Generates a new token with the giventoken_idatcurrent_time. If a token withtoken_idalready exists, its expiration time is overwritten withcurrent_time + time_to_live.renew(token_id: str, current_time: int) -> None: Renews the unexpired token with the giventoken_idatcurrent_time. If the token does not exist or is already expired atcurrent_time, the request is ignored.count_unexpired_tokens(current_time: int) -> int: Returns the total number of unexpired tokens atcurrent_time.
Class Interface
class AuthenticationManager:
def __init__(self, time_to_live: int):
pass
def generate(self, token_id: str, current_time: int) -> None:
pass
def renew(self, token_id: str, current_time: int) -> None:
pass
def count_unexpired_tokens(self, current_time: int) -> int:
pass
Example
Input:
manager = AuthenticationManager(5)
manager.renew("token1", 2) # "token1" does not exist yet; ignored.
manager.generate("token1", 2) # "token1" expires at 2 + 5 = 7.
manager.count_unexpired_tokens(6) # Returns 1 ("token1" expires at 7 > 6).
manager.renew("token1", 7) # "token1" expired at 7 (7 is not < 7); ignored.
manager.renew("token2", 8) # "token2" does not exist; ignored.
manager.count_unexpired_tokens(7) # Returns 0 ("token1" expired at 7).
manager.generate("token2", 10) # "token2" expires at 10 + 5 = 15.
manager.renew("token2", 12) # "token2" renewed; now expires at 12 + 5 = 17.
manager.count_unexpired_tokens(15) # Returns 1 ("token2" expires at 17 > 15).
Constraints
1 <= time_to_live <= 10^81 <= current_time <= 10^81 <= len(token_id) <= 20, containing only lowercase English letters and digits.- All calls across
generate,renew, andcount_unexpired_tokensare made with non-decreasing values ofcurrent_time. - At most
5 * 10^4calls will be made in total.
Edge Cases to Consider
- Renewing at the exact millisecond/second of expiration (
current_time == expiry_time): must be treated as expired and ignored. - Overwriting an existing token via
generate: the new expiry must take precedence. - Multiple renewals extending the same token repeatedly.
Hints
Hint 1
A hash map mapping token_id to its expiration timestamp allows O(1) lookup during renew and generate.
Hint 2
Pay close attention to strict inequality: an action at current_time is valid only if current_time < expiry_time.
Hint 3
Because current_time is non-decreasing, you can maintain an ordered structure (such as an OrderedDict or linked list) to evict expired tokens in amortized O(1) time.
Follow-up questions
- How would you handle out-of-order timestamps if events arrive asynchronously from multiple distributed servers?
- How can you optimize count_unexpired_tokens to run in strictly O(1) time without scanning all entries?
- How would you ensure thread safety if multiple worker threads access the AuthenticationManager concurrently?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.