Integrate Agentic SSO in under 5 minutes. Free.
1. Register your app
curl -X POST https://shadow.geostack.xyz/api/v1/sso-dash/apps \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{
"app_name": "My SaaS",
"domain": "mysaas.com",
"redirect_uri": "https://mysaas.com/callback"
}'
# Returns: app_id + app_secret (save both!)
# Then generate API keys from the dashboard
# You'll get: geo_pk_live_xxx + geo_sk_live_xxx
2. Redirect users to the consent screen
https://geostack.xyz/sso?app_id=YOUR_APP_ID&redirect_uri=https://mysaas.com/callback&scopes=read:data,write:data&state=RANDOM_STATE
3. Validate a visa token
curl -X POST https://shadow.geostack.xyz/api/v1/sso/validate \
-H "Authorization: Bearer geo_sk_live_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"visa_token": "VISA_TOKEN_FROM_USER"}'
4. Set up a webhook (optional)
curl -X POST https://shadow.geostack.xyz/api/v1/sso-dash/webhooks \
-H "Authorization: Bearer geo_sk_live_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"app_id": "YOUR_APP_ID",
"url": "https://mysaas.com/webhooks/geostack",
"events": ["visa.issued", "visa.revoked", "visa.validated"]
}'
1. Redirect user to consent screen
// In your app, redirect the user:
const consentUrl = new URL('https://geostack.xyz/sso');
consentUrl.searchParams.set('app_id', 'YOUR_APP_ID');
consentUrl.searchParams.set('redirect_uri', 'https://mysaas.com/callback');
consentUrl.searchParams.set('scopes', 'read:data,write:data');
consentUrl.searchParams.set('state', crypto.randomUUID());
res.redirect(consentUrl.toString());
2. Handle callback & validate visa
// In your callback route (e.g. /callback)
const visaToken = req.query.visa_token;
const state = req.query.state; // verify this matches!
const res = await fetch('https://shadow.geostack.xyz/api/v1/sso/validate', {
method: 'POST',
headers: {
'Authorization': 'Bearer geo_sk_live_YOUR_SECRET_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ visa_token: visaToken }),
});
const { data } = await res.json();
if (data.valid) {
console.log('Agent authorized:', data.scopes);
// Store visa_token for future agent requests
} else {
console.log('Denied:', data.error);
}
3. Verify webhook signatures
import crypto from 'node:crypto';
app.post('/webhooks/geostack', (req, res) => {
const signature = req.headers['x-geostack-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', 'YOUR_WEBHOOK_SECRET')
.update(JSON.stringify(req.body))
.digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
const event = req.headers['x-geostack-event'];
console.log(`Event: ${event}`, req.body.data);
res.status(200).send('OK');
});
1. Redirect user to consent screen
from urllib.parse import urlencode
import uuid
params = urlencode({
'app_id': 'YOUR_APP_ID',
'redirect_uri': 'https://mysaas.com/callback',
'scopes': 'read:data,write:data',
'state': str(uuid.uuid4()),
})
consent_url = f'https://geostack.xyz/sso?{params}'
# redirect user to consent_url
2. Validate visa token
import requests
def validate_visa(visa_token: str) -> dict:
resp = requests.post(
'https://shadow.geostack.xyz/api/v1/sso/validate',
headers={
'Authorization': 'Bearer geo_sk_live_YOUR_SECRET_KEY',
'Content-Type': 'application/json',
},
json={'visa_token': visa_token},
)
return resp.json()['data']
result = validate_visa(visa_token)
if result['valid']:
print(f"Authorized: {result['scopes']}")
else:
print(f"Denied: {result['error']}")
3. Verify webhook signatures (Flask)
import hmac, hashlib
from flask import Flask, request
WEBHOOK_SECRET = 'YOUR_WEBHOOK_SECRET'
@app.route('/webhooks/geostack', methods=['POST'])
def handle_webhook():
sig = request.headers.get('X-GEOstack-Signature', '')
expected = 'sha256=' + hmac.new(
WEBHOOK_SECRET.encode(),
request.data,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(sig, expected):
return 'Invalid', 401
event = request.headers.get('X-GEOstack-Event')
print(f'Event: {event}', request.json['data'])
return 'OK', 200