Authentication
JWT-based auth flow — registration, login, token shape, and how protected routes verify identity.
Authentication
All SkyTalk API access is secured with JWTs (JSON Web Tokens). This page covers the full auth lifecycle.
Registration
Response 200 OK:
Response 409 Conflict:
Notes:
- All new users are created with
role: "user". There is no public endpoint to register as admin. Promote via the seed script or direct SQL. - If
deviceSerialis provided, the server looks up or creates aDevicerow and links it to the new user. - Password is hashed with bcrypt (cost 10) before storage. The hash is never returned by any endpoint.
Login
Response 200 OK:
Response 401 Unauthorized:
Change Password
Response 200 OK:
JWT Shape
| Claim | Value |
|---|---|
sub | User UUID from the User table |
username | Login name |
role | "admin" or "user" |
iat | Issued-at timestamp |
JWTs are currently issued without an exp (expiry) claim during development. Add expiresIn: '24h' to the app.jwt.sign() call in src/routes/auth.ts before any production deployment.
Using a JWT
REST API
Pass the JWT in the Authorization header:
Any protected route that receives a request without a valid JWT returns 401 Unauthorized.
Admin-only routes additionally return 403 Forbidden if role !== "admin".
WebSocket
Pass the JWT as a query parameter on the initial upgrade request:
The server verifies the JWT before completing the WebSocket handshake. Invalid tokens result in a 1008 Policy Violation close.
Getting a JWT for Testing
To store it as a shell variable:
Shell variables are per-session. If you open a new terminal pane (e.g. in zellij), the $JWT variable won't be set — re-run the login command.