Platform
Shopper accounts for GoCommerce
A Go package that adds shopper accounts to a store whose core has none: registration, sign-in, a saved address book, password reset and an order history. Guest checkout is untouched — an account is somewhere to keep addresses and find orders again, not a gate in front of buying.
- Platform module
- 4 settings
- 12 tests
- 20 API operations
- ext/identity
What it does
The package calls itself the module the design anticipated: “core carries no customer concept and never will, so accounts live here, in their own tables, reached through their own routes.” A shopper with an account still checks out with a cart token and an email.
Everything mounts under /x/identity/. A session is a bearer token issued by register, login and password reset — the same shape as an operator’s session. An account holds an email, a name and a phone, an address book with at most one default, and the orders it has claimed.
Order history is by claim, not by email. An order joins an account when the client presents the order’s own access token, which only whoever placed the order holds. In the package’s words, matching on email alone would let anyone register an address they do not own and read that person’s purchases.
Configuration
Nothing is required. The setting worth making is the reset link: without it the reset email carries a code to paste instead of a link. There is no panel card; the reference binary’s -identity flag reads the link from the environment.
| Setting | Environment variable | Required | What it does |
|---|---|---|---|
SessionTTL | — | No | How long a sign-in lasts without a refresh. 30 days by default — in the package’s words, a shop, not a bank. |
ResetTTL | — | No | How long a password-reset token stays valid. An hour by default. |
ResetURL | GOCOMMERCE_IDENTITY_RESET_URL | No | The storefront page a reset email links to, with {token} where the token goes. Configured rather than taken from the request, because a URL a client can choose is a URL a phisher can choose. |
Notifier | — | No | Overrides how the reset email is delivered. Left nil, it goes to the engine’s own email notifiers — the ones the order emails use. |
The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own main() you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.
Setting it up
- Install the modulePass identity.New to gocommerce.New, as below, or start the reference binary with -identity. It adds five tables of its own and writes none of the core’s.
- Install an email senderThe reset email goes through the store’s email notifier, Resend or SendGrid, with wording an operator can edit. With neither installed, it is written to the log and the shopper receives nothing.
- Wire the storefrontRegister and sign in at /x/identity/register and /x/identity/login, then send Authorization: Bearer <token> to /x/identity/me and the address and order routes. /x/identity/refresh extends a session.
- Claim orders after checkoutCheckout stays guest-shaped. After an order is placed, the storefront posts its number and access token to /x/identity/me/orders to put it in the account’s history.
main.go
import (
"github.com/itswadesh/gocommerce/core"
"github.com/itswadesh/gocommerce/ext/identity"
)
app, err := gocommerce.New(cfg, identity.New(identity.Config{
ResetURL: "https://shop.example.com/auth/reset-password?token={token}",
})) The package doc’s own example, with its imports. cfg is your gocommerce.Config. Import path github.com/itswadesh/gocommerce/ext/identity.
How it works
-
Passwords, standard library only
PBKDF2-HMAC-SHA256 with 600,000 iterations and a 16-byte salt, the construction the engine uses for operators. Each hash records its iteration count, so raising it never breaks an old password. Eight characters at least.
-
Tokens stored hashed
Session and reset tokens are 32 random bytes, and only their hashes are stored. Changing or resetting a password ends every other session and issues a fresh one.
-
The same answer either way
A wrong email and a wrong password get the same error in the same time — a dummy hash is computed for an address with no account. Asking for a reset says nothing about whether the address has one.
-
Guessing slows down
After five failed sign-ins for an email, or from an address, each further attempt waits: 30 seconds, doubling to 15 minutes, forgotten after an hour. A successful sign-in clears it.
-
Orders by claim
POST /x/identity/me/orders with the order’s number and token links it, and the first account to prove an order keeps it. An order deleted later is simply skipped when the history is read.
-
In the admin
The Accounts screen lists who signed up; reading it needs accounts.read, which managers and staff hold by default. Deleting an account, with its sessions, addresses and order links, needs accounts.erase as well.
What it does not do
Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.
- No email verificationAn account is created for any address typed in, and nothing confirms the shopper owns it. Order history is safe from this because it goes by claim, not by email.
- Passwords onlyNo social sign-in, no one-time codes by SMS or email, and no two-factor authentication.
- One address slows everyoneThe throttle reads the connection’s address, not X-Forwarded-For. Behind a reverse proxy — Caddy, in the Docker quick start — every shopper shares one, so five failed sign-ins in a row slow sign-in for all of them.
- The throttle is per processFailed attempts are counted in memory. Several instances each keep their own count, and a restart forgets it.
- Sign-up says an address is takenRegistering an email that already has an account answers 409 and says so, and password-reset requests are not rate-limited — each one sends an email.
- Orders are not claimed for youNothing links an order placed while signed in to the account. The storefront has to post the claim after checkout, or the order stays out of the history.
FAQ
Questions about the Identity module
Does it replace guest checkout?
No. Checkout keeps taking an email and a cart token whether or not the shopper is signed in. The account adds saved addresses and a history of the orders the shopper has claimed.
How is a signed-in shopper’s order added to their history?
By claim. Checkout returns an access token for the order, and the storefront posts the order number and that token to /x/identity/me/orders. Only whoever placed the order holds the token, so nobody can claim someone else’s purchases by registering their email.
Where does the password-reset email come from?
From the store’s own email notifier — the Resend or SendGrid module — using a template an operator can reword in the admin. It links to Config.ResetURL with the token filled in, or carries the token as a code when no URL is set.
Can an operator see or delete accounts?
Yes. The admin’s Accounts screen lists them for anyone with accounts.read. Deleting one — with its sessions, address book and order links, but not the orders themselves — needs accounts.erase, which no role but the owner holds until it is granted.
Does Svelte Commerce use these accounts?
Not yet. Svelte Commerce’s GoCommerce connector, version 0.1.0, covers catalogue, cart, checkout and order lookup, not customer accounts. A storefront you write can call these routes directly.
Source
Everything on this page is read from ext/identity in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.
Try it against a store of your own
The one-command stack gives you GoCommerce’s API and admin on your own machine in minutes. Add this module to it and try it on test orders before a real one depends on it.