A client portal is a private part of a business website that each client signs in to and finds their own work in. Where their project stands, what has happened lately, what the work is waiting on from them, and their invoices.
It is not an app, and it is not a separate service with its own login screen and its own monthly bill. It is the same website, with a half of it reserved for the people the business works for.
This one runs here, on hansmartens.dev, and has its own page in the portfolio. It is the first of two projects I built to answer the same kind of question: how do you give people access to part of a website, without turning that website into an application with a database behind it.
What the project is
A set of pages inside this website, reached by signing in with an emailed link: a dashboard, a page for each of the client’s projects, and their invoices.
The invoices are read from the bookkeeping over its API while the page loads, so the portal never holds a copy of them. The project pages are Markdown files in the site’s own repository. Who a client is comes from the bookkeeping too — the contact record that exists because that is where their invoices are sent from.
That is the whole of it. No database, no second service to log in to, no monthly bill for the portal. It is built with the rest of the site and deployed with it, on the hosting the site already runs on.
What a client sees
Signing in lands on a dashboard. Three figures sit across the top: the outstanding balance, how many invoices are open, how many projects are active. Under them is a card for each project, and the most recent invoices under that. Every figure is read as the page loads, so none of it can be out of date.
A project card carries the project’s name, a badge naming the phase it is in, and — when the project needs something — a marker reading 2 waiting on you. At a glance, a client knows whether the work is sitting with them or with the business.
Opening a project puts the whole of it on one page. A phase tracker across the top with the current step lit. A checklist of what the project is waiting on from the client, with what has arrived ticked off. A dated list of what has happened, so the history sits in one place instead of scattered through an inbox. A form for asking for a change, which arrives as an email with the client and the project already attached. And whatever links the project needs — a staging address, a shared drive, a design file.
The invoice list shows each invoice’s number, what it was for, its date and due date, and its amount, with a badge reading Open, Paid or Late. Two buttons sit on each row: one opens the payment page, the other downloads the invoice PDF. That PDF is the document from the bookkeeping, not something the portal drew.
How signing in works
A client types the email address their invoices are sent to, and a link arrives. Clicking it signs them in. There is no password to choose and nothing to install.
The link is valid for fifteen minutes. The session it opens lasts thirty days, held in a cookie that the browser will not hand to any script on the page. After that, another link.
There is no signup form and no account to create. A client is a contact in the bookkeeping — the record that exists already, because that is where their invoices come from. Add a contact there and the portal works for them.
The project side is the same idea in the repository. One project is one Markdown file, listing the email addresses that may open it, the phases it runs through, what it is waiting on, and what has happened. Editing that file and pushing it is how a project is updated; the deploy is the publish.
Which businesses need one
Agencies and studios, whose clients want to know where a project stands without having to ask.
Photographers and video studios, whose jobs run through phases over weeks.
Architects, accountants, consultants, planners — anyone billing for work that takes long enough that the client starts to wonder about it.
The common thread is a business whose clients each have their own work in progress, and who ask the same three questions: where are we, is anything happening, and do you need something from me. The portal answers all three at any hour, so nobody has to ask.
It is not tied to one trade. The phases follow how a particular business works — Proposal → Design → Development → Review → Live for a website, Briefing → Shoot → Edit → Review → Delivery for a film — and the checklist items are whatever that project is waiting on.
How it differs from a members area
The members area is the other project, and it answers the opposite half of the same question. It has its own page in the portfolio too.
A members area gives everyone the same. There is one set of hidden content and every member sees all of it — minutes for a club, material for a course, posts kept for readers who asked. Nobody has their own records, because there are none to have.
A client portal gives each person their own. Two clients signing in see two different sets of pages, because the whole point is the work being done for that one client.
Which one a website needs comes down to whether the people signing in are looking at the same thing or at different things.
Why it is safe
Neither project has a password in it, and that is what makes them safer rather than weaker. There is no password to reuse on another site, to be phished out of somebody, to be guessed, or to appear in a breach that had nothing to do with this website. There is none for me to store and none for anyone to reset.
What arrives instead is a cryptographically signed link. Change one character of it and it stops verifying, so it cannot be edited or forged. Clicking it sets a cookie signed the same way, which JavaScript on the page cannot read and which is only sent over HTTPS.
The sign-in page gives nothing away. An address belonging to nobody gets the same answer as one belonging to a client, in the same words, so the form cannot be used to work out who a business’s clients are.
Every route into the portal is guarded in one place rather than page by page, and every request is scoped to the contact who signed in. Asking for an invoice belonging to somebody else returns exactly what asking for an invoice that does not exist returns. The PDFs are passed through the site itself, so the bookkeeping’s access token never reaches a browser and no public address to any document exists. The pages carry noindex and the routes are disallowed in robots.txt, because a client portal has no business in a search result.
And there is no database, so there is no store of anybody’s data to break into. The bookkeeping was already the record; the portal reads it and keeps nothing.
Why it runs on my own website
To make it work and to test it. Building for yourself is the best way, because you can test it all the time — on the site you use every day, where you would notice the morning it broke.
I built both of these because I am interested in this kind of solution on an Astro website, for the businesses and clubs and other organisations that need one. A business needs the client portal. A club needs the members area. It is interesting how it works, and it has to be safe.