Katkat

Manage Authentication Tokens with a Fixed Time to Live

IBMSoftware Engineer · Online Assessment

Last 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 fixed time_to_live (in seconds).
  • generate(token_id: str, current_time: int) -> None: Generates a new token with the given token_id at current_time. If a token with token_id already exists, its expiration time is overwritten with current_time + time_to_live.
  • renew(token_id: str, current_time: int) -> None: Renews the unexpired token with the given token_id at current_time. If the token does not exist or is already expired at current_time, the request is ignored.
  • count_unexpired_tokens(current_time: int) -> int: Returns the total number of unexpired tokens at current_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^8
  • 1 <= current_time <= 10^8
  • 1 <= len(token_id) <= 20, containing only lowercase English letters and digits.
  • All calls across generate, renew, and count_unexpired_tokens are made with non-decreasing values of current_time.
  • At most 5 * 10^4 calls 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?