KPS Tester: Debugging Türkiye's Identity Verification Service
KPS Tester reproduces the complete Kimlik Paylaşım Sistemi (KPS) v2 identity verification flow: a WS-Trust 1.3 STS handshake, an Exclusive-C14N and HMAC-SHA1 signed WS-Security request, and a TumKutukDogrulama query across all population registries. Test the real service from an approved network without deploying an application.
Türkiye's KPS (Kimlik Paylaşım Sistemi) is the identity-sharing service run by the General Directorate of Population and Citizenship Affairs (NVI). It's built on SOAP and the WS-* security stack, a mature and well-defined standard, but one that asks for more care than a typical REST API. Tokens, XML canonicalization and message signatures all have to line up exactly before the first verification succeeds.
We built KPS Tester to make that part easier. It's a single Python script that runs the complete KPS v2 authentication and verification flow from your terminal, prints every request and response, and tells you clearly whether the identity was confirmed.
Why a standalone tester?
KPS isn't reachable from just anywhere. Access is limited to authorized institutions on approved networks, typically through a VPN or an allow-listed IP range. You can check that a host reaches the endpoint, but that alone doesn't tell you whether your credentials, token exchange and signatures actually work.
The usual way to find out is to deploy something: an application, or a small service written just for testing. That's a lot of effort to answer one question. KPS Tester needs only Python and two libraries, so you can copy it to any machine already inside the approved network and test the real service in seconds, with no deployment.
It's just as useful later on. When verifications start failing, running the tester from the same network quickly separates the possible causes: credentials, network access, request signing, or the data being checked.
What it does
KPS v2 sits behind a WS-Trust / WS-Security handshake. The script performs all of it:
- Get a token. It sends a WS-Trust 1.3
RequestSecurityTokenwith your username and password to the STS (Security Token Service) and receives a SAML assertion plus a proof key (BinarySecret). - Sign the query. It builds a
wsu:Timestamp, canonicalizes it with Exclusive XML C14N, and signs it with HMAC-SHA1 using the proof key. The signature references the SAML assertion by its ID. - Verify. It sends the signed
TumKutukDogrulamaquery (ID number, first name, surname, date of birth) to the KPS routing service and parses the result.
pip install -r requirements.txt python kps_test.py \ --username <sts_username> --password <sts_password> \ --tc <id_number> --name <FIRST_NAME> --surname <SURNAME> \ --birth DD-MM-YYYY
Exit codes make it easy to script around: 0 means the identity was verified, 1 means it was not, and 2 means something else went wrong (network, parsing, or a token problem).
One query, three registries
The current KPS v2 service (namespace 2025/08/01) checks a person against all population registries in one call:
- TC Vatandaşı Kütüğü: Turkish citizens
- Mavi Kartlı Kütüğü: Blue Card holders (former citizens)
- Yabancı Kütüğü: foreign residents (ID numbers starting with
98or99)
This is where most "it says not found but the person exists" confusion comes from. The response carries an error block for each registry that doesn't apply. Checking a citizen, you will always see "record not found" for the Blue Card and foreigner registries. That's expected, not a failure. The tester only reports a top-level error as an error, reads DoluBilesenler to show which registry actually matched, and prints the full record from that registry: name, date of birth, status code, nationality, and date of death if there is one.
The details that cost us time
None of these are hard once you know them. All of them are invisible until you do.
Exclusive canonicalization, not inclusive. The digest and the signature are computed over Exclusive C14N output, without comments. Get this wrong and the signature won't validate. The script uses lxml's write_c14n(exclusive=True) so the bytes it signs are exactly the bytes the server checks.
The proof key signs, not your password. After the STS handshake your credentials are out of the picture. The BinarySecret from the STS response is the HMAC key, and the KeyIdentifier points at the SAML assertion. Mix those up and you get a valid-looking request that never verifies.
Short-lived timestamps. Each request carries a five-minute Created/Expires window. A server with a drifting clock will fail in ways that look like signature errors. Check NTP before you check your code.
Connection: close on every request. The services run on WCF, and its keep-alive handling caused intermittent failures on reused connections. The tester sends Connection: close and fetches a fresh token on every run. That's slightly slower, and far more predictable.
A note on handling it safely
This is a debugging tool for teams that already hold KPS credentials from NVI. It doesn't grant access to anything, and it only talks to the endpoints you point it at (--sts-url and --kps-url let you aim it at a test environment or a proxy).
Because its job is to show you exactly what goes over the wire, its output includes the raw request envelopes (the STS request carries your username and password) and the identity data you query. Treat that output like the credentials and personal data it is. Run it on a trusted machine, don't paste the output into tickets or chat, and don't run it in CI logs that other people can read.
Try it
The code is on GitHub: github.com/Raspiska-Ltd/nvi-kps-tester. It's one Python file with two dependencies (requests and lxml), and the README is in Turkish.
If you're integrating KPS into an onboarding or KYC flow and keep getting stuck between the STS and the verification call, we've been through it. Get in touch.
Technologies Used
Backend
Other
Related Projects

Hirobaso
The white-label engagement platform for iGaming operators — Social, Competitions, Loyalty, Quests, and Races in one embed. Live under your brand in five working days, with zero player personal data ever crossing to us.
Package Cleaner for macOS
A native macOS application that helps developers reclaim disk space by finding and removing package dependency directories.
Have a project in mind?
Let's work together to bring your ideas to life. Our team of experts is ready to help you build something amazing.
