Skip to content
This repository has been archived by the owner on Jan 6, 2023. It is now read-only.

Session Auth Flow

Binal Gajjar edited this page Sep 25, 2019 · 2 revisions

This document is a working draft. Please leave any and all comments in the discussion issue: https://github.com/directus/api/issues/1263


Web App Authentication Flow using Sessions and Cookies

Table of Contents

  • Introduction
  • The Problem
    • Short expiration time + no background refresh
    • Multiple APIs
    • Insecure storage
  • Proposed Solution
    • Detailed Design
      • Start session
      • Stop session
      • Kill all sessions for user
  • Drawbacks / considerations
  • Alternatives
  • Adoption Strategy
  • Unresolved Questions
  • Useful Links
  • TL;DR

Introduction

Authenticating into the API is one of the most foundational and crucial functionalities of the Directus API. Currently, the Directus API utilizes a system based on a JSON Web-Token (JWT) in order to provide the user with the ability to authenticate against the API without having to resubmit the user email and password in every request. While this system works relatively well for the secure server to server communication, there are a couple of things that are left to be desired when you're working with a Web App.

The Problem

The most notable problem with relying on JWTs is storing it. The JWT is a key that gives the user full access to everything in the system that the user that's authenticated with. Therefore, you can consider the JWT to be as sensitive to information as a username and password combination.

When working with the JWT, the browser needs to store this information somewhere in order to be able to send it in the request to authenticate against the API. Right now, the Application (by relying on the JS SDK) stores this information in sessionStorage.

While this works relatively well when using a single API endpoint, it has a couple of drawbacks that are hard to overcome:

Short expiration time + no background refresh

The JS SDK can only refresh the token when the browser window is active and in the foreground. Therefore, if the user is not actively using the Application, they'll be logged as soon as the token expires. The expiration time of the Access Token is very short (by design).

Multiple APIs

As mentioned before, the JWT is the key to the house. We want to be able to be logged in to multiple APIs at the same time (multi-tenant) from the same application (or project) instance, which would mean that we'd have to store the key right next to the location of the API. This makes it way too easy for

Insecure storage

There is no way to store information to the browser from JavaScript that is secure enough to hold these tokens. Every JavaScript that runs in the same context as the Application—whether that be third party analytics software, custom scripts, and extensions, or malicious packages that snuck into the core codebase—can read and write to the storage. When storing these high-sensitive keys combined with the APIs that they unlock, this is too big of a security vulnerability.

Proposed Solution

Add a third—next to JWTs and the Static Token—way of being authenticated in the API: Session Cookies.

Instead of returning a token or key, this login method will set a cookie to the browser that made the request. This cookie can then be used on the server-side to look up what user authenticated through this method. This form of authentication is very common and traditionally used in "regular" websites where some data needs to persist across the page.

Detailed Design

We should add some new endpoints and update the current one to the API that the project can use to manage authentication:

Start Session

Endpoint: POST /auth/authenticate Param : mode: cookie / jwt

  • mode=cookie

    On successful login, this endpoint will create a session cookie and return a 200 OK status indicating to the project that the user is successfully authenticated and that the cookie has been set.

  • mode=jwt (If will work as the current flow)

    On successful login, this endpoint will return the jwt auth token and return a 200 OK status indicating to the project that the user is successfully authenticated.

Note: By default, the `mode` will be consider as `jwt`.

Input

This should be the same as earlier

{
  "email": "[email protected]",
  "password": "d1r3ctu5"
}

Output

This endpoint won't return any data on a successful login. On errors, it should match the output of the existing /auth/authenticate endpoint.

Stop Session

Seeing that session cookie will not expire, we need to have a way to log out as well.

Endpoint: POST /auth/logout

Input

This endpoint doesn't get any input. It will destroy the session data based on the cookie that's sent along in this request.

Output

There's no JSON output either. The API will destroy the cookie and the session and return a 200 OK afterward.

Kill all sessions for given user

Seeing that you can theoretically be logged in on multiple devices for an infinite amount of time, we need a way to log the user out of all devices they're logged into. This can be done with a "kill all sessions" endpoint.

Endpoint: /auth/logout/:user Params: user: The ID of the Directus User

Input

n/a

Output

Either a empty 200 OK payload or an error object

Kill particular session for given user

This endpoint will logout the user from a particular session

Endpoint: /auth/logout/:user/:id Params: user: The ID of the Directus User id : The ID of the user session

Input

n/a

Output

Either an empty 200 OK payload or an error object

Get all sessions for logged in user

This endpoint will return all the session of currently logged in user.

Endpoint: /auth/sessions

Input

n/a

Output

User sessions object or an empty payload with 200 status code.

Drawbacks / Considerations

Having an API with large throughput and a lot of users might introduce issues on a bigger scale.

Alternatives

I was researching a setup where we use refresh tokens next to the access token to be able to refresh an access token for a longer period of time. This would solve the staying logged in problem, but wouldn't do anything to negate the storing of tokens in the public client-side.

Adoption Strategy

These should be new endpoints and not affect the current setup. I don't think it's a breaking change.

Unresolved Questions

  • What kind of errors can we run into when creating/destroying sessions and cookies? These need error objects and codes.
  • Do we need a "kill all sessions" globally endpoint?
  • Are there any foreseeable problems in CORS + cross-domain cookies between the API and projects that are on separate domains?

Useful links

TL;DR

  • Add session cookie auth system
  • Add three endpoints: /auth/authenticate, /auth/logout, /auth/logout/:user, /auth/logout/:user/:id and /auth/sessions