Fauna doesn't (yet?) provide guaranteed expiration/TTL for ABAC tokens, so we need to implement it ourselves if we care about it.
3 javascript functions, each of which can be imported into your project or run from the command-line
using node path/to/script.js arg1 arg2 ... argN
:
deploy-schema.js
: a javascript function for creating supporting collections and indexes in your Fauna database.- Intended to be called once per Fauna database. Safe to call it multiple times (will not cause harm).
- Needs to be called with a Fauna SERVER secret prior to using the other provided functions.
- Logic
- args:
fauna_server_secret
- returns: a
Promise
- Creates a collection named
auth0_token_exchanges
.- This collection will store one document for each user secret issued in exchange for an Auth0 token.
- The documents will contain this data:
token_ref
: the Ref of the Fauna user token which was issuedexpires_at
: the Time at which this token should expiremeta
: any other data passed into thecustom_metadata
argument toexchange-jwt-for-secret.js
- For example, you might want to log the decoded JWT payload or the entire JWT, which could be useful for your own indexing/querying/auditing/debugging purposes.
- We exclude any identifying data by default to avoid unintentionally storing any sensitive user data which may be governed by HIPAA, etc.
- Creates an index named
auth0_token_exchanges_by_expiration
which indexes the documents bydata.jwt_payload.exp
.
- args:
exchange-jwt-for-secret.js
: verifies Auth0 JWTs, looks-up the user in Fauna by auth0_id, creates an ABAC token for the user, records the token and JWT expiration time in Fauna, and returns the token secret.- This is intended to be served in an API endpoint that you create.
- Clients should call this endpoint upon receiving a JWT to obtain a Fauna user secret.
- Clients can then use this Fauna user secret to communicate directly with your Fauna database, e.g. for the native GraphQL endpoint.
- Logic
- args:
auth0_jwt, custom_metadata, auth0_client_id, auth0_client_cert_pubkey, fauna_server_secret, fauna_index_users_by_auth0_id
- returns: a
Promise
- Verifies the JWT (via Auth0 for safety). Rejects the promise if invalid or expired.
- Looks up user by Auth0 user ID (from index named by
fauna_user_index_auth0_id
)- you need to setup this index prior to using this function.
- Create a user token for the user (i.e.
Login()
, but viaCreate(Tokens(), ...)
). - Create a document in the
auth0_token_exchanges
collection containing theref
of the user token (NOT the secret), the provided JWT, and the decoded JWT payload. - Return the user secret (by resolving the promise).
- args:
- In my app's integration, I added some logic at the beginning to create the User document for this Auth0 ID if there isn't one already.
function findOrCreateUserRef(index_users_by_auth0_id, auth0_id) { return q.Select( ['ref'], q.Let( {userMatch: q.Match(q.Index(index_users_by_auth0_id), auth0_id)}, q.If( q.Exists(q.Var('userMatch')), q.Get(q.Var('userMatch')), q.Create(q.Collection('users'), { data: { auth0_id: auth0_id } }) ) ) ) }
delete-expired-tokens.js
: deletes all expired tokens which were issued in exchange for Auth0 JWTs.- This is intended to be called in a cron, and can be called as often as desired. The lower limit is probably once/day and the upper limit is probably once every 5 minutes.
- If you don't call this function in a cron, then the rest of this code is pointless, because your user tokens will never expire (which is the normal behavior without any of this code).
- Logic
- args:
fauna_server_secret
- returns: a
Promise
- Queries index
auth0_token_exchanges_by_expiration
for all documents which are past the expiration timestamp specified indata.jwt_payload.exp
. - For each matching instance returned from
auth0_token_exchanges
, deletes the ABAC token referenced bydata.token_ref
. - Deletes each of the matching instances returned from
auth0_token_exchanges
.
- args: