Skip to main content

Creating webhooks

The first step to adding webhooks to your Indent integration is to build your own custom endpoint. Creating a webhook endpoint on your server is no different from creating any page on your website. With PHP, you might create a new .php file on your server; with a Ruby framework like Sinatra or Rails, you would add a new route.

We recommend considering a serverless or containerized option and a dedicated cloud account for additional levels of security isolation. Check out the Okta webhook as an example.

What does a webhook look like?

Webhooks should first verify the request is valid before handling any requests, then check if there's available logic to process the request and respond with a 200 HTTP status code. Here's an example in pseudocode of how Indent webhooks are structured:

async function(request: Request) {
if (verify(request) === false) {
throw new Error
}
if (match(request)) {
return await process(request)
}
return { statusCode: 200, body: '{}'}
}

Verifying the request

Indent signs all webhook requests it sends to your endpoints with a unique signature in the X-Indent-Signature header. This signature lets you verify that requests are from Indent and not a third party. You can verify signatures with Indent's official library, or manually using your own solution.

Indent requests also include an X-Indent-Timestamp header to provide an extra layer of certainty when it comes to the timeliness of requests, to mitigate time replay attacks.

Types of webhooks

There are two kinds of webhooks:

  1. Apply update — these webhooks receive Events like access/grant or access/revoke to provision or deprovision access in a remote system.
  2. Pull update — these webhooks receive a list of Resource Kinds like myapp.v1.Admin and respond with a list of API objects to update as Resources in your Space.

Apply update webhooks

Looking for a specific provider? Use one of our pre-built sample webhooks to get started.

Here is the starting point of a sample apply update webhook:

const { verify } = require('@indent/webhook')

exports['webhook'] = async function handle(req, res) {
const { body } = req

try {
await verify({
secret: process.env.INDENT_WEBHOOK_SECRET,
headers: req.headers,
body,
})
} catch (err) {
console.error('@indent/webhook.verify(): failed')
console.error(err)
return res.status(500).json({ error: { message: err.message } })
}

const { events } = body

console.log(`@indent/webhook: received ${events.length} events`)
console.log(JSON.stringify(events, null, 2))

await Promise.all(
events.map((auditEvent) => {
switch (auditEvent.event) {
case 'access/grant':
return grantPermission(auditEvent)
case 'access/revoke':
return revokePermission(auditEvent)
default:
return Promise.resolve()
}
})
)

return res.status(200).json({})
}

Pull update webhooks

Looking for a specific provider? Download one of our pre-built sample webhooks to get started.

Here is the starting point of a sample pull update webhook:

const { verify } = require('@indent/webhook')

exports['webhook'] = async function handle(req, res) {
const { body } = req

try {
await verify({
secret: process.env.INDENT_WEBHOOK_SECRET,
headers: req.headers,
body,
})
} catch (err) {
console.error('@indent/webhook.verify(): failed')
console.error(err)
return res.status(500).json({ error: { message: err.message } })
}

const { kinds } = body

console.log(`@indent/webhook: received kinds: ${kinds}`)

const resources = (
await Promise.all(kinds.map((kind) => loadResourcesForKind(kind)))
).flat()

return res.status(200).json({ resources })
}