feat: add access tokens and rotating refresh tokens #82
+1,206
−78
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.
Summary
The session part of the login: a 15-minute access token and a 14-day refresh token that is replaced at every use. Part of #70. The Google check and
loginare a separate pull request (#71, draft), because Sign in with Google needs https (except on localhost) and the course server has only http.Stacked. The base is
74-graphql-codegen(#78), which is on #77 (calculator), which is on #65 (server). Change the base todevwhen the one below is merged.What's in it
refreshSession(refreshToken): gives a new access token and a new refresh token, and the old one stops working. Using an old one again means it was copied, so the whole login (the family of tokens) is revoked.logout(refreshToken)revokes the login and istruealso for an unknown token.me: the coach for a valid access token (JWT,jose, HS256,SESSION_SECRET),nullfor a missing, wrong, changed, foreign or expired token. Protected operations throwUNAUTHENTICATED.sessionstable (migration included). Expired rows are deleted at each refresh.loginanswersNOT_IMPLEMENTEDuntil feat: login with Google #71 is merged. Until then the tokens can only be made by the code (the tests do it).AuthPayloadhasaccessTokenandrefreshToken(the oldtokenis renamed), andrefreshSessionandlogoutare added.SESSION_SECRET(at least 32 characters, the server does not start with a shorter one). Without it the catalog works andmeisnull.vite.config.tssetstest.fileParallelism: false, because the session tests write to the local database while other tests check that the row counts do not change.docs/API.md(flow, lifetimes, what the client must do, the decision to return the tokens in the body and not in a cookie),docs/DATA_MODEL.md(Session), the README,backend/.env.example,docs/AI_USAGE.md.Decisions to check
docs/API.md).Testing
tsc --noEmitand 152 tests pass against a database (0 skipped). 30 are new:mewith no/changed/foreign/expired token, refresh and rotation, reuse ends the login, expired and unknown refresh tokens,logout, two requests with the same token (only one gets through), a hash is stored and not the token, expired rows are removed, theAuthorizationheader over HTTP, and the settings. The time is set by the tests, and the first tokens are made directly because there is no login yet.🤖 Generated with Claude Code