PHPackages                             vatsake/mobile-id-php-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. vatsake/mobile-id-php-client

ActiveLibrary[API Development](/categories/api)

vatsake/mobile-id-php-client
============================

Mobile-ID Relying Party PHP Api client

1.2.1.1(4mo ago)03MITPHPPHP &gt;=7.4||~8

Since Dec 31Pushed 4mo agoCompare

[ Source](https://github.com/vatsake/mid-rest-php-client)[ Packagist](https://packagist.org/packages/vatsake/mobile-id-php-client)[ RSS](/packages/vatsake-mobile-id-php-client/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

MOBILE-ID-PHP-CLIENT
====================

[](#mobile-id-php-client)

Signing functionality
=====================

[](#signing-functionality)

```
// step #1 - validate user input

try {
    $phoneNumber = MidInputUtil::getValidatedPhoneNumber($this->userData['phoneNumber']);
    $nationalIdentityNumber = MidInputUtil::getValidatedNationalIdentityNumber($this->userData['nationalIdentityNumber']);
}
catch (MidInvalidPhoneNumberException $e) {
    echo 'The phone number you entered is invalid';
}
catch (MidInvalidNationalIdentityNumberException $e) {
    echo 'The national identity number you entered is invalid';
}

// step #2 - create client with long-polling.
// withSslPinnedPublicKeys() is optional

$client = MobileIdClient::newBuilder()
        ->withRelyingPartyUUID($this->config['relyingPartyUUID'])
        ->withRelyingPartyName($this->config['relyingPartyName'])
        ->withHostUrl($this->config['hostUrl'])
        ->withLongPollingTimeoutSeconds(60)
        ->withSslPinnedPublicKeys("sha256//k/w7/9MIvdN6O/rE1ON+HjbGx9PRh/zSnNJ61pldpCs=;sha256//some-future-ssl-host-key") // You can remove it (but that isn't recommended)
        ->build();

// step #3 - calculate verification code and display to user

$hash = MobileIdSignatureHashToSign::newBuilder()->withHashInBase64(/* DATA TO BE SIGNED */)->withHashType('sha256')->build();
$verificationCode = $hash->calculateVerificationCode();

// step #4 - display $verificationCode (4 digit code) to user

echo 'Verification code: '.$verificationCode."\n";

// step #5 - create request to be sent to user's phone

$request = SignatureRequest::newBuilder()
        ->withPhoneNumber($phoneNumber)
        ->withNationalIdentityNumber($nationalIdentityNumber)
        ->withHashToSign($hash)
        ->withLanguage(ENG::asType())
        ->withDisplayText("Sign document?")
        ->withDisplayTextFormat(DisplayTextFormat::GSM7)
        ->build();

// step #6 - send request to user's phone and catch possible errors

try {
    $response = $client->getMobileIdConnector()->initSignature($request);
} catch (MidNotMidClientException $e) {
    echo "User is not a MID client or user's certificates are revoked.";
} catch (MidUnauthorizedException $e) {
    echo 'Integration error with Mobile-ID. Invalid MID credentials';
} catch (MissingOrInvalidParameterException $e) {
    echo 'Problem with MID integration';
} catch (MidInternalErrorException $e) {
    echo 'MID internal error';
}

// step #7 - keep polling for session status until we have a final status from phone

$finalSessionStatus = $client
        ->getSessionStatusPoller()
        ->fetchFinalSignatureSessionStatus($response->getSessionID());

// step #8 - get signature result

try {
    $result = $client->createMobileIdSignature($finalSessionStatus, $hash);
}
catch (MidUserCancellationException $e) {
    echo "User cancelled operation from his/her phone.";
}
catch (MidNotMidClientException $e) {
    echo "User is not a MID client or user's certificates are revoked.";
}
catch (MidSessionTimeoutException $e) {
    echo "User did not type in PIN code or communication error.";
}
catch (MidPhoneNotAvailableException $e) {
    echo "Unable to reach phone/SIM card. User needs to check if phone has coverage.";
}
catch (MidDeliveryException $e) {
    echo "Error communicating with the phone/SIM card.";
}
catch (MidInvalidUserConfigurationException $e) {
    echo "Mobile-ID configuration on user's SIM card differs from what is configured on service provider's side. User needs to contact his/her mobile operator.";
}
catch (MidSessionNotFoundException | MissingOrInvalidParameterException | MidUnauthorizedException | MidSslException $e) {
    throw new RuntimeException("Integrator-side error with MID integration or configuration. Error code:". $e->getCode());
}
catch (MidServiceUnavailableException $e) {
    echo "MID service is currently unavailable. User shold try again later.";
}
catch (MidInternalErrorException $internalError) {
    echo "Something went wrong with Mobile-ID service";
}
```

Breaking changes
----------------

[](#breaking-changes)

- Renamed AuthenticationResponse -&gt; SessionResponse

Minor changes
-------------

[](#minor-changes)

- Setting client's public SSL keys is now optional

Original documentation
======================

[](#original-documentation)

Mobile-ID (MID) PHP Rest Client
===============================

[](#mobile-id-mid-php-rest-client)

[![Tests](https://github.com/SK-EID/mid-rest-php-client/actions/workflows/tests.yaml/badge.svg)](https://github.com/SK-EID/mid-rest-php-client/actions/workflows/tests.yaml)[![Coverage Status](https://camo.githubusercontent.com/111b6188b63ea812797fb0c1980dd5a3a0007d2047efe96c15a829508c9d79e9/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f534b2d4549442f6d69642d726573742d7068702d636c69656e742e737667)](https://codecov.io/gh/SK-EID/mid-rest-php-client)[![License: MIT](https://camo.githubusercontent.com/850eae1099d2b05f53383473d7cd51f9bc1ab09b7d0d9e5122f1dd930efdcc6d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6d6173686170652f6170697374617475732e737667)](https://opensource.org/licenses/MIT)

Running locally
---------------

[](#running-locally)

Run `composer install` to get all the dependencies. Then you can run tests `php vendor/phpunit/phpunit/phpunit`

Demo application
----------------

[](#demo-application)

There is a [demo application](https://github.com/SK-EID/mid-rest-php-demo) that you can run locally.

Features
--------

[](#features)

- Simple interface for mobile-id authentication
- Pulling user's signing certificate

This PHP client cannot be used to create digitally signed containers as there no library like [DigiDoc4J](https://github.com/open-eid/digidoc4j) exists for PHP.

Requirements
------------

[](#requirements)

- PHP 7.4 or later
- [PHP must be compiled with GMP support by using the --with-gmp option](https://www.php.net/manual/en/gmp.installation.php)

Installation
------------

[](#installation)

The recommended way to install Mobile-ID PHP Client is through [Composer](https://getcomposer.org/)

```
composer require sk-id-solutions/mobile-id-php-client ""

```

How to use it
-------------

[](#how-to-use-it)

Here are examples of authentication with Mobile-ID PHP client

### You need to have Composer auto loading available for your application

[](#you-need-to-have-composer-auto-loading-available-for-your-application)

```
require_once __DIR__ . '/vendor/autoload.php';
```

### Example of authentication

[](#example-of-authentication)

// See [ReadmeTest.php](blob/master/tests/ReadmeTest.php) for list of classes to 'use'

```

    // step #1 - validate user input

    try {
        $phoneNumber = MidInputUtil::getValidatedPhoneNumber($this->userData['phoneNumber']);
        $nationalIdentityNumber = MidInputUtil::getValidatedNationalIdentityNumber($this->userData['nationalIdentityNumber']);
    }
    catch (MidInvalidPhoneNumberException $e) {
        echo 'The phone number you entered is invalid';
    }
    catch (MidInvalidNationalIdentityNumberException $e) {
        echo 'The national identity number you entered is invalid';
    }

    // step #2 - create client with long-polling.
    // withSslPinnedPublicKeys() is explained later in this document

    $client = MobileIdClient::newBuilder()
            ->withRelyingPartyUUID($this->config['relyingPartyUUID'])
            ->withRelyingPartyName($this->config['relyingPartyName'])
            ->withHostUrl($this->config['hostUrl'])
            ->withLongPollingTimeoutSeconds(60)
            ->withSslPinnedPublicKeys("sha256//k/w7/9MIvdN6O/rE1ON+HjbGx9PRh/zSnNJ61pldpCs=;sha256//some-future-ssl-host-key")
            ->build();

    // step #3 - generate hash & calculate verification code and display to user

    $authenticationHash = MobileIdAuthenticationHashToSign::generateRandomHashOfDefaultType();
    $verificationCode = $authenticationHash->calculateVerificationCode();

    // step #4 - display $verificationCode (4 digit code) to user

    echo 'Verification code: '.$verificationCode."\n";

    // step #5 - create request to be sent to user's phone

    $request = AuthenticationRequest::newBuilder()
            ->withPhoneNumber($phoneNumber)
            ->withNationalIdentityNumber($nationalIdentityNumber)
            ->withHashToSign($authenticationHash)
            ->withLanguage(ENG::asType())
            ->withDisplayText("Log into self-service?")
            ->withDisplayTextFormat(DisplayTextFormat::GSM7)
            ->build();

    // step #6 - send request to user's phone and catch possible errors

    try {
        $response = $client->getMobileIdConnector()->initAuthentication($request);
    }
    catch (MidNotMidClientException $e) {
        echo "User is not a MID client or user's certificates are revoked.";
    }
    catch (MidUnauthorizedException $e) {
        echo 'Integration error with Mobile-ID. Invalid MID credentials';
    }
    catch (MissingOrInvalidParameterException $e) {
        echo 'Problem with MID integration';
    }
    catch (MidInternalErrorException $e) {
        echo 'MID internal error';
    }

    // step #7 - keep polling for session status until we have a final status from phone

    $finalSessionStatus = $client
            ->getSessionStatusPoller()
            ->fetchFinalSessionStatus($response->getSessionID());

    // step #8 - get authenticationResult

    try {
        $authenticationResult = $client
            ->createMobileIdAuthentication($finalSessionStatus, $authenticationHash);

    }
    catch (MidUserCancellationException $e) {
        echo "User cancelled operation from his/her phone.";
    }
    catch (MidNotMidClientException $e) {
        echo "User is not a MID client or user's certificates are revoked.";
    }
    catch (MidSessionTimeoutException $e) {
        echo "User did not type in PIN code or communication error.";
    }
    catch (MidPhoneNotAvailableException $e) {
        echo "Unable to reach phone/SIM card. User needs to check if phone has coverage.";
    }
    catch (MidDeliveryException $e) {
        echo "Error communicating with the phone/SIM card.";
    }
    catch (MidInvalidUserConfigurationException $e) {
        echo "Mobile-ID configuration on user's SIM card differs from what is configured on service provider's side. User needs to contact his/her mobile operator.";
    }
    catch (MidSessionNotFoundException | MissingOrInvalidParameterException | MidUnauthorizedException | MidSslException $e) {
        throw new RuntimeException("Integrator-side error with MID integration or configuration. Error code:". $e->getCode());
    }
    catch (MidServiceUnavailableException $e) {
        echo "MID service is currently unavailable. User shold try again later.";
    }
    catch (MidInternalErrorException $internalError) {
        echo "Something went wrong with Mobile-ID service";
    }

    # step #9 - validate returned result (to protect yourself from man-in-the-middle attack)
    $validator = AuthenticationResponseValidator::newBuilder()
        ->withTrustedCaCertificatesFolder(__DIR__ . "/test_numbers_ca_certificates/")
        ->build();

    $validator->validate($authenticationResult);

    # step #10 - read out authenticated person details

    $authenticatedPerson = $authenticationResult->constructAuthenticationIdentity();

    echo 'Welcome, '.$authenticatedPerson->getGivenName().' '.$authenticatedPerson->getSurName().' ';
    echo ' (ID code '.$authenticatedPerson->getIdentityCode().') ';
    echo 'from '. $authenticatedPerson->getCountry(). '!';
```

See [mid-rest-php-demo](https://github.com/SK-EID/mid-rest-php-demo) for a more detailed real-world example.

Long-polling configuration
==========================

[](#long-polling-configuration)

You have two options for asking status of authentication session. You can configure long polling which means that the server doesn't respond immediately to session status request but waits until there is input from user (User has entered PIN1 or pressed cancel) or if there is a timeout. However, this blocks the thread on caller's side and may be unwanted. For this there is also option to withPollingSleepTimeoutSeconds(2) which means that the client keeps making requests towards the server every 2 seconds.

If you don't set a positive value either to longPollingTimeoutSeconds or pollingSleepTimeoutSeconds then pollingSleepTimeoutSeconds defaults to value 3 seconds.

With long-polling
-----------------

[](#with-long-polling)

```
    $this->client = MobileIdClient::newBuilder()
        ->withHostUrl("https://...")
        ->withRelyingPartyUUID("...")
        ->withRelyingPartyName("...")
        ->withSslPinnedPublicKeys("sha256//...")
        ->withLongPollingTimeoutSeconds(60)
        ->build();
```

Without long-polling
--------------------

[](#without-long-polling)

```
    $this->client = MobileIdClient::newBuilder()
        ->withHostUrl("https://...")
        ->withRelyingPartyUUID("...")
        ->withRelyingPartyName("...")
        ->withSslPinnedPublicKeys("sha256//...")
        ->withPollingSleepTimeoutSeconds(2)
        ->build();
```

Checking if MID API host is trusted
===================================

[](#checking-if-mid-api-host-is-trusted)

When negotiating SSL connection with MID API, the MID server sends a certificate indicating its identity. A public key is extracted from this certificate and sha256 hash of the public key is calculated. This hash must exactly match with one of the hashes provided to this library:

```
    $this->client = MobileIdClient::newBuilder()
        ->withHostUrl("https://...")
        ->withRelyingPartyUUID("...")
        ->withRelyingPartyName("...")
        ->withSslPinnedPublicKeys("sha256//hash-of-current-mid-api-ssl-host-public-key;sha256//hash-of-future-mid-api-ssl-host-public-key")
        ->build();

```

Otherwise, the connection to MID API is aborted before sending or receiving any data.

Internally the library uses [https://curl.se/libcurl/c/CURLOPT\_PINNEDPUBLICKEY.html](https://curl.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html) for this.

Obtaining digest of production API endpoint certificate
-------------------------------------------------------

[](#obtaining-digest-of-production-api-endpoint-certificate)

Open And download mid.sk.ee certificate in PEM format and save it as "mid\_sk\_ee.PEM.cer".

```
openssl x509 -in mid_sk_ee.PEM.cer -pubkey -noout > mid.sk.ee.pubkey.pem
openssl asn1parse -noout -inform pem -in mid.sk.ee.pubkey.pem -out mid.sk.ee.pubkey.der
openssl dgst -sha256 -binary mid.sk.ee.pubkey.der | openssl base64
```

Copy the output (something like "fqp7yWK7iGGKj+3unYdm2DA3VCPDkwtyX+DrdZYSC6o=" and add "sha256//" in front of it) so the outcome would be: "sha256//fqp7yWK7iGGKj+3unYdm2DA3VCPDkwtyX+DrdZYSC6o="

Adding future production certificate
------------------------------------

[](#adding-future-production-certificate)

About once a year the server's SSL certificate gets switched. All RP-s get a notification by e-mail from SK when this is going to happen. Download new certificate and calculate its sha-256 digest (using instructions above) and add the digest to the list by separating it with a semicolon. So the value is going to be something like this:

"sha256//fqp7yWK7iGGKj+3unYdm2DA3VCPDkwtyX+DrdZYSC6o=;sha256//digest-of-future-prod-certificate"

Obtaining digest of demo API endpoint certificate
-------------------------------------------------

[](#obtaining-digest-of-demo-api-endpoint-certificate)

Demo server (tsp.demo.sk.ee) certificate is be available here: or you can download it directly from server.

```
openssl s_client -servername tsp.demo.sk.ee -connect tsp.demo.sk.ee:443  tsp.demo.sk.ee.pem
openssl x509 -in tsp.demo.sk.ee.pem -pubkey -noout > tsp.demo.sk.ee.pubkey.pem
openssl asn1parse -noout -inform pem -in tsp.demo.sk.ee.pubkey.pem -out tsp.demo.sk.ee.pubkey.der
openssl dgst -sha256 -binary tsp.demo.sk.ee.pubkey.der | openssl base64
```

Setting public IP or interface
==============================

[](#setting-public-ip-or-interface)

Sometimes the server has multiple network interfaces or IP addresses and the client needs to specify which one to use for MID requests. This can be done using withNetworkInterface() paramter.

```
    $this->client = MobileIdClient::newBuilder()
        ->withHostUrl("https://...")
        ->withRelyingPartyUUID("...")
        ->withRelyingPartyName("...")
        ->withSslPinnedPublicKeys("sha256//...")
        ->withNetworkInterface("10.11.12.13")
        ->build();
```

Internally this sets [CURLOPT\_INTERFACE flag](https://curl.se/libcurl/c/CURLOPT_INTERFACE.html)

Pulling user's signing certificate
==================================

[](#pulling-users-signing-certificate)

This client also supports downloading user's mobile-id signing certificate.

```
  $client = MobileIdClient::newBuilder()
           ->withRelyingPartyUUID(TestData::DEMO_RELYING_PARTY_UUID)
           ->withRelyingPartyName(TestData::DEMO_RELYING_PARTY_NAME)
           ->withHostUrl(TestData::DEMO_HOST_URL)
           ->withSslPinnedPublicKeys("sha256//k/w7/9MIvdN6O/rE1ON+HjbGx9PRh/zSnNJ61pldpCs=;sha256//some-future-ssl-host-key")
           ->build();

   $request = CertificateRequest::newBuilder()
       ->withPhoneNumber("+37200000766")
       ->withNationalIdentityNumber("60001019906")
       ->build();

   try {
       $response = $client->getMobileIdConnector()->pullCertificate($request);
       $person = $client->parseMobileIdIdentity($response);

       echo 'This is a Mobile-ID user.';
       echo 'Name, '.$person->getGivenName().' '.$person->getSurName().' ';
       echo ' (ID code '.$person->getIdentityCode().') ';
       echo 'from '. $person->getCountry(). '!';
   }
   catch (MidNotMidClientException $e) {
       // if user is not MID client then this exception is thrown and caught already during first request (see above)
       echo "You are not a Mobile-ID client or your Mobile-ID certificates are revoked. Please contact your mobile operator.";
   }
   catch (MissingOrInvalidParameterException | MidUnauthorizedException $e) {
       throw new RuntimeException("Client side error with mobile-ID integration. Error code:". $e->getCode());
   }
   catch (MidInternalErrorException $internalError) {
       echo "Something went wrong with Mobile-ID service";
   }
```

Signing
=======

[](#signing)

Signing is not supported with PHP library.

Set up logging
==============

[](#set-up-logging)

Look into src/Util/Logger.php The most basic option is to add

```
    echo $message."\n";
```

into debug\_to\_console() method.

###  Health Score

32

—

LowBetter than 72% of packages

Maintenance75

Regular maintenance activity

Popularity3

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

136d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/06799e5d32624d2538e602535a169a38953809b00459d0f7e6ddfcc97ffdc61c?d=identicon)[vatsake](/maintainers/vatsake)

---

Top Contributors

[![aasaru](https://avatars.githubusercontent.com/u/6773052?v=4)](https://github.com/aasaru "aasaru (18 commits)")[![rasmuskukk](https://avatars.githubusercontent.com/u/86092912?v=4)](https://github.com/rasmuskukk "rasmuskukk (9 commits)")[![kimmo-sk](https://avatars.githubusercontent.com/u/196067792?v=4)](https://github.com/kimmo-sk "kimmo-sk (7 commits)")[![vatsake](https://avatars.githubusercontent.com/u/36108180?v=4)](https://github.com/vatsake "vatsake (5 commits)")[![Amblikmees](https://avatars.githubusercontent.com/u/43197560?v=4)](https://github.com/Amblikmees "Amblikmees (2 commits)")[![ttoomema](https://avatars.githubusercontent.com/u/17985911?v=4)](https://github.com/ttoomema "ttoomema (2 commits)")[![sanderkaljula180](https://avatars.githubusercontent.com/u/69426898?v=4)](https://github.com/sanderkaljula180 "sanderkaljula180 (2 commits)")[![alvar-sk](https://avatars.githubusercontent.com/u/28784388?v=4)](https://github.com/alvar-sk "alvar-sk (1 commits)")[![sk-natalja](https://avatars.githubusercontent.com/u/70746337?v=4)](https://github.com/sk-natalja "sk-natalja (1 commits)")[![andrevka](https://avatars.githubusercontent.com/u/10754177?v=4)](https://github.com/andrevka "andrevka (1 commits)")[![aixkalur](https://avatars.githubusercontent.com/u/9655268?v=4)](https://github.com/aixkalur "aixkalur (1 commits)")[![IndrekHaav](https://avatars.githubusercontent.com/u/4806952?v=4)](https://github.com/IndrekHaav "IndrekHaav (1 commits)")[![jalukse](https://avatars.githubusercontent.com/u/19568991?v=4)](https://github.com/jalukse "jalukse (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/vatsake-mobile-id-php-client/health.svg)

```
[![Health](https://phpackages.com/badges/vatsake-mobile-id-php-client/health.svg)](https://phpackages.com/packages/vatsake-mobile-id-php-client)
```

###  Alternatives

[stripe/stripe-php

Stripe PHP Library

4.0k143.3M480](/packages/stripe-stripe-php)[twilio/sdk

A PHP wrapper for Twilio's API

1.6k92.9M272](/packages/twilio-sdk)[knplabs/github-api

GitHub API v3 client

2.2k15.8M187](/packages/knplabs-github-api)[facebook/php-business-sdk

PHP SDK for Facebook Business

90121.9M34](/packages/facebook-php-business-sdk)[meilisearch/meilisearch-php

PHP wrapper for the Meilisearch API

73813.7M114](/packages/meilisearch-meilisearch-php)[google/gax

Google API Core for PHP

263103.1M454](/packages/google-gax)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
