Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save User ID/name when not using tokens #154

Open
SaschaFroelich opened this issue Feb 25, 2022 · 12 comments
Open

Save User ID/name when not using tokens #154

SaschaFroelich opened this issue Feb 25, 2022 · 12 comments

Comments

@SaschaFroelich
Copy link

Hi all,

for a couple of reasons using tokens is suboptimal for our purposes (one reason is that sometimes tokens are not being accepted although they should be valid - but it is not clear whether this is an expfactory issue, because our participants have to perform several experiments that run on expfactory on the same server).

Anyway, I know that there is the possibility to use expfactory without tokens. In this case, participants are asked to enter a name before the experiment starts, however as far as I know in the past that name/ ID was not stored anywhere in the data. Unfortunately we need to be able to associate the data with the corresponding participants, so it is necessary that the entered name/ID appears somewhere in the data.

Has this been implemented in the meanwhile, or is it possible to do this?

Thank you!

Best,
Sascha

@vsoch
Copy link
Member

vsoch commented Feb 25, 2022

Hi @SaschaFroelich ! My suggestion would be to debug and figure out this issue:

one reason is that sometimes tokens are not being accepted although they should be valid - but it is not clear whether this is an expfactory issue, because our participants have to perform several experiments that run on expfactory on the same server

Typically a set of experiments (a battery) associated with one token will close the session if they hit the last experiment OR if they start a new session and the token is no longer in the browser. So if you are coming back later to do more the participant would be required to login again, e.g., this part:

def next():
# Headless mode requires logged in user with token
if app.headless and "token" not in session:
return headless_denied()
# To generate redirect to experiment
experiment = app.get_next(session)
if experiment is not None:
app.logger.debug("Next experiment is %s" % experiment)
template = "/experiments/%s" % experiment
# Do we have runtime variables?
token = session.get("token")
if app.vars is not None:
variables = get_runtime_vars(
token=token, varset=app.vars, experiment=experiment
)
template = "%s?%s" % (template, variables)
return perform_checks(template=template, do_redirect=True, next=experiment)
return redirect("/finish")

We also keep track of experiments that are finished, and only direct the participant to the next if there are unfinished experiments.

# To generate redirect to experiment
experiment = app.get_next(session)

So likely a little debugging would be needed on your part to figure out what is going on. I would take a look at the function that does the checks

def perform_checks(template, do_redirect=False, context=None, next=None, quiet=False):
"""return all checks for required variables before returning to
desired view
Parameters
==========
template: the html template to render
do_redirect: if True, perform a redirect and not render
context: dictionary of context variables to pass to render_template
next: a pre-defined next experiment, will calculate if None
quiet: decrease verbosity
"""
from expfactory.server import app
username = session.get("username")
subid = session.get("subid")
# If redirect, "last" is currently active (about to start)
# If render, "last" is last completed / active experiment (just finished)
last = session.get("exp_id")
if next is None:
next = app.get_next(session)
session["exp_id"] = next
# Headless mode requires token
if "token" not in session and app.headless is True:
flash("A token is required for these experiments.")
return redirect("/")
# Update the user / log
if quiet is False:
app.logger.info(
"[router] %s --> %s [subid] %s [user] %s" % (last, next, subid, username)
)
if username is None and app.headless is False:
flash("You must start a session before doing experiments.")
return redirect("/")
if subid is None:
flash("You must have a participant identifier before doing experiments")
return redirect("/")
if next is None:
flash("Congratulations, you have finished the battery!")
return redirect("/finish")
if do_redirect is True:
app.logger.debug("Redirecting to %s" % template)
return redirect(template)
if context is not None and isinstance(context, dict):
app.logger.debug("Rendering %s" % template)
return render_template(template, **context)
return render_template(template)
and we can discuss how to proceed. Note that the session is cleared on finish, and on logout:
# Reset/Logout
@app.route("/logout", methods=["POST", "GET"])
def logout():
# If the user has finished, clear session
clear_session()
return redirect("/")
# Finish
@app.route("/finish", methods=["POST", "GET"])
def finish():
subid = session.get("subid")
# If the user has finished, clear session
if subid is not None:
# Filesystem will rename folder to _finished
# Relational removes token so not accessible
app.finish_user(subid)
clear_session()
return render_template("routes/finish.html")
return redirect("/")

I don't think using a different string (an identifier) would resolve this issue, because you'd run into the same issue but with a different string. Also, it's bad practice from a research data standpoint to store personal information alongside study data. In labs where I was an RA in the past we would always generate a study ID and only keep their actual name in a locked file to link to that ID.

@SaschaFroelich
Copy link
Author

SaschaFroelich commented Feb 26, 2022

Hi Vanessa,

thanks for your reply. Of course we don't store personal data alongside study data, we always use the anonymized study ID that each participant is assigned. Somewhere else, we have the personal data associated to the study ID saved in a secure file. The way it is now, we additionally have to keep track of the study tokens, which is a bit cumbersome, because any individual participant will need several tokens for our purposes. Furthermore, participants are told their study ID, which is done automatically, and they could simply use it to start the experiment. Instead, we have to manually send out tokens to each individual participant.

You didn't really answer my question whether the newest version of expfactory would store the entered ID if we didn't use tokens but judging from your reply I suppose it doesn't.

Btw, we have figured out why tokens are sometimes not accepted. It was an error on our part that had nothing to do with expfactory, as I had expected.

@vsoch
Copy link
Member

vsoch commented Feb 26, 2022

That’s fantastic! So you would want to use the study ID to login instead?

@SaschaFroelich
Copy link
Author

Yes, ideally that's what I'd like to do.

@vsoch
Copy link
Member

vsoch commented Feb 28, 2022

How would you handle authentication? E.g., if you have STUDYID1 and STUDYID2 there is nothing to prevent someone from guessing there is a STUDYID3. And then you'd need something like a token anyway. Does generating the full links to send to people not make it easy enough? E.g., you can send them:

https://<your-server>/login?token=a34d4512-1841-4219-873d-2b9d1727a37a

and they don't have to think about login.

@SaschaFroelich
Copy link
Author

I think I didn't explain it right, by studyID I actually mean a participant's personal ID. So the idea is that at each experiment, they enter their personal ID, which is short and simple (think 12345). There would be no verification, but the occasional mix-up (if any) could easily be resolved on our side.

Sending the participants the link with the token is a neat idea, but it's not really what I'm talking about.

So currently, we have our research assistants invite the participants, giving them their personal ID (e.g. 12345), the token for day 1 of our experiment, and another token for the second day of the experiment, while saving participant IDs, associated tokens, and participant names in a separate file. This takes time, because each participant must be told their tokens individually. I thought this process could be streamlined by simply having the option to store a user's "login input" (in place of a token, here: 12345) in the resulting data file, or as its name. It would make everything a bit easier on everyone, including our participants. The advantage for experiments that last more than two days is obvious, although I admit those experiments are rare.

I was told that this feature doesn't yet exist, and I thought it would be rather easy to implement. But perhaps it's not as easy as I had thought? Or perhaps I'm missing some obvious downsides that would come with it?

Anyway thanks for your time and help.

@vsoch
Copy link
Member

vsoch commented Mar 1, 2022

It's not about how hard/easy it is to implement, I'm trying to make sure I don't implement something that leads to either erroneous protocol (e.g, being able to guess an ID and enter the system using someone else's ID) or bad research practices (e.g., storing their personal information in expfactory). I will think more about it.

@vsoch
Copy link
Member

vsoch commented Mar 1, 2022

The easiest thing we cold do is allow (akin to --vars) a --tokens variable that takes in a single delimited file of tokens, and then creates the tokens from file instead of randomly, e.g.,:

# tokens.txt
12345
67890
# Assuming $PWD is bound to /data
docker exec test-parse-url expfactory users --new /data/tokens.txt

Would that work?

@SaschaFroelich
Copy link
Author

SaschaFroelich commented Mar 1, 2022

Ok I see!

Yes I think that is a great idea. Would previously generated tokens still be valid in such a case, i.e. would it be possible to iterately create more tokens as more participants come in, without creating conflicts with existing tokens or data?

@vsoch
Copy link
Member

vsoch commented Mar 1, 2022

Yes I think so. If you ran the command with a repeated file it would just skip generation of ids already found.

If this sounds good I’ll try to find some time in an evening to start on this.

@vsoch
Copy link
Member

vsoch commented Mar 2, 2022

okay here is a start! #155 I could only test up through the end and couldn't test logging in because I use brave with really strong cookie preferences. But this should get us started if you want to follow the instructions there to test things out. Thanks!

@vsoch
Copy link
Member

vsoch commented Sep 27, 2022

This PR was never reviewed - are you still interested in this @SaschaFroelich ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants