[Epic] Session Management
- Dominant language
- Elixir
- Stars
- 141
- Forks
- 8
- PR merge metrics
- No merged PRs in 30d
Description
# 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!](https://youtu.be/T9ikpoF2GH0)
> 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
+ [x] **`inserted_at`** - (standard ecto/phoenix [schema](https://hexdocs.pm/ecto/Ecto.Schema.html) 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.
+ [x] **`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.
+ [x] **`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 https://github.com/dwyl/auth/issues/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.
+ [x] User Agents Table
+ [x] **`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.
+ [x] **`ip_address`** - (Ecto type `:binary`, Use ) the device IP used for securing the session.
+ [x] **`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_address`2 | `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`**](https://github.com/dwyl/hits-elixir/blob/b2ed9c10aabc20a36f24c185a5f2a9831b1652d3/lib/hits.ex#L45)
> 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:
+ [x] 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!
+ [x] Registration with Email address.
+ [x] Verification - email address is verified by clicking a link sent by email.
+ [x] 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_)
+ [x] 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".
+ [x] 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_"](https://en.wikipedia.org/wiki/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.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.