e-mission / e-mission-docs

Repository for docs and issues. If you need help, please file an issue here. Public conversations are better for open source projects than private email.
https://e-mission.readthedocs.io/en/latest
BSD 3-Clause "New" or "Revised" License
15 stars 34 forks source link

Allow Account Recovery without PII #724

Open aGuttman opened 2 years ago

aGuttman commented 2 years ago

Currently, if users lose/forget their token, they have no way to recover their account when changing phones/recovering from a wipe. We want to create a way to allow account recovery in these instances without needing to store any additional info about the user.

aGuttman commented 2 years ago

Proposed solution: Have the app display a QR code that can be scanned by the app running on the user's new phone, which will allow the user to transfer ownership of the account and data to the new phone. As a backup, on creating a new account, generate QR code, ask the user to share it to some other app/service (email, SMS, etc...) and send it to themselves (or some other private place they will be able to access later) so they have it available to them if needed.

shankari commented 2 years ago
shankari commented 2 years ago

@aGuttman By tomorrow: Add another row to the profile screen that popups the privacy policy or "QR code coming soon"

shankari commented 2 years ago

@aGuttman Ah, we do have a QR code generator in the server repo. It is angular-qrcode (https://github.com/e-mission/e-mission-server/blob/master/webapp/bower.json#L13) and is used here: https://github.com/e-mission/e-mission-server/blob/master/webapp/www/templates/client_setup.html#L38 and https://github.com/e-mission/e-mission-server/blob/master/webapp/www/templates/client_setup.html#L66

shankari commented 2 years ago

@aGuttman I have created a new release for the devapp: https://github.com/e-mission/e-mission-devapp/releases/tag/v3.2.3 I have tested, and it is compatible with the tip of the phone master.

So this dependency (https://github.com/e-mission/e-mission-docs/issues/724#issuecomment-1116718312) should now be resolved:

Dependency: get new version of devapp up

aGuttman commented 2 years ago

Added row with popup to profile screen. QR code not yet generated, but will go in popup. Pull request created.

shankari commented 2 years ago

@sebastianbarry Installing angular-qrcode using bower hangs consistently at

bower cached        git://github.com/monospaced/bower-qrcode-generator.git#0.2.0
bower validate      0.2.0 against git://github.com/monospaced/bower-qrcode-generator.git#v0.2.0

and then fails with

bower ECMDERR       Failed to execute "git ls-remote --tags --heads git://github.com/monospaced/bower-qrcode-generator.git", exit code of #128 fatal: unable to connect to github.com: github.com[0: 140.82.112.4]: errno=Operation timed out

Additional error details:
fatal: unable to connect to github.com:
github.com[0: 140.82.112.4]: errno=Operation timed out

This is because the main repo has an incorrect bower.json.

npm uses package.json, where the dependencies are

"dependencies": {
"angular": ">=1.0.6",
"qrcode-generator": "^1.1.0"
}

bower uses bower.json, where the dependencies are:

"dependencies": {
"angular": ">=1.0.6",
"qrcode-generator": "git://github.com/monospaced/bower-qrcode-generator.git#v0.2.0"
}

Retrieving the bower dependency hangs.

Potential solutions are:

Normally, we would submit a fix to the angular-qrcode repo, but it has been deprecated so they are unlikely to merge any fixes.

shankari commented 2 years ago

In fact, there is an existing issue that refers to this: https://github.com/monospaced/angular-qrcode/issues/78

shankari commented 2 years ago

@sebastianbarry please make sure to put your findings about the library here. And for the long term, don't forget to create a tag for your change instead of using master.

sebastianbarry commented 2 years ago

There are 3 issues to take note of:

1: The "angular-qrcode" dependency (in https://github.com/monospaced/angular-qrcode/blob/master/bower.json) does not point to the right location

What did I change?

The goal of this is that it will remove the load errors we see in the console when interacting with the QR code button on the app, which have stopped since implementing this. However the QR code is still not displaying

2: The Test webpage for the monospaced/angular-qrcode is not working (http://monospaced.github.io/angular-qrcode/)

3: e-mission-server is actively using monospaced/angular-qrcode and it is working properly

sebastianbarry commented 2 years ago

The 3 issues mentioned above have been resolved

Turns out we did not need to change the dependency to get it to work. I copied the settings from e-mission-server and the QR code generation began working Changes can be seen in https://github.com/e-mission/e-mission-phone/pull/829/

shankari commented 2 years ago

Next step is to share the QR code so people can save it in drive or via email or text it to themselves. There is an existing example of sharing text in the share button on the profile page. We need to share an image in this case.

We use the cordova social sharing plugin for sharing (that is the window.plugins.socialsharing); we call the shareWithOptions method from it

you need to look at the documentation to figure out how to share an image (hint: base64 is your friend)

sebastianbarry commented 2 years ago

Right now, we have 2 pieces we want to connect…

  1. The app displays a QR code using the monospaced/angular-qrcode library like so <qrcode class="col" aria-label="qrcode for user email" data="{{settings.auth.email}}" size="220" download></qrcode>

  2. The app shares using the cordova social sharing plugin window.plugins.socialsharing

    There are a few ways we can share an image using the cordova social sharing plugin

  3. Using a file location

    • This would require us to save the QR code as an image onto some location locally or remotely and call the file location when sharing, this could work\
    • The monospaced/angular-qrcode has a feature for href OR download, and I couldn't get the download feature to work (it may not work on a mobile app in an ionic popover, or it may just not work). There is not documentation on the angular-qrcode github repo explaining how to operate the built in download feature
  4. Using base64 image data

    • This would require us to somehow generate the QR code as base64 data.
    • Currently the monospaced/angular-qrcode does not have an export to base64 feature
    • We can either find a way to "screenshot" the HTML element and convert it into base64 - Or we can find a separate library which converts data="{{settings.auth.email}}" into the base64 data for a QR code, and pass that into the cordova socailsharing plugin This is how I think we should solve this
  5. Using a URL

    • a. This would require us to get the QR code image to a public location on the internet. Because these QR codes are private identifiers, we should not do it this way
shankari commented 2 years ago

@sebastianbarry

Agree that using a URL is out. Downloads are a bit tricky in a hybrid mobile app, and an additional consideration is that saving the file could potentially be a security leak if another app (e.g. photo app/google drive) has access to the phone filesystem. Agree that base64 is the way to go

although the monospaced/angular-qrcode doesn't have an existing public interface to expose the data, it generates this under the hood anyway. And it is open source. So a couple of options are:

Hint: the second option and the canvas methods are your friend. Look at the components of the qrcode directive in the inspector as well.

sebastianbarry commented 2 years ago

I found through testing that adding the "download" modifier on the qrcode HTML element automatically generates the base64 image data for the QR code, shown here

Screen Shot 2022-06-03 at 2 18 14 PM Screen Shot 2022-06-03 at 2 16 56 PM

That being said, how do we save that HTML element containing the href=... base64 data into a variable, that we can then call in our Angularjs code here?:

Screen Shot 2022-06-03 at 2 18 42 PM
shankari commented 2 years ago

@SebastianBarry angular1 comes with an integrated jquery-lite, and I think we also include jquery explicitly. So you can select an element using class $('.<class>') or id$('#')` and then get its attributes.

jquery is super well known and there's a lot of documentation on how to use it to traverse the HTML DOM (e.g. search for "jquery find element by id") and extract values.

sebastianbarry commented 2 years ago

Created view QR code button, and created share QR code button [working] You can view the PR here: [https://github.com/e-mission/e-mission-phone/pull/840]

Next steps:

  1. On my version, from the profile screen, the My Bikecode (view QR code) button is working and creates a popover, HOWEVER the View Privacy Policy button is also displaying the My Bikecode popover, despite the html code saying it runs the viewPrivacyPolicy() function
  2. I was thinking it would be a good idea to also share the email token while also sharing the QR code image (so that the sharing message is set to the $scope.settings.auth.email or $scope.getEmail(), but I just wasn't able to figure out how to get this to work in the general-settings.js code inside the prepopulateQRMessage variable
  3. The Share button looks like the bottom is getting clipped off by some unknown box. I tried playing with the HTML in qrc.html but I could not get it to stop cutting off the bottom
shankari commented 2 years ago

The Share button looks like the bottom is getting clipped off by some unknown box. I tried playing with the HTML in qrc.html but I could not get it to stop cutting off the bottom

What concretely did you do to play with it?

I was thinking it would be a good idea to also share the email token while also sharing the QR code image (so that the sharing message is set to the $scope.settings.auth.email or $scope.getEmail(), but I just wasn't able to figure out how to get this to work in the general-settings.js code inside the prepopulateQRMessage variable

The qr code does represent the email token, so this is lower priority. You can file it as a potential enhancement for the future and move on.

sebastianbarry commented 2 years ago

The Share button looks like the bottom is getting clipped off by some unknown box. I tried playing with the HTML in qrc.html but I could not get it to stop cutting off the bottom

What concretely did you do to play with it?

I just adjusted the width and height values, do you have any other idea how I can play with it?

Screen Shot 2022-06-08 at 11 48 50 AM

I was thinking it would be a good idea to also share the email token while also sharing the QR code image (so that the sharing message is set to the $scope.settings.auth.email or $scope.getEmail(), but I just wasn't able to figure out how to get this to work in the general-settings.js code inside the prepopulateQRMessage variable

The qr code does represent the email token, so this is lower priority. You can file it as a potential enhancement for the future and move on.

Will do

shankari commented 2 years ago

I just adjusted the width and height values, do you have any other idea how I can play with it?

That sound right. When you adjusted the values, did it not change the box? Did you try to view the elements in the debugger and see what the source of the mysterious box is in the HTML?

sebastianbarry commented 2 years ago

Increasing the values of the percentages for width and height by 5% did not affect the Share button in any way


These pictures show the different elements on this popover for the QR code.

I'm not seeing any other element interacting or cutting off the button. I even tried changing it back and forth from a clickable <div> to a clickable <button> and neither seems to affect at all the way the button displays on my iPhone emulator

I still have not been able to get my android emulator, even from the e-mission/e-mission-phone:master my android app just blank white screens after the IP-entering screen

Screen Shot 2022-06-08 at 12 50 01 PM Screen Shot 2022-06-08 at 12 49 54 PM Screen Shot 2022-06-08 at 12 49 39 PM
shankari commented 2 years ago
sebastianbarry commented 2 years ago

See https://github.com/e-mission/e-mission-phone/pull/840 for changes

I updated the HTML for qrc.html and now it is displaying properly

Screen Shot 2022-06-09 at 11 18 26 AM
shankari commented 2 years ago

Next step is to scan the QR code. And I really think that we may actually want to redesign the login screen a bit as well. QR code is clearly the easiest way to get a long string in, but:

And now we will have another button to scan QR code; wondering if this is too many options and is confusing.

Plan:

shankari commented 2 years ago

example of where we scan the QR code in our existing codebase https://github.com/e-mission/e-mission-phone/blob/ceo_ebike_project/www/js/checkinout.js

we again use a native plugin (similar to socialshare) that will open the camera, read the QR code and give it to us as a string.

This plugin cordova.plugins.barcodeScanner.scan and I think it can actually scan either barcode or QR code so we should check that it is actually a QR code.

We should also potentially add some wrapping to the QR code instead of it just being the raw token because if people sign some other random QR code, we don't want it to be their token. In other words, we don't want tokens like https://coolevent.biz

But let's start with the unwrapped token, get it to work and then wrap the token in the next step.

sebastianbarry commented 2 years ago

https://github.com/e-mission/e-mission-phone/pull/841

I started a PR with my first attempt at adding a scanQR code feature to the login screen.


I had some comments in there about things I was getting stuck on, mainly

Added a $scope.scanQRCode function but it also does not have any meaninful code in it. This is how I think we should do it (by having the scanQRCode function and then calling it from the popover button), but I was struggling to get the function to call. I don't know how to type it (is it just "$scope.scanQRCode();" and it starts working?)

And

Added scripts needed to run scan QR code

Is this how it should be done? Are these lines meaningful, or did I just add useless scripts?


I also was wondering if you could help point me in the right direction with what we actually want to put in the scanQRCode function. Will it look very similar to the e-mission-server code for scanning the bike label? https://github.com/e-mission/e-mission-phone/blob/ceo_ebike_project/www/js/checkinout.js

const scanBikeLabel = function() {
    return new Promise(function(resolve, reject) {
        if (!$scope.scanEnabled) {
            reject(new Error("plugins not yet initialized, please retry later"));
        } else {
          cordova.plugins.barcodeScanner.scan(
            function (result) {
                if (result.format == "QR_CODE" &&
                    result.cancelled == false) {
                    try {
                        const bikeLabel = getScannedLabel(result.text);
                        resolve(bikeLabel);
                    } catch(e) {
                        reject(e);
                    }
                } else {
                    reject(new Error("invalid QR code"+result.text));
                }
            },
            function (error) {
                reject(error);
            });
        }
    });
}
shankari commented 2 years ago

@sebastianbarry I had forgotten about the requirement to have a physical phone to test the QR code stuff. I should send you some of the test android phones, but wanted to get the NREL app in the stores first.

The time required to troubleshoot the steps required to run the app on your iPhone is not worth it at this time. My recollection is that it was so painful when prior undergrads tried it that I ended up giving them test android phones as well.

If you happen to have an old android phone around, you can continue with this project. Otherwise, I think you should move on to the summer improvement that you have chosen.

shankari commented 2 years ago

After merging the change to the edit_config branch, we get

Program Study
Screen Shot 2022-06-20 at 3 46 00 PM Screen Shot 2022-06-20 at 3 51 42 PM
shankari commented 2 years ago

@sebastianbarry I added the "scan token" functionality to the login screen (as you can see above).

Pending tasks now are focused around usability

Can you please finish these before #640?

sebastianbarry commented 2 years ago

Added Save step to the intro. See PR https://github.com/e-mission/e-mission-phone/pull/854

shankari commented 2 years ago

@sebastianbarry do any of your testers have android phones? Saving to google drive does not seem to save the token text, only the token QR code. Sending via SMS does seem to save both. Might need to experiment with different "save" methods and ensure that both text and image are saved.

will close this after next round of feedback and final edits to the popup.

sebastianbarry commented 2 years ago

I do not have a tester with an Android phone near me at the moment. However, I tested the Google Drive sharing on my iPhone and it looks like its working properly

image1

image0

Can we get a video or screenshot of it not working on an Android?

shankari commented 2 years ago

@sebastianbarry It looks like if I save to drive, it only saves the png

https://user-images.githubusercontent.com/2423263/178424023-cb2ac1d6-4162-46c9-bc8d-dca344bdd30e.mov

sebastianbarry commented 2 years ago

@shankari I added the Spanish translation in https://github.com/e-mission/e-mission-translate/pull/15 but in the PR I showed how some of the English is hard-coded and I need help translating it.

The PR is for e-mission-translate, but for the hard-coded English I think it needs to be changed in e-mission-phone

sebastianbarry commented 2 years ago

Added Spanish translation to the hard-coded English sections in the login popups. See https://github.com/e-mission/e-mission-phone/pull/863

Screen Shot 2022-07-12 at 3 20 02 PM Screen Shot 2022-07-12 at 3 32 01 PM
sebastianbarry commented 2 years ago

Next Steps:

"Shankari, K."

  • you need to convert to a directive (that is what I have scheduled for the next sprint)
  • and also have time to address any other feedback from friends and family
shankari commented 2 years ago

so you can create this module:

Example of service

.factory("MultiLabelService", function(ConfirmHelper, InputMatcher, $timeout) {

used from here

    surveyoptions.MULTILABEL = {
        filter: "MultiLabelInfScrollFilters",
        service: "MultiLabelService",
        elementTag: "multilabel"
    }

The MultiLabelService may not be the best example of how to use it since we use it via an $injector for greater decoupling. But we use DiaryHelper (in www/js/diary/services.js) in the more traditional way in multiple places

shankari commented 2 years ago

first get it to work with the scope

but that is still sub-optimal because then you are tying together the two modules because the Opcode services depends on the $scope structure of intro.js. If you changed it, the service could break

there is a way to get a $scope from the $rootScope

and so you probably want to change to passing in the token and creating a new scope to set the token in

another option is to see if you can change the intro code so that the popup is not invoked from javascript but can be a button in the HTML

because then you can use a directive for the button

and angular will automatically create a scope for you

<button>Save your OPcode</button> becomes <save-opcode>Save your opcode</save-opcode> <div button save-opcode>Save your opcode</div>

shankari commented 2 years ago

One complexity is that you need to keep track of whether the user has already saved the opcode. Note that the button that checks this is the "Login" button while the button that saved the flag that the opcode is already saved is the save button. So you can't put that logic in the controller like you did before.

So you should make a service that is only called from this module and will save that state of whether the save has happened or not. Note that you need to think about the state in the service needs to be and checked.

Concretely, let's say that the controller logic is:

The behavior then is that:

A way to change the logic is:

shankari commented 2 years ago

<save-opcode alwaysSave=true></save-opcode>

in the multi-label UI, trip is a parameter and recomputeDelay is a parameter

shankari commented 2 years ago

This is the multilabel UI: https://github.com/e-mission/e-mission-phone/blob/master/www/js/survey/multilabel/multi-label-ui.js

sebastianbarry commented 2 years ago

Converted the "Save Opcode" into a directive and implemented the directive: (e-mission-phone changes) https://github.com/e-mission/e-mission-phone/pull/867 (e-mission-translate changes) https://github.com/e-mission/e-mission-translate/pull/16

https://user-images.githubusercontent.com/61334340/180308029-0a734a93-8fd2-4b6a-b4c1-8b2519e3fb9a.mov

https://user-images.githubusercontent.com/61334340/180308044-64d64423-3f6f-42f1-85bd-2067091ff2f4.mov

https://user-images.githubusercontent.com/61334340/180308060-875e07a5-9dc5-444c-b6b9-a680adcdbfdc.mov


Next steps:

"Shankari, K."

  • and also have time to address any other feedback from friends and family
sebastianbarry commented 2 years ago

I also noticed that in my branch's version of login.html, there is not the "study" section for the "study" or "program" distinction in this file, so they both are being displayed:

...
<center><h3>Login via anonymous token (OPcode) </h3></center>
<center>OPcode {{randomToken}}</center></div>

<div class="intro-text">
This unique randomly generated token (OPcode) is your identifier in the system.
<p>&nbsp;</p>
Nobody other than you knows that you are associated with this OPcode. If you
want to communicate with the research team about the data collected about you,
please be prepared to provide this OPcode.
<div class="intro-space"></div>
<button class="button button-block button-balanced" ng-click="loginNew()">Login as {{randomToken}}</button>

If you already have an OPcode from a previous install, you can use it instead to retain the same account. Note that there are no incorrect tokens. If you enter a OPcode that does not match an existing one, we will create a new account.
    <div class="intro-space"></div>
    <button class="button button-block button-energized" ng-click="typeExisting()">Type existing OPcode</button>

    <center> <b> - OR - </b> </center>

    <div class="intro-space"></div>
    <button class="button button-block button-royal" ng-click="scanExisting()">Scan existing OPcode</button>
</div> <!-- program_or_study == 'study' -->
</div> <!-- overall div -->
...

https://user-images.githubusercontent.com/61334340/180309034-eac01161-e859-47f4-913b-1ec4c79956a3.mov

This also isn't in the e-mission-phone master branch

However it is in the e-mission-phone master_for_platform branch

I pushed my PR out with the style of the master branch, that doesn't have the "study" section defined. But I submitted my PR to the master_for_platform branch, so I'm not sure if these changes will be updated automatically or not

shankari commented 2 years ago

@sebastianbarry the master branch does not have a separate section for the program.

that is because master does not have any distinction between studies and programs - it only has the study configuration.

the difference between studies and programs is part of the dynamic_config, which is only available (currently) in the master_for_platform branch.

If you are submitting a PR for the master_for_platform branch, you should start with that branch and retain the difference between studies and programs. Otherwise, we will end up with the situation where the study part of the login is always displayed, even for programs.

shankari commented 2 years ago

@sebastianbarry are you done with interviews related to this? Can we close this issue? You should be able to close it directly...