Security

Who this is for

You are building DeskFlow (or any Pionia API) and need to know who is calling your actions — Alex at Northwind Studio logging in with member.login, receiving a JWT, and only editing tasks they are allowed to change.

What you will learn

  • How authentication backends turn a Bearer token into a ContextUserObject
  • Where to call mustAuthenticate() and can() in TaskService
  • Which guide to read next for crypto helpers vs switch-level wiring

Before you start

Before you start

How it works

DeskFlow lets Northwind Studio members log in and only see tasks they are allowed to edit. Pionia handles that with authentication backends (who you are) and authorization on each action (what you may do).

flowchart LR Client["POST member.login"] --> Switch["/api/v1/ MainSwitch"] Switch --> Member[MemberService] Member --> JWT[JWT in returnData] Client2["POST task.create + Bearer"] --> Switch2["/api/v1/"] Switch2 --> Backend[JwtAuthBackend] Backend --> Task[TaskService] Task --> AuthZ["mustAuthenticate() / can()"] AuthZ --> Action[createAction]

First steps

If you are following the DeskFlow tutorial, Step 9 adds member.login and protects task.create. Start there for a working JWT flow.

Otherwise, read in this order:

StepGuideWhat you learn
1Authentication & authorizationBackends, ContextUserObject, can(), @moonlight-auth
2Security utilitiessecurity(), tokens, hashing, encryption
3ValidationReject bad input before auth runs
4MiddlewareRequest pipeline, CORS, request IDs

DeskFlow scenario

Alex at alex@northwind.studio logs in:

curl -s -X POST http://127.0.0.1:8000/api/v1/ \
  -H "Content-Type: application/json" \
  -d '{"service":"member","action":"login","email":"alex@northwind.studio","password":"secret"}'

The response includes a JWT. Protected actions send Authorization: Bearer … — see Examples for the full envelope.

In TaskService, actions that change data call $this->mustAuthenticate() or $this->can('task.update') so anonymous clients get HTTP 401 with a JSON error envelope.

Guide map

GuideTopic
Authentication & authorizationBackends, secrets, service-level checks
Security utilitiesOTPs, password hashing, encryption helpers
Moonlight security modelSwitch-level auth wiring
MiddlewareGlobal pipeline hooks
ValidationsInput validation on actions

Moonlight actions declare auth with @moonlight-auth PHPDoc tags. Export requirements to OpenAPI via Documenting your API.

Common mistakes

  • Calling mustAuthenticate() only in some mutating actions — protect every action that reads or writes sensitive data consistently
  • Storing JWT_SECRET_KEY in committed settings.ini instead of gitignored environment/.env
  • Expecting auth to run inside the action body — backends run before your method; use $this->auth() to read the result
  • Returning password hashes or raw tokens in API returnData after member.login

What’s next

Authentication & authorization

Scaffold JwtAuthBackend and protect TaskService.

Security utilities

hash_password(), secure_token(), encrypt().

Tutorial Step 9 — Authentication

DeskFlow login and protected task.create.