Skip to content

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=true is 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=happyuser
bash
curl "https://yourdomain.com/member_info.php?username=happyuser&autograph_tag=happyuser&timestamp=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" (with fraud_type: "account_takeover")
    • "fraud_scores" (with fraud_type: "signup_fraud")
  • client_id — Identifies the signing application.
  • payload_size — Body size in bytes (for verification).
  • payload_hmacHmacSHA1(payload, client_secret) in hex — verifies body integrity.
  • signatureHmacSHA1(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.

  1. Build the canonical string: PATH + "?" + querystring without the signature param. Preserve the order as sent. Do not include scheme/host.
  2. Compute calc = HMAC_SHA1(canonical, client_secret) → hex lowercase.
  3. Constant-time compare calc with the received signature.
  4. Validate timestamp freshness & single-use.
  5. 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;
}

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.

Download Webhook PHP Script


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 appends autograph_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"

You can fully customize the markup, but keep these IDs:

  • xvid_code — the input/textarea for the code
  • xvid_resend_button — “Resend” button
  • xvid_submit_button — “Continue” button
  • xvid_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 without signature).
  • [ ] For POST: verify payload_hmac (HMAC-SHA1 over raw body).
  • [ ] Enforce timestamp freshness 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: true and include a compact <style> in your template.
  • Buttons don’t respond → confirm required IDs haven’t changed.