@agently-org/sdk
v1.2.0
Published
A fully-typed TypeScript SDK for SaaS applications to securely encrypt and send authentication tokens to Agently
Maintainers
Readme
Agently SDK
A fully-typed TypeScript SDK for encrypting authentication tokens and securely sending them to Agently. Uses JWS (JSON Web Signature) and JWE (JSON Web Encryption) for maximum security.
Features
✨ Fully Typed - Complete TypeScript support with comprehensive type definitions
🔐 Secure - Industry-standard JWS/JWE for signing and encryption
🎯 Simple API - Just 3 lines of code to authenticate users
🚀 One-Step Authentication - Built-in encryption and HTTP sending
🛡️ Token Validation - Built-in token validation and expiration checking
📦 Zero Config - Sensible defaults with full customization options
Installation
npm install @agently-org/sdkQuick Start
import { AgentlyAuth } from "@agently-org/sdk";
const auth = new AgentlyAuth({
signingPrivateKey: process.env.SIGNING_PRIVATE_KEY!,
signingPublicKey: process.env.SIGNING_PUBLIC_KEY!,
agentlyPublicKey: process.env.AGENTLY_PUBLIC_KEY!,
agentId: process.env.AGENTLY_AGENT_ID!,
});
const tokens = [
{
name: "Authorization",
value: "your-token-value",
exp: Math.floor(Date.now() / 1000) + 3600,
location: "header",
format: "Bearer {token}",
},
];
// Authenticate user with one call (link code auto-generated)
await auth.authenticateUser({
context: {
identifier: "user123",
email: "[email protected]",
username: "johndoe",
},
tokens,
});That's it! The SDK encrypts the tokens and sends them securely to Agently. 🎉
How It Works
The SDK performs two security operations before sending to Agently:
- JWS (Signing): Signs the payload with your SaaS private key
- JWE (Encryption): Encrypts the signed payload with Agently's public key
[Tokens] → Sign with Your Private Key → [JWS] → Encrypt with Agently Public Key → [JWE] → Send to AgentlyOnly Agently can decrypt it (with their private key), and they can verify it came from you (with your public key).
Setup
Set Environment Variables
All keys are provided by Agently - you don't need to generate any keys yourself.
# .env file - All values provided by Agently
SIGNING_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" # Provided by Agently
SIGNING_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" # Provided by Agently
AGENTLY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" # Provided by Agently
AGENTLY_AGENT_ID="your-agent-id" # Provided by Agently3. Use It!
See the Quick Start above.
API Reference
AgentlyAuth
The main class for encrypting and authenticating users with Agently.
Constructor
new AgentlyAuth(config: AuthConfig)AuthConfig:
| Property | Type | Required | Default | Description |
| ---------------------------- | -------- | -------- | ---------------- | ------------------------------------------------- |
| signingPrivateKey | string | ✓ | - | SaaS private key (PKCS#8) - Provided by Agently |
| signingPublicKey | string | ✓ | - | SaaS public key (SPKI) - Provided by Agently |
| agentlyPublicKey | string | ✓ | - | Agently's public key (SPKI) - Provided by Agently |
| agentId | string | ✓ | - | Agent ID - Provided by Agently |
| expirationTime | string | ✗ | '5m' | JWT expiration time |
| signingAlgorithm | string | ✗ | 'RS256' | Signing algorithm |
| encryptionAlgorithm | string | ✗ | 'RSA-OAEP-256' | Encryption algorithm |
| contentEncryptionAlgorithm | string | ✗ | 'A256GCM' | Content encryption |
Methods
authenticateUser(input: Omit<AuthInput, 'linkCode'>): Promise<AuthResult>
Authenticates a user by automatically generating a link code, encrypting and sending their tokens to Agently. This is the recommended method.
const result = await auth.authenticateUser({
context: {
identifier: "user123",
email: "[email protected]",
username: "johndoe",
},
tokens,
// linkCode is automatically generated
});
if (result.success) {
console.log("User authenticated!", result.status);
console.log("Session ID:", result.sessionId);
} else {
console.error("Authentication failed:", result.error);
}authenticateUserWithCode(input: AuthInput): Promise<AuthResult>
Authenticates a user with a provided link code. Use this method when you have a specific link code to use.
const result = await auth.authenticateUserWithCode({
context: {
identifier: "user123",
email: "[email protected]",
username: "johndoe",
},
tokens,
linkCode: "link-code-123", // Must provide linkCode
});
if (result.success) {
console.log("User authenticated!", result.status);
} else {
console.error("Authentication failed:", result.error);
}encryptPayload(input: AuthInput): Promise<EncryptionResult>
Encrypts a payload and returns detailed result with metadata. Use this if you want to send the JWE yourself.
const result = await auth.encryptPayload({
context: {
identifier: "user123",
email: "[email protected]",
username: "johndoe",
},
tokens,
linkCode: "link-code-123",
});
if (result.success) {
console.log("JTI:", result.metadata?.jti);
console.log("JWE:", result.jwe);
// Send JWE yourself if needed
await fetch("https://api.agently.best/client/auth/your-agent-id", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jwe: result.jwe }),
});
}Types
Context
The Context interface provides a flexible way to identify users and include additional metadata:
interface Context {
identifier: string; // unique and mandatory - primary user reference
[x: string]: any; // additional metadata about the user
}Properties:
identifier(required): Unique identifier for the user (userId, email, username, etc.)[x: string](optional): Additional metadata about the user (role, department, etc.)
Examples:
// Simple user identification
const context: Context = {
identifier: "user_12345",
};
// With additional metadata
const context: Context = {
identifier: "user_12345",
email: "[email protected]",
username: "johndoe",
role: "admin",
department: "engineering",
};
// Using email as identifier
const context: Context = {
identifier: "[email protected]",
username: "johndoe",
role: "user",
};AgentlyToken
interface AgentlyToken {
name: string; // Token name - for headers: 'Authorization', 'X-xsrf-token', etc. For cookies: cookie name
value: string; // Token value
exp: number; // Expiration (Unix timestamp in seconds)
location: "header" | "cookie"; // Where to store
format: TokenFormatTemplate; // Format template that must contain {token} placeholder
}Example:
const token: AgentlyToken = {
name: "Authorization", // HTTP header name
value: "eyJhbGc...",
exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
location: "header",
format: "Bearer {token}", // Must contain {token} placeholder
};Format Template Requirements:
The format property must contain the {token} placeholder string. This ensures proper token substitution when the token is used. Common examples:
"Bearer {token}"- For Authorization headers"jwt {token}"- For JWT tokens"{token}"- For raw token values"Token {token}"- For custom token formats
Utility Functions
Token Validation
Validate tokens before sending:
import { validateTokens, filterExpiredTokens } from "@agently-org/sdk";
// Validate all tokens
const validation = validateTokens(tokens);
if (!validation.valid) {
console.error("Invalid tokens:", validation.errors);
}
// Filter expired tokens
const { valid, expired } = filterExpiredTokens(tokens);
console.log(`${valid.length} valid, ${expired.length} expired`);
// Check tokens expiring soon
import { hasTokensExpiringSoon } from "@agently-org/sdk";
const expiringSoon = hasTokensExpiringSoon(tokens, 300); // 5 minutes
if (expiringSoon.expiringSoon) {
console.warn("Some tokens expire soon!");
}Complete Example
import { AgentlyAuth, AgentlyToken, validateTokens } from "@agently-org/sdk";
async function authenticateWithAgently() {
// 1. Initialize auth client
const auth = new AgentlyAuth({
signingPrivateKey: process.env.SIGNING_PRIVATE_KEY!,
signingPublicKey: process.env.SIGNING_PUBLIC_KEY!,
agentlyPublicKey: process.env.AGENTLY_PUBLIC_KEY!,
agentId: process.env.AGENTLY_AGENT_ID!, // Provided by Agently
});
// 2. Prepare tokens
const tokens: AgentlyToken[] = [
{
name: "Authorization",
value: "eyJhbGc...",
exp: Math.floor(Date.now() / 1000) + 3600,
location: "header",
format: "Bearer {token}",
},
{
name: "X-xsrf-token",
value: "xsrf_token_value",
exp: Math.floor(Date.now() / 1000) + 3600,
location: "header",
format: "{token}",
},
{
name: "session_id",
value: "sess_abc123",
exp: Math.floor(Date.now() / 1000) + 86400,
location: "cookie",
format: "jwt {token}",
},
];
// 3. Validate tokens (optional but recommended)
const validation = validateTokens(tokens);
if (!validation.valid) {
throw new Error("Invalid tokens: " + validation.errors.join(", "));
}
// 4. Authenticate user with Agently (link code auto-generated)
const result = await auth.authenticateUser({
context: {
identifier: "user123",
email: "[email protected]",
username: "johndoe",
role: "admin",
},
tokens,
// linkCode is automatically generated
});
if (result.success) {
console.log("✓ User authenticated successfully!");
console.log("Status:", result.status);
console.log("Session ID:", result.sessionId);
} else {
console.error("✗ Authentication failed:", result.error);
}
}Error Handling
import {
EncryptionError,
ConfigurationError,
TokenExpiredError,
} from "@agently-org/sdk";
try {
await auth.authenticateUser({
context: {
identifier: "user123",
email: "[email protected]",
username: "johndoe",
},
tokens,
// linkCode is automatically generated
});
} catch (error) {
if (error instanceof ConfigurationError) {
console.error("Config error:", error.message);
} else if (error instanceof EncryptionError) {
console.error("Encryption failed:", error.message);
} else if (error instanceof TokenExpiredError) {
console.error("Token expired:", error.details);
}
}TypeScript Support
Full TypeScript support with strict mode enabled:
const auth = new AgentlyAuth(config);
// ^? AgentlyAuth
const result = await auth.authenticateUser({
context: {
identifier: "user123",
email: "[email protected]",
username: "johndoe",
},
tokens,
// linkCode is automatically generated
});
// ^? { success: boolean; status?: number; sessionId?: string; ... }Your IDE will provide complete autocomplete and type checking!
Security Best Practices
- Never commit private keys to version control
- Use environment variables for all keys
- Rotate keys regularly in production
- Validate tokens before sending
- Check expiration to ensure tokens are valid
- Use HTTPS always (handled automatically by the SDK)
- Set appropriate expiration times for JWTs (default: 5 minutes)
License
MIT
Made with ❤️ for secure authentication
