TutorialsCustomer sign-in (magic link)

Customer sign-in (magic link)

Orchard customers are passwordless: a shopper enters their email, gets a link, and clicking it signs them in. No passwords to store or reset.

Customer accounts are an opt-in, billable module. Confirm the operator has enabled it for your tenant before building this.

The flow

 enter email ──▶ POST /customer/magic-link/request
                      │  (Orchard emails a link with a one-time token)
 click email link ──▶ your page reads the token from the URL


                 POST /customer/magic-link/consume  ──▶ session (JWT + refresh)


                 GET /customer/me   (Authorization: Bearer <session jwt>)
curl -X POST https://api-test.theorchard.dev/api/v1/customer/magic-link/request \
  -H "x-tenant-slug: your-slug" \
  -H "Content-Type: application/json" \
  -d '{ "email": "shopper@example.com" }'

This always responds the same way ({ "ok": true }) whether or not the email exists — it never reveals whether someone has an account. Orchard sends the email containing a link back to your site with a one-time token, e.g. https://your-store.com/auth/callback?token=....

2. Consume the token

On your callback page, exchange the token for a session:

curl -X POST https://api-test.theorchard.dev/api/v1/customer/magic-link/consume \
  -H "x-tenant-slug: your-slug" \
  -H "Content-Type: application/json" \
  -d '{ "token": "<token-from-the-link>" }'

You get back a session JWT (and a refresh mechanism). Store the session securely (an HttpOnly cookie is best). The JWT is short-lived (15 minutes) — see refresh below.

3. Use the session

Send the session JWT as a Bearer token for customer-scoped calls:

curl https://api-test.theorchard.dev/api/v1/customer/me \
  -H "Authorization: Bearer <session-jwt>"

Customer-scoped surfaces (the customer portal) let a signed-in shopper see and manage their own data:

ActionEndpoint
Current customerGET /api/v1/customer/me
Orders / subscriptions / returnsGET /api/v1/customer-portal[...]
Cancel a subscriptionPOST /api/v1/customer-portal/cancel
Start a returnPOST /api/v1/customer-portal/returns

4. Refresh and sign out

The session JWT expires after 15 minutes; refresh it on a rolling basis (up to 30 days) so the shopper stays signed in:

curl -X POST https://api-test.theorchard.dev/api/v1/customer/refresh \
  -H "Authorization: Bearer <session-jwt>"   # returns a fresh session
 
curl -X POST https://api-test.theorchard.dev/api/v1/customer/signout \
  -H "Authorization: Bearer <session-jwt>"   # ends the session

Security notes

  • Each tenant’s customer sessions are signed with a per-tenant secret — a session from one store is meaningless at another.
  • Keep the session JWT out of localStorage if you can; prefer an HttpOnly, Secure cookie set by your server.
  • See Authentication for the full session model.