How AuthGate Works
AuthGate replaces passwords with push-to-approve authentication. Any application in the AXE Technology stack can integrate in under 5 minutes.
Quick Start — 3 Steps
1. Register a webhook at /wh1 pointing to your app's callback URL
2. Add the auth trigger to your app (see code below)
3. Handle the webhook callback when the user approves/denies
Integration Code
Start Auth Session
// Start an AuthGate session from your app const AUTHGATE = 'https://authgate.cloud'; async function startAuth(appId, email) { const res = await fetch(`${AUTHGATE}/auth/start`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ app_id: appId, email }), }); return res.json(); // Returns: { session_id, code, expires_in } }
Webhook Handler (Express/Fastify)
const crypto = require('crypto'); // Verify AuthGate webhook signature function verifySignature(body, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(body) .digest('hex'); return `sha256=${expected}` === signature; } app.post('/api/authgate/callback', (req, res) => { const sig = req.headers['x-authgate-signature']; if (!verifySignature(JSON.stringify(req.body), sig, WEBHOOK_SECRET)) { return res.status(401).json({error: 'Invalid signature'}); } const { event, session_id, email, device_id } = req.body; if (event === 'approved') { // User authenticated! Create session, set cookie, etc. console.log(`Auth approved: ${email} via ${device_id}`); } else if (event === 'denied') { console.log(`Auth denied: ${email}`); } res.json({received: true}); });
Start Auth Session
import requests AUTHGATE = "https://authgate.cloud" def start_auth(app_id, email): r = requests.post(f"{AUTHGATE}/auth/start", json={ "app_id": app_id, "email": email, }) return r.json() # Returns: {"session_id": ..., "code": ..., "expires_in": 300}
Webhook Handler (FastAPI)
import hmac, hashlib from fastapi import FastAPI, Request, HTTPException WEBHOOK_SECRET = "your-webhook-secret" app = FastAPI() def verify_signature(body: bytes, signature: str) -> bool: expected = hmac.new( WEBHOOK_SECRET.encode(), body, hashlib.sha256 ).hexdigest() return f"sha256={expected}" == signature @app.post("/api/authgate/callback") async def authgate_webhook(request: Request): body = await request.body() sig = request.headers.get("x-authgate-signature", "") if not verify_signature(body, sig): raise HTTPException(401, "Invalid signature") data = await request.json() if data["event"] == "approved": # User authenticated! print(f"Approved: {data['email']} via {data['device_id']}") elif data["event"] == "denied": print(f"Denied: {data['email']}") return {"received": True}
Start Auth Session
# Start a new auth session curl -X POST https://authgate.cloud/auth/start \ -H "Content-Type: application/json" \ -d '{"app_id": "my-app", "email": "user@example.com"}' # Check session status curl https://authgate.cloud/auth/check/SESSION_ID # Register a webhook curl -X POST https://authgate.cloud/api/webhooks \ -H "Content-Type: application/json" \ -d '{"app_id": "my-app", "url": "https://my-app.com/callback"}' # Verify a JWT token curl https://authgate.cloud/auth/verify?token=YOUR_JWT_TOKEN
Drop-in HTML Widget
Add this to any page to get an AuthGate login button. The gate animation runs inline.
<!-- AuthGate Login Button --> <div id="authgate-widget"></div> <script> (function() { const AUTHGATE = 'https://authgate.cloud'; const APP_ID = 'your-app-id'; const el = document.getElementById('authgate-widget'); el.innerHTML = '<button id="ag-btn" style="padding:12px 32px;' + 'background:#0a0e14;color:#00e5ff;border:2px solid #00e5ff;' + 'border-radius:8px;font-size:16px;font-weight:700;' + 'cursor:pointer;letter-spacing:2px">' + 'LOGIN WITH AUTHGATE</button>' + '<div id="ag-code" style="display:none;text-align:center;' + 'margin-top:12px;font-family:monospace;font-size:28px;' + 'color:#00e5ff;letter-spacing:6px"></div>'; document.getElementById('ag-btn').onclick = async () => { const r = await fetch(`${AUTHGATE}/auth/start`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({app_id: APP_ID, email: 'user'}), }); const d = await r.json(); document.getElementById('ag-code').style.display = 'block'; document.getElementById('ag-code').textContent = d.code; // Poll for approval const poll = setInterval(async () => { const c = await (await fetch( `${AUTHGATE}/auth/check/${d.session_id}` )).json(); if (c.status === 'approved') { clearInterval(poll); document.getElementById('ag-code').textContent = 'APPROVED'; document.getElementById('ag-code').style.color = '#00e676'; // c.token contains the JWT — send to your backend } else if (c.status === 'denied' || c.status === 'expired') { clearInterval(poll); document.getElementById('ag-code').textContent = c.status.toUpperCase(); document.getElementById('ag-code').style.color = '#ff5252'; } }, 2000); }; })(); </script>
Webhook Payload Reference
When an auth event occurs, AuthGate POSTs to your registered webhook URL with these headers and body:
Headers
Content-Type: application/json User-Agent: AuthGate/1.0 X-AuthGate-Event: approved X-AuthGate-Delivery: wh_id-1711036800 X-AuthGate-Signature: sha256=abc123...
Body (approved)
{
"event": "approved",
"session_id": "a1b2c3d4",
"timestamp": "2026-03-21T12:00:00Z",
"app_id": "ops-centre",
"email": "james@axe.onl",
"ip": "192.168.1.122",
"device_id": "ag-abc123def456",
"device_name": "James iPhone",
"new_device": false
}
Body (denied)
{
"event": "denied",
"session_id": "a1b2c3d4",
"timestamp": "2026-03-21T12:00:00Z",
"app_id": "ops-centre",
"email": "james@axe.onl",
"ip": "192.168.1.122",
"device_id": "ag-abc123def456"
}
Signature Verification
Every webhook includes an X-AuthGate-Signature header with an HMAC-SHA256 digest of the raw request body, signed with your webhook secret. Always verify this before trusting the payload.
Live Webhook Tester
Test the full auth flow right here. Start a session, then approve it from /app on your phone.
AXE Technology Integration Points
Register webhooks for each AXE app that needs authentication: