Self-hosted OAuth 2.0 authentication for AI agents and machine-to-machine communication.
Quick Start • Features • Dashboard • API • SDKs • Deploy
MachineAuth is a self-hosted OAuth 2.0 server purpose-built for authenticating AI agents and machines. Instead of sharing long-lived API keys, your agents authenticate using OAuth 2.0 Client Credentials and receive short-lived RS256-signed JWT tokens.
Think of it as Auth0, but for bots — lightweight, self-hosted, zero external dependencies.
| Problem | MachineAuth Solution |
|---|---|
| Sharing long-lived API keys | Short-lived JWTs with configurable expiry |
| No credential rotation | One-click rotation, zero downtime |
| No visibility into agent activity | Per-agent usage tracking, audit logs, metrics |
| Complex auth infrastructure | Single binary, JSON file storage for dev, Postgres for prod |
| No webhook notifications | Built-in webhook system with retry & delivery tracking |
| Multi-tenant headaches | Native organization & team scoping with JWT claims |
git clone https://github.com/mandarwagh9/MachineAuth.git
cd MachineAuth
go run ./cmd/serverServer starts on http://localhost:8080. No database needed — uses JSON file storage by default.
# 1. Create an agent
curl -s -X POST http://localhost:8080/api/agents \
-H "Content-Type: application/json" \
-d '{"name": "my-agent", "scopes": ["read", "write"]}' | jq .
# 2. Get a token
curl -s -X POST http://localhost:8080/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" | jq .
# 3. Use the token
curl -s -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
http://localhost:8080/api/agents/me | jq .Live demo: https://auth.writesomething.fun
- OAuth 2.0 Client Credentials — Industry-standard M2M authentication flow
- RS256 JWT Tokens — Asymmetric signing with auto-generated RSA keys
- Token Introspection — Validate tokens via RFC 7662 compliant endpoint
- Token Revocation — Invalidate tokens instantly via RFC 7009
- Refresh Tokens — Renew access without re-authenticating
- JWKS Endpoint — Public key discovery at
/.well-known/jwks.json
- Full CRUD — Create, list, view, update, delete agents
- Credential Rotation — Rotate client secrets with zero downtime
- Scoped Access — Fine-grained scopes per agent
- Usage Tracking — Token count, refresh count, last activity per agent
- Agent Self-Service — Agents manage their own lifecycle via JWT auth
- Activation Control — Deactivate/reactivate agents without deletion
- Organizations — Isolated tenant environments with unique slugs
- Teams — Group agents under teams within organizations
- Org-Scoped Agents — Agents belong to orgs, JWT claims include
org_id/team_id - API Keys — Per-organization API key management
- Event Notifications — Real-time HTTP callbacks for agent/token events
- Delivery Tracking — Full delivery history with status, attempts, errors
- Automatic Retries — Exponential backoff with configurable retry count
- Webhook Testing — Send test payloads to verify endpoint connectivity
- Background Workers — Async delivery processing (configurable worker count)
- Health Checks —
/healthand/health/readyendpoints - Metrics — Token/agent/error statistics at
/metrics - Audit Logging — Track all agent and token operations
- CORS — Configurable cross-origin settings
- Graceful Shutdown — Clean shutdown on SIGINT/SIGTERM
- Zero-DB Mode — JSON file storage for development (no database needed)
MachineAuth ships with a full admin UI built with React 18 + TypeScript + Tailwind CSS.
| Page | Description |
|---|---|
| Dashboard | Real-time metrics, health status, system overview |
| Agents | Browse, search, filter agents; view details, rotate credentials |
| Agent Detail | Credentials, scopes, usage stats, rotation, deactivation |
| Organizations | Multi-tenant org management with teams and agents |
| Token Tools | Generate, introspect, and revoke tokens from the UI |
| Webhooks | Create, manage, test webhooks; view delivery history |
| Metrics | Detailed token issuance, refresh, revocation statistics |
cd web
npm install
npm run devOpen http://localhost:3000 — proxies API calls to the Go backend on port 8081.
Default credentials: admin / admin
⚠️ Change admin credentials viaADMIN_EMAILandADMIN_PASSWORDenv vars before deploying.
cd web
npm run build # Output in web/dist/
npm run start # Serve with built-in static server on port 3000| Method | Endpoint | Description |
|---|---|---|
POST |
/oauth/token |
Issue access + refresh token |
POST |
/oauth/introspect |
Validate and inspect a token |
POST |
/oauth/revoke |
Revoke an access token |
POST |
/oauth/refresh |
Refresh an access token |
GET |
/.well-known/jwks.json |
Public key set (JWKS) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/agents |
List all agents |
POST |
/api/agents |
Create a new agent |
GET |
/api/agents/{id} |
Get agent details |
DELETE |
/api/agents/{id} |
Delete an agent |
POST |
/api/agents/{id}/rotate |
Rotate agent credentials |
POST |
/api/agents/{id}/deactivate |
Deactivate an agent |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/agents/me |
Get own profile |
GET |
/api/agents/me/usage |
Get own usage statistics |
POST |
/api/agents/me/rotate |
Rotate own credentials |
POST |
/api/agents/me/deactivate |
Deactivate own account |
POST |
/api/agents/me/reactivate |
Reactivate own account |
DELETE |
/api/agents/me/delete |
Delete own account |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/webhooks |
List webhooks |
POST |
/api/webhooks |
Create webhook |
GET |
/api/webhooks/{id} |
Get webhook details |
PUT |
/api/webhooks/{id} |
Update webhook |
DELETE |
/api/webhooks/{id} |
Delete webhook |
POST |
/api/webhooks/{id}/test |
Send test delivery |
GET |
/api/webhooks/{id}/deliveries |
Get delivery history |
GET |
/api/webhook-events |
List available event types |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/organizations |
List organizations |
POST |
/api/organizations |
Create organization |
GET |
/api/organizations/{id} |
Get organization |
PUT |
/api/organizations/{id} |
Update organization |
DELETE |
/api/organizations/{id} |
Delete organization |
GET |
/api/organizations/{id}/teams |
List teams |
POST |
/api/organizations/{id}/teams |
Create team |
GET |
/api/organizations/{id}/agents |
List org agents |
POST |
/api/organizations/{id}/agents |
Create org agent |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/organizations/{id}/api-keys |
List API keys |
POST |
/api/organizations/{id}/api-keys |
Create API key |
DELETE |
/api/organizations/{id}/api-keys/{key_id} |
Revoke API key |
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Service info + version |
GET |
/health |
Health check |
GET |
/health/ready |
Readiness check (includes agent count) |
GET |
/metrics |
Token/agent/error metrics |
POST |
/api/verify |
Verify JWT and return agent info |
curl -X POST http://localhost:8080/api/agents \
-H "Content-Type: application/json" \
-d '{"name": "my-agent", "scopes": ["read", "write"]}'{
"agent": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "my-agent",
"client_id": "cid_a1b2c3d4e5f6",
"scopes": ["read", "write"],
"is_active": true,
"created_at": "2026-03-01T12:00:00Z"
},
"client_secret": "cs_xK9mPqR...",
"message": "Save this client_secret - it will not be shown again!"
}
⚠️ Theclient_secretis only returned once. Store it securely.
curl -X POST http://localhost:8080/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=cid_a1b2c3d4e5f6" \
-d "client_secret=cs_xK9mPqR..."{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "rt_8f14e45f..."
}curl -X POST http://localhost:8080/oauth/introspect \
-d "token=eyJhbGciOiJSUzI1NiIs..."{
"active": true,
"client_id": "cid_a1b2c3d4e5f6",
"scope": "read write",
"token_type": "Bearer",
"exp": 1709308800,
"iat": 1709305200
}curl -X POST http://localhost:8080/oauth/refresh \
-d "refresh_token=rt_8f14e45f..." \
-d "client_id=cid_a1b2c3d4e5f6" \
-d "client_secret=cs_xK9mPqR..."curl -X POST http://localhost:8080/oauth/revoke \
-d "token=eyJhbGciOiJSUzI1NiIs..."curl -X POST http://localhost:8080/api/agents/{agent_id}/rotateReturns a new client_secret — the old one is immediately invalidated.
Agents can manage themselves using their JWT token:
# View own profile
curl -H "Authorization: Bearer eyJ..." http://localhost:8080/api/agents/me
# Check usage stats
curl -H "Authorization: Bearer eyJ..." http://localhost:8080/api/agents/me/usage
# Rotate own credentials
curl -X POST -H "Authorization: Bearer eyJ..." http://localhost:8080/api/agents/me/rotate
# Deactivate own account
curl -X POST -H "Authorization: Bearer eyJ..." http://localhost:8080/api/agents/me/deactivate
# Delete own account permanently
curl -X DELETE -H "Authorization: Bearer eyJ..." http://localhost:8080/api/agents/me/delete# Create a webhook
curl -X POST http://localhost:8080/api/webhooks \
-H "Content-Type: application/json" \
-d '{
"name": "Prod Notifications",
"url": "https://example.com/webhooks",
"events": ["agent.created", "agent.deleted", "token.issued"],
"max_retries": 5
}'
# Test it
curl -X POST http://localhost:8080/api/webhooks/{id}/test \
-H "Content-Type: application/json" \
-d '{"event": "webhook.test"}'
# Check delivery history
curl http://localhost:8080/api/webhooks/{id}/deliveries# Create an organization
curl -X POST http://localhost:8080/api/organizations \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "slug": "acme", "owner_email": "admin@acme.com"}'
# Create a team
curl -X POST http://localhost:8080/api/organizations/{org_id}/teams \
-H "Content-Type: application/json" \
-d '{"name": "Engineering", "description": "Engineering team"}'
# Create an API key
curl -X POST http://localhost:8080/api/organizations/{org_id}/api-keys \
-H "Content-Type: application/json" \
-d '{"name": "production-key", "expires_in": 86400}'API keys can be used in place of JWT tokens:
curl -H "Authorization: Bearer sk_1cF4CG1RE..." http://localhost:8080/api/verifyTokens issued by MachineAuth include rich claims for authorization decisions:
{
"iss": "https://auth.yourdomain.com",
"sub": "cid_a1b2c3d4e5f6",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"org_id": "org-uuid",
"team_id": "team-uuid",
"scope": ["read", "write"],
"jti": "unique-token-id",
"exp": 1709308800,
"iat": 1709305200
}Validate tokens using the public key from /.well-known/jwks.json.
Official client libraries for TypeScript and Python.
npm install @machineauth/sdkimport { MachineAuthClient } from '@machineauth/sdk'
const client = new MachineAuthClient({
baseUrl: 'https://auth.yourdomain.com',
clientId: 'cid_a1b2c3d4e5f6',
clientSecret: 'cs_xK9mPqR...',
})
// Get a token
const token = await client.getToken({ scope: 'read write' })
// List agents
const agents = await client.listAgents()
// Self-service
const me = await client.getMe()pip install machineauthfrom machineauth import MachineAuthClient
client = MachineAuthClient(
base_url="https://auth.yourdomain.com",
client_id="cid_a1b2c3d4e5f6",
client_secret="cs_xK9mPqR...",
)
# Get a token
token = client.get_token(scope="read write")
# Async support
from machineauth import AsyncMachineAuthClient
async_client = AsyncMachineAuthClient(...)
token = await async_client.get_token(scope="read write")See sdk/README.md for the full SDK documentation.
All configuration via environment variables (or .env file):
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Server listen port |
ENV |
development |
Environment (development / production) |
DATABASE_URL |
json:machineauth.json |
Database connection string |
JWT_SIGNING_ALGORITHM |
RS256 |
JWT signing algorithm |
JWT_KEY_ID |
key-1 |
JWKS key identifier |
JWT_ACCESS_TOKEN_EXPIRY |
3600 |
Access token TTL in seconds (1 hour) |
ALLOWED_ORIGINS |
http://localhost:3000 |
CORS allowed origins (comma-separated) |
REQUIRE_HTTPS |
false |
Enforce HTTPS redirects |
ADMIN_EMAIL |
admin@example.com |
Admin dashboard email |
ADMIN_PASSWORD |
changeme |
Admin dashboard password |
WEBHOOK_WORKER_COUNT |
3 |
Concurrent webhook delivery workers |
WEBHOOK_MAX_RETRIES |
10 |
Max delivery retry attempts |
WEBHOOK_TIMEOUT_SECS |
10 |
Webhook HTTP request timeout |
# JSON file (default, zero deps, great for dev)
DATABASE_URL=json:machineauth.json
# PostgreSQL (recommended for production)
DATABASE_URL=postgresql://user:pass@localhost:5432/machineauthPORT=8080
ENV=production
DATABASE_URL=postgresql://machineauth:secret@db:5432/machineauth
JWT_ACCESS_TOKEN_EXPIRY=1800
ALLOWED_ORIGINS=https://dashboard.yourdomain.com
ADMIN_PASSWORD=your-secure-password
WEBHOOK_WORKER_COUNT=5git clone https://github.com/mandarwagh9/MachineAuth.git
cd MachineAuth
docker-compose up -dThis starts three services:
| Service | Port | Description |
|---|---|---|
| postgres | 5432 | PostgreSQL 15 database |
| server | 8080 | Go API server |
| web | 80 | React admin dashboard |
# docker/Dockerfile.server — Multi-stage build
FROM golang:1.21-alpine AS builder
# ... builds to /server
FROM alpine:3.19
COPY --from=builder /server .
EXPOSE 8080
CMD ["./server"]docker build -f docker/Dockerfile.server -t machineauth .
docker run -p 8080:8080 -e DATABASE_URL=json:/data/machineauth.json machineauth# Requirements: Go 1.21+
go build -o machineauth ./cmd/server
./machineauth[Unit]
Description=MachineAuth - OAuth 2.0 for AI Agents
After=network.target
[Service]
Type=simple
User=machineauth
WorkingDirectory=/opt/machineauth
ExecStart=/opt/machineauth/machineauth
EnvironmentFile=/opt/machineauth/.env
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetsudo systemctl enable machineauth
sudo systemctl start machineauth┌─────────────────┐ ┌──────────────┐ ┌──────────────┐
│ React Admin UI │────▶│ Go Server │────▶│ PostgreSQL │
│ (Tailwind CSS) │ │ (net/http) │ │ or JSON file │
└─────────────────┘ └──────┬───────┘ └──────────────┘
│
┌──────────┼──────────┐
│ │ │
┌─────▼──┐ ┌────▼───┐ ┌────▼────┐
│ Agents │ │ Tokens │ │Webhooks │
│ Service │ │Service │ │ Worker │
└────────┘ └────────┘ └─────────┘
machineauth/
├── cmd/server/main.go # Server entry point
├── internal/
│ ├── config/config.go # Environment configuration
│ ├── db/db.go # Database layer (Postgres + JSON)
│ ├── handlers/ # HTTP request handlers
│ │ ├── agents.go # Agent CRUD + self-service
│ │ ├── auth.go # OAuth2 token endpoints
│ │ └── webhook.go # Webhook management
│ ├── middleware/ # Logging, CORS
│ ├── models/models.go # All data types and DTOs
│ ├── services/ # Business logic
│ │ ├── agent.go # Agent operations
│ │ ├── audit.go # Audit logging + webhook triggers
│ │ ├── token.go # JWT creation/validation
│ │ ├── webhook.go # Webhook CRUD
│ │ └── webhook_worker.go # Async delivery processing
│ └── utils/crypto.go # Cryptographic helpers
├── web/ # React admin dashboard
│ ├── src/
│ │ ├── pages/ # Dashboard, Agents, Tokens, Webhooks, etc.
│ │ ├── components/ # Layout, Sidebar
│ │ ├── services/ # API client (axios)
│ │ └── types/ # TypeScript interfaces
│ └── vite.config.ts # Vite + proxy config
├── sdk/
│ ├── typescript/ # @machineauth/sdk (npm)
│ └── python/ # machineauth (pip)
├── docker/ # Dockerfiles
├── deploy/ # Deployment scripts
└── docker-compose.yml # Full-stack compose
- Use HTTPS — Always run behind a TLS-terminating reverse proxy in production
- Rotate credentials — Use the rotation API regularly; old secrets are invalidated immediately
- Short token expiry — Default 1 hour; reduce to 15-30 min for sensitive workloads
- Restrict CORS — Set
ALLOWED_ORIGINSto your specific domains - Change admin password — Default is
changeme; setADMIN_PASSWORDbefore deploying - Monitor metrics — Watch
/metricsfor token issuance anomalies - Use Postgres in prod — JSON file storage is for development only
Please email security concerns directly rather than opening public issues. See SECURITY.md.
| Component | Technology |
|---|---|
| Backend | Go 1.21, net/http, golang-jwt/jwt/v5 |
| Frontend | React 18, TypeScript 5.3, Vite 5, Tailwind CSS 3.4 |
| Database | PostgreSQL 15 (prod) / JSON file (dev) |
| Auth | OAuth 2.0 Client Credentials, RS256 JWT |
| Icons | Lucide React |
| Toasts | Sonner |
| HTTP Client | Axios |
Contributions are welcome!
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing) - Make your changes and add tests
- Run
go test -v ./...andcd web && npm run build - Commit (
git commit -m 'feat: add amazing feature') - Push (
git push origin feature/amazing) - Open a Pull Request
See CONTRIBUTING.md for detailed guidelines.
MIT License — see LICENSE for details.
- Live Demo: https://auth.writesomething.fun
- Issues: GitHub Issues
- SDK Docs: sdk/README.md
Built for the AI agent ecosystem 🤖