Skip to main content
This guide covers security best practices for integrating Grantex into production applications. Following these recommendations helps protect your users, agents, and data.

Token Storage

Grant tokens are RS256 JWTs. Where you store them depends on your threat model:
StrategyWhen to useNotes
In-memoryShort-lived agent tasks (minutes)Token is lost on process restart — safest default
Encrypted session storeMulti-step workflows across requestsUse AES-256-GCM; never store tokens in plaintext
Encrypted cache (Redis)High-throughput services sharing tokensSet TTL to match token expiry; use TLS in transit
Never store grant tokens in browser localStorage, cookies, or client-side storage. Grant tokens are meant for server-side agent code only.

Refresh Token Storage

Refresh tokens have a 30-day TTL and are single-use. Store them with the same care as API keys:
  • Encrypt at rest (e.g., envelope encryption with a KMS)
  • Restrict access to the service that performs token refresh
  • Never log refresh tokens — redact them in your logging pipeline

Scope Design

Design scopes using the resource:action pattern with least privilege:
# Good — narrow, specific
calendar:read
payments:initiate:max_500
files:read:folder_abc

# Bad — overly broad
calendar
full_access
admin
Principles:
  • Request only what you need — agents should declare the minimum scopes required for their task
  • Prefer read over write — if the agent only needs to read data, don’t request write access
  • Use parameterized scopespayments:initiate:max_500 is safer than payments:initiate
  • Review scope requests in the consent UI — users see exactly what the agent is requesting

Revocation Handling

Tokens can be revoked at any time by the user or developer. Your agent should handle revocation gracefully:
import { Grantex, GrantexAuthError } from '@grantex/sdk';

async function callWithGrant(grantToken: string, fn: () => Promise<void>) {
  try {
    await fn();
  } catch (err) {
    if (err instanceof GrantexAuthError && err.statusCode === 401) {
      // Token was revoked — stop the agent gracefully
      console.log('Grant token revoked, halting agent');
      return;
    }
    throw err;
  }
}
Best practices:
  • Listen for the grant.revoked webhook event to react in real time
  • On 401, do not retry — the token has been permanently invalidated
  • Clean up any cached tokens after revocation

Delegation Security

When delegating grants to sub-agents (SPEC §9):
  • Minimize delegation depth — each layer adds risk. Keep delegationDepth as low as possible
  • Always narrow scopes — the sub-agent should have fewer scopes than the parent
  • Verify the chain — use verifyGrantToken() to inspect parentAgentDid and delegationDepth claims
  • Set short expiry — delegated grants should expire before the parent grant
// Good: narrowing scopes on delegation
const delegated = await grantex.grants.delegate({
  parentGrantToken: parentToken,
  subAgentId: subAgent.id,
  scopes: ['calendar:read'],  // subset of parent's ['calendar:read', 'calendar:write']
  expiresIn: '1h',            // shorter than parent's 24h
});

PKCE

Always use PKCE (Proof Key for Code Exchange) with S256 in production:
import { Grantex, generatePkce } from '@grantex/sdk';

const grantex = new Grantex({ apiKey: process.env.GRANTEX_API_KEY });
const pkce = generatePkce();

const auth = await grantex.authorize({
  agentId: agent.id,
  userId: 'user_123',
  scopes: ['files:read'],
  codeChallenge: pkce.codeChallenge,
  codeChallengeMethod: 'S256',
});

// Later, when exchanging the code:
const token = await grantex.tokens.exchange({
  code: callbackCode,
  agentId: agent.id,
  codeVerifier: pkce.codeVerifier,
});
PKCE prevents authorization code interception attacks. Without it, an attacker who intercepts the code can exchange it for a grant token.

Audit Trail Best Practices

The audit log is your forensic record. Use it proactively:
  • Log every sensitive action — file access, payment initiation, data deletion
  • Include metadata — resource IDs, amounts, user-facing descriptions
  • Use appropriate status valuessuccess, failure, or blocked
  • Query audit entries regularly — detect anomalies before they become incidents
  • Enable the anomaly detection worker — it flags rate spikes, high failure rates, and off-hours activity automatically
await grantex.audit.log({
  agentId: agent.id,
  grantId: grant.id,
  action: 'payments:initiate',
  metadata: { amount: 250, currency: 'USD', recipient: 'vendor_xyz' },
  status: 'success',
});

Key Rotation

  • Rotate your Grantex API key periodically using POST /v1/me/rotate-key
  • After rotation, update all services that use the old key
  • The old key is immediately invalidated — plan for a brief deployment window

Summary Checklist

Store tokens in memory or encrypted server-side stores
Use resource:action scope format with least privilege
Handle 401 gracefully — never retry revoked tokens
Minimize delegation depth and always narrow scopes
Always use PKCE S256 in production
Log all sensitive agent actions to the audit trail
Enable webhooks for grant.revoked events
Rotate API keys periodically