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

Add account validation capability to the accounts plugin #1982

Merged
merged 36 commits into from Mar 20, 2019

Conversation

magopian
Copy link
Contributor

@magopian magopian commented Jan 18, 2019

Fixes #1973

Try it here: https://kinto.github.io/kinto-account-demo/

  • Register a new account
  • Send an email on account creation with a link to the activation form with the activation key
  • Validate (activate) an account
  • Reset a password
  • Add documentation.
  • Add tests.
  • Add a changelog entry.
  • If you changed the HTTP API, update the API_VERSION constant and add an API changelog entry in the docs
  • If you added a new configuration setting, update the kinto.tpl file with it.
  • Only load validate and reset-password endpoints if the email_validation feature is activated More info
  • Update the capabilities to add a boolean info in the accounts feature rather than having multiple capabilities More info
  • Add a heartbeat that would try to connect to the SMTP service to validate that we can send emails, it would default to true in debug mode. See pyramld_mailer related discussion
  • Move the mail sending to a listener/dedicated functions

This is a work in progress, may I have some feedback on the current work before pushing forward with the reset password functionality and the documentation?

Thanks!

Copy link
Member

@almet almet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the feature, it's great!

Here's some feedback:

  1. Why not adding a flag to note if the account is validated or not, rather than relying on some other fields? I tend to think that explicit is better than implicit.

  2. At first, I had trouble to understand what this activation-form-url was made for, but from what I understand, it's purpose is to add a link in the validation email that is sent to the user.

I have an alternative proposal for this one: what about this flow:

  • WebApp creates a new user. It specifies a validation_redirect_url
  • Kinto stores all the data and sens an email
  • The user clicks on the link in the email
  • Browser issues a GET request to the Kinto server with the activation key
  • Kinto server validates the account
  • Kinto server issues a redirect response to the redirection_url

@magopian
Copy link
Contributor Author

At first, I had trouble to understand what this activation-form-url was made for, but from what I understand, it's purpose is to add a link in the validation email that is sent to the user.

@almet the idea (discussed in #1973) is that modifying the user record should not be done with a GET, but with a POST (or PATCH). So the activation-form-url is the link to the WebApp, which should display a proper form asking the user to confirm that they want to enable/activate/validate the account (probably a simple "call to action" button that will POST to the validate endpoint).

The WebApp should do this POST asynchronously, in the background (everything kinto related should be done this way in a WebApp I believe), so I'm not sure we need a redirection url.

I'm not arguing against the GET and redirection url though ;)

@almet
Copy link
Member

almet commented Jan 21, 2019

At first, I had trouble to understand what this activation-form-url was made for, but from what I understand, it's purpose is to add a link in the validation email that is sent to the user.

@almet the idea (discussed in #1973) is that modifying the user record should not be done with a GET, but with a POST (or PATCH). So the activation-form-url is the link to the WebApp, which should display a proper form asking the user to confirm that they want to enable/activate/validate the account (probably a simple "call to action" button that will POST to the validate endpoint).

Why displaying a form if the user clicks on the link already? The Webapp could do the POST directly, saving an extra click, and staying RESTful. If we decide to go this way, we could have a default webapp shipped direclty with kinto-accounts, which will do the POST on behalf of the user, and letting the ability to the developer to specify a different webapp to use.

@magopian
Copy link
Contributor Author

Why displaying a form if the user clicks on the link already? The Webapp could do the POST directly, saving an extra click, and staying RESTful. If we decide to go this way, we could have a default webapp shipped direclty with kinto-accounts, which will do the POST on behalf of the user, and letting the ability to the developer to specify a different webapp to use.

If we're comfortable with modifying a record without a user explicit action (auto-POSTing a form), I'd be ok with going all in and changing the validate endpoint to accept a simple GET.

If we do that, then there's no more need for an activation-form-url.

In any case, the redirect-url can already be set by modifying the link in the email_body_template setting. We could also accept it as a GET or POST parameter on the validate endpoint.

Maybe we should discuss this in another issue/PR?

@magopian
Copy link
Contributor Author

Ok, the current work on the user creation and validation should be done now, and properly documented.

What's left to discuss before this can be merged:
1/ do we want to get rid of the activation-form-url in favor of validating a user using a simple GET? (see #1982 (comment))
2/ does it make sense to merge this PR as is, without the "reset password" functionality, which could be discussed in another issue, and implemented in another PR?

@magopian magopian force-pushed the 1973-account-validation branch 2 times, most recently from 8268dc3 to 9313147 Compare January 22, 2019 14:47
@magopian
Copy link
Contributor Author

After an out-of-band discussion with @Natim we came to the conclusion that this PR could be slightly modified:

1/ don't require an activation-form-url field
2/ allow the user creator to provide some email context when POSTing on the /accounts/ endpoint (that will be used in addition to the user record and the activation-key to render the email template from the settings)
3/ to validate the user, still require a POST on the /accounts/<user id>/validate/<activation key> endpoint, through whatever means the user creator deems fit (it could be through a form, at a URL that they have provided in the email context in 2/). Here also allow the user creator to provide some email context (to render the "user successfully activated" email in 4/)
4/ when the user is validated, send another mail to confirm its activation

@almet
Copy link
Member

almet commented Jan 22, 2019

Not sure what you mean with « email context ». You mean a template?

Also, In addition to what you've described, I think that if we go with a POST to validate the account, then we should provide a simple WebApp that does the POSTing automatically, in order to have a easy-to-setup account registration mechanism (e.g. as an admin deploying the stack, I want this to be simple enough)

@almet
Copy link
Member

almet commented Jan 22, 2019

As for delaying reset password to another PR, I think it's un-necessary, unless you're in a hurry having this shipped (in which case, do as you prefer!)

@Natim
Copy link
Member

Natim commented Jan 23, 2019

Not sure what you mean with « email context ». You mean a template?

No the context to render the template aka:

{ "first_name": "Alexis", "email_subject": "blah"}

@Natim
Copy link
Member

Natim commented Jan 23, 2019

we should provide a simple WebApp that does the POSTing automatically, in order to have a easy-to-setup account registration mechanism

We could as an example but it is quite orthogonal to the service code.
I can't see someone using this feature not to wish to integrate it with their software UI.

@almet
Copy link
Member

almet commented Jan 23, 2019

As for delaying reset password to another PR, I think it's un-necessary, unless you're in a hurry having this shipped (in which case, do as you prefer!)

we should provide a simple WebApp that does the POSTing automatically, in order to have a easy-to-setup account registration mechanism

We could as an example but it is quite orthogonal to the service code.
I can't see someone using this feature not to wish to integrate it with their software UI.

Some softwares have UI that are not browsers (e.g. a CLI script), which is why I think this is necessary to have by default.

@Natim
Copy link
Member

Natim commented Jan 23, 2019

Some softwares have UI that are not browsers

In that case they might not use links to validate the account.

@almet
Copy link
Member

almet commented Jan 23, 2019

Some softwares have UI that are not browsers

In that case they might not use links to validate the account.

I think I missed something then. To me, if I register an account with the command line, the workflow is as follows:

  1. A request is made from the CLI to the kinto server to create the account
  2. An email is sent to the user, to confirm the email belongs to her / him
    3. The user clicks on the link to validate
  3. Account is validated.

Unfortunately, users use links in the emails to validate the accounts, and this happens through HTTP GET requests only.

What other scenario do you have in mind to avoid this webapp?

@Natim
Copy link
Member

Natim commented Jan 23, 2019

  1. A request is made from the CLI to the kinto server to create the account
  2. An email is sent to the user, to confirm the email belongs to her / him
  3. The user clicks on the link to validate
  4. Account is validated.

This is the case when working with a webapp, but if you are using a software you might want to use the following:

  1. A request is made from the CLI to the kinto server to create the account
  2. An email is sent to the user, to confirm the email belongs to her / him
  3. The user copy the activation key and use the CLI to validate the account.
  4. Account is validated.

@almet
Copy link
Member

almet commented Jan 23, 2019

Ah yes! I haven't thought about this solution. I remember that's what Twitter used to do in the past :-)

That being said, I tend to prefer the « user clicks on the link » solution, because it's simpler. What's the value of having the user copy a verification code over clicking a link in an email, apart not having to implement an UI?

@Natim
Copy link
Member

Natim commented Jan 23, 2019

I think it is up to the developper to define the flow, I am not against building a little Elm demo webapp if it pleases you. We can even pair on it once this lands.

@almet
Copy link
Member

almet commented Jan 23, 2019

I think it is up to the developper to define the flow, I am not against building a little Elm demo webapp if it pleases you. We can even pair on it once this lands.

I agree, we need to let the possibility for the developer to tweak the flow, if they want it to be different. One major selling point of kinto is « don't reinvent the wheel each time », and as such, I believe we should provide the golden path for everything. Something that makes it easy for everyone to use accounts, without having to implement something special.

@Natim
Copy link
Member

Natim commented Jan 23, 2019

I agree with you in the scope of Kinto which is an API. As soon as you have front-end facing experience, it is not the place of Kinto to solve it. However we could decide that the kinto-admin has got a built-in page to handle that.

@almet
Copy link
Member

almet commented Jan 23, 2019

I agree, but I believe that if we start solving one problem (here having accounts validation), then we need to do it completely, and painlessly for the devs that want to use it.

As a side note, I think we're kinda going in circles with this discussion. I don't really mind disagreeing, but if anyone wants to weight in in order to move forward on this topic that would be beneficial :-) cc @leplatrem @glasserc

@glasserc
Copy link
Contributor

I'm not sure you really want to hear my opinion, so I've been trying to stay out of it, but since you asked: I don't think Kinto is a good foundation for the kinds of thing you want to do. I don't like the current Kinto accounts plugin and I would prefer Kinto core not be cluttered with support for all the different possible things some developer might want an accounts system to do.

I think the PostgREST approach here is pretty solid: don't do any user stuff whatsoever -- delegate that to some other service -- but allow people to present JWTs to prove their identities. I recently saw https://github.com/netlify/gotrue which seems like a pretty nice product, but if you wanted to build something open source I would certainly support that too.

@Natim
Copy link
Member

Natim commented Jan 24, 2019

I would prefer Kinto core not be cluttered with support for all the different possible things some developer might want an accounts system to do.

I agree with you and I guess it is why we decided to put the account code in a plugin.

As of why plugins ships with Kinto itself, this is another story.

@magopian
Copy link
Contributor Author

Wow, such long thread.

Here's a plan:
1/ I'll continue working on this PR to implement the modifications suggested in #1982 (comment)
2/ I'll implement the reset password feature

Once this is done, we can decide if it's good enough to land.

Regarding the "sample webapp that kinto should provide": let's discuss this in another issue, I'd like to not delay the landing of this functionality for too long. We can still improve in the future if we want (eg: also allowing a GET to validate an endpoint, providing redirect URL, providing a sample webapp, ...).

@magopian
Copy link
Contributor Author

magopian commented Feb 11, 2019

Ok folks, I hope I have addressed all your comments and reviews, but one thing: the heartbeat request. I've seen @natim PR and would love that they merge it. In any case, I'd love this PR to land as is, and maybe in the future add the heartbeat.

There's also this comment I'm not sure how to deal with it.

What do you think @Natim @leplatrem @almet? Anything else I'm missing?

# according to
# https://docs.pylonsproject.org/projects/pyramid_mailer/en/latest/#debugging
mailer = settings.get("mail.mailer", "")
config.include("pyramid_mailer" + ("." + mailer if mailer else ""))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
config.include("pyramid_mailer" + ("." + mailer if mailer else ""))
config.include("pyramid_mailer" + (f".{mailer}" if mailer else ""))

Copy link
Member

@Natim Natim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for keeping at it! Great work 💃

I agree about the heartbeat. Nonetheless if you try to create an account you will see the issue quite quickly.

@magopian
Copy link
Contributor Author

magopian commented Feb 12, 2019

Quick note: it seems the + character (which can be present in an email address) is not allowed in the ID. Maybe we should fix that?

$ echo '{"data": {"id": "bob+test@example.com", "password": "321"}}' | http POST http://localhost:8888/v1/accounts
HTTP/1.1 400 Bad Request
Access-Control-Expose-Headers: Alert, Backoff, Content-Length, Retry-After
Content-Length: 167
Content-Type: application/json
Date: Tue, 12 Feb 2019 11:03:13 GMT
Server: waitress
X-Content-Type-Options: nosniff

{
    "code": 400,
    "details": [
        {
            "description": "Invalid object id",
            "location": "path",
            "name": null
        }
    ],
    "errno": 107,
    "error": "Invalid parameters",
    "message": "path: Invalid object id"
}

@Natim
Copy link
Member

Natim commented Feb 12, 2019

Quick note: it seems the + character (which can be present in an email address) is not allowed in the ID. Maybe we should fix that?

Yes we should probably fix it. Feel free to add a test for it if you want.

.. code-block:: ini

# Set the regular expression used to validate a proper email address.
kinto.account_validation.email_regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you accept + character

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's an "additional restriction", it doesn't take over/replace what's declared in https://github.com/Kinto/kinto/blob/master/kinto/plugins/accounts/views.py#L33 (if that's the root cause of this issue, not sure yet, I'm investigating)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe shall we use the same regex for both?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's the same use case: in one case (no account validation) you want to allow a few set of characters, in the other case you might want to restrict emails to a given sub-domain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a fix

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's the same use case: in one case (no account validation) you want to allow a few set of characters, in the other case you might want to restrict emails to a given sub-domain.

Yeah makes sense 👍

Copy link
Member

@Natim Natim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this. @leplatrem final r+ ?

Copy link
Contributor

@leplatrem leplatrem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost r+ :)

I re-read everything :) This PR is huge but the feature is a valuable addition! Thanks a lot for your patience on this!

More seriously, my main concern is the organization of docs between settings and API. We should mention the context in the API docs, and leave settings as concise as possible.

And also some repeated code about cache manipulation that could be gathered in utils.

docs/api/1.x/accounts.rst Outdated Show resolved Hide resolved
docs/api/1.x/accounts.rst Outdated Show resolved Hide resolved
docs/api/index.rst Outdated Show resolved Hide resolved
docs/api/index.rst Outdated Show resolved Hide resolved
docs/configuration/settings.rst Outdated Show resolved Hide resolved
kinto/plugins/accounts/views/__init__.py Outdated Show resolved Hide resolved
kinto/plugins/accounts/views/validation.py Outdated Show resolved Hide resolved
kinto/plugins/accounts/views/validation.py Outdated Show resolved Hide resolved
tests/test_config.py Show resolved Hide resolved
tests/plugins/test_accounts.py Outdated Show resolved Hide resolved
@magopian
Copy link
Contributor Author

I believe I've addressed all your comments @leplatrem please let me know if I missed anything (in particular, are you happy with the way the docs are organized now?)

Copy link
Contributor

@leplatrem leplatrem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot better thanks!

🎉 🎉

Congrats for your patience on this!

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

Successfully merging this pull request may close these issues.

None yet

6 participants