Appearance
AutoGraph Anti-Fraud & Extended Auth (2FA) — Integration Guide
Protect video downloads/streams with watermarking and smart, ML-based fraud checks + just‑in‑time step‑up verification (email code in a lightbox).
What you get
- Real-time fraud screening on each AutoGraph link visit when
anti_fraud=trueis set. - Callback (webhook) so you can supply user/account context for ML scoring and receive fraud alert feedback.
- Step-up 2FA (“Extended Auth”) only when access looks risky:
- User sees a lightbox overlay, enters the emailed code, then continues.
- Successful devices are remembered to avoid repeated prompts.
1) Implement the Anti-Fraud Callback (Webhook)
Your endpoint returns JSON about the user identified by autograph_tag. Every request is HMAC-signed.
GET (data fetch) — example:
http
GET /member_info.php?username=happyuserbash
curl "https://yourdomain.com/member_info.php?username=happyuser&autograph_tag=happyuser×tamp=1546859760&client_id=552cae3514ea10cb4d3ac69e&signature=7643e3e94d998978d9116034e08beceac55a7fab"Expected JSON (example):
json
{
"firstname": "Martin",
"username": "happyuser",
"email": "martin123@gmail.com",
"address": "",
"city": "Seoul",
"zip": "100-141",
"country": "KR",
"join_ip": "211.195.13.0",
"joined": "1565396069",
"sale_amount": "29.95"
}POST (notifications/events) — Quick Format
Query parameters (always present)
autograph_tag— The member ID. If the POST concerns multiple users, this is the first user; the full list is in the body.fraud_type—'account_takeover'or'signup_fraud'.payload_type— Format of the body. Currently:"autograph_tags"(withfraud_type: "account_takeover")"fraud_scores"(withfraud_type: "signup_fraud")
client_id— Identifies the signing application.payload_size— Body size in bytes (for verification).payload_hmac—HmacSHA1(payload, client_secret)in hex — verifies body integrity.signature—HmacSHA1(url_path_and_query, client_secret)in hex — verifies query params.
Payloads / body JSON
1) fraud_type: "account_takeover"
payload_type: "autograph_tags"- Body is an array of user IDs.
json
{
"payload_type": "autograph_tags",
"fraud_type": "account_takeover",
"autograph_tags": ["userid1", "userid2", "userid3"]
}Action: Requires Extended Auth. Enforce password reset for listed users before next successful login.
2) fraud_type: "signup_fraud"
payload_type: "fraud_scores"- Array currently contains one user per call.
json
{
"payload_type": "fraud_scores",
"fraud_type": "signup_fraud",
"fraud_scores": [
{ "autograph_tag": "userid1", "fraud_score": 0.9, "blocked": true }
]
}Score: 0.0–1.0 (AI confidence). Thresholds: Blocking occurs when fraud_score > block_threshold; alerts sent when fraud_score > alert_threshold.
Security requirements (HMAC, timestamp, IP)
We sign every request with your app’s client_secret:
- Algorithm:
HMAC-SHA1 - Encoding: lowercase hex
- Freshness: check
timestamp(e.g., ±5 minutes) - Replay protection: store and reject reused signatures/timestamps
- Transport: HTTPS only
- Source IP: optionally allow-list IPs used by
api.xvid.com
Verifying GET signatures
Parameters include: autograph_tag, timestamp, client_id, signature.
- Build the canonical string:
PATH + "?" + querystringwithout thesignatureparam. Preserve the order as sent. Do not include scheme/host. - Compute
calc = HMAC_SHA1(canonical, client_secret)→ hex lowercase. - Constant-time compare
calcwith the receivedsignature. - Validate
timestampfreshness & single-use. - Optionally, verify the source IP is allow-listed.
PHP sketch:
php
$qs = $_SERVER['QUERY_STRING'];
parse_str($qs, $params);
$recv = isset($params['signature']) ? strtolower($params['signature']) : '';
unset($params['signature']); // remove before signing
$canonical = $_SERVER['SCRIPT_NAME'] . '?' . http_build_query($params);
$calc = hash_hmac('sha1', $canonical, $client_secret);
if (!hash_equals($calc, $recv)) {
http_response_code(401); exit;
}
#### Verifying POST signatures
POST includes the same URL signature **plus** `payload_hmac` over the **raw request body**.
Verify **both**:
- URL `signature` over the canonical URL (as above).
- `payload_hmac = HMAC_SHA1(raw_body, client_secret)`.
_PHP sketch:_
```php
$body = file_get_contents('php://input');
$payload_recv = isset($_GET['payload_hmac']) ? strtolower($_GET['payload_hmac']) : '';
$payload_calc = hash_hmac('sha1', $body, $client_secret);
$payload_ok = hash_equals($payload_calc, $payload_recv);
// URL signature (same as GET)
$qs = $_SERVER['QUERY_STRING'];
parse_str($qs, $params);
$url_recv = isset($params['signature']) ? strtolower($params['signature']) : '';
unset($params['signature']);
$canonical = $_SERVER['SCRIPT_NAME'] . '?' . http_build_query($params);
$url_ok = hash_equals(hash_hmac('sha1', $canonical, $client_secret), $url_recv);
if (!($url_ok && $payload_ok)) {
http_response_code(401); exit;
}Sample webhook (recommended starting point)
Our sample webhook demonstrates all of the above and includes:
- IP allow-listing (drop requests from non-approved sources early)
- Signature & payload HMAC checks
- A pluggable user lookup (NATS REST example you can replace with your own DB/service)
Use it as a template even if you don’t use NATS - just swap the user-lookup function with your own custom one.
If you operate multiple sites, deploy one separate webhook per site.
2) Set up CNAME
Create a CNAME in your domain for the application that signs your AutoGraph links to map it under your own domain. You’ll pass this host to the client via the cname option (see below); API calls will then go to https://<cname>/api/v1/. This must be a sub-domain of your own site's domain as our CORS rules allow only calls originating across subdomains, not across arbitrary main domains.
For further instructions how to configure your custom CNAME, follow the instructions how to set up custom CNAMEs in your own domain here
3) Configure the callback via REST
Obtain an OAuth token, then set the callback and base fraud settings:
bash
curl -X PUT -H "application/json" -d '{
"application": {
"antifraud_settings": {
"callback_url": "https://yourdomain.com/member_info.php?username=",
"alert_threshold": 0.5
}
}
}' "https://api.xvid.com/v1/applications/YOUR_APPLICATION_CLIENT_ID?access_token=YOUR_AUTH_TOKEN&human=true"callback_url: your webhook base; runtime appendsautograph_tag,timestamp,signature, etc.alert_threshold: 0..1 decision threshold for ML "risk".
4) Enable Extended Auth (step-up verification)
Include the drop-in on pages that contain AutoGraph links (download or streaming).
HTML <head>
html
<link href="https://assets.xvid.com/assets/xvid/latest/xvid_ex.css" rel="stylesheet">
<script src="https://assets.xvid.com/assets/xvid/latest/xvid_ex.js"></script>HTML <body> (minimal example)
html
<script>
// keep required IDs when customizing
var form_html = '<div class="text-center" style="background-color: white; text-align: center; padding: 20px;"><div class="row-fluid">' +
'<div class="span12"><br/><h1 class="spBelow2"><strong>New Device or Location Detected!</strong></h1><h3>We haven\'t seen ' +
'you accessing the site with this device or from your current location before.</h3><h3>To protect your account, ' +
'we\'ve sent you an email with a verification code to the email address by which you joined.<br/>Please check your email, ' +
'then enter the code you received in the form below and press "Continue" to access your videos.</h3></div></div></br/><br/>' +
'<div class="row-fluid"><div class="span6"><h3 class="center"><strong>Your Device: </strong>@@@DEVICE_DESC@@@</h3></div>' +
'<div class="span6"><h3 class="center"><strong>Your IP: </strong>@@@IP_LOCATION@@@</h3></div><br/><br/><label for="xvid_code" ' +
'style="font-size: 1.5em;">Your Code: <button type="button" id="xvid_resend_button" class="btn btn-small btn-success" ' +
'onclick="xvid.onResendClick();"><i class="icon icon-ccw" style="color: white;"></i> Resend</button></label><textarea ' +
'name="xvid_textbox" id="xvid_code" cols="34" rows="2" class="center-block" style="font-size: 2em; padding-left: 5px; ' +
'max-width: 100%; padding-right: 5px;" autofocus></textarea><br/><br/><div class="row-fluid"><div class="span3 alert" style="' +
'visibility:hidden;"> </div><div id="xvid_error_alert" class="span6 alert alert-danger center" style="visibility:hidden; ' +
'display:none;"><h3></div><div id="xvid_success_alert" class="span6 alert alert-success center" style="visibility:hidden; ' +
'display:none;"></div><div class="span3"> </div></div><button type="button" id="xvid_submit_button" class="btn-large ' +
'btn btn-primary" onclick="xvid.onSubmitClick();">Continue</button><br/> </div>';
xvid.create({
template: form_html,
cname: 'xvidcdn.yourdomain.com'
});
</script>Full example: https://mediahub.xvid.com/examples/auth_test.html
Enable Extended Auth on the application:
bash
curl -X PUT -H "application/json" -d '{
"application": {
"antifraud_settings": {
"extended_auth": {
"enabled": true,
"passive_mode": false,
"enabled_tags": "testuser1, testuser2"
}
}
}
}' "https://api.xvid.com/v1/applications/YOUR_APPLICATION_CLIENT_ID?access_token=YOUR_AUTH_TOKEN&human=true"Lightbox template requirements
You can fully customize the markup, but keep these IDs:
xvid_code— the input/textarea for the codexvid_resend_button— “Resend” buttonxvid_submit_button— “Continue” buttonxvid_error_alert— error container (hidden by default)xvid_success_alert— success container (hidden by default)
Shadow DOM option
If shadow_dom: true, your template is inserted into a Shadow Root for strong CSS isolation. You can include a small <style> scoped to that root.
xvid.create(options) — options reference
js
xvid.create({
// Content/placement
template, // string: HTML for the lightbox (required)
cname, // string: CNAME host for API calls (e.g., "xvidcdn.yourdomain.com")
// Behavior toggles (present & not false -> enabled)
shadow_dom, // bool: use Shadow DOM for CSS isolation
check_href_links, // bool: scan <a href> for AutoGraph links and include them
monitor_xhr, // bool: watch XHR; record failing media URLs (status >= 400)
monitor_fetch, // bool: watch fetch(); record failing media URLs when !res.ok
// Additional inputs/outputs
autograph_urls, // string[]: extra AutoGraph URLs to always include
redirect_url, // string: navigate here after successful 2FA (default: reload)
redirect_always, // bool: also redirect even if no challenge occurred
// Logging
debug // number|string: verbosity (0=quiet)
});Behavior toggle semantics: if the option exists and is not false, the feature is on. Pass false to disable. If omitted, the library’s internal default applies.
Example:
js
xvid.create({
template: form_html,
cname: 'xvidcdn.yourdomain.com',
shadow_dom: true,
monitor_xhr: true,
monitor_fetch: true,
autograph_urls: ['https://xvidcdn.yourdomain.com/autograph/.../file.m3u8'],
redirect_url: '/account/videos',
redirect_always: false,
debug: 1
});Security checklist
- [ ] Verify URL
signature(HMAC-SHA1 over canonical path+query withoutsignature). - [ ] For POST: verify
payload_hmac(HMAC-SHA1 over raw body). - [ ] Enforce
timestampfreshness and replay protection. - [ ] Ensure HTTPS endpoint (no HTTP redirects).
- [ ] Dynamic IP allow-list based on api.xvid.com (drop early).
Troubleshooting
- Styles clash with site CSS → use
shadow_dom: trueand include a compact<style>in your template. - Buttons don’t respond → confirm required IDs haven’t changed.