Server-side SDK for Trustport — phishing-resistant passkey verification for your existing login flow.
This SDK targets the embedded widget (Verify) mode. If you want the OAuth-based "Sign in with Trustport" flow instead, see the Trustport developer docs.
composer require trustport/sdkRequires PHP ≥ 7.4 with ext-curl and ext-json (both bundled with every distro of PHP).
No composer / shared hosting? Use the single-file drop-in instead — same API, no dependencies.
<?php
require __DIR__ . '/vendor/autoload.php';
use Trustport\Client;
use Trustport\TrustportException;
$tp = new Client(['apiKey' => getenv('TRUSTPORT_API_KEY')]);
// In your login handler, after the password check passes:
$session = $tp->startSession(['subject' => $user->username]);
// Send $session->sid to your frontend, KEEP $session->claimToken on the server.
$_SESSION['tp_sid'] = $session->sid;
$_SESSION['tp_claim'] = $session->claimToken;
echo json_encode(['trustport_sid' => $session->sid]);
// Later (after the widget reports onApproved):
$result = $tp->verifySession([
'sid' => $_SESSION['tp_sid'],
'claimToken' => $_SESSION['tp_claim'],
]);
if ($result->ok) {
// $result->subject is the verified user. Now upgrade your PHP session.
$_SESSION['user_id'] = $userId;
}<?php
// /api/login.php
session_start();
require __DIR__ . '/vendor/autoload.php';
use Trustport\Client;
use Trustport\TrustportException;
header('Content-Type: application/json');
$in = json_decode(file_get_contents('php://input'), true) ?: [];
$user = $db->users->findByUsername($in['username'] ?? '');
if (!$user || !password_verify($in['password'] ?? '', $user['password_hash'])) {
http_response_code(401);
echo json_encode(['ok' => false]);
exit;
}
$tp = new Client(['apiKey' => getenv('TRUSTPORT_API_KEY')]);
try {
$session = $tp->startSession(['subject' => $user['username']]);
} catch (TrustportException $e) {
http_response_code($e->errorCode === 'subject_not_enrolled' ? 403 : 500);
echo json_encode(['ok' => false, 'error' => $e->errorCode]);
exit;
}
$_SESSION['tp_pending'] = [
'user_id' => $user['id'],
'subject' => $user['username'],
'sid' => $session->sid,
'claim_token' => $session->claimToken,
];
echo json_encode(['ok' => true, 'trustport_sid' => $session->sid]);<?php
// /api/finalise.php
session_start();
require __DIR__ . '/vendor/autoload.php';
use Trustport\Client;
header('Content-Type: application/json');
if (empty($_SESSION['tp_pending'])) {
http_response_code(401);
echo json_encode(['ok' => false]);
exit;
}
$p = $_SESSION['tp_pending'];
$tp = new Client(['apiKey' => getenv('TRUSTPORT_API_KEY')]);
$result = $tp->verifySession([
'sid' => $p['sid'],
'claimToken' => $p['claim_token'],
]);
if (!$result->ok || $result->subject !== $p['subject']) {
http_response_code(401);
echo json_encode(['ok' => false, 'status' => $result->status]);
exit;
}
$_SESSION['user_id'] = $p['user_id'];
unset($_SESSION['tp_pending']);
echo json_encode(['ok' => true]);| Option | Type | Default | Notes |
|---|---|---|---|
apiKey |
string | — | Required. From Trustport admin → Integrations. |
baseUrl |
string | https://trustport.vospen.com |
For self-hosted instances. |
timeoutSeconds |
int | 10 |
Per-request timeout. |
Returns:
$session->sid // string — give to the frontend / widget
$session->claimToken // string — SERVER ONLY
$session->verifyUrl // string
$session->qrUrl // string
$session->expiresAt // DateTimeImmutableThrows TrustportException with:
errorCode = "subject_not_enrolled"(HTTP 404)errorCode = "no_active_device"(HTTP 409)errorCode = "invalid_api_key"(HTTP 401)
Single-use server-to-server confirmation.
Returns VerifyResult:
if ($result->ok) {
$result->subject; // string
$result->userId; // string
$result->verifiedAt; // DateTimeImmutable
} else {
$result->status; // "pending" | "expired" | "rejected" | "claimed"
}Throws TrustportException for transport/auth errors.
catch (TrustportException $e) {
$e->httpStatus; // int — HTTP status (0 = transport error)
$e->errorCode; // string — e.g. "subject_not_enrolled"
$e->body; // array|null — raw response body
$e->getMessage(); // string
}- Never expose
claimTokento the browser. Keep it in$_SESSIONor another server-side store. - Never expose
apiKeyto the browser. Load from environment. - Sessions auto-expire after 2 minutes.
verifySessionis single-use — replay attempts return['ok' => false, 'status' => 'claimed'].
- Trustport developer docs: https://trustport.vospen.com/docs
- Source: https://github.com/Emin4ik/trustport
- Issues: https://github.com/Emin4ik/trustport/issues
MIT