Class ApprovalToken

java.lang.Object
com.tnsai.security.approval.ApprovalToken

public final class ApprovalToken extends Object
Token for human-in-the-loop approval of sensitive actions.

Approval tokens are used to require human confirmation before executing high-risk actions. Tokens are:

  • Bound to a specific agent and action
  • Time-limited with configurable expiry
  • Optionally single-use (consumed on validation)

Token Lifecycle

 1. Action requires approval (@ApprovalRequired)
    ↓
 2. Token created with agentId, actionName, expiry
    ↓
 3. Token sent to approval system (UI, CLI, API)
    ↓
 4. Human approves → token status = APPROVED
    ↓
 5. Action executes with approved token
    ↓
 6. Token consumed (if single-use)
 

Usage Example


 // Create token for action
 ApprovalToken token = ApprovalToken.builder()
     .agentId("agent-1")
     .actionName("delete-user")
     .parameters(Map.of("userId", "123"))
     .timeToLive(Duration.ofMinutes(5))
     .singleUse(true)
     .build();

 // Store and wait for approval
 store.save(token);

 // Check status
 if (token.isApproved()) {
     executeAction();
 }
 
  • Method Details

    • builder

      public static ApprovalToken.Builder builder()
      Creates a token builder.
      Returns:
      new builder
    • create

      public static ApprovalToken create(String agentId, String actionName, Duration ttl)
      Creates a simple token.
      Parameters:
      agentId - agent ID
      actionName - action name
      ttl - time to live
      Returns:
      new token
    • getTokenId

      public String getTokenId()
    • getAgentId

      public String getAgentId()
    • getActionName

      public String getActionName()
    • getParameters

      public Map<String,Object> getParameters()
    • getCreatedAt

      public Instant getCreatedAt()
    • getExpiresAt

      public Instant getExpiresAt()
    • isSingleUse

      public boolean isSingleUse()
    • getRequestedBy

      public String getRequestedBy()
    • getReason

      public String getReason()
    • getStatus

      public ApprovalToken.Status getStatus()
    • getApprovedBy

      public String getApprovedBy()
    • getApprovedAt

      public Instant getApprovedAt()
    • getRejectionReason

      public String getRejectionReason()
    • getRejectedBy

      public String getRejectedBy()
      Returns who rejected the token, or null if it was never rejected. Kept strictly separate from getApprovedBy() so the audit trail never names the rejecter as the approver (TAN-5895).
    • isApproved

      public boolean isApproved()
      Checks if token is approved.
      Returns:
      true if approved
    • isPending

      public boolean isPending()
      Checks if token is pending.
      Returns:
      true if pending
    • isRejected

      public boolean isRejected()
      Checks if token is rejected.
      Returns:
      true if rejected
    • isExpired

      public boolean isExpired()
      Checks if token is expired.
      Returns:
      true if expired
    • isConsumed

      public boolean isConsumed()
      Checks if token is consumed.
      Returns:
      true if consumed
    • isValid

      public boolean isValid()
      Checks if token can be used for approval.
      Returns:
      true if usable
    • getRemainingTime

      public Duration getRemainingTime()
      Gets remaining time until expiry.
      Returns:
      remaining duration
    • approve

      public boolean approve(String approver)
      Marks token as approved.

      Atomic with reject, consume, and expiry. Only a non-expired ApprovalToken.Status.PENDING or ApprovalToken.Status.APPROVED token can be approved. A consumed, rejected, or expired token cannot be resurrected.

      Parameters:
      approver - who approved
      Returns:
      true if successfully approved
    • reject

      public boolean reject(String rejector, String reason)
      Marks token as rejected.

      Atomic with approve, consume, and expiry. Only a non-expired ApprovalToken.Status.PENDING token can be rejected.

      Parameters:
      rejector - who rejected
      reason - rejection reason
      Returns:
      true if successfully rejected
    • consume

      public boolean consume()
      Authorizes use of an approved token.

      Atomic with approve, reject, and expiry. An expired token is rejected inside this same transition and marked ApprovalToken.Status.EXPIRED. Single-use tokens move to ApprovalToken.Status.CONSUMED; multi-use tokens stay ApprovalToken.Status.APPROVED so they can authorize again.

      Returns:
      true if the token authorized this call
    • matches

      public boolean matches(String agentId, String actionName)
      Checks if token matches the expected action.
      Parameters:
      agentId - expected agent ID
      actionName - expected action name
      Returns:
      true if matches
    • toString

      public String toString()
      Overrides:
      toString in class Object