Moonlight security model

Who this is for

You are securing Pionia Shop and want to understand where auth runs — at the switch, in middleware, or inside an action.

What you will learn

  • Why Moonlight action payloads use POST JSON bodies
  • How switch-level authentication protects groups of actions
  • The difference between HTTP 401 and envelope returnCode

Before you start

Before you start

How it works

Credentials and business fields travel in the POST JSON body. Authentication backends run before your action — so a missing Bearer token fails early on order.place, not halfway through a wallet debit.

POST bodies and access logs

Moonlight sends credentials and business fields in the request body, not query strings:

{
  "service": "customer",
  "action": "login",
  "email": "ada@pionia.shop",
  "password": "your-secret"
}

Query strings often appear in proxy and CDN access logs. POST bodies typically do not — still use HTTPS in production and never log raw passwords in application code.

Health checks like GET /api/v1/ping remain ordinary GET requests.

Switch-level authentication

Pionia v3 evaluates authentication before dispatching to your action — at the switch layer. Pionia Shop can require JWT for all product.create calls while leaving product.list public, configured on MainSwitch.

See Authentication & authorization for JWT setup with customer.login.

Action-level rules

Prefer attributes on the service or method (#[Authenticated], #[Can], #[CanAny]) so authorization runs before the action body. Use $this->can() inside an action only when the rule depends on the payload or a row (for example “only the assignee may close this task”).

Full guide: Protecting actions with attributes.

Real HTTP status codes

SituationHTTP statusEnvelope
Success200returnCode: 0
Validation error422returnCode non-zero
Not authenticated401error message in envelope
Forbidden403error message in envelope

Do not assume HTTP 200 for every error — clients must read both status and JSON.

Common mistakes

  • Putting passwords in query strings — use POST JSON for customer.login; never ?password= in URLs.
  • Checking auth only inside actions — configure switch-level rules so unauthenticated requests fail before business logic runs.
  • Logging raw request bodies in production — redact passwords; use [logging] HIDE_IN_LOGS in settings.ini.
  • Ignoring HTTP status because returnCode exists — mobile clients must handle 401, 403, and 422 explicitly.

What’s next

Authentication

Implement customer.login with JWT.

Security utilities

Password hashing for customer.login.

Middleware

Request IDs for support tickets.