Blind-ID sign-in
Standard OpenID Connect. Any library that supports it works: point it at the discovery document, use the authorization code flow with a proof key, and ask for the openid scope. Each person presents a government ID from their phone wallet at every sign-in; your site receives an identifier for them and nothing from the ID.
1. Register your site
In the developer console, register the site with the address your library sends people back to, the name and logo a person's wallet shows them when your site asks for their ID, and your privacy policy. You get a client ID and a client secret. A new site is in testing, for a limited number of people; verify your domain and take it live when you're ready. On WordPress, the plugin does all of this with one button.
2. Configure your library
Working examples for ASP.NET Core, Next.js, Python and PHP are on Code samples.
3. What your site receives
In the ID token and from user info:
| Claim | What it is |
|---|---|
| sub | The person's identifier at your site. The same every time they present that ID at your site, unique to your site, and meaningless anywhere else: two sites comparing their users find nothing in common. Use it as the account key. It is computed from the ID at every sign-in and we do not store it. |
| preferred_username | A readable username made from sub, such as irlproof_me_k7q2m9x2vq. Exactly as unique and private as sub. |
| auth_time | When the person presented their ID for this sign-in. |
| site_data | What your site asked the person for during sign-in, set under "What you ask for" in the console, as an object of field and answer. Present only in the token of the sign-in that collected it: IRLProof keeps none of it, so store what you need on your side. A person is asked each needed field once. |
| consent_receipt | A signed receipt naming the fields the person agreed to give you, and when. Sent on every sign-in once it exists. Keep it with the account. |
| age_verification | With the age verification add-on: {"id", "result", "verified_at", "receipt"}, where result is over_18, under_18 or unknown (the ID doesn't carry the answer). {"result": "not_checked"} if the check wasn't made. Let someone in only on over_18. |
| age_over_18 | The same result as a boolean, present only when the ID answered. |
There is no name, email, date of birth or document number, ever. If you need an email, ask for it in your data contract; the person types it.
A new document number is a new person. If someone's ID is replaced with a different number, their next sign-in produces a different sub, and nothing on our side can connect the two: we keep no record of people. If your site needs account recovery across that, keep your own way of doing it, as you would for any sign-in. The same person with a passport and a license is two sub values unless your site lets them link two sign-ins to one account.
4. The button
Put the Blind-ID button on your sign-in page, the way you would Google's or Apple's. The kit is one stylesheet and one block of markup, with a light and a dark version:
Download the button kit blind-id-button.css The sign, on its own
<link rel="stylesheet" href="https://sandbox.irlproof.me/brand/blind-id-button.css"> <button class="blindid-btn blindid-btn--sub blindid-btn--right" style="--bh:44px">…</button> <button class="blindid-btn blindid-btn--sub blindid-btn--right blindid-btn--dark" style="--bh:44px">…</button>
- Size it with --bh, the button height. Everything scales from it; 24, 32, 40 and 52 are the tested sizes.
- Add blindid-btn--auto to follow the reader's light or dark setting, and blindid-btn--block to fill a narrow column.
- The sign hangs past the button's left edge, so keep overflow: visible on whatever wraps it.
- The words never change: "Continue with Blind-ID", with "by IRLProof.me" underneath. Use the kit's markup as it is, the way you would Google's or Apple's button. Below 40px the second line is hidden, so add blindid-btn--sm at 24 and 32px.
- On WordPress, the plugin draws it for you, from the same source as this page.
- The rules for using it, and when to say plain "Sign in" instead, are on Brand.
5. How often the ID is asked for
- At every sign-in. A normal sign-in request asks the person to present their ID each time. There is no session at Blind-ID to fall back on, because nothing about the person is kept.
- prompt=login always asks for the ID, whatever else the request says.
- max_age=N lets a check made in the last N seconds for your site stand, when the same browser comes straight back. Send max_age=0 to require a fresh check.
- prompt=none returns login_required to your redirect address unless a check for your site within max_age is in hand.
- To never reuse a check, whatever your library sends, tick "Ask for the ID at every sign-in" on your site's Settings tab. Sites that check age should leave it ticked: most age laws want the check per visit.
- auth_time in the ID token is always when the ID was actually presented, so you can enforce your own window too.
- How long the person stays signed in to your site afterwards is yours to decide: your session, your cookie, your refresh tokens. Nobody has to tap their wallet daily.
6. Sign-out
Send the browser to https://sandbox.irlproof.me/connect/logout with id_token_hint and a post_logout_redirect_uri you registered. The next sign-in asks for the ID again.
7. Webhooks
Set a webhook address on your site's Webhooks tab to hear about things only IRLProof.me knows:
- subject.deleted: everything we held for that sub at your site (the receipt, sign-in log rows, age-check records) has been deleted, following a deletion request.
POST /your/webhook
IRLProof-Signature: t=1789000000,v1=5f2a...
Content-Type: application/json
{"event":"subject.deleted","sent_at":"2026-09-16T12:00:00Z","sub":"..."}
The signature is HMAC-SHA256, keyed with your webhook secret, over the timestamp, a full stop and the exact body. Refuse anything older than five minutes. Checking code is on Code samples. A delivery that fails is retried five more times, over about eight and a half hours.
8. Receipts
Consent receipts and age verification receipts are ES256 JWTs signed with the keys at /.well-known/irlproof/jwks.json. Check one with any JWT library, or paste it into /receipts.
9. What can interrupt sign-in at your site
Blind-ID presents IDs from Google Wallet under Google's digital identity program. Under it we tell Google which sites use the sign-in and what for, and if Google tells us to stop a site, we have to stop it promptly. What that means for your site, in plain words:
- New sign-ins at your site pause. People already signed in to your site are unaffected: their session is yours.
- We tell you what we were told, pass on your response, and restore sign-in if the request is withdrawn. That can take days.
- It is rare, and it is about a site misrepresenting itself or being a kind of business the program excludes, not about ordinary use.
- If your site cannot tolerate a pause, keep another sign-in method beside the Blind-ID button.
10. Limits and errors
- A request over a rate limit gets 429 Too Many Requests with Retry-After. Token requests are limited per site; sign-in requests per IP address.
- Sites registered through the console have a monthly sign-in allowance, shown on the site's Usage tab.
- Refused requests, failed token calls and limits reached are listed on the site's Problems tab, with what to do about each.