# Sign-ins you can only pass by picking pictures

Source: https://easeweb.dev/learn/picture-sign-in
Topics: Page structure > Sign-in does not ask people to recognise objects or pictures (WCAG 3.3.9)
The fix: Take the pictures out of the sign-in. Either replace the picture step with an emailed sign-in link, or offer a passkey there with the emailed link beside it, so nobody has to recognise an object or remember which photo was theirs.
Test with: screen reader, keyboard
References: https://www.w3.org/WAI/WCAG22/Understanding/accessible-authentication-enhanced.html https://www.w3.org/WAI/WCAG22/Understanding/accessible-authentication-minimum.html https://www.w3.org/WAI/WCAG22/Techniques/general/G218 https://www.w3.org/TR/turingtest/ https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential/parseRequestOptionsFromJSON_static https://www.cl.cam.ac.uk/~jo262/papers/emperor.pdf

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A bank's sign-in asks for an email address and a password, then shows one more step before the account opens. Banks build that step in two common ways, and both end the same way.

The first is a bot check. Visitors the check doubts, such as someone on a new browser, a shared network or a privacy tool, see a grid of nine pictures under "Select every picture with a bus". The grid usually comes from a CAPTCHA service, drawn in its own frame. Someone who can't tell whether the tram counts, or misses a bus at the edge of a photo, gets a new grid and tries again.

The second is a security photo. When the account was opened, the bank asked people to upload a photo of their own. Now, under "Choose the photo you uploaded", it shows that photo among eight others, and only the right one lets them in. Someone who uploaded it years ago, or who can't remember whether it was the beach or the garden, is stuck.

Either way, the step has only one way through: pick the right pictures. There is no button to email a sign-in link instead, no passkey and nothing else. A screen reader user on the bot check hears "Select every picture with a bus, group", then "Picture 1, checkbox, not checked", and so on to Picture 9, and can't start.

## Why it fails

Success criterion 3.3.9, Accessible Authentication (Enhanced), is level AAA and new in WCAG 2.2. No step of signing in may need a cognitive function test, unless the same step offers another way in that needs none, or a mechanism that helps people pass it. It is the same rule as 3.3.8, the AA criterion, with two of its exceptions taken away. At AA, a test of recognising objects, such as picking the buses, and a test of identifying content the person gave the site, such as a photo they uploaded, are allowed. At AAA, neither is. The Understanding document names both pictures here: a step can't show a selection of images where people choose the ones with a given object, or the one they provided.

Nothing in HTML stands in the way: the grid is ordinary images and checkboxes, or a CAPTCHA service's frame, and a page can put either in a sign-in. Alt text doesn't rescue the step either. A bot check's pictures can't be described without telling the bot the answer, and nobody describes a photo someone uploaded, nor the photos beside it; even if they did, the test of recognising a photo would only become a test of remembering a description. Adding an audio version, as some CAPTCHA services do, doesn't count as the other way in: the Understanding document for 3.3.8 says typing what you hear is transcription, which is a test too.

## Who is affected

The step comes after the password is right, so it stops people who have done everything else the sign-in asked, often when they most need the account, such as to pay a bill.

## What automation and AI miss

The pictures have alt text, the checkboxes and radio buttons have a group with a legend, and the Continue button has a name, so axe and other scanners pass the step. The step comes after the password, and the bot check shows its pictures only to visitors it doubts, so a scan of the sign-in page sees neither. EaseWeb's audit, which never signs in, lists two things as leads: a picture challenge service, such as reCAPTCHA or hCaptcha, inside a sign-in form; and four or more options that are pictures with no words, under a question about pictures, on a sign-in. A person has to sign in to see whether pictures appear and whether a way in with no test sits beside them. A code assistant asked to protect a sign-in can reach for a picture CAPTCHA or a security photo, and asked to make it accessible, can add an audio version, which leaves the test in place.

## Before: every picture with a bus

```html
<form action="/sign-in/check" method="post">
  <p>Signing in as sam@example.com</p>
  <fieldset>
    <legend>Select every picture with a bus</legend>
    <label>
      <input type="checkbox" name="picture" value="1">
      <img src="/check/1.jpg" alt="Picture 1">
    </label>
    …
  </fieldset>
  <button type="submit">Continue</button>
</form>
```

The `…` stands for the other eight pictures, each the same as the first. The markup is what a CAPTCHA service's grid amounts to, whether it is drawn in the page or in the service's frame.

- The only way on is to recognise the buses among the pictures.
- The alt text can't say what each picture shows without giving the answer away.
- Nothing in the step offers another way in.

## Before: the photo uploaded when the account was opened

```html
<form action="/sign-in/check" method="post">
  <p>Signing in as sam@example.com</p>
  <fieldset>
    <legend>Choose the photo you uploaded</legend>
    <label>
      <input type="radio" name="picture" value="1">
      <img src="/check/1.jpg" alt="Photo 1">
    </label>
    …
  </fieldset>
  <button type="submit">Continue</button>
</form>
```

The `…` stands for the other eight photos: the one the person uploaded, and seven the bank put beside it. It differs from the first Before in what it asks:

- The only way on is to pick out the photo uploaded when the account was opened.
- Nobody described the uploaded photo, so each is read as "Photo 1" to "Photo 9", and a screen reader user can't tell which is theirs.
- Nothing in the step offers another way in.

## After: an emailed sign-in link in place of the pictures

```html
<form action="/sign-in/email-link" method="post">
  <p>Signing in as sam@example.com</p>
  <button type="submit">Email me a sign-in link</button>
</form>
```

1. Take out the pictures and their question. There is nothing left to recognise or remember.
2. Send the form to an address where the server emails a link to the account's own address, and the link finishes signing in (technique G218).
3. Name the button for what it does: "Email me a sign-in link".

This fixes both Befores and passes 3.3.9. It asks people to reach their email and open the link before it expires, so say on the next page where the link went and how long it works.

## After: a passkey, with the emailed link beside it

```html
<form action="/sign-in/email-link" method="post">
  <p>Signing in as sam@example.com</p>
  <button type="button" id="passkey">Use a passkey</button>
  <button type="submit">Email me a sign-in link</button>
</form>
```

```js
const passkey = document.querySelector('#passkey');
passkey.addEventListener('click', async () => {
  const options = await fetch('/sign-in/passkey-options').then((response) => response.json());
  const credential = await navigator.credentials.get({
    publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options)
  });
  const result = await fetch('/sign-in/passkey', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(credential.toJSON())
  });
  if (result.ok) location.assign('/account');
});
```

1. Take out the pictures and their question.
2. Add a "Use a passkey" button. When it is pressed, the script asks the server for a challenge and passes it to `navigator.credentials.get()`. The device then asks for a fingerprint, a face or its screen lock, and the server checks the answer it sends back.
3. Keep the "Email me a sign-in link" button beside it, for people with no passkey on this device.

This fixes both Befores and passes 3.3.9. The Understanding document gives Web Authentication, the standard behind passkeys, as a way to sign in with the device instead of a username and password, and unlocking the device isn't a test the site sets. It works only for people who have already made a passkey for the account, which is why the emailed link stays beside it.

## Which option to use

Both pass 3.3.9, and both take the test away for everyone rather than adding a way round it. Use the emailed link alone when the site has no passkeys yet: it is one form and one button, with nothing new to build on the page. Add the passkey when the site supports them: people who have one finish in a single step without leaving the page, and the emailed link still covers everyone else. Where the picture step can't change, such as a CAPTCHA service a policy requires, putting the emailed link beside the pictures also passes, since 3.3.9 allows a test that has another way in beside it; the [guide on CAPTCHAs](/learn/captcha) shows that pattern.

## Implementation decisions

If the pictures were only there to stop programs that try password after password, the step can go without a replacement: limits on attempts per account and per address do that job on the server, with no test for the person, as [CAPTCHAs with no way past them except solving the puzzle](/learn/captcha) explains.

A security picture that the bank only shows, for people to check before they type their password, asks nothing that the sign-in checks, so it isn't the test this criterion is about. It doesn't do much either: in a 2007 study of bank customers, 23 of the 25 who used their own accounts typed their password even when the picture was gone. Picking an uploaded photo out of several is the step 3.3.9 rules out, and the Understanding document for 3.3.8 warns that such personal content can be guessed: one in nine, here. A picture people chose from the bank's own set, rather than uploaded, fails sooner: 3.3.8's exception covers only content the person provided, so picking it out already fails 3.3.8 at AA, the criterion behind [password fields that won't take a pasted password](/learn/password-paste).

Make the emailed link work once and for a short time, and say both on the page that tells people it was sent. A code sent by text message is another second step, but typing it is transcription; it needs the paste and autofill that [one-time code boxes that won't take a pasted code](/learn/one-time-codes) covers.

Offer a passkey beside other ways in, never as the only one. Some people can't use a fingerprint or a face, and EN 301 549 clause 5.3 asks that no single biological characteristic be the only way to identify a user; a device's screen lock PIN works for passkeys too. Asking people to make a passkey once they are signed in, rather than in the middle of signing in, keeps the sign-in itself short.

The rest of signing in needs the same care: a password field that refuses a paste fails 3.3.8; see [password fields that won't take a pasted password](/learn/password-paste).

## Verify the fix

1. Go through the sign-in to the step that showed pictures, and finish signing in without picking any, through the emailed link and, where there is one, the passkey.
2. With a screen reader, and with only a keyboard, reach and use each way the step offers, and confirm each is read with its name.
3. Sign in from a fresh browser or a private window, where a bot check has the least to go on, and confirm that no picture step appears without a way round it.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the sign-in, its bot check or its second step changes.

## Limits

This is a starting pattern, not a guarantee for every sign-in. 3.3.9 is level AAA, which most laws and policies don't require, but the people it protects are the same people a sign-in at AA still shuts out with pictures. It covers signing in, not creating an account or recovering one, though a picture step there stops the same people. An emailed link helps only people who can reach their email, and a passkey only people who have one, so keep more than one way in. Test the whole sign-in, including what appears only to visitors a bot check doubts, with the tools your users rely on.

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
