
Security News
GPT-6 Astra Attempts Supply Chain Attacks Against Open Source Maintainers in Testing
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.
@grantex/sdk
Advanced tools
TypeScript SDK for the Grantex delegated authorization protocol — OAuth 2.0 for AI agents.
Homepage | Docs | API Reference | Sign Up Free | GitHub
npm install @grantex/sdk
import { Grantex, verifyGrantToken } from '@grantex/sdk';
const grantex = new Grantex({ apiKey: 'YOUR_API_KEY' });
// 1. Register an agent
const agent = await grantex.agents.register({
name: 'Email Assistant',
description: 'Reads and sends email on behalf of users',
scopes: ['email:read', 'email:send'],
});
// 2. Request authorization
const { consentUrl } = await grantex.authorize({
agentId: agent.id,
userId: 'usr_01J...',
scopes: ['email:read', 'email:send'],
});
// Redirect the user to consentUrl — they approve in plain language
// 3. Exchange authorization code for a grant token
// (your redirect callback receives the `code` after user approves)
const token = await grantex.tokens.exchange({ code, agentId: agent.id });
console.log(token.grantToken); // RS256-signed JWT
console.log(token.scopes); // ['email:read', 'email:send']
console.log(token.grantId); // 'grnt_01J...'
// 4. Verify the grant token offline (no network call)
const grant = await verifyGrantToken(token.grantToken, {
jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
});
console.log(grant.principalId); // 'usr_01J...'
// 5. Revoke when done
await grantex.tokens.revoke(grant.tokenId);
const grantex = new Grantex({
apiKey: 'gx_....', // or set GRANTEX_API_KEY env var
baseUrl: 'https://api.grantex.dev', // default
timeout: 30000, // request timeout in ms (default: 30s)
});
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | process.env.GRANTEX_API_KEY | API key for authentication |
baseUrl | string | https://api.grantex.dev | Base URL of the Grantex API |
timeout | number | 30000 | Request timeout in milliseconds |
The SDK includes built-in PKCE (Proof Key for Code Exchange) support using the S256 method for secure authorization flows:
import { Grantex, generatePkce } from '@grantex/sdk';
const grantex = new Grantex({ apiKey: 'YOUR_API_KEY' });
// 1. Generate a PKCE challenge
const pkce = generatePkce();
// pkce.codeVerifier — random 43-char string (keep secret)
// pkce.codeChallenge — SHA-256 hash of verifier (send to server)
// pkce.codeChallengeMethod — 'S256'
// 2. Pass the challenge when requesting authorization
const { consentUrl } = await grantex.authorize({
agentId: 'ag_01J...',
userId: 'usr_01J...',
scopes: ['files:read'],
codeChallenge: pkce.codeChallenge,
codeChallengeMethod: pkce.codeChallengeMethod,
});
// 3. Exchange the code with the verifier
const token = await grantex.tokens.exchange({
code: 'auth_code_from_redirect',
agentId: 'ag_01J...',
codeVerifier: pkce.codeVerifier,
});
grantex.authorize(params)Initiate the delegated authorization flow. Returns a consent URL to redirect the user to.
const request = await grantex.authorize({
agentId: 'ag_01J...',
userId: 'usr_01J...',
scopes: ['files:read', 'email:send'],
expiresIn: '24h', // optional
redirectUri: 'https://...' // optional
});
console.log(request.consentUrl); // redirect user here
console.log(request.authRequestId); // track the request
console.log(request.expiresAt); // ISO 8601 timestamp
Returns: AuthorizationRequest
| Field | Type | Description |
|---|---|---|
authRequestId | string | Unique ID for this authorization request |
consentUrl | string | URL to redirect the user to for consent |
agentId | string | The agent requesting authorization |
principalId | string | The user being asked for consent |
scopes | string[] | Requested scopes |
expiresAt | string | When the request expires (ISO 8601) |
status | string | 'pending', 'approved', 'denied', or 'expired' |
grantex.agents.register(params)Register a new AI agent.
const agent = await grantex.agents.register({
name: 'Code Review Bot',
description: 'Reviews pull requests and suggests improvements',
scopes: ['repo:read', 'pr:comment'],
});
grantex.agents.get(agentId)const agent = await grantex.agents.get('ag_01J...');
grantex.agents.list()const { agents, total } = await grantex.agents.list();
grantex.agents.update(agentId, params)const agent = await grantex.agents.update('ag_01J...', {
name: 'Updated Name',
scopes: ['repo:read', 'pr:comment', 'pr:approve'],
});
grantex.agents.delete(agentId)await grantex.agents.delete('ag_01J...');
grantex.grants.get(grantId)const grant = await grantex.grants.get('grnt_01J...');
grantex.grants.list(params?)const { grants, total } = await grantex.grants.list({
agentId: 'ag_01J...', // optional filter
principalId: 'usr_01J...', // optional filter
status: 'active', // 'active' | 'revoked' | 'expired'
page: 1,
pageSize: 20,
});
grantex.grants.revoke(grantId)await grantex.grants.revoke('grnt_01J...');
grantex.grants.delegate(params)Create a delegated sub-agent grant (per SPEC Section 9).
const delegation = await grantex.grants.delegate({
parentGrantToken: 'eyJhbG...',
subAgentId: 'ag_02K...',
scopes: ['files:read'], // must be subset of parent scopes
expiresIn: '1h', // optional, cannot exceed parent
});
console.log(delegation.grantToken); // new JWT for the sub-agent
console.log(delegation.grantId);
grantex.grants.verify(token)Verify a grant token via the API (online verification with real-time revocation check).
const verified = await grantex.grants.verify('eyJhbG...');
console.log(verified.principalId);
console.log(verified.scopes);
grantex.tokens.exchange(params)Exchange an authorization code for a grant token. This is the standard way to obtain a grant token after the user approves the consent request.
const token = await grantex.tokens.exchange({
code: 'auth_code_from_redirect', // from your redirect callback
agentId: 'ag_01J...',
});
console.log(token.grantToken); // RS256-signed JWT — pass this to your agent
console.log(token.grantId); // grant record ID
console.log(token.scopes); // granted scopes
console.log(token.expiresAt); // ISO 8601 expiry
console.log(token.refreshToken); // for token refresh
Returns: ExchangeTokenResponse
| Field | Type | Description |
|---|---|---|
grantToken | string | Signed RS256 JWT — the agent's bearer credential |
grantId | string | Grant record ID |
scopes | string[] | Scopes the user approved |
expiresAt | string | Token expiry (ISO 8601) |
refreshToken | string | Refresh token for obtaining new grant tokens |
grantex.tokens.verify(token)Online token verification with revocation status.
const result = await grantex.tokens.verify('eyJhbG...');
if (result.valid) {
console.log(result.scopes); // ['files:read']
console.log(result.principal); // 'usr_01J...'
console.log(result.agent); // 'ag_01J...'
console.log(result.grantId);
console.log(result.expiresAt);
}
grantex.tokens.revoke(tokenId)Revoke a token by its JTI. Blocklisted in Redis immediately; all sub-delegated tokens are also invalidated.
await grantex.tokens.revoke('tok_01J...');
verifyGrantToken(token, options)Verify a grant token offline using the published JWKS. No API call needed — signature is verified locally using RS256.
import { verifyGrantToken } from '@grantex/sdk';
const grant = await verifyGrantToken('eyJhbG...', {
jwksUri: 'https://api.grantex.dev/.well-known/jwks.json',
requiredScopes: ['files:read'], // optional — rejects if missing
audience: 'https://myapp.com', // optional — validates aud claim
});
Returns: VerifiedGrant
| Field | Type | Description |
|---|---|---|
tokenId | string | Unique token ID (JWT jti claim) |
grantId | string | Grant record ID |
principalId | string | User who authorized the grant (sub claim) |
agentDid | string | Agent's DID (agt claim) |
developerId | string | Developer org ID (dev claim) |
scopes | string[] | Granted scopes (scp claim) |
issuedAt | number | Issued-at timestamp (seconds since epoch) |
expiresAt | number | Expiry timestamp (seconds since epoch) |
parentAgentDid | string? | Parent agent DID (delegation only) |
parentGrantId | string? | Parent grant ID (delegation only) |
delegationDepth | number? | Delegation depth (0 = root) |
grantex.audit.log(params)Log an auditable action taken by an agent.
const entry = await grantex.audit.log({
agentId: 'ag_01J...',
grantId: 'grnt_01J...',
action: 'email:send',
metadata: { to: 'user@example.com', subject: 'Hello' },
status: 'success', // 'success' | 'failure' | 'blocked'
});
grantex.audit.list(params?)const { entries, total } = await grantex.audit.list({
agentId: 'ag_01J...',
action: 'email:send',
since: '2026-01-01T00:00:00Z',
until: '2026-02-28T23:59:59Z',
page: 1,
pageSize: 50,
});
grantex.audit.get(entryId)const entry = await grantex.audit.get('aud_01J...');
console.log(entry.hash); // SHA-256 hash for tamper evidence
console.log(entry.prevHash); // previous entry hash (chain integrity)
grantex.webhooks.create(params)const webhook = await grantex.webhooks.create({
url: 'https://myapp.com/webhooks/grantex',
events: ['grant.created', 'grant.revoked', 'token.issued'],
});
console.log(webhook.secret); // HMAC secret for signature verification
grantex.webhooks.list()const { webhooks } = await grantex.webhooks.list();
grantex.webhooks.delete(webhookId)await grantex.webhooks.delete('wh_01J...');
import { verifyWebhookSignature } from '@grantex/sdk';
// In your webhook handler
verifyWebhookSignature(requestBody, signatureHeader, webhookSecret);
Define fine-grained access control rules for agents.
grantex.policies.create(params)const policy = await grantex.policies.create({
name: 'Block after hours',
effect: 'deny',
priority: 10,
scopes: ['email:send'],
timeOfDayStart: '18:00',
timeOfDayEnd: '08:00',
});
grantex.policies.list()const { policies, total } = await grantex.policies.list();
grantex.policies.get(policyId) / update(policyId, params) / delete(policyId)const policy = await grantex.policies.get('pol_01J...');
await grantex.policies.update('pol_01J...', { effect: 'allow' });
await grantex.policies.delete('pol_01J...');
grantex.compliance.getSummary(params?)const summary = await grantex.compliance.getSummary({
since: '2026-01-01T00:00:00Z',
until: '2026-02-28T23:59:59Z',
});
console.log(summary.agents); // { total, active, suspended, revoked }
console.log(summary.grants); // { total, active, revoked, expired }
console.log(summary.auditEntries); // { total, success, failure, blocked }
grantex.compliance.exportGrants(params?)const { grants, total } = await grantex.compliance.exportGrants({
status: 'active',
});
grantex.compliance.exportAudit(params?)const { entries, total } = await grantex.compliance.exportAudit({
since: '2026-01-01T00:00:00Z',
agentId: 'ag_01J...',
});
grantex.compliance.evidencePack(params?)Generate a full SOC 2 / GDPR evidence pack with audit chain integrity verification.
const pack = await grantex.compliance.evidencePack({
framework: 'soc2', // 'soc2' | 'gdpr' | 'all'
since: '2026-01-01T00:00:00Z',
});
console.log(pack.chainIntegrity.valid); // true
console.log(pack.chainIntegrity.checkedEntries); // 1042
console.log(pack.summary);
console.log(pack.grants);
console.log(pack.auditEntries);
console.log(pack.policies);
grantex.anomalies.detect()Run anomaly detection across all agents.
const { anomalies, total } = await grantex.anomalies.detect();
// anomaly types: 'rate_spike' | 'high_failure_rate' | 'new_principal' | 'off_hours_activity'
grantex.anomalies.list(params?)const { anomalies } = await grantex.anomalies.list({
unacknowledged: true, // only open anomalies
});
grantex.anomalies.acknowledge(anomalyId)const anomaly = await grantex.anomalies.acknowledge('anom_01J...');
grantex.billing.getSubscription()const sub = await grantex.billing.getSubscription();
console.log(sub.plan); // 'free' | 'pro' | 'enterprise'
console.log(sub.status); // 'active' | 'past_due' | 'canceled'
console.log(sub.currentPeriodEnd); // ISO 8601 or null
grantex.billing.createCheckout(params)const { checkoutUrl } = await grantex.billing.createCheckout({
plan: 'pro',
successUrl: 'https://myapp.com/billing/success',
cancelUrl: 'https://myapp.com/billing/cancel',
});
// Redirect user to checkoutUrl
grantex.billing.createPortal(params)const { portalUrl } = await grantex.billing.createPortal({
returnUrl: 'https://myapp.com/settings',
});
Sync users from your identity provider.
// Create a SCIM bearer token
const { token, id, label } = await grantex.scim.createToken({
label: 'Okta SCIM integration',
});
// token is returned once — store it securely
const { tokens } = await grantex.scim.listTokens();
await grantex.scim.revokeToken('scimtok_01J...');
// List provisioned users
const { Resources, totalResults } = await grantex.scim.listUsers({
startIndex: 1,
count: 100,
});
// Create a user
const user = await grantex.scim.createUser({
userName: 'alice@example.com',
displayName: 'Alice',
emails: [{ value: 'alice@example.com', primary: true }],
});
// Get / Replace / Patch / Delete
const user = await grantex.scim.getUser('scimusr_01J...');
await grantex.scim.replaceUser('scimusr_01J...', { userName: 'alice@new.com' });
await grantex.scim.updateUser('scimusr_01J...', [
{ op: 'replace', path: 'active', value: false },
]);
await grantex.scim.deleteUser('scimusr_01J...');
grantex.sso.createConfig(params)const config = await grantex.sso.createConfig({
issuerUrl: 'https://accounts.google.com',
clientId: 'xxx.apps.googleusercontent.com',
clientSecret: 'GOCSPX-...',
redirectUri: 'https://myapp.com/auth/callback',
});
grantex.sso.getConfig() / deleteConfig()const config = await grantex.sso.getConfig();
await grantex.sso.deleteConfig();
grantex.sso.getLoginUrl(org)const { authorizeUrl } = await grantex.sso.getLoginUrl('dev_01J...');
// Redirect user to authorizeUrl
grantex.sso.handleCallback(code, state)const { email, name, sub, developerId } = await grantex.sso.handleCallback(code, state);
All errors extend GrantexError:
import {
GrantexError, // base class
GrantexApiError, // API returned an error (has statusCode, body, requestId)
GrantexAuthError, // 401/403 — invalid or missing API key
GrantexTokenError, // token verification failed (invalid signature, expired, etc.)
GrantexNetworkError, // network failure (timeout, DNS, connection refused)
} from '@grantex/sdk';
try {
await grantex.agents.get('ag_invalid');
} catch (err) {
if (err instanceof GrantexAuthError) {
console.error('Auth failed:', err.statusCode); // 401 or 403
console.error('Request ID:', err.requestId);
} else if (err instanceof GrantexApiError) {
console.error('API error:', err.statusCode, err.body);
} else if (err instanceof GrantexNetworkError) {
console.error('Network error:', err.message, err.cause);
}
}
"type": "module" in your package.json, or use dynamic import())Enforce tool-level permissions on any connector — define your own manifests or use the 53 pre-built ones.
import { Grantex, ToolManifest, Permission } from '@grantex/sdk';
const grantex = new Grantex({ apiKey: 'gx_...' });
// Define a manifest for any connector — no dependency on Grantex to add support
grantex.loadManifest(new ToolManifest({
connector: 'my-crm',
tools: { search: Permission.READ, create_deal: Permission.WRITE, delete_account: Permission.DELETE },
}));
const result = await grantex.enforce({ grantToken: token, connector: 'my-crm', tool: 'delete_account' });
// result.allowed = false — "write scope does not permit delete operations"
Features:
enforce() — verify JWT + check tool permission via manifest, <1mswrapTool() — auto-enforce on LangChain toolsenforceMiddleware() — Express/Fastify HTTP middlewareadmin > delete > write > readenforceMode: 'permissive')| Package | Description |
|---|---|
grantex | Python SDK |
@grantex/langchain | LangChain integration |
@grantex/autogen | AutoGen integration |
@grantex/vercel-ai | Vercel AI SDK integration |
grantex-crewai | CrewAI integration |
grantex-openai-agents | OpenAI Agents SDK integration |
grantex-adk | Google ADK integration |
@grantex/mcp | MCP server for Claude Desktop / Cursor / Windsurf |
@grantex/cli | Command-line tool |
FAQs
TypeScript SDK for the Grantex delegated authorization protocol
The npm package @grantex/sdk receives a total of 1,955 weekly downloads. As such, @grantex/sdk popularity was classified as popular.
We found that @grantex/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.

Product
Socket can now send alerts and supply chain attack notifications to Microsoft Teams, with filters that route the right updates to each channel.

Security News
pnpm 12 rewrites the package manager in Rust, cutting install times by up to 90% while preserving pnpm 11 workflows and lockfiles.