> ## Documentation Index
> Fetch the complete documentation index at: https://dokumentation.websale.de/llms.txt
> Use this file to discover all available pages before exploring further.

# EOL- (Login by email)

> The tags for passwordless login by email: request a one-time code or login link, enter the code, build the content of the login email, and display errors specifically.

With login by email, a buyer logs in without a password. They enter their email address on the login page. The shop then sends them an email. The content of this email logs them in. Depending on the configuration, the email contains either a one-time code that the buyer must enter in the shop, or a login link that completes the login directly when clicked.

The `EOL-` tags control both sides of this flow: the login page with request and code entry, and the email with the code or link. In addition, there are two button tags and one form tag that are only used in this context and are therefore described here.

Guide: [Login by email without a password](https://dokumentation.websale.de/v8s/kundenkonto/anmeldung-registrierung.md#login-per-e-mail-ohne-passwort) · Reference: [EmailOTPLogin section](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/kunde-anmeldung-und-kundenkonto.md#emailotplogin)

## Where the tags apply

The entire flow in the shop runs through a single template, namely the login template from the `Templates` section of the shop configuration (`ws_login.htm`). The shop system displays this template in three situations: as the normal login page, after an email has been requested, and after an error. The template itself decides which view is displayed based on the value of `EOL-PendingEmail`.

The email is created from a separate template that is entered in the `EmailOTPLoginMail` section. Only there are `EOL-Code` and `EOL-MagicLink` filled. All other `EOL-` tags are active in both templates.

## Is the function enabled

| Tag | Bracketing applies if |
| - | - |
| `EOL-Enabled` | login by email is active for this request |
| `EOL-MethodIsCode` | the one-time code is set as the method |
| `EOL-MethodIsLink` | the login link is set as the method |

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{EOL-Enabled}
   ... Anforderung und Codeeingabe ...
{/EOL-Enabled}
```

`EOL-Enabled` takes test operation into account. If the configuration contains `allow = test`, the bracketing only applies in the shop's test mode. This lets you build the function in the live shop without it being visible to buyers.

`EOL-MethodIsCode` and `EOL-MethodIsLink` only read the `Method` setting and do not check whether the function is enabled. Therefore, always use them within `EOL-Enabled`.

All three tags are pure bracketings, both positive and negative.

## Request or code entry: EOL-PendingEmail

When a buyer has requested an email, the shop remembers the entered address in the session. `EOL-PendingEmail` outputs it as a single tag and serves as a bracketing as long as it is present. The template uses this to switch between the two views.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{!EOL-PendingEmail}
   ... Eingabe der E-Mail-Adresse ...
{/!EOL-PendingEmail}

{EOL-PendingEmail}
   <p>Wir haben eine E-Mail an <strong>~EOL-PendingEmail~</strong> gesendet.</p>
   ... Codeeingabe oder Hinweis auf den Link ...
{/EOL-PendingEmail}
```

The shop sets the address even if no customer account exists for it. In this case, no email is sent, but the same view is displayed. Visitors therefore cannot tell which addresses are registered in the shop.

The address is removed again when the login succeeds, the buyer cancels the process, sending the email fails, the validity period since the request has expired, or the login is locked due to too many failed attempts. In this case, the template automatically falls back to the request view.

## Form fields and buttons

The request does not need its own form tag. It runs via the normal login form `FORM-LoginKnownUser`. Only the code entry has its own target with `FORM-EOL_Verify`.

| Tag | Returns | Belongs in |
| - | - | - |
| `EOL-MailInput` | the field name for the email address, fixed `ws_eolin_input_email` | request form |
| `BT-EOL_Request` | the name of the button that requests the email, fixed `button_eolin_request` | request form |
| `EOL-CodeInput` | the field name for the one-time code, fixed `ws_eolin_code` | form with `FORM-EOL_Verify` |
| `BT-EOL_CancelVerify` | the name of the button that cancels the code entry, fixed `button_eolin_cancel` | form with `FORM-EOL_Verify` |
| `FORM-EOL_Verify` | the target of the form for the code entry | `action` attribute |

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<form action="~FORM-LoginKnownUser~" method="post">
   <input type="email" name="~EOL-MailInput~" autocomplete="email" required>
   <button type="submit" name="~BT-EOL_Request~" value="1">Login-Code anfordern</button>
</form>
```

`BT-EOL_Request` only outputs something if the function is active and the login template is displayed. On any other template, and when the function is disabled, the `name` attribute remains empty and the button does not trigger any action.

> [!WARNING]
>
> Put the request field in a separate form and not in the existing form with address and password. Both forms may have the same target `FORM-LoginKnownUser`. If the password form contains a required field for login by email, the browser prevents the normal login as long as the field is empty.

Clicking the "Cancel" button deletes the remembered address and returns the buyer to the login page. Because it is in the same form as the code field, it needs the `formnovalidate` attribute. Otherwise, the browser first requires a code before it submits the cancellation at all.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<form action="~FORM-EOL_Verify~" method="post">
   <input type="text" name="~EOL-CodeInput~" maxlength="~EOL-CodeLength~"
          inputmode="numeric" autocomplete="one-time-code" required>
   <button type="submit">Anmelden</button>
   <button type="submit" name="~BT-EOL_CancelVerify~" value="1" formnovalidate>Abbrechen</button>
</form>
```

The button for submitting the code entry does not need a name. The target `FORM-EOL_Verify` is enough for the shop to know what to do.

> [!WARNING]
>
> The shop compares the code character by character, without removing spaces and without normalizing upper and lower case. With `CodeAlphabet = alphanumeric`, the code consists of uppercase letters and digits. Anyone who types it in lowercase gets error 3. In this case, replace `inputmode="numeric"` with `autocapitalize="characters"` and point out the capitalization in the text. No tag tells the template which alphabet is set; you need to know the configuration.

## Content of the email

| Tag | Returns |
| - | - |
| `EOL-Code` | the one-time code in plain text, only with `Method = code` |
| `EOL-MagicLink` | the complete login link, only with `Method = link` |
| `EOL-CodeLength` | the configured length of the code |
| `EOL-ValidityMinutes` | the validity period of the code and link in minutes |

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{EOL-MethodIsCode}
   <p>Ihr Anmeldecode lautet:</p>
   <p class="code"><strong>~EOL-Code~</strong></p>
   <p>Der Code ist ~EOL-ValidityMinutes~ Minuten gültig.</p>
{/EOL-MethodIsCode}

{EOL-MethodIsLink}
   <p><a href="~EOL-MagicLink~">Jetzt anmelden</a></p>
   <p>Der Link ist ~EOL-ValidityMinutes~ Minuten gültig.</p>
{/EOL-MethodIsLink}
```

Both `EOL-Code` and `EOL-MagicLink` are only filled while the email is being created. Afterwards, the shop deletes the code and the link from memory and only keeps a check value. On the login page, both tags therefore return nothing and cannot be used as a bracketing either.

`EOL-MagicLink` is an absolute HTTPS address without a session ID. The buyer can therefore open the link on a different device than the one on which they requested the email. The login then takes place on the device on which they click. If `MagicLinkBindIP = yes` is set in the configuration, this only works if both devices access the internet with the same IP address. Otherwise, error message 5 appears.

The fields `EOL-CodeLength` and `EOL-ValidityMinutes` output the value actually used. A value outside the permitted range has already been pulled to the limit. Both always apply as a bracketing.

## Displaying errors

There is a separate error tag for the request and for the check. As a single tag, it outputs the error number. As a bracketing without a parameter, it applies as soon as the number is not 0. With a parameter, it applies for exactly this number.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{EOL-VerifyError(4)}
   <p class="error">Der Code ist abgelaufen. Bitte fordern Sie einen neuen an.</p>
{/EOL-VerifyError(4)}
```

### EOL-RequestError

| Number | Meaning | Afterwards the shop shows |
| - | - | - |
| 0 | Request accepted. This also applies to an unknown address to which no email is sent. | code entry or note about the link |
| 1 | function not active | login page |
| 2 | email address empty or invalid | request |
| 3 | captcha failed | request |
| 4 | too many requests in the time window, or login locked due to failed attempts | request |
| 5 | email could not be sent | request |
| 6 | internal error | request |

Error 5 also occurs if no template is entered in the `EmailOTPLoginMail` section. It is therefore the typical error of an incomplete setup.

### EOL-VerifyError

| Number | Meaning | Afterwards the shop shows |
| - | - | - |
| 0 | no error | |
| 1 | no request in this session, or validity period since the request has expired | request |
| 2 | too many incorrect entries for this code, or login locked due to failed attempts | code entry or login page |
| 3 | code or link incorrect, already used, or not present for this address | code entry or request |
| 4 | code or link expired | code entry or request |
| 5 | link opened from a different IP address, only with `MagicLinkBindIP = yes` | request |
| 200 to 203 | Code or link was valid, but the login could not be completed: 200 account not found, 201 no access permission for this subshop, 202 account could not be loaded, 203 other error | login page |

For errors 3 and 4, the following view depends on the method. After an incorrect code entry, the buyer stays in the code entry form. After an invalid link, they end up in the request view, often on a device on which there is no remembered address at all.

> [!WARNING]
>
> Therefore, place the output of `EOL-VerifyError` outside the `EOL-PendingEmail` bracketing. Errors 1, 5, and 200 to 203, the lock due to failed attempts, and any error when opening a link appear in the request view. Within `{EOL-PendingEmail}`, they would remain invisible.

For request errors 4 to 6, the shop often also sets `EOL-VerifyError` to `1`, because no valid request was created. In this case, only the request error should be displayed, because otherwise the buyer sees two messages for the same problem.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{EOL-RequestError}
   <p class="error">Die E-Mail konnte nicht angefordert werden (Fehler ~EOL-RequestError~).</p>
{/EOL-RequestError}

{!EOL-RequestError}
   {EOL-VerifyError}
      <p class="error">Die Anmeldung ist fehlgeschlagen (Fehler ~EOL-VerifyError~).</p>
   {/EOL-VerifyError}
{/!EOL-RequestError}
```

An error number only applies to the request in which it occurs. On the next page request, both tags are reset to 0.

## After a successful check

If the code or link is correct, the shop logs the buyer in. A new session ID is assigned, the counter for failed attempts is reset, and the time of the last login is recorded.

If the buyer has set up two-factor login, they do not go directly to the customer account afterwards.

| Configured second factor | Continues to |
| - | - |
| Authenticator app (TOTP) | the check of the second factor |
| Passkey, if passkeys are active in the shop | the passkey check |
| Code by email | not applicable, the buyer has just identified themselves by email |
| none | the customer account |

## The tags at a glance

| Tag | Single tag | Area tag | Negative area tag | Loop tag |
| - | - | - | - | - |
| `EOL-Enabled` | – | yes | yes | – |
| `EOL-MethodIsCode` | – | yes | yes | – |
| `EOL-MethodIsLink` | – | yes | yes | – |
| `EOL-PendingEmail` | yes | yes | yes | – |
| `EOL-RequestError` | yes | yes | yes | – |
| `EOL-VerifyError` | yes | yes | yes | – |
| `EOL-Code` | yes | yes | yes | – |
| `EOL-MagicLink` | yes | yes | yes | – |
| `EOL-CodeLength` | yes | yes | yes | – |
| `EOL-ValidityMinutes` | yes | yes | yes | – |
| `EOL-MailInput` | yes | – | – | – |
| `EOL-CodeInput` | yes | – | – | – |
| `BT-EOL_Request` | yes | – | – | – |
| `BT-EOL_CancelVerify` | yes | – | – | – |
| `FORM-EOL_Verify` | yes | – | – | – |

## Related topics

* Guide: [Login by email without a password](https://dokumentation.websale.de/v8s/kundenkonto/anmeldung-registrierung.md#login-per-e-mail-ohne-passwort)
* Reference: [EmailOTPLogin section](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/kunde-anmeldung-und-kundenkonto.md#emailotplogin)
* Reference: [EmailOTPLoginMail section](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/e-mails.md#emailotploginmail)
* Reference: [FORM- (Forms)](https://dokumentation.websale.de/en/v8s/referenz/tags/form-formulare.md)
* Reference: [Buttons for customer account and login](https://dokumentation.websale.de/en/v8s/referenz/tags/bt/kundenkonto-und-anmeldung.md)


## Related topics

- [Emails](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/e-mails.md)
- [Buttons for customer account and login](https://dokumentation.websale.de/en/v8s/referenz/tags/bt/kundenkonto-und-anmeldung.md)
- [Customer, login, and customer account](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/kunde-anmeldung-und-kundenkonto.md)
- [Login and session](https://dokumentation.websale.de/en/v8s/referenz/tags/affiluser/anmeldung-und-sitzung.md)
- [FORM- (Forms)](https://dokumentation.websale.de/en/v8s/referenz/tags/form-formulare.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.