Skip to main content

Overview

Credentials are authentication definitions that allow your plugin to securely access external services on behalf of users. The SDK provides a type-safe way to define credential schemas and implement authentication logic.

Defining Credentials

Use the addCredential method to register a credential definition:
plugin.addCredential({
  name: "github_api_key",
  display_name: { 
    en_US: "GitHub API Key" 
  },
  description: { 
    en_US: "Your GitHub personal access token" 
  },
  schema: {
    type: "object",
    properties: {
      api_key: {
        type: "string",
        title: "API Key",
        description: "Your GitHub personal access token"
      }
    },
    required: ["api_key"]
  },
  authenticate: async ({ args }) => {
    const { credential } = args
    
    // Validate the credential by making a test API call
    const response = await fetch("https://api.github.com/user", {
      headers: {
        Authorization: `token ${credential.api_key}`
      }
    })
    
    if (!response.ok) {
      throw new Error("Invalid GitHub API key")
    }
    
    const user = await response.json()
    return { 
      valid: true, 
      username: user.login 
    }
  }
})

Credential Schema

name
string
required
Unique identifier for the credential. Used when referencing the credential in tools.
display_name
object
required
Localized display names shown to users in the UI.
description
object
Localized descriptions explaining what the credential is for.
schema
JSONSchema
required
JSON Schema defining the structure of the credential. Must be a valid JSON Schema object.
authenticate
function
Optional function to validate the credential. Called when users add or update credentials.

Authentication Function

The authenticate function is optional but highly recommended. It validates credentials when users first add them:

Function Signature

authenticate: async ({ args }: {
  args: {
    credential: Record<string, any>
    extra: Record<string, any>
  }
}) => Promise<any>

Parameters

args.credential
object
required
The credential data provided by the user, matching your schema definition.
args.extra
object
Additional metadata or context passed from the Hub Server.

Return Value

You can return any data from the authenticate function. Common patterns include:
// Simple validation
return { valid: true }

// Validation with user info
return { 
  valid: true, 
  username: "john_doe",
  email: "[email protected]" 
}

// Store additional metadata
return {
  valid: true,
  account_id: "12345",
  permissions: ["read", "write"]
}
The authentication function should throw an error if validation fails. The error message will be shown to the user.

Credential Types

The SDK supports various credential types through JSON Schema:

API Key Credentials

{
  name: "api_key",
  display_name: { en_US: "API Key" },
  schema: {
    type: "object",
    properties: {
      key: { 
        type: "string",
        format: "password" // Hides input in UI
      }
    },
    required: ["key"]
  }
}

OAuth-style Credentials

{
  name: "oauth_token",
  display_name: { en_US: "OAuth Token" },
  schema: {
    type: "object",
    properties: {
      access_token: { type: "string" },
      refresh_token: { type: "string" },
      expires_at: { type: "number" }
    },
    required: ["access_token"]
  },
  authenticate: async ({ args }) => {
    // Verify token and optionally refresh
    const { credential } = args
    
    if (credential.expires_at < Date.now()) {
      // Token expired - could refresh here
      throw new Error("Token has expired")
    }
    
    return { valid: true }
  }
}

Username/Password Credentials

{
  name: "basic_auth",
  display_name: { en_US: "Username and Password" },
  schema: {
    type: "object",
    properties: {
      username: { type: "string" },
      password: { 
        type: "string",
        format: "password"
      }
    },
    required: ["username", "password"]
  },
  authenticate: async ({ args }) => {
    const { credential } = args
    // Verify credentials with the service
    const isValid = await verifyCredentials(
      credential.username, 
      credential.password
    )
    
    if (!isValid) {
      throw new Error("Invalid username or password")
    }
    
    return { valid: true, username: credential.username }
  }
}

Complex Credentials

{
  name: "aws_credentials",
  display_name: { en_US: "AWS Credentials" },
  schema: {
    type: "object",
    properties: {
      access_key_id: { type: "string" },
      secret_access_key: { 
        type: "string",
        format: "password" 
      },
      region: { 
        type: "string",
        enum: ["us-east-1", "us-west-2", "eu-west-1"],
        default: "us-east-1"
      },
      session_token: { 
        type: "string",
        description: "Optional session token for temporary credentials"
      }
    },
    required: ["access_key_id", "secret_access_key", "region"]
  }
}

Using Credentials in Tools

When a tool requires credentials, they are automatically passed by the Hub Server:
plugin.addTool({
  name: "list_repositories",
  display_name: { en_US: "List Repositories" },
  credentials: ["github_api_key"], // Reference credential by name
  invoke: async ({ args }) => {
    const { credentials, parameters } = args
    
    // Access the credential data
    const apiKey = credentials.github_api_key.api_key
    
    const response = await fetch("https://api.github.com/user/repos", {
      headers: {
        Authorization: `token ${apiKey}`
      }
    })
    
    return await response.json()
  }
})

Authentication Flow

When a user adds a credential:
  1. User fills in the credential form based on your schema
  2. Hub Server sends credential_auth_spec message to your plugin
  3. Plugin resolves the credential definition from the registry
  4. Plugin calls the authenticate function with the credential data
  5. Plugin sends response back to Hub Server:
    • credential_auth_spec_response on success
    • credential_auth_spec_error on failure
// Internal SDK implementation (for reference)
channel.on("credential_auth_spec", async (message) => {
  const { request_id, credential_name, credential, extra } = message
  
  try {
    const definition = registry.resolve("credential", credential_name)
    
    if (!definition.authenticate) {
      throw new Error("Credential authenticate method is not implemented")
    }
    
    const result = await definition.authenticate({ 
      args: { credential, extra } 
    })
    
    channel.push("credential_auth_spec_response", { 
      request_id, 
      data: result 
    })
  } catch (error) {
    channel.push("credential_auth_spec_error", { 
      request_id, 
      message: error.message 
    })
  }
})

Schema Validation

Credential definitions are validated using the CredentialDefinitionSchema from @choiceopen/atomemo-plugin-schema:
import { CredentialDefinitionSchema } from "@choiceopen/atomemo-plugin-schema/schemas"

const definition = CredentialDefinitionSchema.parse(credential)
registry.register("credential", definition)
If validation fails, a Zod error will be thrown with details about what’s invalid.

Best Practices

While optional, the authenticate function provides crucial validation and improves user experience by catching invalid credentials early.
Use format: "password" for sensitive fields to ensure they’re hidden in the UI.
When authentication fails, throw errors with clear, actionable messages that help users fix the issue.
For OAuth-style credentials, implement token refresh logic or clear error messages about expired tokens.
Always test your authentication function with invalid credentials to ensure proper error handling.

Security Considerations

Never log or expose credential data. Credentials should only be used for authentication and API calls, never stored in logs or error messages.
  • Credentials are stored securely by the Hub Server
  • Your plugin only receives credentials when executing tools that require them
  • Use HTTPS/WSS for all credential transmission
  • Implement proper error handling to avoid leaking credential information

Next Steps

Tools

Learn how to use credentials in your tool implementations

Registry

Understand how credentials are registered and resolved

Build docs developers (and LLMs) love