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
| Status | Meaning |
|---|---|
pending | Token created — user has not yet authorized |
active | User authorized — response includes account data and session token |
expired | 10 minutes passed without authorization — create a new token |
session_expired | Token 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
| Scenario | What happens |
|---|---|
| User is already logged in to Vanelton ID | Authorization is automatic — user is redirected to redirect_uri within seconds, no interaction needed |
| User is not logged in | A banner shows which app is requesting access; user logs in normally and is redirected after login |
| Token is expired or invalid | Login page shows an error message — create a new SSO token |
Notes
| Detail | Description |
|---|---|
| Token expiry | The 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 use | Each sso_token can only be authorized once. |
| Session token | The 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 only | sso_create.php and sso_check.php do not accept WebKey authentication — only AppID:AppKey. |
| Polling interval | 3 seconds is recommended. Do not poll faster than 1 second to avoid rate limiting. |