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.
ContextUserObjectmustAuthenticate() and can() in TaskServicetask.list on port 8000{ service, action } reaches your service classDeskFlow 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).
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:
| Step | Guide | What you learn |
|---|---|---|
| 1 | Authentication & authorization | Backends, ContextUserObject, can(), @moonlight-auth |
| 2 | Security utilities | security(), tokens, hashing, encryption |
| 3 | Validation | Reject bad input before auth runs |
| 4 | Middleware | Request pipeline, CORS, request IDs |
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 | Topic |
|---|---|
| Authentication & authorization | Backends, secrets, service-level checks |
| Security utilities | OTPs, password hashing, encryption helpers |
| Moonlight security model | Switch-level auth wiring |
| Middleware | Global pipeline hooks |
| Validations | Input validation on actions |
Moonlight actions declare auth with @moonlight-auth PHPDoc tags. Export requirements to OpenAPI via Documenting your API.
mustAuthenticate() only in some mutating actions — protect every action that reads or writes sensitive data consistentlyJWT_SECRET_KEY in committed settings.ini instead of gitignored environment/.env$this->auth() to read the resultreturnData after member.loginScaffold JwtAuthBackend and protect TaskService.
hash_password(), secure_token(), encrypt().
DeskFlow login and protected task.create.