Merge the SDAD session into the NextAuth session #199
No reviewers
Labels
No labels
backend
bug
CI
dependencies
design
documentation
duplicate
enhancement
frontend
github_actions
good first issue
help wanted
invalid
javascript
question
refactoring
security
tooling
UX
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
NBIS/sda-download-ui!199
Loading…
Reference in a new issue
No description provided.
Delete branch "104-merge-cookie-sessions"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Related issue(s) and PR(s)
This PR closes #104 and also #146 as a side effect.
The app maintained two parallel session stores: NextAuth's own JWE cookie (next-auth.session-token) for sign-in state, and a custom SessionManager-backed cookie (sdad-session) for the OAuth access token and the user's Crypt4GH public key. This PR consolidates them into the NextAuth session and removes the custom store.
Everything the server needs — access token, expiresAt, Crypt4GH public key — now lives on the NextAuth JWT, encrypted with NEXTAUTH_SECRET and readable only server-side.
The change adhere to previous principles where the access token never crossed to the browser. The client-facing session object (reachable via useSession() and GET /api/auth/session) exposes only
expiresandpemChecksum;Type of change
List of changes made
The JWT contract.
types/next-auth.d.tsaugments both JWT (with accessToken, refreshToken, expiresAt, publicKey) and Session (with pemChecksum). Note that refreshToken is kept here as placeholder since the implementation for using it is pending (#103).Server-only helpers.
app/lib/serverToken.tsprovides:getServerToken()- decrypts and returns the JWT, or null.getSession()- a SessionData-shaped projection over the JWT ({ token, publicKey }), used by every reader that previously imported from@/app/lib/session.updateServerToken(patch)— verifies the current access token against the provider's JWK endpoint, then re-encrypts the cookie with the patch merged in. Refuses to write when the session is missing, has no expiresAt, or is already expired. This is called when the user wants to submit a different c4gh pub key.clearServerToken()— deletes the session cookie plus NextAuth's csrf-token and callback-url cookies for a tidy logout.NextAuth session expiry bound to the access-token TTL. getAuthOptions() overrides jwt.encode to derive maxAge from token.expiresAt, so NextAuth's own exp check, SessionExpiryWatcher, and our helpers all agree on when the session ends.
Access-token verification against JWKS. app/lib/oidc.ts::verifyAccessToken is called on sign-in (extractJWT) and before every patch (updateServerToken). Discovery + JWKS are cached per process. On JOSE errors we clear the cookie and throw SessionInvalidError; on network errors we leave the cookie alone and propagate.
Typed error handling in the Crypt4GH action. Validation errors surface via Crypt4ghValidationError; session-invalid errors surface via SessionInvalidError; anything unexpected is logged server-side and replaced with a generic client message. Prevents future validation-code changes from accidentally leaking sensitive data through the form state.
All callers are migrated to getSession() or getServerToken(). All server components (app/(pages)/*, app/page.tsx, app/layout.tsx), route handlers (app/api/files/[fileId]/route.ts, app/api/datasets/[datasetId]/download.tar/route.ts), and server actions (app/actions/crypt4ghKey.ts, app/actions/logout.ts) now import from @/app/lib/serverToken.
Custom session code removed. app/lib/SessionManager.ts, app/lib/session.ts, and their tests are gone.
Config cleanup. sessionSecretPath is removed from the config schema.
Testing
Manual testing:
Further comments
Breaking config change.
sessionSecretPathis no longer a valid key in sdad-config.json - remove it before deploying. The secret file it pointed at is no longer needed and can be deleted from the deployment.Definition of Done checklist
I have made an effort making the commit history understandable
I have performed a self-review of my own code and commented any hard-to-understand areas
Existing tests are passing and I wrote unit tests for new functionality
My changes generate no new warnings
All Acceptance Criteria (AC) Fulfilled and checked in the Related issue
Documentation Updated (if applicable):
I have had a look at most of the new code and I think that this is a clear improvement in many ways. I have tested the functionality and it seems to work as expected. I will take a more detailed look at the tests before approving.
@ -0,0 +1,400 @@import { beforeEach, describe, expect, it, vi } from "vitest";I feel like the name of this test hides what is actually being tester here. We are making sure it uses
__Secure-when the auth url is https right? Does this always mean a production setup? I don't think this needs to change but it made me have a second look.@ -0,0 +1,400 @@import { beforeEach, describe, expect, it, vi } from "vitest";Yes, great catch. I just noticed that the production image does not work as expected and I suspect I missed something related to https and cookie settings. I am looking at it now. I am setting the PR to draft until this is fixed.
@ -0,0 +1,400 @@import { beforeEach, describe, expect, it, vi } from "vitest";this is probably unrelated to what I said and may be due to changed docker networking internals. I will keep looking.
EDIT: False alarm. It was due to docker compose internals (network configuration) after all.
@ -0,0 +1,400 @@import { beforeEach, describe, expect, it, vi } from "vitest";Back to your comment, you're right the title says production but what the test actually looks for is
https. It is just that we assume that production is always https. Made that explicit now.This "expect" statement kind of makes the following "expect" statement redundant. Is one of them more important than the other? Do we really need both?
@ -0,0 +1,400 @@import { beforeEach, describe, expect, it, vi } from "vitest";My comment applies to a number of other tests too that expects to be using
__Secure-when encountering https. Most of the cases seem to also refer to this as production. So in order to be consistent I think the adjustment needs to be applied to the other tests too.@aaperis I think this looks good. I haven't tested this again since I expect there not to be any significant changes since last time I looked at it. Please have a look at my follow up for the adjust to the test and my new question regarding the crypt4ghKey test. Other than that I am ready to approve this.
Great work!
@ -0,0 +1,400 @@import { beforeEach, describe, expect, it, vi } from "vitest";Good point, now I hope I fixed this in all places needed.
Yes, actually the 2nd
expectstatement is a guard for theEqualToof the firstexpectbut this is probably too paranoid. I'll take it away.Thanks @pdmnyberg for the review and nice comments! I think I have made the changes needed. Please have another look.
If you wanted too keep both I guess you could also loosen the first expect by only checking
expect(data.errors).toEqual...That way you could test the error while leaving the object open for explicit checks for
SECRET_INTERNAL_DETAIL. But the current implementation will make the test fail either way if we have some data leaks.Anyways. This looks good now.
LGTM