Skip to main content

Python SDK

Exclusive On Demand

The Python SDK is available exclusively for enterprise customers.

Request Access

Alternative: REST API

While waiting for SDK access, you can integrate directly with our REST API using OAuth2 Client Credentials authentication.

import requests
import os
import base64
from datetime import datetime, timedelta

GLIDE_CLIENT_ID = os.environ.get("GLIDE_CLIENT_ID")
GLIDE_CLIENT_SECRET = os.environ.get("GLIDE_CLIENT_SECRET")
GLIDE_API_URL = "https://api.glideidentity.app"

# Token cache
_token_cache = {"access_token": None, "expires_at": None}

def get_access_token():
"""Get a valid access token, refreshing if necessary."""
now = datetime.now()

# Return cached token if still valid (with 60s buffer)
if (_token_cache["access_token"] and
_token_cache["expires_at"] and
_token_cache["expires_at"] > now + timedelta(seconds=60)):
return _token_cache["access_token"]

# Fetch new token
credentials = base64.b64encode(
f"{GLIDE_CLIENT_ID}:{GLIDE_CLIENT_SECRET}".encode()
).decode()

response = requests.post(
f"{GLIDE_API_URL}/oauth2-cc/token",
headers={
"Authorization": f"Basic {credentials}",
"Content-Type": "application/x-www-form-urlencoded"
},
data="grant_type=client_credentials"
)
response.raise_for_status()
token_data = response.json()

# Cache the token
_token_cache["access_token"] = token_data["access_token"]
_token_cache["expires_at"] = now + timedelta(seconds=token_data["expires_in"])

return _token_cache["access_token"]

def prepare(request_data):
token = get_access_token()
response = requests.post(
f"{GLIDE_API_URL}/magic-auth/v2/auth/prepare",
json=request_data,
headers={"Authorization": f"Bearer {token}"}
)
response.raise_for_status()
return response.json()

def verify_phone_number(session, credential):
token = get_access_token()
response = requests.post(
f"{GLIDE_API_URL}/magic-auth/v2/auth/verify-phone-number",
json={
"session_info": session,
"credential": credential
},
headers={"Authorization": f"Bearer {token}"}
)
response.raise_for_status()
return response.json()

Anti-Fraud Signals

Both verify_phone_number() and get_phone_number() responses include SIM swap and device swap (IMEI change) fraud detection signals.

Each signal includes risk_level, age_band, carrier_name, and checked_at.

result = verify_phone_number(session, credential)

# Example response:
# {
# "phone_number": "+14155551234",
# "verified": True,
# "sim_swap": {
# "checked": True, "risk_level": "RISK_LEVEL_LOW",
# "age_band": "older than query window",
# "carrier_name": "Verizon Wireless",
# "checked_at": "2025-01-04T14:30:00Z"
# },
# "device_swap": {
# "checked": True, "risk_level": "RISK_LEVEL_LOW",
# "age_band": "older than query window",
# "carrier_name": "Verizon Wireless",
# "checked_at": "2025-01-04T14:30:00Z"
# }
# }

# Check SIM swap
sim_swap = result.get("sim_swap")
if sim_swap and sim_swap.get("checked"):
print(f"SIM swap risk: {sim_swap.get('risk_level')}")
print(f"Last SIM change: {sim_swap.get('age_band')}")

# Check device swap (IMEI change)
device_swap = result.get("device_swap")
if device_swap and device_swap.get("checked"):
print(f"Device swap risk: {device_swap.get('risk_level')}")
print(f"Last device change: {device_swap.get('age_band')}")

# Handle failed checks
if sim_swap and not sim_swap.get("checked"):
print(f"SIM swap check failed: {sim_swap.get('reason')}")
if device_swap and not device_swap.get("checked"):
print(f"Device swap check failed: {device_swap.get('reason')}")

For Link protocol sessions (e.g., Verizon via App Clips), device binding is mandatory. Your backend must generate a cryptographic fe_code, compute its SHA256 hash, and include fe_hash in the /prepare request. After carrier authentication, a second code (agg_code) arrives via URL fragment. Your backend then validates both codes by calling /complete.

This prevents session fixation attacks where an attacker tricks a victim into authenticating on the attacker's session.

import hashlib
import os

# During /prepare — generate fe_code and include fe_hash
fe_code = os.urandom(32).hex() # 64 hex chars
fe_hash = hashlib.sha256(fe_code.encode()).hexdigest() # 64 hex chars

prepare_resp = prepare({
'nonce': nonce,
'use_case': 'VerifyPhoneNumber',
'phone_number': phone_number,
'fe_hash': fe_hash, # Required for Link protocol
})

# Set HttpOnly cookie in your Flask/Django response
resp.set_cookie(
'_glide_bind', fe_code,
httponly=True, secure=True, samesite='Strict',
path='/api/auth', max_age=600,
)

# During /complete — validate both codes
def complete_auth():
fe_code = request.cookies.get('_glide_bind')
agg_code = request.json['agg_code']
session_key = request.json['session_key']

token = get_access_token()
resp = requests.post(
f'{GLIDE_API_URL}/magic-auth/v2/auth/complete',
json={'session_key': session_key, 'fe_code': fe_code, 'agg_code': agg_code},
headers={'Authorization': f'Bearer {token}'},
)
resp.raise_for_status()
return resp.json()

Read the full Device Binding Security guide →

Next Steps