Console
Vanelton ID

Single Sign-On (SSO)

Authenticate users through the Vanelton ID login page without collecting email and password directly. Your app creates a token, redirects the user to the hosted login UI, and polls for the result.

SSO is ideal for desktop apps (Electron, native) and situations where you prefer to delegate the login UI to Vanelton ID. For server-side web apps or mobile apps where you control the UI, use the standard login endpoint instead.

How it works

1
Create an SSO token POST /api/vaneltonid/sso_create.php — returns a short-lived token and the login URL.
2
Redirect the user Open vaneltonmedia.com/login?sso=TOKEN in the system browser or a webview.
3
User logs in Vanelton ID handles credentials. If the user is already logged in, authorization happens automatically.
4
Poll or catch the redirect Desktop apps poll sso_check.php every few seconds. Web/SPA apps catch the redirect_uri callback and call sso_check.php once.
5
Receive the session token When status is active, the response includes the user's data and a standard session token — use it in all subsequent authenticated API calls.

Token status

StatusMeaning
pendingToken created — user has not yet authorized
activeUser authorized — response includes account data and session token
expired10 minutes passed without authorization — create a new token
session_expiredToken was authorized but the session is now older than 6 days

Desktop / Electron — polling flow

Open the login URL in the system browser, then poll sso_check.php every 3 seconds until the status changes from pending.

const APP_ID  = 'com.myapp:MyAppKey';
const BASE    = 'https://vaneltonmedia.com';

async function ssoLogin() {
    // 1. Create SSO token
    const res = await fetch(`${BASE}/api/vaneltonid/sso_create.php`, {
        method: 'POST',
        headers: { 'Authorization': APP_ID, 'Content-Type': 'application/json' },
        body: JSON.stringify({ redirect_uri: 'myapp://sso-callback' }),
    });
    const { sso_token, login_url } = await res.json();

    // 2. Open in system browser (Electron)
    shell.openExternal(login_url);

    // 3. Poll until authorized or expired
    while (true) {
        await new Promise(r => setTimeout(r, 3000));

        const check = await fetch(`${BASE}/api/vaneltonid/sso_check.php`, {
            method: 'POST',
            headers: { 'Authorization': APP_ID, 'Content-Type': 'application/json' },
            body: JSON.stringify({ sso_token }),
        });
        const data = await check.json();

        if (data.status === 'active') {
            saveToken(data.user.token);   // standard session token
            saveUser(data.user);
            return data.user;
        }
        if (data.status === 'expired') {
            throw new Error('Login expired. Please try again.');
        }
        // 'pending' → keep polling
    }
}
const APP_ID = 'com.myapp:MyAppKey';
const BASE   = 'https://vaneltonmedia.com';

interface SSOUser {
    id: number; userKey: string; username: string; name: string;
    email: string; profileimg: string; idverify: number;
    coins: number; vip_plan: string; token: string;
}

async function ssoLogin(): Promise<SSOUser> {
    const res = await fetch(`${BASE}/api/vaneltonid/sso_create.php`, {
        method: 'POST',
        headers: { 'Authorization': APP_ID, 'Content-Type': 'application/json' },
        body: JSON.stringify({ redirect_uri: 'myapp://sso-callback' }),
    });
    const { sso_token, login_url } = await res.json();

    // Open in Electron's system browser
    (window as any).electronAPI.openExternal(login_url);

    while (true) {
        await new Promise(r => setTimeout(r, 3000));
        const data = await fetch(`${BASE}/api/vaneltonid/sso_check.php`, {
            method: 'POST',
            headers: { 'Authorization': APP_ID, 'Content-Type': 'application/json' },
            body: JSON.stringify({ sso_token }),
        }).then(r => r.json());

        if (data.status === 'active') return data.user as SSOUser;
        if (data.status === 'expired') throw new Error('SSO login expired.');
    }
}
<?php
// Web/SPA redirect flow (see below for full example)
// Desktop CLI example — blocking poll
define('APP_ID',  'com.myapp');
define('APP_KEY', 'MyAppKey');
define('BASE',    'https://vaneltonmedia.com');

function ssoRequest(string $path, array $body): array {
    $ch = curl_init(BASE . $path);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: ' . APP_ID . ':' . APP_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS     => json_encode($body),
    ]);
    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result;
}

$created = ssoRequest('/api/vaneltonid/sso_create.php', [
    'redirect_uri' => 'https://myapp.com/sso/callback',
]);
$sso_token = $created['sso_token'];
echo "Open: " . $created['login_url'] . PHP_EOL;

// Poll
while (true) {
    sleep(3);
    $data = ssoRequest('/api/vaneltonid/sso_check.php', ['sso_token' => $sso_token]);

    if ($data['status'] === 'active') {
        $token = $data['user']['token']; // session token
        break;
    }
    if ($data['status'] === 'expired') {
        die("SSO expired.\n");
    }
}
import time, webbrowser, requests

APP_AUTH = {'Authorization': 'com.myapp:MyAppKey', 'Content-Type': 'application/json'}
BASE     = 'https://vaneltonmedia.com'

def sso_login(redirect_uri: str = '') -> dict:
    # 1. Create token
    res = requests.post(f'{BASE}/api/vaneltonid/sso_create.php',
                        headers=APP_AUTH,
                        json={'redirect_uri': redirect_uri})
    data = res.json()
    sso_token = data['sso_token']

    # 2. Open browser
    webbrowser.open(data['login_url'])

    # 3. Poll
    while True:
        time.sleep(3)
        check = requests.post(f'{BASE}/api/vaneltonid/sso_check.php',
                              headers=APP_AUTH,
                              json={'sso_token': sso_token}).json()

        if check['status'] == 'active':
            return check['user']   # contains 'token', 'username', etc.
        if check['status'] == 'expired':
            raise RuntimeError('SSO login expired. Please try again.')
        # 'pending' → keep polling

user = sso_login()
print(f"Logged in as {user['username']}, token: {user['token']}")
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Diagnostics;

static class SSOLogin
{
    private const string AppAuth = "com.myapp:MyAppKey";
    private const string Base    = "https://vaneltonmedia.com";
    private static readonly HttpClient Http = new() { Timeout = TimeSpan.FromSeconds(10) };

    public static async Task<JsonElement> LoginAsync(string redirectUri = "")
    {
        Http.DefaultRequestHeaders.Clear();
        Http.DefaultRequestHeaders.Add("Authorization", AppAuth);

        // 1. Create token
        var createBody = JsonSerializer.Serialize(new { redirect_uri = redirectUri });
        var createResp = await Http.PostAsync($"{Base}/api/vaneltonid/sso_create.php",
            new StringContent(createBody, Encoding.UTF8, "application/json"));
        using var createDoc = JsonDocument.Parse(await createResp.Content.ReadAsStringAsync());
        var ssoToken = createDoc.RootElement.GetProperty("sso_token").GetString()!;
        var loginUrl = createDoc.RootElement.GetProperty("login_url").GetString()!;

        // 2. Open browser
        Process.Start(new ProcessStartInfo(loginUrl) { UseShellExecute = true });

        // 3. Poll
        var checkBody = JsonSerializer.Serialize(new { sso_token = ssoToken });
        while (true)
        {
            await Task.Delay(3000);
            var checkResp = await Http.PostAsync($"{Base}/api/vaneltonid/sso_check.php",
                new StringContent(checkBody, Encoding.UTF8, "application/json"));
            using var doc = JsonDocument.Parse(await checkResp.Content.ReadAsStringAsync());
            var status = doc.RootElement.GetProperty("status").GetString();

            if (status == "active")  return doc.RootElement.GetProperty("user").Clone();
            if (status == "expired") throw new Exception("SSO login expired. Please try again.");
        }
    }
}

// Usage
var user  = await SSOLogin.LoginAsync("myapp://sso-callback");
var token = user.GetProperty("token").GetString(); // session token
APP_AUTH="com.myapp:MyAppKey"
BASE="https://vaneltonmedia.com"

# 1. Create SSO token
RESP=$(curl -s -X POST "$BASE/api/vaneltonid/sso_create.php" \
  -H "Authorization: $APP_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"redirect_uri":"https://myapp.com/callback"}')

SSO_TOKEN=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['sso_token'])")
LOGIN_URL=$(echo "$RESP"  | python3 -c "import sys,json; print(json.load(sys.stdin)['login_url'])")
echo "Open this URL: $LOGIN_URL"

# 2. Poll until active or expired
while true; do
  sleep 3
  CHECK=$(curl -s -X POST "$BASE/api/vaneltonid/sso_check.php" \
    -H "Authorization: $APP_AUTH" \
    -H "Content-Type: application/json" \
    -d "{\"sso_token\":\"$SSO_TOKEN\"}")
  STATUS=$(echo "$CHECK" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  if [ "$STATUS" = "active" ]; then echo "Logged in! $CHECK"; break; fi
  if [ "$STATUS" = "expired" ]; then echo "SSO expired."; exit 1; fi
  echo "Waiting... ($STATUS)"
done

Web / SPA — redirect flow

Instead of polling, redirect the user's browser directly to the login URL and catch the redirect_uri callback. Call sso_check.php once after the redirect.

// Step 1 — on your login page, create and redirect
async function startSSOLogin() {
    const res = await fetch('https://vaneltonmedia.com/api/vaneltonid/sso_create.php', {
        method: 'POST',
        headers: { 'Authorization': 'com.myapp:MyAppKey', 'Content-Type': 'application/json' },
        body: JSON.stringify({ redirect_uri: 'https://myapp.com/auth/callback' }),
    });
    const { login_url, sso_token } = await res.json();
    sessionStorage.setItem('sso_token', sso_token);
    window.location.href = login_url;
}

// Step 2 — on your callback page (/auth/callback?sso=TOKEN)
async function handleSSOCallback() {
    const params    = new URLSearchParams(location.search);
    const sso_token = params.get('sso') ?? sessionStorage.getItem('sso_token');

    const data = await fetch('https://vaneltonmedia.com/api/vaneltonid/sso_check.php', {
        method: 'POST',
        headers: { 'Authorization': 'com.myapp:MyAppKey', 'Content-Type': 'application/json' },
        body: JSON.stringify({ sso_token }),
    }).then(r => r.json());

    if (data.status === 'active') {
        localStorage.setItem('vm_token', data.user.token);
        // redirect to app dashboard
    }
}
<?php
// Step 1 — initiate SSO (e.g. login.php)
$res = json_decode(file_get_contents('https://vaneltonmedia.com/api/vaneltonid/sso_create.php', false,
    stream_context_create(['http' => [
        'method'  => 'POST',
        'header'  => "Authorization: com.myapp:MyAppKey\r\nContent-Type: application/json",
        'content' => json_encode(['redirect_uri' => 'https://myapp.com/auth/callback']),
    ]])
), true);

$_SESSION['sso_token'] = $res['sso_token'];
header('Location: ' . $res['login_url']);
exit;

// -------------------------------------------------------
// Step 2 — callback page (auth/callback.php?sso=TOKEN)
$sso_token = $_GET['sso'] ?? $_SESSION['sso_token'] ?? '';

$data = json_decode(file_get_contents('https://vaneltonmedia.com/api/vaneltonid/sso_check.php', false,
    stream_context_create(['http' => [
        'method'  => 'POST',
        'header'  => "Authorization: com.myapp:MyAppKey\r\nContent-Type: application/json",
        'content' => json_encode(['sso_token' => $sso_token]),
    ]])
), true);

if ($data['status'] === 'active') {
    $_SESSION['vm_token'] = $data['user']['token'];
    header('Location: /dashboard');
    exit;
}
# Flask example
from flask import Flask, redirect, request, session
import requests

app     = Flask(__name__)
APP_AUTH = {'Authorization': 'com.myapp:MyAppKey', 'Content-Type': 'application/json'}
BASE    = 'https://vaneltonmedia.com'

@app.route('/login/sso')
def sso_start():
    res = requests.post(f'{BASE}/api/vaneltonid/sso_create.php',
                        headers=APP_AUTH,
                        json={'redirect_uri': 'https://myapp.com/auth/callback'}).json()
    session['sso_token'] = res['sso_token']
    return redirect(res['login_url'])

@app.route('/auth/callback')
def sso_callback():
    sso_token = request.args.get('sso') or session.get('sso_token', '')
    data = requests.post(f'{BASE}/api/vaneltonid/sso_check.php',
                         headers=APP_AUTH,
                         json={'sso_token': sso_token}).json()
    if data['status'] == 'active':
        session['vm_token'] = data['user']['token']
        return redirect('/dashboard')
    return 'SSO failed', 400
// ASP.NET Core — controller actions
[HttpGet("login/sso")]
public async Task<IActionResult> StartSSO()
{
    var body = JsonSerializer.Serialize(new { redirect_uri = "https://myapp.com/auth/callback" });
    var resp = await _http.PostAsync("https://vaneltonmedia.com/api/vaneltonid/sso_create.php",
        new StringContent(body, Encoding.UTF8, "application/json"));
    using var doc      = JsonDocument.Parse(await resp.Content.ReadAsStringAsync());
    var ssoToken       = doc.RootElement.GetProperty("sso_token").GetString()!;
    var loginUrl       = doc.RootElement.GetProperty("login_url").GetString()!;
    HttpContext.Session.SetString("sso_token", ssoToken);
    return Redirect(loginUrl);
}

[HttpGet("auth/callback")]
public async Task<IActionResult> SSOCallback([FromQuery] string? sso)
{
    var ssoToken = sso ?? HttpContext.Session.GetString("sso_token") ?? "";
    var body     = JsonSerializer.Serialize(new { sso_token = ssoToken });
    var resp     = await _http.PostAsync("https://vaneltonmedia.com/api/vaneltonid/sso_check.php",
        new StringContent(body, Encoding.UTF8, "application/json"));
    using var doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync());
    if (doc.RootElement.GetProperty("status").GetString() == "active") {
        var token = doc.RootElement.GetProperty("user").GetProperty("token").GetString()!;
        HttpContext.Session.SetString("vm_token", token);
        return Redirect("/dashboard");
    }
    return BadRequest("SSO failed");
}

Login page behavior

ScenarioWhat happens
User is already logged in to Vanelton IDAuthorization is automatic — user is redirected to redirect_uri within seconds, no interaction needed
User is not logged inA banner shows which app is requesting access; user logs in normally and is redirected after login
Token is expired or invalidLogin page shows an error message — create a new SSO token

Notes

DetailDescription
Token expiryThe sso_token expires in 10 minutes while pending. After authorization it stays active for 6 days (same as a standard session). Create a new token if polling times out.
Single useEach sso_token can only be authorized once.
Session tokenThe token field in the active response is a standard Vanelton ID session token — use it in Authorization or in the JSON body of all user-scoped API calls, exactly like a token from login.php.
AppID:AppKey onlysso_create.php and sso_check.php do not accept WebKey authentication — only AppID:AppKey.
Polling interval3 seconds is recommended. Do not poll faster than 1 second to avoid rate limiting.