blogprojectshajspace
Back to blog

reverse engineering egovph qr verification

·3 min read

I wanted to verify eGovPH and PSA IDs without paying for third-party verification services like Sumsub. So I reverse engineered the public QR verification flow from eVerify and rebuilt it as an open source project.

The scope was limited to public endpoints and observable client behavior. No auth tokens, no private APIs—just what's visible in the bundled frontend and source maps.

how the official flow works

I loaded the minified React bundle with source maps enabled and traced the route that handles /check. It mounts a verification page with a QR scanner.

The scanner posts the raw QR string to a public classification endpoint. The response tells the app what type of QR it received and which UI template to render.

For eGovPH codes, there's a Firebase realtime database listener keyed by tracking number. The app waits for consent from the ID owner. Once consent arrives, it fetches the full profile, including a signed URL for the face image.

For PSA codes (National ID, ePhilID, PhilSys Card), the decoded identity data renders immediately—no consent flow needed.

Loading diagram...

the pseudocode

python
function onScan(rawQr):
    result = classifyQr(rawQr)
    if result.type == "eGovPH":
        showConsentWaiting(result.tracking_number)
        consent = waitForConsent(result.tracking_number)
        if consent.accepted:
            profile = fetchEgovProfile(result.tracking_number)
            renderProfile(profile)
    else if result.type in ["National ID", "ePhilId", "Philsys Card"]:
        data = transformIdentity(result)
        renderIdentityModal(data)

function classifyQr(value):
    res = http.post(PUB_API + "/api/pub/qr/check", { value })
    return {
        type: res.json().meta.qr_type,
        ...res.json().data
    }

function waitForConsent(trackingNumber):
    ws = openFirebaseSocket(APP_ID, NAMESPACE)
    subscribe(ws, "/egov_ph_profile_consent/" + trackingNumber)
    loop:
        msg = ws.read()
        if msg.data.is_accepted == true:
            return { accepted: true }
        if timerExpired():
            return { accepted: false }

what I learned

The public bundle and source maps gave me everything I needed to reconstruct the client-visible flow. The consent step uses Firebase realtime database, which is just a WebSocket subscription to a known path.

The face image URLs are signed and short-lived, so you need to fetch and display them immediately. The minimal contract for an open verifier is simple: one classify call, an optional consent wait, and a profile fetch.

why open source this

Verification services charge per check. For civic projects and small businesses, those costs add up fast.

An open implementation lets anyone verify government IDs for free. It makes audits possible, invites contributions for new QR formats, and removes dependence on expensive third-party providers.

If you're building something that needs Philippine ID verification, you shouldn't have to pay rent-seeking middlemen for access to public infrastructure.

Back to blog