After a user successfully authenticates via SSO (Single Sign-On), the LLM Proxy performs an authorization check to determine if the user should be granted access. This document explains the two authorization modes and how to configure them.
Best for personal use or small teams where the server administrator controls access.
How it works:
- User completes SSO authentication
- Proxy generates a 6-digit confirmation code
- Code is logged to the server console (WARNING level)
- User enters the code in the web interface
- Upon correct code entry, proxy generates and displays the agent token
Use when:
- Running the proxy locally for personal use
- Small team with trusted users
- No external authorization infrastructure
- Simple access control is sufficient
Best for organizations with existing authorization systems or complex access control requirements.
How it works:
- User completes SSO authentication
- Proxy queries an external authorization API
- API receives user identity and IP address
- API returns true/false to grant/deny access
- If approved, proxy generates and displays the agent token
Use when:
- Need centralized access control
- Integration with existing authorization systems
- Complex authorization rules (roles, groups, IP restrictions)
- Audit and compliance requirements
- Multiple proxy instances with shared authorization
sso:
enabled: true
authorization:
mode: "single_user"
session_lifetime_hours: 24
confirmation_code_expiry_minutes: 10
max_confirmation_attempts: 3
providers:
google:
# ... provider config ...| Option | Type | Default | Description |
|---|---|---|---|
mode |
string | - | Must be "single_user" |
session_lifetime_hours |
integer | 24 | SSO session lifetime in hours |
confirmation_code_expiry_minutes |
integer | 10 | How long confirmation code is valid |
max_confirmation_attempts |
integer | 3 | Maximum attempts before re-authentication required |
1. User authenticates via SSO (Google, Microsoft, etc.)
|
v
2. Proxy generates 6-digit confirmation code
|
v
3. Proxy logs code to server console:
WARNING: SSO Authorization Required
User: user@example.com
Confirmation Code: 123456
|
v
4. User sees web page: "Check server console for confirmation code"
|
v
5. User enters code in web form
|
v
6. If correct: Display agent token
If incorrect: Decrement attempts, show remaining attempts
If exhausted: Require re-authentication
When a user completes SSO authentication, you'll see output like this in the server console:
2025-01-15 10:30:45 WARNING SSO Authorization Required
2025-01-15 10:30:45 WARNING User: alice@example.com
2025-01-15 10:30:45 WARNING Provider: google
2025-01-15 10:30:45 WARNING Confirmation Code: 847293
2025-01-15 10:30:45 WARNING Code expires in 10 minutes
Important: The confirmation code is only displayed in the server console. The user must have access to the console to complete authorization.
Failed confirmation code attempts trigger exponential backoff:
- 1st failure: No delay
- 2nd failure: 2 second delay
- 3rd failure: 4 second delay
- After 3 failures: Must re-authenticate via SSO
- Maximum 3 attempts per authorization session
- Exponential backoff between SSO attempts after failures
- Per-IP rate limiting to prevent distributed attacks
- Confirmation codes expire after 10 minutes (configurable)
- Expired codes require re-authentication
- Prevents replay attacks
Symptom: No confirmation code logged after SSO authentication
Solutions:
- Check log level is set to WARNING or lower (INFO, DEBUG)
- Verify SSO authentication completed successfully
- Check server logs for errors during authorization
- Ensure authorization mode is set to "single_user"
Symptom: "Confirmation code expired" error
Solutions:
- Re-authenticate via SSO to generate a new code
- Increase
confirmation_code_expiry_minutesif needed - Ensure server and client clocks are synchronized
Symptom: "Maximum attempts exceeded" error
Solutions:
- Re-authenticate via SSO to get a new code
- Verify you're entering the code correctly (no spaces, correct digits)
- Check for typos in the code
- Ensure code hasn't expired
Symptom: "Please wait before trying again" message
Solutions:
- Wait for the specified time period
- Exponential backoff increases with each failure
- After waiting, re-authenticate via SSO
sso:
enabled: true
authorization:
mode: "enterprise"
api_url: "https://company.com/api/authorize"
api_timeout_seconds: 5
session_lifetime_hours: 48
providers:
microsoft:
# ... provider config ...| Option | Type | Default | Description |
|---|---|---|---|
mode |
string | - | Must be "enterprise" |
api_url |
string | - | Authorization API endpoint URL |
api_timeout_seconds |
integer | 5 | Request timeout in seconds |
session_lifetime_hours |
integer | 24 | SSO session lifetime in hours |
The proxy validates api_url (and each redirect target when redirects are followed) so it cannot resolve to disallowed private or loopback addresses. See Outbound URL safety.
1. User authenticates via SSO
|
v
2. Proxy sends POST request to authorization API:
{
"user_id": "alice@example.com",
"user_email": "alice@example.com",
"provider": "microsoft",
"client_ip": "192.168.1.100"
}
|
v
3. Authorization API processes request:
- Check user roles/groups
- Verify IP allowlist
- Check time-based restrictions
- Query internal systems
|
v
4. API returns response:
{"authorized": true} or {"authorized": false}
|
v
5. If authorized: Display agent token
If denied: Display "Access Denied" message
If error: Display error and deny access
Method: POST
URL: Configured in api_url
Headers:
Content-Type: application/json
User-Agent: LLM-Proxy/1.0
Body:
{
"user_id": "alice@example.com",
"user_email": "alice@example.com",
"provider": "microsoft",
"client_ip": "192.168.1.100",
"timestamp": "2025-01-15T10:30:45Z"
}Fields:
user_id: User identifier from IdP (usually email)user_email: User email address from IdPprovider: Identity provider name (google, microsoft, github, linkedin, aws)client_ip: Client IP address making the requesttimestamp: ISO 8601 timestamp of the authorization request
Success (Authorized):
{
"authorized": true
}Success (Denied):
{
"authorized": false,
"reason": "User not in allowed group"
}Error:
{
"error": "Internal server error",
"details": "Database connection failed"
}HTTP Status Codes:
200 OK: Authorization decision made (checkauthorizedfield)400 Bad Request: Invalid request format401 Unauthorized: API authentication failed500 Internal Server Error: API error (access denied by default)503 Service Unavailable: API unavailable (access denied by default)
See examples/sso_authorization_api.py for a complete Flask implementation:
from flask import Flask, request, jsonify
app = Flask(__name__)
# Allowed users (in production, use database or LDAP)
ALLOWED_USERS = {
"alice@example.com",
"bob@example.com"
}
@app.route("/api/authorize", methods=["POST"])
def authorize():
data = request.json
user_email = data.get("user_email")
client_ip = data.get("client_ip")
# Check if user is allowed
if user_email in ALLOWED_USERS:
return jsonify({"authorized": True})
else:
return jsonify({
"authorized": False,
"reason": "User not in allowed list"
})
if __name__ == "__main__":
app.run(port=5000)from flask import Flask, request, jsonify
import ldap
app = Flask(__name__)
REQUIRED_GROUP = "cn=llm-proxy-users,ou=groups,dc=company,dc=com"
@app.route("/api/authorize", methods=["POST"])
def authorize():
data = request.json
user_email = data.get("user_email")
# Query LDAP for user groups
try:
conn = ldap.initialize("ldap://ldap.company.com")
conn.simple_bind_s("cn=admin,dc=company,dc=com", "password")
result = conn.search_s(
"ou=users,dc=company,dc=com",
ldap.SCOPE_SUBTREE,
f"(mail={user_email})",
["memberOf"]
)
if result:
groups = result[0][1].get("memberOf", [])
if REQUIRED_GROUP.encode() in groups:
return jsonify({"authorized": True})
return jsonify({
"authorized": False,
"reason": "User not in required group"
})
except Exception as e:
return jsonify({"error": str(e)}), 500from flask import Flask, request, jsonify
import ipaddress
app = Flask(__name__)
ALLOWED_NETWORKS = [
ipaddress.ip_network("10.0.0.0/8"),
ipaddress.ip_network("192.168.0.0/16")
]
@app.route("/api/authorize", methods=["POST"])
def authorize():
data = request.json
client_ip = ipaddress.ip_address(data.get("client_ip"))
# Check if IP is in allowed networks
for network in ALLOWED_NETWORKS:
if client_ip in network:
return jsonify({"authorized": True})
return jsonify({
"authorized": False,
"reason": "IP address not in allowed networks"
})from flask import Flask, request, jsonify
from datetime import datetime, time
app = Flask(__name__)
ALLOWED_HOURS = (time(9, 0), time(17, 0)) # 9 AM to 5 PM
@app.route("/api/authorize", methods=["POST"])
def authorize():
data = request.json
user_email = data.get("user_email")
# Check time restrictions
now = datetime.now().time()
if ALLOWED_HOURS[0] <= now <= ALLOWED_HOURS[1]:
return jsonify({"authorized": True})
else:
return jsonify({
"authorized": False,
"reason": "Access only allowed during business hours"
})Protect your authorization API with authentication:
from flask import Flask, request, jsonify
import hmac
import hashlib
app = Flask(__name__)
API_SECRET = "your-secret-key"
@app.route("/api/authorize", methods=["POST"])
def authorize():
# Verify HMAC signature
signature = request.headers.get("X-Signature")
body = request.get_data()
expected = hmac.new(
API_SECRET.encode(),
body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
return jsonify({"error": "Invalid signature"}), 401
# Process authorization...Configure proxy to send signature:
authorization:
api_url: "https://company.com/api/authorize"
api_secret: "your-secret-key"Always use HTTPS for authorization API in production:
# GOOD
authorization:
api_url: "https://company.com/api/authorize"
# BAD - never use HTTP in production
authorization:
api_url: "http://company.com/api/authorize"Set appropriate timeouts to prevent hanging:
authorization:
api_timeout_seconds: 5 # Fail fast if API is slowAlways deny access on errors (fail closed):
@app.route("/api/authorize", methods=["POST"])
def authorize():
try:
# Authorization logic...
return jsonify({"authorized": True})
except Exception as e:
# Log error but deny access
app.logger.error(f"Authorization error: {e}")
return jsonify({
"authorized": False,
"reason": "Internal error"
}), 500Symptom: "Authorization API timeout" error
Solutions:
- Verify API URL is correct and accessible
- Check API is running and responding
- Increase
api_timeout_secondsif API is slow - Check network connectivity between proxy and API
- Review API logs for errors
Symptom: "Authorization API error" in logs
Solutions:
- Check API logs for detailed error messages
- Verify request format matches API expectations
- Ensure API has access to required resources (database, LDAP, etc.)
- Test API independently with curl or Postman
Symptom: All users denied access
Solutions:
- Check API authorization logic
- Verify user identifiers match (email format, case sensitivity)
- Review API logs to see why users are denied
- Test API with known-good user credentials
Symptom: "401 Unauthorized" from API
Solutions:
- Verify API authentication credentials are configured
- Check API secret/key is correct
- Ensure authentication headers are being sent
- Review API authentication requirements
You can switch between single-user and enterprise modes by updating the configuration:
# Switch to single-user mode
authorization:
mode: "single_user"
# Switch to enterprise mode
authorization:
mode: "enterprise"
api_url: "https://company.com/api/authorize"Note: Existing agent tokens remain valid when switching modes. Only new authorizations use the new mode.
- Secure console access: Only authorized users should have access to server console
- Use strong codes: Default 6-digit codes provide adequate security
- Monitor logs: Review authorization logs for suspicious activity
- Set appropriate expiry: Balance security and convenience (default 10 minutes)
- Implement proper authentication: Protect your authorization API
- Use HTTPS: Always use HTTPS for API communication
- Log authorization decisions: Maintain audit trail of access grants/denials
- Implement rate limiting: Prevent abuse of authorization API
- Fail closed: Deny access on errors or timeouts
- Monitor API health: Alert on API failures or slow responses
- Test thoroughly: Verify authorization logic with various scenarios
- Agent Configuration - Configure AI agents with tokens
- Security Considerations - Understand security features
- Troubleshooting - Common issues and solutions
- Example Authorization API - Complete Flask implementation