PHP code example of sk-id-solutions / smart-id-php-client

1. Go to this page and download the library: Download sk-id-solutions/smart-id-php-client library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

sk-id-solutions / smart-id-php-client example snippets


use Sk\SmartId\Api\SmartIdRestConnector;
use Sk\SmartId\Ssl\SslPinnedPublicKeyStore;

use Sk\SmartId\SmartIdClient;
use Sk\SmartId\Ssl\SslPinnedPublicKeyStore;

// Demo environment
$client = new SmartIdClient(
    relyingPartyUUID: '00000000-0000-4000-8000-000000000000',
    relyingPartyName: 'DEMO',
    hostUrl: 'https://sid.demo.sk.ee/smart-id-rp/v3',
    sslPinnedKeys: SslPinnedPublicKeyStore::loadDemo(),
);

// Production environment with logging (see "Logging" section below)
$sslKeys = SslPinnedPublicKeyStore::create()
    ->addPublicKeyHash('sha256//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=')
    ->addPublicKeyHash('sha256//YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY=');

$client = new SmartIdClient(
    relyingPartyUUID: 'your-relying-party-uuid',
    relyingPartyName: 'Your RP Name',
    hostUrl: 'https://rp-api.smart-id.com/v3',
    sslPinnedKeys: $sslKeys,
    logger: $logger, // optional PSR-3 LoggerInterface
);

// Create authentication builders directly from the client
$deviceLinkBuilder = $client->createDeviceLinkAuthentication();
$notificationBuilder = $client->createNotificationAuthentication();

// Get the session status poller
$poller = $client->getSessionStatusPoller();

// Create validator (OCSP revocation checking is enabled automatically)
$validator = $client->createAuthenticationResponseValidator();

// Configure polling parameters (optional)
$client->setPollTimeoutMs(30000);
$client->setPollIntervalMs(1000);

use Sk\SmartId\Api\SmartIdRestConnector;
use Sk\SmartId\Ssl\SslPinnedPublicKeyStore;

// Demo environment
$connector = new SmartIdRestConnector(
    'https://sid.demo.sk.ee/smart-id-rp/v3',
    SslPinnedPublicKeyStore::loadDemo(),
);

// Production environment
$sslKeys = SslPinnedPublicKeyStore::create()
    ->addPublicKeyHash('sha256//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=')
    ->addPublicKeyHash('sha256//YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY=');

$connector = new SmartIdRestConnector('https://rp-api.smart-id.com/v3', $sslKeys);

$connector = new SmartIdRestConnector(
    'https://sid.demo.sk.ee/smart-id-rp/v3',
    SslPinnedPublicKeyStore::loadDemo(),
);

$sslKeys = SslPinnedPublicKeyStore::create()
    ->addPublicKeyHash('sha256//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=')
    ->addPublicKeyHash('sha256//YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY=');

$connector = new SmartIdRestConnector('https://rp-api.smart-id.com/v3', $sslKeys);

$sslKeys = SslPinnedPublicKeyStore::loadFromDirectory('/path/to/your/keys');
$connector = new SmartIdRestConnector('https://rp-api.smart-id.com/v3', $sslKeys);

$sslKeys = SslPinnedPublicKeyStore::fromString(getenv('SMARTID_SSL_PINS'));

$connector = new SmartIdRestConnector('https://rp-api.smart-id.com/v3', $sslKeys);

// Semicolon-separated
$sslKeys = SslPinnedPublicKeyStore::fromString(getenv('SMARTID_SSL_PINS'), ';');

$hashes = $secretManager->getSecret('smartid-ssl-pins'); // returns string[]

$sslKeys = SslPinnedPublicKeyStore::fromArray($hashes);

$connector = new SmartIdRestConnector('https://rp-api.smart-id.com/v3', $sslKeys);

use Sk\SmartId\DeviceLink\DeviceLinkAuthenticationRequestBuilder;
use Sk\SmartId\Enum\CertificateLevel;
use Sk\SmartId\DeviceLink\DeviceLinkInteraction;

$builder = new DeviceLinkAuthenticationRequestBuilder(
    $connector,
    '00000000-0000-4000-8000-000000000000', // relyingPartyUUID
    'DEMO',                                  // relyingPartyName
);

// Initiate anonymous authentication (no user identifier needed)
$session = $builder
    ->withCertificateLevel(CertificateLevel::QUALIFIED)
    ->withAllowedInteractionsOrder([
        DeviceLinkInteraction::displayTextAndPin('Log in to example.com'),
    ])
    ->initiate();

// Session ID for polling
$sessionId = $session->getSessionId();

// Verification code to display (if using notification-style interaction)
$verificationCode = $session->getVerificationCode();

// Build QR code URL (see "Generating QR code or device link" section)
$qrCodeUrl = $session->buildQrCodeUrl();

use Sk\SmartId\Util\RpChallengeGenerator;

$rpChallenge = RpChallengeGenerator::generate();

$session = $builder
    ->withRpChallenge($rpChallenge)
    ->withAllowedInteractionsOrder([
        DeviceLinkInteraction::displayTextAndPin('Log in to example.com'),
    ])
    ->initiate();

use Sk\SmartId\DeviceLink\DeviceLinkAuthenticationRequest;
use Sk\SmartId\Enum\CertificateLevel;
use Sk\SmartId\Enum\HashAlgorithm;
use Sk\SmartId\DeviceLink\DeviceLinkInteraction;
use Sk\SmartId\Util\RpChallengeGenerator;

// For security, generate a new challenge for each request
$rpChallenge = RpChallengeGenerator::generate();

$interactions = [DeviceLinkInteraction::displayTextAndPin('Log in to example.com')];

$request = new DeviceLinkAuthenticationRequest(
    relyingPartyUUID: '00000000-0000-4000-8000-000000000000',
    relyingPartyName: 'DEMO',
    rpChallenge: $rpChallenge,
    hashAlgorithm: HashAlgorithm::SHA512,
    allowedInteractionsOrder: $interactions,
    certificateLevel: CertificateLevel::QUALIFIED,
);

$response = $connector->initiateDeviceLinkAuthentication($request);

// Store these on the backend for later use
$sessionId = $response->getSessionID();
$sessionToken = $response->getSessionToken();
$sessionSecret = $response->getSessionSecret(); // Keep secret, do not expose to client
$deviceLinkBase = $response->getDeviceLinkBase();

// Using the session object (simplest approach)
$session = $builder->initiate();

// QR code URL auto-calculates elapsed time from session creation
$qrCodeUrl = $session->buildQrCodeUrl();

// Or provide elapsed seconds explicitly
$qrCodeUrl = $session->buildQrCodeUrl(elapsedSeconds: 5);

$qrCodeUrl = $session->createDeviceLinkBuilder()
    ->withElapsedSeconds($elapsedSeconds)
    ->withDemoEnvironment() // for demo environment
    ->withLang('est')       // override language
    ->buildQrCodeUrl();

use Sk\SmartId\Util\CallbackUrlUtil;
use Sk\SmartId\Util\CallbackUrlValidator;

// Validate and create callback URL with a cryptographically random token
$callbackBase = 'https://your-app.com/callback';
$callbackResult = CallbackUrlUtil::createCallbackUrl($callbackBase);
$callbackUrl = $callbackResult['callbackUrl']; // e.g., https://your-app.com/callback?value=<random-token>
$callbackToken = $callbackResult['token'];        // Store to verify the callback later

// Callback URL must be set when initiating the session
$session = $builder
    ->withCallbackUrl($callbackUrl)
    ->withAllowedInteractionsOrder([
        DeviceLinkInteraction::displayTextAndPin('Log in'),
    ])
    ->initiate();

$web2AppUrl = $session->buildWeb2AppUrl();

$builder = $session->createDeviceLinkBuilder()
    ->withDemoEnvironment()             // override scheme for demo
    ->withLang('est')                  // override language
    ->withElapsedSeconds($elapsed);

$qrCodeUrl = $builder->buildQrCodeUrl();

$builder->withAllowedInteractionsOrder([
    DeviceLinkInteraction::confirmationMessage('Up to 200 characters of text here..'),
    DeviceLinkInteraction::displayTextAndPin('Up to 60 characters of text here..'),
]);

$builder->withAllowedInteractionsOrder([
    DeviceLinkInteraction::confirmationMessage('Up to 200 characters of text here..'),
]);

use Sk\SmartId\Notification\NotificationAuthenticationRequestBuilder;
use Sk\SmartId\Notification\NotificationInteraction;
use Sk\SmartId\Enum\CertificateLevel;
use Sk\SmartId\Model\SemanticsIdentifier;

// Create semantics identifier:
// Type: PNO (personal number), PAS (passport), IDC (national identity card)
// Country: 2-letter ISO 3166-1 alpha-2 code
$semanticsIdentifier = SemanticsIdentifier::forPerson('EE', '30303039914');
// Or from a full string:
// $semanticsIdentifier = SemanticsIdentifier::fromString('PNOEE-30303039914');

$builder = new NotificationAuthenticationRequestBuilder(
    $connector,
    '00000000-0000-4000-8000-000000000000', // relyingPartyUUID
    'DEMO',                                  // relyingPartyName
);

$session = $builder
    ->withSemanticsIdentifier($semanticsIdentifier)
    ->withCertificateLevel(CertificateLevel::QUALIFIED)
    ->withAllowedInteractionsOrder([
        NotificationInteraction::confirmationMessageAndVerificationCodeChoice('Log in to example.com'),
        NotificationInteraction::displayTextAndPin('Log in to example.com'),
    ])
    ->initiate();

// Display verification code to the user
$verificationCode = $session->getVerificationCode();

// Use session ID to poll for status
$sessionId = $session->getSessionId();

$session = $builder
    ->withDocumentNumber('PNOLT-40504040001-MOCK-Q')
    ->withCertificateLevel(CertificateLevel::QUALIFIED)
    ->withAllowedInteractionsOrder([
        NotificationInteraction::displayTextAndPin('Log in to example.com'),
    ])
    ->initiate();

$verificationCode = $session->getVerificationCode();
$sessionId = $session->getSessionId();

$builder->withAllowedInteractionsOrder([
    NotificationInteraction::confirmationMessageAndVerificationCodeChoice('Up to 200 characters of text here...'),
    NotificationInteraction::confirmationMessage('Up to 200 characters of text here...'),
    NotificationInteraction::displayTextAndPin('Up to 60 characters of text here...'),
]);

$builder->withAllowedInteractionsOrder([
    NotificationInteraction::confirmationMessageAndVerificationCodeChoice('Up to 200 characters of text here...'),
]);

use Sk\SmartId\Session\SessionStatusPoller;

$poller = new SessionStatusPoller($connector);

// Poll until session completes (blocks with long polling)
$sessionStatus = $poller->pollUntilComplete($sessionId);

if ($sessionStatus->isComplete()) {
    $endResult = $sessionStatus->getResult()->getEndResult();
    // 'OK' means authentication succeeded
}

$poller = new SessionStatusPoller($connector);
$poller->setPollTimeoutMs(30000);  // Server-side long poll timeout (default: 30s)
$poller->setPollIntervalMs(1000);  // Interval between poll attempts (default: 1s)

// Limit the number of poll attempts
$sessionStatus = $poller->pollUntilComplete($sessionId, maxAttempts: 60);

$poller = new SessionStatusPoller($connector);

$sessionStatus = $poller->poll($sessionId);

if ($sessionStatus->isRunning()) {
    // Session still in progress — refresh QR code and poll again
} elseif ($sessionStatus->isComplete()) {
    // Proceed to validation
}

$sessionStatus = $connector->getSessionStatus($sessionId, timeoutMs: 1000);

use Sk\SmartId\Validation\TrustedCACertificateStore;

// Create via SmartIdClient (OCSP revocation checking is enabled automatically)
$validator = $client->createAuthenticationResponseValidator();

// PRODUCTION — load bundled certificates
TrustedCACertificateStore::loadFromDefaults()->configureValidator($validator);

// DEMO — load test certificates (upload certs to demo OCSP first, see "Uploading certificates to demo OCSP")
// TrustedCACertificateStore::loadTestCertificates()->configureValidator($validator);

// Custom directory
// TrustedCACertificateStore::loadFromDirectory('/path/to/certs')->configureValidator($validator);

// Manual certificates
// $store = TrustedCACertificateStore::create()
//     ->addCertificate($pemEncodedCert)
//     ->addCertificateFromFile('/path/to/cert.pem.crt');
// $store->configureValidator($validator);

use Sk\SmartId\Validation\TrustedCACertificateStore;
use Sk\SmartId\Enum\CertificateLevel;
use Sk\SmartId\Enum\SchemeName;
use Sk\SmartId\DeviceLink\DeviceLinkInteraction;

// Set up validator (demo environment — upload certs to demo OCSP first, see "Uploading certificates to demo OCSP")
$validator = $client->createAuthenticationResponseValidator();
TrustedCACertificateStore::loadTestCertificates()->configureValidator($validator);

// The interactions Base64 value must match what was sent in the original request
$interactions = [DeviceLinkInteraction::displayTextAndPin('Log in to example.com')];

// Initiate the authentication session
$session = $client->createDeviceLinkAuthentication()
    ->withRpChallenge($rpChallenge)
    ->withHashAlgorithm(HashAlgorithm::SHA512)
    ->withAllowedInteractionsOrder($interactions)
    ->initiate();

// The session now contains the pre-encoded interactions Base64 string
// This ensures the same encoding is used for both API requests and signature verification
$interactionsBase64 = $session->getInteractionsBase64();

// Validate and extract identity
$identity = $validator->validate(
    $sessionStatus,
    $rpChallenge,                             // Base64-encoded RP challenge from the original request
    'DEMO',                                   // Relying Party name
    $interactionsBase64,                      // Base64-encoded interactions JSON from the session
    

$identity = $validator->validate(
    $sessionStatus,
    $session->getRpChallenge(),
    'DEMO',
    $session->getInteractionsBase64(),
    

// 1. Verify session secret from callback URL
$validator->verifySessionSecret(
    $sessionSecret,        // from the original session response
    $sessionSecretDigest,  // from the callback URL query parameter
);

// 2. Verify user challenge from callback URL
$validator->verifyUserChallenge(
    $userChallengeVerifier,      // from the callback URL query parameter
    $userChallengeFromResponse,  // from session status response signature.userChallenge
);

// 3. Then proceed with standard validation — pass the callback URL for Web2App signature verification
$identity = $validator->validate(
    $sessionStatus,
    $rpChallenge,
    'DEMO',
    $interactionsBase64,
    $callbackUrl,                                 // initialCallbackUrl used in Web2App flow
    

use Sk\SmartId\Util\CallbackUrlUtil;

// Generate a callback URL with a cryptographically random token
$result = CallbackUrlUtil::createCallbackUrl('https://your-app.com/callback');
$callbackUrl = $result['callbackUrl']; // e.g., https://your-app.com/callback?value=abc123...
$token = $result['token'];             // Store this to verify the callback later

// Use the callback URL when initiating the session
$session = $builder
    ->withCallbackUrl($callbackUrl)
    ->withAllowedInteractionsOrder([...])
    ->initiate();

use Sk\SmartId\Util\CallbackUrlUtil;

// Throws ValidationException if digest does not match
CallbackUrlUtil::validateSessionSecretDigest(
    $sessionSecretDigest,  // from the callback URL query parameter
    $sessionSecret,        // from the original session init response
);

$identity->getGivenName();    // e.g., 'QUALIFIED OK1'
$identity->getSurname();      // e.g., 'TESTNUMBER'
$identity->getFullName();     // e.g., 'QUALIFIED OK1 TESTNUMBER'
$identity->getIdentityCode(); // e.g., '30303039914'
$identity->getCountry();      // e.g., 'EE'

// Additional methods for Baltic states (EE, LV, LT):
$identity->getDateOfBirth();  // DateTimeImmutable or null
$identity->getGender();       // 'M', 'F', or null
$identity->getAge();          // int or null

$documentNumber = $sessionStatus->getResult()->getDocumentNumber();

// Device link authentication with IP address sharing
$session = $builder
    ->withCertificateLevel(CertificateLevel::QUALIFIED)
    ->withAllowedInteractionsOrder([
        DeviceLinkInteraction::displayTextAndPin('Log in to example.com'),
    ])
    ->withShareMdClientIpAddress()
    ->initiate();

// After session completes, retrieve the device IP address
$sessionStatus = $poller->pollUntilComplete($session->getSessionId());
$deviceIpAddress = $sessionStatus->getDeviceIpAddress(); // IP address or null

use Sk\SmartId\Exception\SmartIdException;
use Sk\SmartId\Exception\UserRefusedException;
use Sk\SmartId\Exception\UserRefusedInteractionException;
use Sk\SmartId\Exception\SessionTimeoutException;
use Sk\SmartId\Exception\WrongVerificationCodeException;
use Sk\SmartId\Exception\UserAccountException;
use Sk\SmartId\Exception\DocumentUnusableException;
use Sk\SmartId\Exception\RequiredInteractionNotSupportedException;
use Sk\SmartId\Exception\ProtocolFailureException;
use Sk\SmartId\Exception\ServerErrorException;
use Sk\SmartId\Exception\ValidationException;
use Sk\SmartId\Exception\UnderMaintenanceException;

try {
    $sessionStatus = $poller->pollUntilComplete($sessionId);
    $identity = $validator->validate($sessionStatus, ...);
} catch (SessionTimeoutException $e) {
    // User did not respond in time
} catch (UserRefusedInteractionException $e) {
    // User refused a specific interaction — check which one
    $interaction = $e->getInteraction(); // e.g., 'displayTextAndPIN'
} catch (UserRefusedException $e) {
    // User refused the operation (covers USER_REFUSED and all USER_REFUSED_* variants)
} catch (WrongVerificationCodeException $e) {
    // User selected wrong verification code
} catch (DocumentUnusableException $e) {
    // Document is unusable for this operation
} catch (RequiredInteractionNotSupportedException $e) {
    // Required interaction not supported by user's app
} catch (UserAccountException $e) {
    if ($e->isNoSuitableAccount()) {
        // No suitable Smart-ID account
    } elseif ($e->isPersonShouldViewApp()) {
        // User should check Smart-ID app
    } elseif ($e->isClientTooOld()) {
        // Client-side API too old
    }
} catch (ProtocolFailureException $e) {
    // Protocol failure — retry may help
} catch (ServerErrorException $e) {
    // Smart-ID server error — retry later
} catch (ValidationException $e) {
    // Authentication response validation failed — do not trust!
} catch (UnderMaintenanceException $e) {
    // System is under maintenance (HTTP 580) — retry later
} catch (SmartIdException $e) {
    // General Smart-ID error (

use Psr\Log\LoggerInterface;
use Sk\SmartId\SmartIdClient;
use Sk\SmartId\Ssl\SslPinnedPublicKeyStore;

// Example with Monolog
$logger = new \Monolog\Logger('smart-id');
$logger->pushHandler(new \Monolog\Handler\StreamHandler('php://stderr', \Monolog\Level::Debug));

$client = new SmartIdClient(
    relyingPartyUUID: '00000000-0000-4000-8000-000000000000',
    relyingPartyName: 'DEMO',
    hostUrl: 'https://sid.demo.sk.ee/smart-id-rp/v3',
    sslPinnedKeys: SslPinnedPublicKeyStore::loadDemo(),
    logger: $logger,
);