## Auth0 + FaunaDB ABAC integration: How to expire Fauna user secrets. Fauna doesn't (yet?) provide guaranteed expiration/TTL for ABAC tokens, so we need to implement it ourselves if we care about it. ### What's in the box? 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`: 1. `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 issued - `expires_at`: the Time at which this token should expire - `meta`: any other data passed into the `custom_metadata` argument to `exchange-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 by `data.jwt_payload.exp`. 1. `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 via `Create(Tokens(), ...)`). - Create a document in the `auth0_token_exchanges` collection containing the `ref` of the user token (NOT the secret), the provided JWT, and the decoded JWT payload. - Return the user secret (by resolving the promise). - 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 } }) ) ) ) } ``` 1. `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 in `data.jwt_payload.exp`. - For each matching instance returned from `auth0_token_exchanges`, deletes the ABAC token referenced by `data.token_ref`. - Deletes each of the matching instances returned from `auth0_token_exchanges`.