This guide explains how to set up Machine-to-Machine (M2M) authentication between the bot services (CandidateRepresenter.Bot and JobApplier.Bot) and the JobTracker API using Clerk.
The JobTracker API is now protected with Clerk M2M token authentication. Services like CandidateRepresenter.Bot and JobApplier.Bot must authenticate using M2M tokens to access the API.
┌─────────────────────────────────────┐
│ CandidateRepresenter.Bot (Python) │
│ │
│ 1. Load CLERK_MACHINE_SECRET_KEY │
│ 2. On startup: Create M2M token │
│ 3. Cache token for session (1 hour) │
│ 4. Attach to all API requests │
└────────────┬────────────────────────┘
│ HTTP Request
│ Authorization: Bearer mt_xxx
▼
┌─────────────────────────────────────┐
│ JobTracker API (.NET) │
│ │
│ 1. Extract Bearer token │
│ 2. If starts with "mt_": │
│ - Call Clerk verify endpoint │
│ - Validate scopes/expiration │
│ 3. If standard JWT: │
│ - Use existing JWKS validation │
│ 4. Proceed with request │
└─────────────────────────────────────┘
▲
│ HTTP Request
│ Authorization: Bearer mt_xxx
┌────────────┴────────────────────────┐
│ JobApplier.Bot (Python) │
│ │
│ 1. Load CLERK_MACHINE_SECRET_KEY │
│ 2. On startup: Create M2M token │
│ 3. Cache token for session (1 hour) │
│ 4. Attach to all API requests │
└─────────────────────────────────────┘
-
Navigate to the Clerk Dashboard Machines page
-
Create CandidateRepresenter.Bot machine:
- Click "Add machine"
- Name:
CandidateRepresenter.Bot - Description: "Python bot for candidate question answering"
- Click "Create"
- Important: Copy and save the machine secret key (starts with
ak_) - You'll need this for the Bot's
.envfile
-
Create JobApplier.Bot machine:
- Click "Add machine"
- Name:
JobApplier.Bot - Description: "Python bot for job application automation"
- Click "Create"
- Important: Copy and save the machine secret key (starts with
ak_) - You'll need this for the Bot's
.envfile
-
Create JobTracker.API machine:
- Click "Add machine"
- Name:
JobTracker.API - Description: ".NET Web API for job tracking"
- In the "Scopes" section, select both
CandidateRepresenter.BotandJobApplier.Bot - Click "Create"
- Important: Copy and save the machine secret key (starts with
ak_) - You'll need this for the API's
appsettings.jsonfile
-
Configure bi-directional communication:
-
Find
CandidateRepresenter.Botin the list -
Click "Edit machine"
-
In the "Scopes" section, select
JobTracker.API -
Click "Update"
-
Find
JobApplier.Botin the list -
Click "Edit machine"
-
In the "Scopes" section, select
JobTracker.API -
Click "Update"
-
All machines can now create tokens to authenticate with each other.
- Update environment variables:
cd CandidateRepresenter.Bot
cp .env.example .env- Edit
.envfile:
# Clerk M2M Authentication
CLERK_MACHINE_SECRET_KEY=ak_your_actual_secret_key_here
CLERK_API_BASE_URL=https://api.clerk.com/v1
# JobTracker API Configuration
JOBTRACKER_API_URL=http://localhost:5000- The JobTrackerClient will automatically:
- Create M2M tokens on initialization
- Cache tokens for 1 hour (with 5-minute safety buffer)
- Refresh tokens automatically before expiration
- Retry with fresh token on 401 responses
- Update environment variables:
cd JobApplier.Bot
cp .env.example .env- Edit
.envfile:
# Clerk M2M Authentication
CLERK_MACHINE_SECRET_KEY=ak_your_actual_secret_key_here
CLERK_API_BASE_URL=https://api.clerk.com/v1
# JobTracker API Configuration
JOBTRACKER_API_URL=http://localhost:5138- The JobTrackerClient will automatically:
- Create M2M tokens on initialization
- Cache tokens for 1 hour (with 5-minute safety buffer)
- Refresh tokens automatically before expiration
- Retry with fresh token on 401 responses
The API needs its own machine secret key to authenticate with Clerk when verifying tokens.
- Copy the example configuration file:
cd JobTracker
cp appsettings.Development.json.example appsettings.Development.json- Edit
appsettings.Development.jsonwith your actual secret key:
{
"Clerk": {
"M2M": {
"MachineSecretKey": "ak_your_actual_jobtracker_api_secret_key_here"
}
}
}- Important Security Note:
- Never commit
appsettings.Development.jsonto source control (already in.gitignore) - The example file (
appsettings.Development.json.example) is tracked in git as a template - In production, use environment variables or Azure Key Vault for secrets
- The base
appsettings.jsoncontains no secrets and is safe to commit
- Never commit
How it works:
- When the Bot sends a request with an M2M token (
mt_xxx) - The API uses its own machine secret key to authenticate with Clerk
- Clerk verifies the incoming token and returns validation results
- The API allows or denies the request based on verification
The authentication is automatic when using the JobTrackerClient:
from src.clients.jobtracker_client import JobTrackerClient
# M2M authentication is enabled by default
async with JobTrackerClient() as client:
# Automatically adds M2M token to all requests
application = await client.get_job_application(application_id)
user = await client.get_user(user_id)
# For JobApplier.Bot: Additional operations
await client.update_status(application_id, status=2) # Mark as Submitted
await client.download_resume(resume_id, save_path)To disable M2M authentication (for testing):
async with JobTrackerClient(enable_m2m_auth=False) as client:
# No authentication headers
passThe API automatically handles both user JWT tokens and M2M tokens:
- User JWTs (standard Clerk tokens): Validated via JWKS
- M2M tokens (start with
mt_): Validated via Clerk API - Both authentication methods work simultaneously
Clerk charges for M2M tokens:
- Token creation: $0.001 per token
- Token verification: $0.0001 per verification
With 1-hour token lifetime and session-based caching:
- CandidateRepresenter.Bot: ~$0.024/day (1 token/hour × 24 hours)
- JobApplier.Bot: ~$0.024/day (1 token/hour × 24 hours)
- Total Bot Token Creation: ~$0.048/day
- API verifications: Variable based on request volume
Python side (Both Bots):
INFO: M2M authentication enabled for JobTracker API
INFO: Creating new M2M token (lifetime: 3600s)
INFO: M2M token created successfully (expires at 2025-01-07 12:43:00, will refresh at 12:38:00)
DEBUG: Using cached M2M token (expires in 0:54:23)
DEBUG: Added M2M token to request headers
API side (JobTracker):
DEBUG: Detected M2M token, attempting verification
DEBUG: Verifying M2M token with Clerk API
INFO: M2M token verified successfully. Subject: mch_xxx, Expired: False, Revoked: False
INFO: M2M authentication successful. Machine: mch_xxx, Scopes: mch_yyy
Problem: API returns 401 even with M2M token
Solutions:
- Verify machine secret key is correct in
.env - Check that machines are configured with proper scopes in Clerk Dashboard
- Ensure token hasn't been revoked in Clerk Dashboard
- Check API logs for specific error messages
Problem: Bot fails to refresh expired tokens
Solution: The client automatically refreshes tokens 5 minutes before expiration. If issues persist:
- Check network connectivity to
api.clerk.com - Verify Clerk API is accessible
- Check logs for specific error messages
Problem: Bot makes requests without authentication
Solutions:
- Verify
CLERK_MACHINE_SECRET_KEYis set in.env - Check that
enable_m2m_auth=True(default) - Look for warning logs about missing configuration
-
Never commit machine secret keys
- Always use
.envfiles (already in.gitignore) - Rotate keys periodically in Clerk Dashboard
- Always use
-
Monitor token usage
- Review logs regularly for authentication failures
- Set up alerts for unusual token creation patterns
-
Scope management
- Only grant necessary scopes between machines
- Review and update scopes when adding new services
-
Token lifetime
- Default 1-hour lifetime balances security and cost
- Adjust via
ClerkM2MClient(token_lifetime_seconds=X)
- Start JobTracker API
- Navigate to
http://localhost:5000/swagger - Click "Authorize"
- Enter an M2M token in the format:
mt_xxx - Try API endpoints
Test files verify M2M authentication:
CandidateRepresenter.Bot/tests/test_jobtracker_client.py- Mock Clerk API responses for token creation/verification
To add M2M authentication to other services:
- Create machine in Clerk Dashboard
- Add configuration (similar to CandidateRepresenter.Bot)
- Update HTTP client to include M2M token in Authorization header
- Test thoroughly before deploying
See CandidateRepresenter.Bot/src/clients/clerk_m2m_client.py as reference implementation.
- Clerk M2M Documentation
- Clerk Dashboard
- JobTracker API:
/swaggerendpoint