Provide /register
and /login
endpoints for Redis-based API bearer token auth.
See https://github.com/evanx/fastify-auth-mlk/blob/master/bin/test.sh
The Administrator will generate a registration token and deadline for a Client.
regToken=`node bin/bcrypt.js hash test-regToken`
regBy=`node -e 'console.log(Date.now()+3600*1000)'`
The registration details, namely regToken
and regBy,
are stored in Redis for the Client e.g. test-client.
redis-cli hset fr:client:test-client:h regToken "${regToken}"
redis-cli hset fr:client:test-client:h regBy "${regBy}"
The Client generates their secret.
openssl rand 24 -base64
The Client registers their secret
using the regToken
provided by the Administrator.
curl -s -X 'POST' \
-d 'client=test-client&secret=my-secret®Token=test-regToken' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json' \
http://127.0.0.1:3000/register
The Client logs in using their secret,
and receives a session token.
token=`curl -s -X 'POST' -d 'client=test-client&secret=my-secret' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json' \
http://127.0.0.1:3000/login | jq -r '.token'`
The Client accesses a related API using the session token as a Bearer
token in the Authorization
header.
Authorization: Bearer {token}
See implementation:
https://github.com/evanx/fastify-auth-mlk/blob/master/lib/server.js
client
- the ID of the Clientsecret
- the secret chosen by the ClientregToken
- the token provided for registration
Pre-authorisation of the registration via hashes key client:${client}:h
with fields:
regToken
- the bcrypted hash of the token issued to the Client for registrationregBy
- the epoch deadline for the registration by the Client
The secret is hashed using Bcrypt and stored in Redis.
See https://github.com/kelektiv/node.bcrypt.js
The Client might generate its secret as follows:
$ openssl rand 24 -base64
TTDJ2uqo6VxIvaqiX52xEn8b2daxEhFV
The regToken
might be similarly generated by the Administrator and provisioned to the Client.
We retrieve the regToken
and regBy
details from Redis.
const [regTokenRes, regBy] = await redis.hmget(
`client:${client}:h`,
'regToken',
'regBy',
)
We authenticate the regToken
provided.
const compareRes = await bcrypt.compare(regToken, regTokenRes)
We salt and hash the secret
using Bcrypt.
const bcryptRes = await bcrypt.hash(secret, config.bcrypt.rounds)
We persist the bcrypted secret
in Redis.
await redis.hset(`client:${client}:h`, 'secret', bcryptRes)
client
- the ID of the Clientsecret
- the secret chosen by the Client
Hashes key client:${client}:h
with field:
secret
- the/register
secret, salted and hashed using Bcrypt
token
- a session token
We get the hash
of the secret
from Redis.
const hash = await redis.hget(`client:${client}:h`, 'secret')
We compare the provided secret
to the hash.
await bcrypt.compare(secret, hash)
We generate a session token
using Math.random().
const randomToken = () =>
Math.random()
.toString(36)
.substring(2)
const token = randomToken() + randomToken()
We store and expire the session in Redis.
const { ttlSeconds } = config.session
await redis
.multi([
['del', `session:${token}:h`],
['hset', `session:${token}:h`, 'client', client],
['expire', `session:${token}:h`, ttlSeconds],
])
.exec()
See https://github.com/evanx/fastify-xadd-mlk
This project enables Redis stream ingress from authenticated clients via "Bearer" token.
fastify.register(require('fastify-bearer-auth'), {
auth: async (token, request) => {
const client = await fastify.redis.hget(`session:${token}:h`, 'client')
if (client) {
request.client = client
return true
}
return false
},
errorResponse: err => {
return { code: 401, message: err.message }
},
})
where the client includes the token
from /login
in the HTTP Authorization
header:
Authorization: Bearer {token}