From 708ae86444080277c515c46b27f3f53d0c107311 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Tue, 14 Dec 2021 19:21:28 +0100 Subject: [PATCH] docs: explain the choice of sha-512 for auth tokens Signed-off-by: David Mehren --- docs/content/dev/2.0.md | 25 +++++++++++++++++++++++++ src/auth/auth.service.ts | 1 + 2 files changed, 26 insertions(+) diff --git a/docs/content/dev/2.0.md b/docs/content/dev/2.0.md index 797dd67ad..863b0b5a4 100644 --- a/docs/content/dev/2.0.md +++ b/docs/content/dev/2.0.md @@ -39,3 +39,28 @@ Because we need to have empty constructors in our entity classes for TypeORM to - Should either return a complete and fully useable instance or return a Pick/Omit type. - Exceptions to these rules are allowed, if they are mentioned in the method documentation +## Auth tokens for the public API +The public API uses bearer tokens for authentication. + +When a new token is requested via the private API, the backend generates a 64 bytes-long secret of +cryptographically secure data and returns it as a base64url-encoded string, along with an identifier. +That string can then be used by clients as a bearer token. + +A SHA-512 hash of the secret is stored in the database. To validate tokens, the backend computes the hash of the provided +secret and checks it against the stored hash for the provided identifier. + +### Choosing a hash function +Unfortunately, there does not seem to be any explicit documentation about our exact use-case. +Most docs describe classic password-saving scenarios and recommend bcrypt, scrypt or argon2. +These hashing functions are slow to stop brute-force or dictionary attacks, which would expose the original, +user-provided password, that may have been reused across multiple services. + +We have a very different scenario: +Our API tokens are 64 bytes of cryptographically strong pseudorandom data. +Brute-force or dictionary attacks are therefore virtually impossible, and tokens are not reused across multiple services. +We therefore need to only guard against one scenario: +An attacker gains read-only access to the database. Saving only hashes in the database prevents the attacker +from authenticating themselves as a user. The hash-function does not need to be very slow, +as the randomness of the original token prevents inverting the hash. The function actually needs to be reasonably fast, +as the hash must be computed on every request to the public API. +SHA-512 (or alternatively SHA3) fits this use-case. diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index e69f98e18..88a982e67 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -65,6 +65,7 @@ export class AuthService { } const secret = bufferToBase64Url(randomBytes(64)); const keyId = bufferToBase64Url(randomBytes(8)); + // More about the choice of SHA-512 in the dev docs const accessTokenHash = crypto .createHash('sha512') .update(secret)