Giter VIP home page Giter VIP logo

dwyl / auth Goto Github PK

View Code? Open in Web Editor NEW
125.0 23.0 9.0 1.33 MB

๐Ÿšช ๐Ÿ” UX-focussed Turnkey Authentication Solution for Web Apps/APIs (Documented, Tested & Maintained)

Home Page: https://authdemo.fly.dev

License: GNU General Public License v2.0

JavaScript 1.20% CSS 4.47% Elixir 80.48% HTML 12.60% Dockerfile 1.04% Shell 0.16% Batchfile 0.03% Procfile 0.03%
phoenix auth authentication oauth2 elixir email oauth separation-of-concerns

auth's People

Contributors

camilorecce avatar cleop avatar danwhy avatar dependabot[bot] avatar iteles avatar nelsonic avatar samhstn avatar simonlab avatar th0mas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

auth's Issues

Create /admin Page to test Referrer/Redirection

At present the / (root route) of the Auth App displays the login buttons: http://localhost:4000/
image
This does not allow us to test redirection (back to the originating URL after successful login).
I suggest we create a different page in the Admin App to serve as a test for redirection.

Todo

  • Create /admin route
  • Extract referer from request headers
  • Include referer in state when constructing url for 3rd Party Auth

[Epic] Multi-factor Authentication (MFA)

High value apps or those with sensitive personal data need extra security.
We need to make time to investigate Multi-factor Authentication (MFA)
https://en.wikipedia.org/wiki/Multi-factor_authentication

Relevant Reading:

Todo

  • Investigate Open Source solutions
  • Investigate 3rd Party Solutions.

    Note: we would prefer not use SMS-based solutions because:

    • A lot of people have SMS messages appear on their "lock screen" so if a person has physical access to the device (e.g: a rogue employee) they can access the token without detection!
    • SMS incur a cost without a corresponding security advantage over OTP apps.
  • Determine if there is a free or low-cost solution that does not compromise on security
    • If no free/low-cost solution exists, why not? (is it a technical or intellectual property challenge?)
  • Document/Share findings in a mfa-research.md file.

If you have time + curiosity to help on this, please comment below!

โœ‰๏ธ Separate Email into an Independent Service

Context

As described in dwyl/app#267, email is very important to the success of our App.

At present the Email section of the README.md https://github.com/dwyl/auth#email indicates that auth uses SES and Bamboo for sending email. It informs people that they need to have environment variables for SMTP_USERNAME, SMTP_PASSWORD, SES_SERVER and SES_PORT and links to dwyl/learn-phoenix-framework/sending-emails.md for more detail.

auth-sending-emails

This is good for a barebones MVP that just needs to show a PO/PM that "email works",
but it is not a long-term solution to for reliably sending email because:

The Problem ๐Ÿ˜•

a) We are sending email in a "fire and forget" manner expecting them to automagically reach their target recipient. But we all know there is a lot more to email deliverability than simply sending the email.
How do we know it was actually delivered to the recipient?

b) We don't have any end-to-end tests for email so we have no way of knowing it's working other than to manually test sending an email to ourselves. Manual testing gets old fast.

c) Assuming the email is successfully delivered we have no way of knowing if it was opened/read.
We have no metrics for how effective our email is so we cannot be data-driven.

d) What happens when the email bounces or worse there is a complaint about our service? Our deliverability rating will plummet and along with it the fate of our App.

e) We are forced to maintain email related code in the auth App which is really not the "focus" of the auth App. Email should be a single function invocation that delegates to a dedicated service.

All of these issues lead us to believe that there is a much better way of doing email.

More detail on why email is super important to @dwyl is contained in: /app/issues/267

Proposed Solution ๐Ÿ’ก

Instead of using Bamboo and writing Elixir code to send email,
we build a separate service that has only one job email. ๐Ÿ’Œ

Given that we are already using Amazon Simple Email Service (SES) for sending our email,
why not make the email service a Lambda Function that we can invoke from any App or Programming Language?

Advantages ๐ŸŽ‰

  • A separate email Lambda function/service is much easier to test and maintain.
    • It allows us to reuse and extend our existing work https://github.com/dwyl/sendemail
      that in turn allows people to create email templates in Handlebars
      which is arguably a simpler and more beginner-friendly templating language than EEx.
  • auth can delegate email and focus on the one thing it needs to do; authenticate people.
  • The Lambda service will handle all Notifications for SES e.g: delivery, bounce & complaint
    see: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html
    All events/notifications will be fed back to the Auth App and the status of the email will be recorded
    so that the team can see the delivery, bounce, open and clickthrough rate metrics! ๐Ÿ“ˆ
  • A separate email system can more easily be used and improved upon by the community.

Downside? ๐Ÿ˜ž

The downside of having a separate project/function for email is that it's another thing to think about (and hold in your head) from the developer's perspective.

I feel this is minor given that a developer is already invoking a Bamboo function,
the only difference will be we invoke a Lambda function with the same data.


In case you're wondering, we intend to re-use most of the work done in #36 and #35 (responsive email templates), so almost no work will be "wasted", we just think there is a more effective and long-term maintainable way of doing email.


Todo

There is obviously a lot more we can do with email and that's part of the reason we want to split it out into it's own independent service. But this is all we need for the auth service for now.
This will allow us to get back to focussing on building our App. ๐Ÿ“ฑ

Forgot (Reset) Password Workflow

"Forgot password is a useless feature if people are not even getting started on using your product"
Suhail Doshi (mixpanel) - How to Measure Your Product https://youtu.be/MABmQhOlmJA?t=975

We should build this feature for ourselves if we feel we need it.
Or if someone using our product sends us a support email requesting it.

Todo

  • Draw Flow Diagram of Reset Password Workflow
  • Build it!

[Epic] Roles, Permissions and Grants

On Monday we drew out the basic auth schema (including Roles, Permissions and User_Roles) on the witeboard in the Office together:

auth-schema-whiteboard-diagram

This issue/epic is our attempt to capture as much detail as we can so we can implement it.

If you are interested in this challenge, please read: wikipedia.org/wiki/Role-based_access_control

More detail on the roles schema in #27 (comment)
I will be adding it to here shortly โณ

#ย Todo

  • Research and Write-up RBAC Systems #82

Use SVG Buttons for OAuth Buttons

At present the Auth MVP is using the <img> versions for the login with buttons.
https://app-mvp-elm.herokuapp.com/auth
image
It works but looks inconsistent.

We can easily solve this by using the SVG+CSS buttons we painstakingly created:

This should not take very long as the code is already working. see: dwyl/elixir-auth-github#33 (comment)

image

The two biggest advantages of using an HTML+CSS+SVG button is the reduction of bandwidth
(faster page load time without relying on an image server i.e. no additional HTTP requests)
and the fact that button can easily be translated to any language. ๐Ÿ’ก

Send verification/activation email via SES

As someone who has just registered using my email address I would like to receive a confirmation email informing me that I have successfully registered.

Note: this is not an especially compelling "user story", perhaps a better one is the "forgotten password" journey...?

UI/UX of API Page

The API Settings page should be clean and focussed:

image

This is obviously just a rough sketch, but hopefully you get the idea.
(It should have some sort of navigation bar/menu to help people situate themselves, not shown here)
#42 (comment)

Invoke Email /pixel When Auth App Boots

The slowest part of the Auth App is waking up the Email App to send an email once auth is successful. I propose that we invoke the Email /ping endpoint to wake the Email app immediately when the page is rendered. Ideally do this asynchronously.

How do we want to handle multiple applications using auth?

My current understanding of how auth will work is that it will be a separate application that a calling app forwards requests to.

If this is correct, how do we want to handle multiple applications using auth?
Would we deploy multiple versions of the auth app?
Would one auth application handle multiple calling apps?

Allow Password Creation for Accounts Created with Google/GitHub Auth

If a person login to the application using Google first, no password is saved in the database. If then the same person try to register/login with email/password the application find the existing user by email and will try to compare the hash password, however the current one on the database is nil which break the password check:

image

We can not update the password when the user login as it will allow anyone to set the password for this user.
My idea is to send an reset email to the user when she can will be able to setup the new password via a specific link

Display Friendly "404 Not Found" When No API Key is Found

I keep resetting the Database to test Auth and when the API Key I created is no longer there,
I see this error:

Ecto.NoResultsError at GET /profile/apikeys/7
expected at least one result but got none in query:
from a0 in Auth.Apikey,
  where: a0.id == ^"7"

image

We need a better 404 error with some styling.

[Epic] Session Management

Context

People expect that "Login" (Authentication / Authorisation) "just works โ„ข".
Nobody wants to have to re-authenticate each time they use a Web App1.
Being forced to re-authenticate creates usage friction and in some cases abandonment.
When Login fails it can be "catastrophic" to any app, getting it right is worth the investment!


1The exception to this rule is Online Banking, which has conditioned people to expect a short session duration. This makes perfect sense because financial services are transactional by nature; people login to their Online Banking to perform a specific task like "check balance" or "transfer money" which only takes a few clicks. Very few people have a reason to keep their Online Banking open for longer than a few minutes.
Automatically timing-out a session after 10 minutes of inactivity is the appropriate UX.

Story

As a "user" (person using a web application)
I want to be able to login once and have the App remember me for as long as I'm using the app.
So that I don't have to keep logging in each time I use the App.

We don't want to force people to constantly login to the App because it gets "old" very fast and will result in people being less effective with their time (wasting time logging into apps is a "time tax" nobody wants to keep paying!)

sessions Schema

  • inserted_at - (standard ecto/phoenix schema type: :naive_datetime) when a record is inserted. Automatically set by the database which ensures record integrity. This is the start of the session.

  • cid (Primary Key. Ecto type :string) - the hash of the data being inserted (the Ecto changeset minus the inserted_at timestamp) such that we know that a record is unique and verifiable.

  • session_id - (Ecto type :string) - the hash of the data being inserted (the Ecto changeset minus the inserted_at timestamp) such that we know that a record is unique and verifiable.

  • person_id - (Foreign Key: Auth.People.person_id, Ecto type :string) A reference to the person record. Note: as illustrated in the people schema example #32 the person_id can refer to an anonomyous (unregistered) person. The same session will continue after they register, this allows for traceability through the analytics/user-journey.

  • User Agents Table

    • device_id - the unique identifier of the device including browser user agent and IP Address. this is an irreversible hash which is checked on each request to reduce the chance of session spoofing.

    • ip_address - (Ecto type :binary, Use ) the device IP used for securing the session.

  • end - (Ecto type: :naive_datetime) the time when the session ended (usually via "logout" in the case of a registered/logged-in person).

session example

The row2 number in the table below corresponds to the action taken.

  1. Start - start the session with a particular device. Notice how there is no prev when the session starts.
  2. IP Address Change - Whenever a mobile device moves between cell towers its' IP Address can/will change. Some Auth systems will reject subsequent requests from the new IP and force the device to re-authenticate, we need to make this configurable for high-stakes apps (like fintech) , but for now, we are simply going to allow an IP address change provided the session is still valid.
    see: https://android.stackexchange.com/questions/182998/does-ip-address-change-mobile-net
  3. End - the session is ended and a timestamp is inserted for the end_at column.
row inserted_at cid (PK) session_id person_id ip_address2 end_at device_id prev
1 1541609554 2oGsEgN 2oGsEgN 9c 208.67.13.92 null 1BA6546A null
2 1541609554 e096d100 2oGsEgN 9c 172.34.85.14 null 1BA6546A 2oGsEgN
3 1541609876 ab4362a3 2oGsEgN 9c 172.34.85.14 1541609876 1BA6546A e096d100

1Again the row number is included purely for illustrative purposes and would not be needed in the actual table as we already have a cid as Primary Key.

device_id

You may have noticed in the sessions schema above that a session record includes a reference to device_id this is an attempt2 to track which device is being used for a particular session so that we can provide a better service.3

We have implemented this Device data "anonymisation" and hashing before in:
https://github.com/dwyl/hits#implementation-detail and https://github.com/dwyl/hits-elixir
So we can easily get the user_agent and ip_address data from the conn

2 The reason we say an "attempt" to track which device is making the requests is because we are aware of the fact that both IP Address and Browser User Agent are "spoofable" see: https://en.wikipedia.org/wiki/Spoofing_attack and therefore should not be the only means of trust when a sensitive query is performed.

3 Device list will be stored independently of personal information and used for service quality and analytics exclusively, not to charge iOS/Mac users more, airline/travel industry!!

Todo

We need a sophisticated approach to session management that will ensure
both flawless UX and excellent security for all people using our App on any device.
These are the areas we need to cover:

  • Anonymous Sessions - for people who are curious about the App/Site
    but have not yet registered their email address to persist their interactions. ... going to come back to this later!

  • Registration with Email address.

  • Verification - email address is verified by clicking a link sent by email.

  • Re/Set Password - once the person has verified their email address we ask them to define a password so they can login again. This is the same form to be used in the case where the person cannot remember their password and wants to re-set it. (all that changes is the copy)

  • Login - the person logs into the App/Site using an email address and password. If either of these two are not present in the people table (or invalid in the case when a password is incorrect), then login will fail with the appropriate (friendly) message. If email and password are valid, show the page they were attempting to reach or their "dashboard".

  • Logout - destroy the session on their machine/device and set the end_time in sessions table. #158

Each one of these checklist items will need it's own issue/story with UX/UI flow & logic.
I will get these opened shortly. โณ

If you are ever in any doubt as to what/how we should implement sessions (or anything else) for Auth, Google is the "reference implementation" to consult.
if you are not already using any Google Apps (G Suite, Gmail, Calendar, Drive, Docs, Meet, etc),
consider trying it out just for professional curiosity.

Upsert (Update or Insert) Person Record

At present we are inserting an initial person into people in seeds.exs based on the ADMIN_EMAIL environment variable. This person is the (first) "admin" of the auth app.
They will have full control over the auth app. The issue is that we don't want to hard-code too much data into the seed.exs and we don't want to have 5 environment variables for defining the Admin person.
So when the Admin logs into the App for the first time, they see this:
image

When it should show this:
image

We need to update the person record for the Admin when they successfully login with their Google Account.

With that in mind we need an upsert_person/1 function.
This will be useful for anyone who updates their Google/GitHub profile e.g: their profile picture and wants that change to flow through to their @dwyl profile.

#ย Todo

  • Create an upsert_person/1 function that accepts a person record and either creates a new record if one does not already exist or updates the existing record with the new values.

When to apply versioning

@nelsonic We recently spoke about the types of tables that will be needed to create the auth module.

Almost all of the tables had a primary key of a CID.

I understand using a CID in some of the tables (for example, a users table). There was a table, roles which was suggested, that didn't seem to have any confidential information in it but still contained a cid rather than a regular auto incrementing id.

I was wondering when is it 'over complication' to use a CID?

Store Session Token (JWT) as Cookie?

I'm a fan of the simplicity of storing the session token (JWT) in a cookie to reduce the amount of code that needs to be written to maintain the session. What do you think?

Simplest Possible Registration Form: Just Email Address

As a person using the dwyl app for the first time,
I want to get started with the minimal possible "friction"
So that nothing gets in my way on my journey to be more time-effective.

Todo

  • Build a registration form (partial) featuring just an email address
  • Use a Phoenix "Changeset" to help us to do input validation

Extract client_id from HTTP referer

auth_plug sends the client_id to the auth_url when no valid JWT is found: lib/auth_plug.ex#L167
We need to verify the client_id (decode_decrypt/1 followed by lookup in apikeys)
and if the client_id is valid, use the client_secret to sign the JWT on successful authentication.

Todo

  • Extract the client_id from the HTTP referer
    • Check if the client_id is valid before displaying the "login buttons" page
      • If client_id not valid, return a friendly Error: 401: AUTH_API_KEY not valid
  • Include the client_id in the state prop that gets sent to GitHub/Google
    I checked and it's RFC3986 compliant to have multiple question marks in a URL Query:
    https://stackoverflow.com/questions/2924160/is-it-valid-more-than-one-question-mark-in-a-url
  • Extract the the client_id from the state (returned by Auth Provider)
  • Confirm that it's still valid (not altered by the Auth Provider > decode_decrypt/1)
  • Lookup the client_id in apikeys
  • Use corresponding client_secret to sign JWT
  • Redirect back to original HTTP referer with JWT

With the completion of this issue Auth dwyl/app#268 will be fully functional!
Let's get it done!

EPIC: AUTH_API_KEY How to Run the App with a Single Environment Variable

As noted in the discussion in #34 ("How do we want to handle multiple applications using auth?"),
our objective is to let anyone connect to the App/API server or run the App on their localhost using a single environment variable: DWYL_API_KEY

  • Allow any person to create their own DWYL_API_KEY they can use for the App.
  • The DWYL_API_KEY should be the encrypted person.id so we can easily check if a Key is valid simply by confirming that it decrypts into an integer. ๐Ÿ’ก
iex(1)> key = Fields.AES.encrypt(1)
<<48, 48, 48, 49, 56, 39, 172, 166, 103, 23, 208, 232, 132, 247, 86, 129, 106,
  103, 135, 215, 140, 220, 234, 8, 178, 124, 86, 165, 28, 83, 55, 230, 137, 178,
  114, 127, 27>>
iex(2)> Fields.AES.decrypt(key)
"1"
  • auth_plug should send the first portion of the DWYL_API_KEY (corresponding to the client_id) to the auth_url when a request fails. > dwyl/auth_plug#14
  • decrypt_decode the client_id and then lookup in apikeys #55

Responsive Email Confirmation/Password Reset Template

In order to verify people's email address (for "double opt-in") we need to send them an email with a link that they need to click in order to verify their email address.

  • Create a template for sending emails that allow us to include a link.

    We have done before for Healthlocker and TC please see:
    https://github.com/dwyl/learn-phoenix-framework/blob/master/sending-emails.md
    If there are any "gaps" in the setup/sending please update/clarify. (thanks!)

  • Investigate responsive email templates. (please timebox this)
    • Include a Logo image in the email to reassure people that it's a legit email.
    • include some basic styles

@Cleop please work with @RobStallion on sending the "verify email address" email for Auth.
If you have any questions, please comment. (Thanks!)

A Study of the Worlds Most Successful Registration & Login Forms?

What makes a _Great_ Registration Form?

Task: take screenshots of all the most successful login/registration forms...
Preferably for the services _before_ they were popular because now (_after_) they are ubiquitous their network effect forces people to register regardless of how much personal information they require...

facebook-registration-october-2015

- https://github.com/braitsch/node-login - https://github.com/jedireza/drywall - https://github.com/jedireza/aqua

Initial implementation

Our initial requirements for this module:

  • Create an auth table/schema that is linked to a users table in the calling app. This means that we have some schema fields that we will define (username/email, password) but allows the developer using the module to define the fields that want their users to have. We'll need to make sure this works both with and without Append Only data.

  • Signup functions - endpoints or controller functions that create both the user and auth table. This will need to use the relevant auth/user changeset functions for casting/verifying passwords etc.

  • Encryption (see https://github.com/dwyl/phoenix-ecto-encryption-example) - encrypting user data for GDPR compliance.

  • Auth plug - a plug (or multiple plugs) that will allow us to authorise users and check permissions (eg. Admin permissions, edit permissions)

@nelsonic Does this sound like we're thinking along the right lines? Anything you think we should also think about to begin with?

Create Person Schema

People are the heart of the both the dwyl ("main") app and Auth system.
A person record securely stores all personal data.

person - the person using the App (AKA the "user" but we prefer the word "person" to "user" don't you?)

  • id: Int1
  • inserted_at: Timestamp
  • updated_at: Timestamp
  • username: Binary (encrypted; personal data is never stored in plaintext)

    Keeping the option of usernames open for now: #22

  • username_hash: Binary (salted & hashed for fast lookup during registration/login)
  • givenName: Binary (encrypted) - first name of a person https://schema.org/Person
  • familyName: Binary (encrypted) - last or surname of the person
  • email: Binary (encrypted) - so we can contact the person by email duh.
  • email_hash: Binary (salted & hashed for quick lookup)
  • password_hash: Binary (encrypted)
  • key_id: String - the ID of the encryption key used to encrypt personal data (NOT the key itself!)
    see: dwyl/phoenix-ecto-encryption-example
  • status: Int (FK status.id) - e.g: "0: unverified, 1: verified", etc.

We've already created this schema as part of the MVP.
See: https://github.com/dwyl/app-mvp-phoenix#create-schemas
I don't think we need any additional fields right now.

So I'm thinking of just borrowing the migrations:

And schemas:

With a minor twist. I hate the Context in Phoenix. It adds an unnecessary layer of complexity.
So I'm going to remove it and just put the functions directly in the schema file in /auth.

How To (End-to-End) Test Email?

I'm thinking of writing an AWS Lambda function that receives an email and saves the contents to AWS S3.
Then we can read the file from S3 and confirm that the contents is what we expect.
This would prove that email is "working".

@iteles thoughts?

Note: it does not prove "deliverability", but we expect AWS SES to handle that for us.

Attempting to login with a blank password

As a person registering with their email address and password,
I should be required to enter a password of at least 8 characters.
to enhance the security of my account.

Need to

  • Require password field on Client (HTML)
  • Check for string length before running checkpw

Note: we will enhance this further with #66 in a future sprint.
For now we just need to prevent people from having a blank password (or one with too few chars).

[Epic] Authentication in the dwyl App?

Currently we are assuming people want to Authenticate into the dwyl app using Email & Password.
We have not collected any data on this so we don't know what people want/expect.

Todo

  • Allow people to register using their email address. #63
    • Build simplest possible registration form requiring only an email address. #14
  • Automatically log them in to the app so they can start using it immediately (if their email address is valid)
  • send them a "please verify your email address" email as soon as they enter a valid email address. #63
  • When they click (or copy-paste) the link to verify email address (which we sent to their email address) we should ask them to record a few more details starting with a password

To Discuss

  • Should we prevent "unverified" people from using certain parts of the app/site until they have successfully verified their email address?
  • What other details do we want people to provide when they are registering to use the app? e.g:
    • First Name
    • Last Name
    • How did you hear about dwyl app?

Authenticating with Google, Facebook or GitHub could be an option depending on the use-case.

Please share your thoughts on which 3rd Party services you would like to login with
and which ones you have used in other apps or websites.

How to use in other projects?

@nelsonic
What we've got so far is a working implementation of auth in an example app, but how should we go about using it in other projects?

Is the intention to keep the code in the form it is now when requiring it into other apps? By this I mean the logic being performed in the controllers so that all the parent application has to do is forward all requests to /auth endpoints to this application, rather than the alternative which, would just be exposing the functions in this app and the parent module calling these functions to perform authorisation.

You mentioned in dwyl/technology-stack#67 (comment) that Auth should eventually be its own, separately hosted umbrella app, so to me this does seem like a step towards that.

I have managed to get this method of integrating the apps working, but just wanted to check I wasn't heading in the wrong direction.

The main issue I had in getting this approach to work was the configuration. Dependency config files are not included when they are required as a dependency, as it is expected that the calling module will provide the config.
This was a problem as the Ueberauth plug that is being used in the auth_controller expects some configuration to be set before it is compiled, so an error was being thrown. My solution to overcome this was to use Application.put_env to set the config before the plug is called. This way we can set the config we want to specify in Auth, and also read from the parent app's config file (with Application.get_env), allowing certain configurations to be set.

[Epic] Auth Schema

On Monday we drew out the basic auth schema on the witeboard in the Office together:

auth-schema-whiteboard-diagram

This issue/epic is our attempt to capture as much detail as we can so we can implement it.
The schema defined herein is not meant to be "set in stone", rather it's a starting point which will allow us to ship and then iterate as new requirements surface. :shipit:

Schema/Table/Record Naming?

While the photo of the schema above uses the word "users", this was only for the purposes of "sketching" it out on the whiteboard and is not the table/schema name we are going to use.
For more detail on why we are choosing to name the schema people, see: dwyl/app#33

people ๐Ÿ‘ค

At the most basic level, we need a table to store encrypted email addresses and hashed passwords to allow a person to register and login to the app/service where auth is being used.

Schema

  • inserted_at - (standard ecto/phoenix schema type: :naive_datetime) when a record is inserted. Automatically set by the database which ensures record integrity.

  • cid (Primary Key. Ecto type :string) - the hash of the data being inserted (the Ecto changeset minus the inserted_at timestamp) such that we know that a record is unique and verifiable.
    This will use our excid implementation: https://github.com/dwyl/cid

  • person_id (Ecto type :string) - this is a unique id for the person which allows queries to be performed across the table. For example, the following query will return the latest version of person record: SELECT * FROM people WHERE person_id = 'e096' ORDER BY inserted_at DESC LIMIT 1;

  • prev (Ecto type :string) - the reference to the previous cid for the record.
    This is "metadata" included by alog which the person using the app will never see but is useful in an "audit trail" if we ever need to determine if a "rogue" DBA has altered records. If you haven't had exposure to data fraud/forensics see: wikipedia.org/wiki/Forensic_data_analysis

  • email_encrypted (Ecto type :binary) - encrypted email address which can be decrypted if/when we need to send someone an email from our App. This will use Fields.EmailEncrypted to transparently apply strong encryption to the data before storing it.

  • email_hash (Ecto type :binary. Uses Fields.EmailHash. Unique Index) - a (per application) salted sha256 hash of the person's email address which allows for fast lookup when the person is attempting to authenticate. This will use Fields.EmailHash to transparently hash the email address. For more detail on why this is needed and how it works,
    see: https://github.com/dwyl/phoenix-ecto-encryption-example

  • verified (Ecto type :naive_datetime) - the timestamp when the email address was verified. If this field is null (not yet set) it means that the email address has not been verified. This will appear on the person's Profile as email unverified and limit their actions in the App.
    For example: on CS it will mean their reviews of a drinks/venues will only be visible to admins/editors until the email address is verified. This is to avoid spam content.

  • password_hash - (Ecto type :binary, uses Fields.Password) - the Argon2 hash of the person's password, safely stored to avoid any risk of reversal in the unlikely event of a "database breach".

  • first_name - (Ecto type :binary, uses Fields.Encrypted) the person's name. How we address them in emails and in UI customisations.

Notes: since we are never overwriting data in our append-only log, updated_at is not needed.
Also, you may notice that other schemas in our App have a created_at field to capture when a record was created (the timestamp when a task/timer that was created while the person using the app was offline...) this is not relevant to Auth because all changes to the Auth tables must be performed while the person is online.

people Table Example (Encrypted/Hashed)1

In the following example, the person record "progresses"2 through various stages, these correspond to the row number:

  1. anonymous - the person is using the app or website anonymously. That's "ok". They can still perform certain actions, they just won't be able to "save" them or receive updates. The UI should prompt them to "register to save your preferences".
  2. initial registration - the person inputs their email address to "register" to use the App. The App simply captures their email address and sends them an email requesting them to click a link to verify that they own that email address.

When the person first registers they will not have had an previous contact with the App,
therefore there is no prev (previous cid) to be linked back to.

  1. verification - the person clicks the link sent to them by email and verifies their email address.

Notice how the prev (previous hash) in the verification step refers to the cid in the initial registration step e096d1004 this referential integrity means that we know the data is valid (and has not been tampered with!)

  1. password creation - the person defines a password so they can re-authenticate.
  2. naming - the person tells the App their name so the interface can be personalised.3
  3. aliasing their person_id - the person wants to have a specific "alias" in the App, so they update their person_id to their desired handle.
row inserted_at cid (PK) person_id prev email_encrypted email_hash verified password_hash first_name
1 1541609554 9cC4os9MH 9c null null null null null null
2 1541609554 e096d1004 9c 9cC4os9MH 1BA6546A0C5e7 bmt3d2KpD null null null
3 1541609876 ab4362a37 9c e096d1004 1BA6546A0C5e7 bmt3d2KpD 1541609876 null null
4 1541610203 MqEzBmto9 9c ab4362a37 1BA6546A0C5e7 bmt3d2KpD 1541609876 wKIGu6djDt null
5 1541611381 tjGbE5BUd 9c MqEzBmto9 1BA6546A0C5e7 bmt3d2KpD 1541609876 wKIGu6djDt NzKibHfx
6 1541612984 ktBcXWZtp alex tjGbE5BUd 1BA6546A0C5e7 bmt3d2KpD 1541609876 wKIGu6djDt NzKibHfx

1 values for cid, person_id, email_encrypted, email_hash, password_hash and prev have all been truncated for brevity in this table (to reduce scrolling). In the real app, we would store the full hash, encrypted blob or CID for these fields/columns. The only exception to this is person_id which is a temporary value that the person can change e.g from 9c to alex.

2 If you feel the "progression" of the person record is a "wasteful" way of capturing the data, remember that we are not concerned with how much disk space a record (or the evolution of a record) takes up, data is cheap! The value of the insight we derive from analytics far outweighs the cost of storing the extra data. Ask Amazon, Facebook or Google if they worry about data storage costs when capturing incremental analytics data ... they don't! They capture everything!
If anyone else using the Auth module/app in their own project feels they need to "clear out" older records, they can easily run a batch process to free up the disk space.

3If the UX designer prefers to capture both the person's email address, first name and password in a single registration form, they can! The Auth.people table will accept any combination of data to be inserted at any time. This means that registration can be easily A/B tested! ๐Ÿ†Ž

##ย Anonymous

More Schemas/Tables?

As you noticed, the tables from the Whiteboard photo (above) are not all included in this issue.
This is because we don't want this issue to be enormous so instead we are splitting out the remaining tables into linked sub-issues. And, in the case of Analytics a sub-project: https://github.com/dwyl/atm

Todo

  • Finish defining how alog stores and retrieves records.
  • Use alog and fields to create the people schema.
  • Ship! :shipit:

Apple Auth

Story

As a person who (uses an iPhone and) wants to protect their privacy,
I want to be able use Sign in with Apple to authenticate with the @dwyl app
So that I can use the app without giving up my email address
(but still get email notifications routed via Apple).

image

Context

One of our key differentiators as an app/company is our focus on privacy.
We aren't sending Analytics data to Google/Facebook/etc.
and we aren't mining anyone's data to build a "consumer profile" to sell to advertisers.
We are treating people's personal data as private and encrypting it wherever we can.

Hypothesis

People who care about personal privacy1 tend to use Apple iPhone because Apple's business model is high quality hardware and relates services (iCloud, Apps, etc) not harvesting personal data.
(yes, there are plenty of posers who buy iPhones as a status symbol, but let's focus on privacy)

We expect people who are privacy-focussed to identify strongly with what we are building.
And giving them the option to login with their Apple Account will reduce friction to adoption.

Benefits

The benefits of Apple Login accrue to end-users in terms of privacy.
People whom we want to help maintain their privacy.

WSJ made an informative video explaining how Apple preserves privacy in their login service:
image
https://youtu.be/pmfjt2PPuVA

Disadvantages

  • Quite recently announced (June 2019) so uncertain market demand.
  • Strong vendor lock-in tied to the Apple ecosystem.
  • A commercial disadvantage is that Apple ID/iCloud is not typically used by teams of people,
    it's more of an individual/personal account. By contrast many companies/organisations use Google.
  • From (briefly) reading the docs it doesn't look simple to implement ... ๐Ÿ™„

Todo


1 On the subject of "Which is the most private phone?" available,
Some people say the BlackBerry Key2 has even better privacy controls than Apple.
see: https://smartphones.gadgethacks.com/how-to/5-best-phones-for-privacy-security-0176106
But BB has only 0.04% market share in 2019 ... Down from 33% percent in December 2011 ๐Ÿ“‰ ๐Ÿ˜ฎ
So it's not really worth spending too much time discussing BB, except as a lesson in strategic failure. ๐Ÿ™„
Also, BB Key2 is powered by Android which means by default it still sends lots of data to Google!

Reject Auth Requests with Invalid API_KEY or HTTP Referer

This is fairly obvious, but worth spelling out in terms of a Acceptance Criteria:

  • Requests to the Auth Homepage that do not include an HTTP Referer should:

    • render the buttons with state=dwylauth.herokuapp.com
    • get redirected to /profile on successful authentication.
  • Requests to display the Auth buttons that include an HTTP Referer should:

    • include a client_id in the URL query params.
    • the client_id should be decoded and decrypted using decode_decrypt/1
      • If the decoded and decrypted client_id is an Integer look it up in apikeys table!
      • Write a test for failure cases where the decode_decrypt/1 does not result in a valid Int.
    • Render the Buttons with state=referer?client_id=client_id
      • On successful auth
        • Lookup the client_secret for the given client_id
          • Sign the JWT with the
    • Redirect back to the HTTP Referer on successful auth.
  • Reject requests that fail with 401 status code and friendly error message:

1. Sorry, the URL you defined for API KEY does not match the HTTP Referer. Please visit: dwylauth.herokuapp.com/profile/apikeys and confirm you have set the URL field correctly. 
2. The API_KEY is not valid. Please visit: dwylauth.herokuapp.com/profile/apikeys and confirm you have the correct key. 

Create Administrator as Seed Data

At present we don't have any seed data so the database is empty:
image
We are having to manually create the data before people register otherwise there is no status=verified.

I propose that we create a ADMIN_EMAIL environment variable that defines the "administrator" of the Auth App. The Admin will be the "owner" of various status records necessary for running the App.
And when the person (owner of the ADMIN_EMAIL) logs into the app using their Google/GitHub account the remaining profile data will be updated.

see: https://www.phoenixframework.org/blog/seeding-data

  • Create Admin Person (User)
  • Create status="verified" which can be applied to people who signin with 3rd Party OAuth

{:error, %HTTPoison.Error{id: nil, reason: :timeout}}

The email heroku app goes to sleep after a 30 mins of inactivity.
So the tests that sends an email fails with the following timeout:

** (MatchError) no match of right hand side value: {:error, %HTTPoison.Error{id: nil, reason: :timeout}}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.