Writing a Webhook
Writing a Webhook
In order to appropriately handle requests from the FusionAuth event, you must build a simple HTTP Webhook that listens for requests from the FusionAuth event system. Your Webhook must be designed to receive to simple HTTP POST
requests with a JSON request body. The HTTP request will be sent using a Content-Type
header value of application/json
.
Additional headers may be added to the request by adding headers to the Webhook configuration.
Responses
Your Webhook must handle the RESTful request described above and send back an appropriate status code. Your Webhook must send back to FusionAuth an HTTP response code that indicates whether or not the event was successfully handled or not. If your Webhook handled the event properly, it must send back an HTTP response status code of 2xx
. If there was any type of error or failure, your Webhook must send back a non 2xx
HTTP response status.
Configuration
Once your Webhook is complete and listening for events, you must configure your Webhook URL in FusionAuth. To add a webhook navigate to
. If you have multiple Webhooks configured for a single Application, the transaction setting for the event or the User Action will dictate if FusionAuth will commit the transaction or not.Calling FusionAuth APIs In Webhooks
Some events fire on creation of an entity in FusionAuth, such as user.create
. You may want to modify the created entity, but if your webhook tries to modify the newly created object in a webhook handling the create event, the operation will fail. This is due to the fact that the operation occurs in a transaction and has not yet completed when the webhook runs.
In fact, the created user will not be visible to any other API request until the transaction is committed. The operation fails because the webhook is trying to modify an object that has not yet been completely created and has not yet been committed to persistant storage. Depending upon your transaction configuration for a particular event, FusionAuth may wait until all webhooks have responded before committing the transaction.
Even if you configure your webhook transaction to not require any webhooks to succeed, it is unlikely your code will operate as intended due to the parallel timing of the requests. The user.create
event was not designed to allow a webhook to retrieve and modify the user.
Here’s a scenario:
-
You have a webhook that catches the
user.create
event. -
It extracts the user’s email address.
-
Then it queries a non FusionAuth database and adds a custom
user.data.premiumUser
field to the FusionAuth user object based on the query results. -
At user login, the value of the
user.data.premiumUser
field will be placed into a JWT for other applications to access.
In this example, you have a few options; which one is best depends on when you need to be able to read from the user.data.premiumUser
field.
-
Provide the custom data field at user creation, instead of updating the user via a webhook. This option is the simplest, but may not be possible if users are self registering. In this case, the field is available from the moment the user is created.
-
Review available events and determine if a subsequent event occurs in your workflow. For example,
user.registration.create
may occur after a user is created. At this point, the user will exist and can be modified. If an event happens repeatedly, make the modification idempotent. In this case, the field is available as soon as the other event fires. -
Don’t process the data in the webhook. Instead, push the event JSON to a queue and return success. Have a queue consumer pull the data off and update the
user.data.premiumUser
field. The consumer can retry multiple times if the user object has not yet been fully created, which can happen if there are other webhooks whose completion is required. In this case, the field is available when the consumer finishes.
While this scenario is most obvious when a user or registration is being created, it applies to all webhooks. The final state of the operation which caused the webhook is not persisted to FusionAuth until after the webhook finishes.
Example Code
Here’s a simple example of a Webhook written in Node using Express. In this example, if the event is a user.delete
event, this code deletes all of the user’s Todos.
In this example we are also checking the HTTP Authorization header for an API key. Using an API key or some type of authentication helps secure your Webhook to prevent malicious requests. You can configure the API key via the FusionAuth Web Interface or the API using the Headers of the Webhook configuration.
router.route('/fusionauth-webhook').post((req, res) => {
const authorization = req.header('Authorization');
if (authorization !== 'API-KEY') {
res.status(401).send({
'errors': [{
'code': '[notAuthorized]'
}]
});
return;
}
const request = req.body;
if (request.event.type === 'user.delete') {
todo.deleteAll(request.event.user.id)
.then(() => {
res.sendStatus(200);
})
.catch(function(err) {
_handleDatabaseError(res, err);
});
}
});