Skip to content
This repository has been archived by the owner on Jun 30, 2021. It is now read-only.

Allow the eWallet to work without integration #401

Merged
merged 88 commits into from
Aug 24, 2018
Merged

Conversation

unnawut
Copy link
Contributor

@unnawut unnawut commented Aug 3, 2018

Closes #357.

Overview

This PR allows an eWallet provider to setup and uses the eWallet without having to implement or integrate into their own solutions.

Changes

Email configs

  • Moved Bamboo and email-related configs from AdminAPI to EWallet app (to share with EWalletAPI).
  • Generalized AdminAPI.Inviter into EWallet.Web.Inviter so that it accepts the email template as a parameter. So AdminAPI and EWalletAPI can send the confirmation email with different template/messages.

Authentication

  • Updated AdminAPI.V1.AdminUserAuthenticator to reject authentication of non-admin users
  • Added EWalletAPI.V1.EndUserAuthenticator to authenticate a user with email and password

Error handling

  • Moved :invalid_login_credentials, :invalid_email, :invite_not_found, :passwords_mismatch from AdminAPI.V1.ErrorHandler to EWallet.V1.ErrorHandler

Signup flow

  • Added /api/client/user.signup to support user signup with an email through the new EWalletAPI.V1.SignupController.signup/2
  • Added /api/client.user.verify_email to support user email verification through the new EWalletAPI.V1.SignupController.verify_email/2
  • Added /api/client/user.login to support user login with email/password through the new EWalletAPI.V1.AuthController.login/2

Supporting signup flow

  • Added a new EWalletAPI.VerificationEmail template, separated from AdminAPI.InviteEmail
  • Updated EWallet.Web.V1.UserAuthTokenSerializer.serialize/1 to return user along with the authentication token
  • Added :invite_uuid to User.update_changeset/2 so that invites can be removed after verification
  • Updated EWalletDB.User.has_membership?/1 to also handle user's UUID as the parameter.
  • Added EWalletDB.User.admin?/1

Docs

  • Updated OpenAPI spec with /user.login, /user.signup, /user.verify_email
  • Added docs/design.users.md to explain different user types and the checker functions
  • Added typespecs for EWalletDB.User functions

Implementation Details

Since this functionality can be built on the existing invitation system for admins (and admins are also users), most of the changes are for reusing the existing code.

Usage

Try sign up with /api/client/user.signup followed by /api/client.user/verify_email, then /api/client/user.login.

Example curl for /user.sign_up

curl 10.5.10.10:4000/api/client/user.signup \
-H "Accept: application/vnd.omisego.v1+json" \
-H "Content-Type: application/json" \
-d '{
"email": "test_signup@example.com",
"redirect_url": "http://localhost:4000/api/client/user.verify_email?email={email}&token={token}"
}' \
-v -w "\n" | jq

Example curl for /user.verify_email

curl 10.5.10.10:4000/api/client/user.verify_email \
-H "Accept: application/vnd.omisego.v1+json" \
-H "Content-Type: application/json" \
-d '{
"email": "test_signup@example.com",
"token": "kXN6Kd6HaJiYabpg7LWM3FCCHBMMLRKMBnIkMxxqjE4",
"password": "the_password",
"password_confirmation": "the_password"
}' \
-v -w "\n" | jq

Example curl for /user.login

curl 10.5.10.10:4000/api/client/user.login \
-H "Accept: application/vnd.omisego.v1+json" \
-H "Content-Type: application/json" \
-d '{
"email": "test_signup@example.com",
"password": "the_password",
}' \
-v -w "\n" | jq

Impact

  • Run mix ecto.migrate to migrate the database
  • Set the environment variable ENABLE_STANDALONE=true to enable standalone
  • Optionally add REDIRECT_URL_PREFIXES as a comma-separated string to allow redirect URLs that are external to the eWallet. If a redirect URL is already prefixed with BASE_URL, it does not need to be added here.

@unnawut unnawut self-assigned this Aug 3, 2018
@ghost ghost added the s2/wip 🚧 label Aug 3, 2018
@unnawut
Copy link
Contributor Author

unnawut commented Aug 23, 2018

Remaining:

  • Validate success_url before creating the user, not after
  • Swagger update
  • Deprecate redirect_url in favor of verification_url
  • verification_url & success_url whitelist that supports http(s) + deep linking
  • Refactor verification_url and success_url validation to the same level
  • Refine error to "Your user account hasn't been confirmed yet. Please check your emails."
  • Retest all endpoints

Copy link

@T-Dnzt T-Dnzt left a comment

Choose a reason for hiding this comment

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

Approving with a few questions/comments :)

Crypto.fake_verify()
handle_fail_auth(conn, :invalid_login_credentials)

:user_not_admin ->
Copy link

Choose a reason for hiding this comment

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

Any difference between this case and the one above? Can't we juse use:

_ -> 
  Crypto.fake_verify()
  handle_fail_auth(conn, :invalid_login_credentials)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. Midway it was a bit different but not anymore. Updating.


error ->
handle_error(conn, error)
{:error, code} when is_atom(code) ->
Copy link

Choose a reason for hiding this comment

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

💯

@@ -257,7 +257,7 @@ defmodule AdminAPI.V1.AdminAuth.AccountMembershipControllerTest do
assert response["data"]["code"] == "client:invalid_parameter"

assert response["data"]["description"] ==
"The `redirect_url` is not allowed to be used. Got: '#{redirect_url}'."
"The given `redirect_url` is not allowed to be used. Got: '#{redirect_url}'."
Copy link

Choose a reason for hiding this comment

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

Can we change this error to -> "The given redirect_url is not allowed. Got: '#{redirect_url}'." ?

@@ -7,6 +7,7 @@ defmodule EWallet.Application do

def start(_type, _args) do
import Supervisor.Spec
DeferredConfig.populate(:ewallet)
Copy link

Choose a reason for hiding this comment

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

Nice!

defp html(link) do
"""
<p>
<strong>Click the link to complete the email verification: </strong>
Copy link

Choose a reason for hiding this comment

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

to complete the email verification process

{:error, error} ->
case Map.get(ErrorHandler.errors(), error) do
%{description: description} ->
text(conn, "Unable to verify your email address. " <> description)
Copy link

Choose a reason for hiding this comment

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

We were unable to verify your email address. Error description: .

text(conn, "Unable to verify your email address. " <> description)

nil ->
text(conn, "Unable to verify your email address. An unknown error occured.")
Copy link

Choose a reason for hiding this comment

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

We were unable to verify your email address.

Renders the page to show when email verification is successful.
"""
def success(conn, _attrs) do
text(conn, "Your email address is successfully verified!")
Copy link

Choose a reason for hiding this comment

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

Your email address has been successfully verified!

@@ -259,14 +259,30 @@ defmodule EWalletDB.Account do
@doc """
Get the master account for the current wallet setup.
"""
@spec get_preloaded_primary_wallet(keyword()) :: %Account{} | nil
@spec get_master_account(keyword()) :: %Account{} | nil
Copy link

Choose a reason for hiding this comment

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

🎉


case User.update_without_password(invite.user, %{invite_uuid: nil}) do
{:ok, _user} ->
Repo.delete(invite)
Copy link

Choose a reason for hiding this comment

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

Do we actually delete invites or is it soft-delete? Can't we just set them to used or something and keep them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It’s hard delete right now. To do soft delete, I'll do this:

  1. Disassociate the invite from the user (user.invite_uuid) after verification. So user.invite_uuid == nil.
  2. Add a new invite.user_uuid column to keep the user->invite trace after the disassociation from user.invite_uuid.
  3. Add a new invite.verified_at column so it’s easy to see if and when the invite was used.

@unnawut
Copy link
Contributor Author

unnawut commented Aug 24, 2018

Updated with @T-Dnzt 's comments as well as @mederic-p 's suggestion to auto-include BASE_URL as a redirect_url_prefixes

@unnawut unnawut merged commit 6e9eb33 into master Aug 24, 2018
@ghost ghost removed the s2/wip 🚧 label Aug 24, 2018
@unnawut unnawut deleted the 357-standalone-ewallet branch August 24, 2018 11:14
@unnawut unnawut added flag/needs upgrade path 🛠️ This requires an upgrade path e.g. running an extra command or settings change and removed flag/needs upgrade path 🛠️ This requires an upgrade path e.g. running an extra command or settings change labels Jan 21, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow the eWallet to work without integration
3 participants