> Source: https://upvera.tech/guides/person-not-bot-without-identity/
> You can make fake accounts expensive without collecting identity by tying each account to keys held on a person's device, ideally ones the platform can attest belong to a genuine app on a genuine device. Passkeys, app device keys, Apple App Attest, Google Play Integrity and Privacy Pass tokens each prove something narrow, and none proves that one person has only one account. Report which signals a sign-in met, as a tier, and let each site set its own bar.

[Guides](/guides/)Explainers

Explainer · · For developers · Upvera ID

# How to tell a person from a bot without asking who they are

Short answer

You can make fake accounts expensive without collecting identity by tying each account to keys held on a person's device, ideally ones the platform can attest belong to a genuine app on a genuine device. Passkeys, app device keys, Apple App Attest, Google Play Integrity and Privacy Pass tokens each prove something narrow, and none proves that one person has only one account. Report which signals a sign-in met, as a tier, and let each site set its own bar.

## The question you can actually answer

"Is this a unique human?" can't be answered without identity, and identity is what you are trying not to collect. A many-accounts attack, called a Sybil attack, only needs each fake account to be cheap. So ask a narrower question: what did this sign-in prove, and how much would it cost to fake a thousand of them?

Every signal below raises that cost in a different way. None of them names the person.

## What each signal proves

| Signal | What it proves | What it does not prove |
| --- | --- | --- |
| Browser key or passkey (WebAuthn) | The same key holder came back, and the key is bound to your site | That a person holds it, or that it is the only one they have |
| Device key in your app | The request came from an install that holds the private key it registered earlier | That the device or the app is genuine |
| Apple App Attest | Apple certifies the key belongs to a valid instance of your app on an Apple device | Who uses the device, or how many devices they own |
| Google Play Integrity | Google reports whether the request came from your genuine app on a genuine Android device | Who uses the device, or how many devices they own |
| Privacy Pass token | An issuer vouched for this client, for example after a challenge, without being able to link that to where the token is spent | Anything about the person, only that the issuer's check passed |
| CAPTCHA | A challenge was solved at that moment | Anything about the next request |

A passkey proves possession of a private key, and the WebAuthn standard lets the authenticator send an attestation statement about itself. Whether one is sent, and what it says, depends on the authenticator and on what your site requests. Treat a passkey as a strong login, not as proof of a device type.

App Attest needs an Apple device that supports it, and Apple's documentation asks apps to fall back gracefully when it is unavailable. Your design needs a lower tier for those users rather than a locked door.

## Report a tier, not a verdict

Different sites need different bars. A comment section might accept any signed key; a vote or a reward might want device attestation. Instead of answering "human: yes or no", report which level the sign-in reached and since when, in a fixed order from weakest to strongest. Each site then decides what is enough for its own action, and you never have to publish the rule that turned a level into a yes.

## Keep it private

-   **Don't store device identifiers.** The public half of the key is enough to recognise a returning install.
-   **Give every site its own identifier for the person.** Otherwise the proof of personhood becomes a tracking ID. See [pairwise identifiers](/guides/pairwise-identifiers/).
-   **Keep attestation data with the provider.** A site needs the level, not the device's certificate chain.
-   **Leave email out by default.** Ask for it only when the person turns it on.

## Common mistakes

-   **Treating one signal as proof of a unique person.** One person can hold many passkeys and many devices, and devices can be bought in bulk.
-   **Checking once, forever.** Keys get lost and devices change hands. Record when a level was reached, and let it be re-proven.
-   **Publishing the thresholds.** If you count accounts per device or per network, keep the numbers private. A published limit tells an attacker exactly how far to go.
-   **Locking out people on older devices.** Without a fallback tier, attestation becomes a device purchase requirement.

## Questions

### Do passkeys stop bots?

They stop cheap account takeover and password stuffing. A bot can still create its own passkeys, so passkeys alone don't stop fake sign-ups.

### Is device attestation the same as identity verification?

No. Attestation says the app and device are genuine. It says nothing about who is holding the device.

### What is Privacy Pass?

An IETF standard for tokens that prove a client passed some check, without letting the site that accepts the token link it to the moment it was issued. Apple's Private Access Tokens are one deployment.

### Can a site learn which device someone uses?

Not from a level alone. It learns that the level was reached and roughly when, which is the point of reporting a tier.

## How Upvera does it

Upvera ID tells a site how strongly the person signing in is verified, with the claim `upvera:human`: one of four tiers, `unsigned`, `web_key`, `device_key` and `device_attested`, and the month it was reached. The site weighs the tier by its own rule. Every site sees the person under a pairwise identifier made for that site alone, and email is off by default. The claim is documented for developers at [upvera.dev](https://upvera.dev/sign-in/), and the design is in the [Upvera ID report](/identity/).

## Sources

-   [Web Authentication Level 3 (W3C)](https://www.w3.org/TR/webauthn-3/): passkeys, key scoping and attestation.
-   [Establishing your app's integrity (Apple)](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity): App Attest and its availability check.
-   [Play Integrity API (Android)](https://developer.android.com/google/play/integrity): Google's app and device verdicts.
-   [RFC 9576: The Privacy Pass Architecture](https://www.rfc-editor.org/rfc/rfc9576): issuance and redemption that can't be linked.
-   [Private Access Tokens (Apple)](https://developer.apple.com/news/?id=huqjyh7k): a Privacy Pass deployment.
-   [Sybil attack](https://en.wikipedia.org/wiki/Sybil_attack): the many-accounts problem.

## Related

[UTR-26-04**Upvera ID**One person. A different key for every door.System reportRead](/identity/)[Explainer**What is a pairwise identifier in OpenID Connect?**How pairwise sub values work in OpenID Connect, how providers derive them, and what a relying party has to do differently.DevelopersRead](/guides/pairwise-identifiers/)[Explainer**How to let an AI agent draft but never publish**Keep the publish tool out of the model's reach, bind the button press to a hash of the exact text, and add undo, scopes and limits.DevelopersRead](/guides/ai-agent-draft-not-publish/)
