Machine-to-Machine (M2M) Authentication
This guide explains how to authenticate programmatically with SubImage using Machine-to-Machine (M2M) credentials for API automation, CI/CD pipelines, and external integrations.
Overview
Key Features
- Role-Based Access Control: M2M applications inherit a specific role (member, operator, or admin)
- Secure Token Exchange: Client secrets are only shown once during creation
- Token Rotation: Easily rotate credentials without downtime
Use Cases
- API Automation: Query security findings, inventory, and attack paths programmatically
- CI/CD Integration: Integrate SubImage security checks into your deployment pipeline
- Custom Dashboards: Build custom visualizations using SubImage data
- MCP Server Access: Connect AI assistants to SubImage
Creating an M2M Application
Prerequisites
- Admin role in your SubImage organization
- Access to the SubImage Settings page
Step-by-Step Instructions
Navigate to Settings
- Log in to your SubImage tenant (e.g.,
)<TENANT_URL> - Click on Settings in the navigation menu
- Select the API Keys tab
- Log in to your SubImage tenant (e.g.,
Create New Application
- Click the Create SubImage Application button
- Fill in the application details:
- Name: A descriptive name for your application (e.g., "CI/CD Pipeline", "Slack Bot")
- Role: Choose the appropriate permission level
member: Read-only access to findings, inventory, and reportsoperator: Full access to findings, inventory, and reportsadmin: Full access including settings and user management
- Description: Optional notes about the application's purpose
Save Your Credentials
- Click Create Application
- IMPORTANT: The
client_secretis displayed only once - Copy and securely store both:
client_id- Your application's public identifierclient_secret- Your application's secret key (treat like a password)
Secure Storage
- Store credentials in a secure password manager or secrets vault
- Never commit credentials to version control
- Use environment variables or secrets management tools in production
Using M2M Credentials
Authentication Flow
- Request Access Token - Exchange client credentials for an access token
- Use Access Token - Include the token in API requests as a Bearer token
- Token Expiration - Tokens expire after a set period; request a new token when needed
Example: Python
Here's a complete example of authenticating and using the SubImage MCP server:
import asyncio
import httpx
from fastmcp import Client
class SubImageM2MClient:
"""Client for SubImage M2M authentication."""
def __init__(
self,
client_id: str,
client_secret: str,
tenant_url: str = "<TENANT_URL>",
auth_url: str = "{{ AUTH_URL }}",
):
self.client_id = client_id
self.client_secret = client_secret
self.auth_url = auth_url.rstrip("/")
self.tenant_url = tenant_url
self.access_token = None
async def get_access_token(self) -> str:
"""
Obtain an access token using client credentials.
Returns:
Access token string for API authentication
"""
token_url = f"{self.auth_url}/oauth2/token"
async with httpx.AsyncClient() as client:
response = await client.post(
token_url,
data={
"grant_type": "client_credentials",
"client_id": self.client_id,
"client_secret": self.client_secret,
"scope": "openid profile email",
},
headers={
"Content-Type": "application/x-www-form-urlencoded",
},
)
response.raise_for_status()
token_data = response.json()
self.access_token = token_data["access_token"]
return self.access_token
async def query_api(self, endpoint: str) -> dict:
"""
Query SubImage API with authenticated access.
Args:
endpoint: API endpoint path (e.g., "/api/findings")
Returns:
JSON response from the API
"""
if not self.access_token:
await self.get_access_token()
async with httpx.AsyncClient() as client:
response = await client.get(
f"{self.tenant_url}{endpoint}",
headers={
"Authorization": f"Bearer {self.access_token}",
},
)
response.raise_for_status()
return response.json()
async def main():
# Initialize the client with your credentials
client = SubImageM2MClient(
client_id="client_ABC123", # Replace with your client_id
client_secret="secret_XYZ789", # Replace with your client_secret
tenant_url="<TENANT_URL>", # Your tenant URL
)
# Get an access token
access_token = await client.get_access_token()
print(f"✓ Successfully authenticated")
# Query the API
findings = await client.query_api("/api/findings")
print(f"Found {len(findings)} security findings")
# Connect to MCP server for AI assistant access
async with Client(
f"{client.tenant_url}/mcp",
auth=access_token,
) as mcp_client:
tools = await mcp_client.list_tools()
print(f"Available MCP tools: {len(tools)}")
if __name__ == "__main__":
asyncio.run(main())Example: cURL
For quick testing or shell scripts, you can use cURL:
# 1. Get access token
TOKEN_RESPONSE=$(curl -X POST {{ AUTH_URL }}/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=client_ABC123" \
-d "client_secret=secret_XYZ789")
# Extract access token
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token')
# 2. Query the API
curl <TENANT_URL>/api/findings \
-H "Authorization: Bearer $ACCESS_TOKEN"Example: Using Environment Variables
For better security, use environment variables:
# Set environment variables
export SUBIMAGE_CLIENT_ID="client_ABC123"
export SUBIMAGE_CLIENT_SECRET="secret_XYZ789"
export SUBIMAGE_TENANT_URL="<TENANT_URL>"
# Run your script
python your_script.pyimport os
client = SubImageM2MClient(
client_id=os.environ["SUBIMAGE_CLIENT_ID"],
client_secret=os.environ["SUBIMAGE_CLIENT_SECRET"],
tenant_url=os.environ["SUBIMAGE_TENANT_URL"],
)