Admin Experience

Members sign in to your site, not to your admin

A second auth collection gives your readers, clients or students an account of their own. They log in on your pages, they see the sections their group allows, and the dashboard stays closed to them.

The default auth collection is the one the dashboard uses. Build a second collection from the same auth schema — members, clients, students — and those people sign in on your own pages instead. Users in a custom auth collection are not allowed into the dashboard at all. One exception: a user in the default auth collection who is also in the admin group is a super admin and reaches everything, whichever collection a page checks.

Locking a whole page sends a visitor who is not signed in to the central login page, and returns them to the page they asked for once they are. Locking a section is a Twig conditional: cms.auth.userLoggedIn('members') around the part of the page members get, cms.auth.userHasAccess() when a section belongs to one group, and cms.auth.userData() for the signed-in person's own fields. cms.auth.login() and cms.auth.logout() build the URLs, each taking the collection and where to land afterwards.

Members reset their own passwords. /forgot-password handles the default collection and /forgot-password/clients a custom one; the reset link carries a single-use token that expires after 30 minutes by default, and the response reads the same whether the address has an account or not, so the form cannot be used to work out who is a member. The emails use a built-in template or a mailer of your own, and the login and reset screens take whitelabel templates above and below the form.

Self-signup is a form and an opt-in. cms.form.builder('members', {register: true}) posts to /admin/register/{collection}, and that collection has to be listed in auth.publicRegistration in config/tcms.php or the endpoint answers 403. The list is empty by default, so the operator-only auth collection is never exposed by accident. Turn on Require Email Verification for the collection and the account is created inactive behind a tokenized link; leave it off and the registrant is signed in as part of the same request. Either way, anyone who reaches that endpoint can create a record, so put a CAPTCHA or a rate limit in front of the form, and check which access group new users land in — whatever that group reaches, every signup reaches.

A client portal is the shape this takes most often: a clients collection for the accounts, a group per client, pages whose sections check that group, and the deliverables in a collection whose groups list names them, so a shared download URL turns away everyone else.

What you get

Your own login collection

Create a second collection from the auth schema for members or clients. They authenticate on your pages and are never allowed into the dashboard.

Whole page or one section

A page lock redirects to the login screen and back again after sign-in. A section lock is a Twig conditional on userLoggedIn() or userHasAccess().

Password reset included

Forgot-password and reset-password routes per collection, single-use tokens that expire in 30 minutes, and a reply that never reveals whether an account exists.

Self-signup behind an allow-list

A register form only works for collections named in auth.publicRegistration. Everything else gets a 403, and email verification is a per-collection switch.

Groups decide the reach

Access groups say which sections a member sees, and a collection's groups list says who can download or stream its files. Both are in Standard.

In practice

One snippet

{% if cms.auth.userLoggedIn('members') %}
	{% set user = cms.auth.userData() %}
	<p>Welcome back, {{ user.name }}</p>

	{% if cms.auth.userHasAccess('premium') %}
		{# only the premium group sees this #}
	{% endif %}

	<a href="{{ cms.auth.logout('/') }}">Sign out</a>
{% else %}
	<a href="{{ cms.auth.login('members') }}">Sign in</a>
{% endif %}

A section lock. login() takes the collection and an optional redirect — null returns the visitor to the current page, and a string sends them anywhere you name.

FAQ

Common questions

Are members separate from the people who edit the site?

Yes, when you put them in their own collection. The dashboard authenticates against the default auth collection only, so users in a custom auth collection can sign in on your pages and nowhere else. The one crossover is a super admin — a user in the default auth collection who is in the admin group bypasses every check, including ones written against your members collection.

Can members use passkeys instead of a password?

Passkeys are included in Standard, and cms.auth.passkeyManager() renders the registration and management interface inside a template of your own, so it does not have to live in the admin.

Can visitors sign themselves up?

Yes, once you opt the collection in. Build the form with cms.form.builder('members', {register: true}) and add the collection to auth.publicRegistration in config/tcms.php — without that entry the endpoint returns 403. Decide the follow-up per collection: Require Email Verification creates the account inactive and mails a tokenized link, otherwise the registrant is signed in with the same request. Neither stops a bot from creating records, so add a CAPTCHA or a rate limit yourself.

What can a signed-in member actually see?

Whatever your templates decide. Nothing is gated until a page or a section checks userLoggedIn() or userHasAccess(), and files are a separate control — a collection's groups list is what stops a shared download or stream URL from working for people outside those groups. Check which group new registrants land in before you open signups, because that group's reach becomes theirs.