fix(auth): send signed out visitors to sign in before a study - #2348
Merged
KarinePistili merged 2 commits intoSep 14, 2026
Merged
KarinePistili merged 2 commits into
KarinePistili merged 2 commits into
Conversation
A study link is often the first RUXAILAB page a participant opens. TestView let anyone through when the study was public, because the access check only asked whether the study was public and never whether somebody was signed in, so a signed out visitor could enter and answer a study anonymously. On a private study they were bounced to /admin with a "no access" error and lost the link they had been sent. Require a signed in user to enter a study, with the one exception the maintainers described: an invitation that explicitly waives login still lets a participant answer without an account. Everybody else is sent to the sign in screen carrying the path they asked for, and is returned to the study once signed in. Only same-origin paths are replayed, so the parameter cannot be used as an open redirect. Access denials also stop overwriting their own destination: denyAccess used to replace the computed fallback path with /admin right after it had been applied.
Contributor
Author
|
Friendly bump on this one as well. Still ready for review and merging cleanly into develop. SonarCloud quality gate passed, GitGuardian clean, no conflicts. Happy to make changes if anything should be handled differently. Thanks! |
|
KarinePistili
approved these changes
Sep 14, 2026
KarinePistili
left a comment
Member
There was a problem hiding this comment.
Tested in multiple flows and it worked well for me. Thank you for the contribution!
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.



What
A signed out visitor who opens a study link is now asked to sign in and is returned to that study afterwards — except when the invitation they followed says login is not required.
Closes #2336
Demo
A signed out browser opens the same public Heuristic study link. Left is
develop, right is this PR.Full clip: redirect-before-after.mp4
Recorded with Playwright against the app running on the Firebase emulator suite.
Not blindly redirecting people
Following up on @KarinePistili's context in the issue, and @tim48-robot's note to carry it into the PR — the rule implemented here is:
requiredLogin === false, matching this studyThe anonymous-invite path is the one way into a study without an account, and it keeps working exactly as before. Everything else is asked to identify itself first, which is option 1 from the issue (sign in, then return to the test) rather than dropping the participant on the homepage.
Why it was letting people in
getTestViewAccessRedirectdecided access with:For a public study the first term is already false, so the whole check was skipped and
null(meaning "allowed") was returned — without ever asking whether anybody was signed in. That is how a signed out visitor reached a Heuristic test and could start answering it.This was also inconsistent with the capability model next door, where
resolveStudyAccesscomputesisPublicParticipant: Boolean(userId && study?.isPublic)— a public study already required a signed-in participant everywhere else.On a private study the visitor did get bounced, but to
/admincarrying a red "You do not have access" error, and the study link was lost. For someone who was simply not logged in yet, that reads as a rejection rather than an invitation to sign in.Changes
src/shared/utils/authRedirect.js/signin?redirect=…, and resolves it back after sign-insrc/shared/utils/studyNavigation.jsgetTestViewAccessRedirectrequires a signed-in user unless the invitation waives loginsrc/views/public/TestView.vuesrc/features/auth/views/SignInView.vue?redirect=after email and Google sign-intests/unit/authRedirect.spec.jstests/unit/studyNavigation.spec.jsOne related fix that this needed
denyAccess()ranredirectIfNeeded('/admin')afterhandleLoadedStudyhad already navigated to the destination it computed, so the computed fallback was immediately overwritten by/admin. The sign-in redirect could not survive that, so denial is now reported by the same function that chooses the destination, anddenyAccessis left for the two cases it is actually about — a study or a session that could not be loaded.Open redirect
redirectis replayed only when it is a same-origin path.//host,/\host, anything containing a backslash, anything not starting with/, and/signinitself all fall back to/admin. There is a browser check for this below, and unit tests covering each rejected shape.Testing
npm test— 33 suites, 281 tests passing (259 before this branch)npm run lintand Prettier clean on the changed filesVerified in a real browser
Playwright against the app on the Firebase emulator suite, with a public Heuristic study and a signed out browser. The same script was run before and after the change:
/testview/…/signin?redirect=…redirectparameterredirect=/testview/…/testview/…redirectis ignored/adminThe issue mentions this needs checking on other test types too.
/testview/:idis the shared entry point for Heuristic, unmoderated User and Card Sorting studies, so all three go through this check.Moderated User sessions are the exception. They enter through their own session-token branch in
TestView, which returns before this check runs, so the change above does not apply there. A signed out visitor is still blocked on that path, but by the existing behaviour: they end up on/signinwithout the study link being remembered, and with the "no access" error. I have left that branch alone rather than restructure a flow whose session-loading rules I could not exercise end to end locally — and it may well be separate on purpose. Happy to extend the same treatment to it in this PR if you would like, or to open a follow-up.