Who this is for. The theme developer building the sign-in page and the account menu's sign-out link. Afterwards members land where they meant to go, and the error states say no more than they should.
The form
The sign-in page, users/login.html, posts username and password to /login. The account is looked up by username; a member signs in with an email address only where the username is the email address, as it is with the Users setting Use email as username on. Label the field to match.
The page receives localPasswordEnabled, false when password sign-in is switched off for the site (the Local Password setting Enable), so show the password form only when it is true. Buttons for sign-in providers and passkeys are covered in Authenticators, SSO and linked accounts.
Error states, and what not to reveal
A failed sign-in renders the page again with HTTP 400, the message, and the username in user so the field stays filled:
| Case | Message |
|---|---|
| A field left empty | "Please specify username and password." |
| An unknown username, a wrong password, or an account with no password sign-in | "Username / password combination invalid." |
| The right password for an account that is not active | "The user account is inactive - please ensure you have verified your email address." |
| Password sign-in switched off for the site | "Login with local account has been disabled" |
The second row is deliberate: the page never says whether a username exists. Keep it that way in your template. Do not add "No account with that email", and do not show different messages for the cases the platform answers alike. Offer the way forward instead: links to the forgotten-password page and, where it is open, registration.
What happens on success
Sign-in is a short chain of redirects. /login checks the password and redirects to /oauth/authenticate, which starts the session, sets the sign-in cookie and redirects again: to the remember-me prompt when that is on, otherwise to the destination. A script that signs a member in must follow redirects rather than expect an answer from the first response. The sign-in lasts twelve hours (What a sign-in is).
Where the member lands
The destination is the first of these that is set:
- The page they wanted. When a members-only page sends a visitor to sign in, it keeps the page in their session, and they return to it once. A
redirectUrlfield posted with the sign-in form replaces it; only its path, query and fragment are kept, so it cannot send anyone to another site. - The Users setting Login redirect url.
/my-account.
A ?redirectUrl= on the sign-in page's own URL does nothing until your template copies it into the form. It matters: a record refused under the security setting Access Denied set to redirect sends the visitor to /login?redirectUrl=<the record's path>, and the framework's own sign-in template does not pass it on, so the member lands on their account instead of the record (What a refused visitor sees). Copy it into a hidden field, escaped, because it came from the URL: