Feature/token auth rbac and user crud#4
Open
lertrel wants to merge 4 commits into
Open
Conversation
Introduces a /tokens resource for minting and revoking bearer tokens,
and gates all /users endpoints behind Authorization: Bearer authentication.
New endpoints:
POST /tokens — create a bearer token (open, bootstrapping)
DELETE /tokens/:id — revoke a token by id; idempotent (always 204)
Auth behaviour:
- GET/POST/PUT /users now require a valid Authorization: Bearer header
- GET /health remains open
- requireToken is mounted as a global default after the open routes, so
any future router added below it is protected automatically
Implementation details:
- middleware/auth.js — requireToken: case-insensitive scheme check,
trims extracted value to tolerate extra whitespace
- routes/tokens.js — NaN guard on DELETE id (400 before store call);
idempotent DELETE returns 204 whether or not the
token existed
- db/store.js — token collection mirrors users pattern; uses ?? null
so empty-string names are preserved; listTokens
removed (was dead code)
- tests/tokens.test.js — covers create, revoke, idempotent re-revoke,
never-existing id (204), and non-numeric id (400)
- tests/users.test.js — beforeEach mints a token; all /users requests carry
the Bearer header; asserts 401 without a token
Seed data extended to 4 users (Grace Hopper id:3, Linus Torvalds id:4);
nextId bumped to 5 accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Input validation:
- lib/parseId.js (new) — shared helper; rejects NaN, floats, negatives,
and non-numerics in one check (Number.isInteger && >= 1)
- routes/tokens.js — use parseId for DELETE /:id; req.body ?? {} on POST
so a non-JSON body never crashes the handler
- routes/users.js — use parseId for GET/:id and PUT/:id; req.body ?? {}
on POST and PUT for the same reason
- db/store.js — revokeToken no longer returns a boolean; the route is
idempotent and no caller used the return value
- package.json — extend lint glob to include lib/
RBAC:
- middleware/requireAdmin.js (new) — 403 if req.token.role !== 'admin'
- db/store.js — createToken gains role (default 'client') and value params
- routes/tokens.js — DELETE /:id now chains requireToken + requireAdmin;
POST always creates role:'client' regardless of request body
- server.js — seeds an admin token at startup; logs it once if ADMIN_TOKEN
env var is not set
Tests:
- tokens.test.js — admin/client token setup in beforeEach; new tests for
fractional id (400), non-JSON POST (201 not crash), role-in-body ignored,
401 without token, 403 for client token
- users.test.js — new tests for GET/PUT with non-numeric id (400)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Completes full CRUD for the users resource. Mirrors the token revocation
pattern: idempotent (204 whether or not the user existed), id validated
through the shared parseId helper.
- db/store.js — deleteUser(id): splice by index, no return value
- routes/users.js — DELETE /:id: parseId guard then store.deleteUser
- tests/users.test.js — three cases: removes and confirms 404, idempotent
on missing id, 400 on non-numeric id
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Skill (.claude/skills/route-convention-check/SKILL.md):
Proactive skill that auto-triggers when a route handler is added or
changed. Checks auth middleware placement, parseId usage, error response
shape, and store-only data access against project conventions.
Command (.claude/commands/test.md):
/test slash command — runs npm test and reports a structured pass/fail
summary with failing test names and assertion errors if any.
MCP servers (.mcp.json):
- fetch (uvx mcp-server-fetch): lets Claude pull live docs during dev
- memory (npx @modelcontextprotocol/server-memory): persists project
context (decisions, conventions) across sessions. Note: MEMORY_FILE_PATH
is set to an absolute local path and needs adjustment on other machines.
Notes (NOTES.md):
Course notes documenting the MCP server setup, skill, command, hook, and
headless run from Project 3.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.