BlogDocumentation
Products
Identity ProtectionIdentity ManagementBlogDocumentation
Joseph Gardner
Joseph Gardner
20 Jul, 2023
The Why The What The How Summary
New Feature
Synchronous Webhooks

We are excited to release synchronous webhooks, the latest addition to our webhooks features.

With synchronous webhooks, you can extend SlashID Access to suit your business needs in a few simple steps, in whatever language and environment makes sense for you.

Synchronous Webhooks

We are excited to release synchronous webhooks, the latest addition to our webhooks features. With synchronous webhooks, you can extend SlashID Access to suit your business needs in a few simple steps, in whatever language and environment makes sense for you.

In this blogpost, we will introduce synchronous webhooks by answering three questions – why, what, and how, including a short example.

The Why

Our initial webhooks release focused on asynchronous webhooks triggered by events, which are ideal for analytics and monitoring.

However, there are many use cases where you need to extend the identity logic in some business-specific way. For example, you may need to enrich an authentication token with information you keep internally, or ensure that certain actions are included in your internal audit log.

With SlashID synchronous webhooks, you can quickly and easily integrate your custom identity logic into SlashID’s existing flows, using the language and deployment environment that makes the most sense for you and your organization. You gain visibility into crucial identity flows, and have the opportunity to affect their outcomes.

The What

Synchronous hooks are the extension points in SlashID flows where your webhooks will be called. Each type of synchronous hook has its own content, and accepts specific responses that can be used to affect the relevant flows.

For this initial release, we have focused on hooks when the user registers or signs-in. Specifically, we hook the point during sign-in after authentication has succeeded, but before we sign and issue the authentication token to the user. The hook content contains the claims that will be present in the issued token. The response from your webhook can optionally contain custom claims as key-value pairs, which will be added as claims in the token before it is issued.

This hook allows you to change the authentication/registration flow and add your custom business logic before the token is returned to the requesting application. Crucially, the webhook is executed before the token is signed to avoid potential security concerns.

The How

Synchronous webhooks build on SlashID’s existing webhooks functionality – so you can use exactly the same APIs to manage your webhooks, and the webhook requests follow the same pattern. You will need to implement the handler for your webhook endpoint, and then you’re ready to start receiving synchronous hook requests.

Let’s see how this works in practice.

First, you need to implement the API for handling webhook requests. In this example, we will fetch some personal details about the user logging in and use these to enrich the token. For brevity, we have omitted error handling.

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"

	"github.com/google/tink/go/jwt"
)

const (
	sidOrgID       = "my-slashid-organization-id"
	sidBaseURL     = "https://api.slashid.com"
	svcBaseURL     = "https://my-service.com"
	sidWebhookPath = "/sid/webhook"
)

func HandleSlashIDWebhook(r *http.Request, w http.ResponseWriter) {
	// Read the body from the request - this will be a signed and encoded JWT
	reqBody, _ := io.ReadAll(r.Body)
	defer r.Body.Close()

	// Retrieve the verification key for your organization using the SlashID APIs
	verificationJWKS := getVerificationJWKS()
	verificationKeyset, _ := jwt.JWKSetToPublicKeysetHandle(verificationJWKS)

	// Build the verifier and validator
	verifier, _ := jwt.NewVerifier(verificationKeyset)

	issuer := sidBaseURL
	aud := sidOrgID
	validator, _ := jwt.NewValidator(&jwt.ValidatorOpts{
		ExpectedIssuer:         &issuer,
		AllowMissingExpiration: false,
		ExpectedAudience:       &aud,
	})

	// Verify the JWT - this will include the issuer, audience, and expiration
	verifiedJWT, err := verifier.VerifyAndDecode(string(reqBody), validator)
	if err != nil {
		// The request body cannot be verified as coming from SlashID and so should not be trusted!
	}

	// Check the target URL matches the endpoint this handler is associated with
	targetURL, _ := verifiedJWT.StringClaim("target_url")
	expectedTargetURL, _ := url.JoinPath(svcBaseURL, sidWebhookPath)
	if targetURL != expectedTargetURL {
		// This URL does not seem to be the intended target - ignore, log
	}

	// We are happy that the webhook request is legitimate, so we can handle the actual content
	// In this case, we are expecting a token minted sync hook, so the trigger content is the
	// claims that will appear in the user token SlashID is about to issue to the authenticated user.
	userTokenClaims, _ := verifiedJWT.ObjectClaim("trigger_content")
	personID := userTokenClaims["sub"]

	// Here we have your business logic to retrieve name (string) and address (array of strings)
	name, address := getPersonalDetails(personID.(string))

	// Write these as JSON in the response body
	respMap := map[string]any{
		"name":    name,
		"address": address,
	}
	respBody, _ := json.Marshal(respMap)
	_, _ = w.Write(respBody)

	return
}

Now, we will make two API calls to SlashID - one to create a webhook, and one to create a trigger for that webhook.

curl -X POST --location 'https://api.slashid.com/organizations/webhooks' \
--header 'SlashID-OrgID: my-slashid-organization-id' \
--header 'SlashID-API-Key: my-slashid-api-key' \
--header 'Content-Type: application/json' \
--data '{
    "target_url": "https://my-service.com/sid/webhook",
    "name": "token minted webhook",
}'


{
  "result": {
    "id": "e64c7123-497d-7e12-b665-341b5defdec1",
    "target_url": "https://my-service.com/sid/webhook",
  }
}

curl -X POST --location 'https://api.slashid.com/organizations/webhooks/e64c7123-497d-7e12-b665-341b5defdec1/triggers' \
--header 'SlashID-OrgID: my-slashid-organization-id' \
--header 'SlashID-API-Key: my-slashid-api-key' \
--header 'Content-Type: application/json' \
--data '{
    "trigger_type": "sync_hook",
    "trigger_name": "token_minted"
}'

Now suppose a user authenticates to your application via SlashID, using an email link.

Once they have successfully authenticated, the SlashID backend prepares a token. Before the token is signed and issued back to the user, SlashID reaches the hook point and checks for webhooks registered for this trigger. It finds the webhook you created earlier, and so your webhook is called.

The body will be a signed JWT, which your handler function verifies and decodes. If we decode the JWT payload, we see that the trigger content contains the claims that will be present in the user token:

{
  "aud": "my-slashid-organization-id",
  "iss": "https://slashid.local",
  "target_url": "https://my-service.com/sid/webhook",
  "trigger_name": "token_minted",
  "trigger_type": "sync_hook",
  "webhook_id": "e64c7123-497d-7e12-b665-341b5defdec1",

  "trigger_content": {
    "aud": "my-slashid-organization-id",
    "authentications": [
      {
        "handle": {
          "type": "email_address",
          "value": "[email protected]"
        },
        "method": "email_link",
        "timestamp": "2023-07-18T12:25:56.390080959Z"
      }
    ],
    "first_token": false,
    "groups": ["admins"],
    "groups_claim_name": "groups",
    "iss": "https://slashid.local",
    "region": "us-iowa",
    "sub": "165c7138-545d-7065-8ff2-13850368729"
  }
}

Your handler retrieves the personal information, and returns it in the response. The SlashID backend uses this response to enrich the token, which is then signed and issued to the user. The final token payload will look like this:

{
  "aud": "my-slashid-organization-id",
  "authentications": [
    {
      "handle": {
        "type": "email_address",
        "value": "[email protected]"
      },
      "method": "email_link",
      "timestamp": "2023-07-18T12:25:56.390080959Z"
    }
  ],
  "first_token": false,
  "groups": ["admins"],
  "groups_claim_name": "groups",
  "iss": "https://slashid.local",
  "region": "us-iowa",
  "sub": "165c7138-545d-7065-8ff2-13850368729",
  "name": "Alex Singh",
  "address": ["123 Main Street", "Honolulu", "Hawaii", "96801"]
}

It contains the claims that your webhook received in the request, plus those returned in the response.

Summary

With this release, you can use synchronous webhooks to extend SlashID Access. We will be adding more hooks and features in the near future, so stay tuned for release announcements.

Ready to try SlashID? Register here!

Is there a feature you’d like to see, or have you tried out webhooks and have some feedback? Let us know!

Related articles

Achieving Least Privilege: Unused Entitlement Removal

New Feature

/ 5 May, 2025

Achieving Least Privilege: Unused Entitlement Removal

Unused entitlements are one of the easiest ways for an attacker to move laterally in a target environment.

However, reducing permissions is often very difficult due to availability concerns and the complexity of the permission systems.

This blog post explores how SlashID solves this problem so that customers can automatically resize identity permissions and

achieve least privilege.

Vincenzo Iozzo
Vincenzo Iozzo
Detecting Man-in-the-Middle Attacks with SlashID

New Feature

/ 26 Aug, 2024

Detecting Man-in-the-Middle Attacks with SlashID

Detect when attackers access your website through malicious proxies with SlashID.

Ivan Kovic
Ivan Kovic
SlashID RBAC: Globally-available role-based access control

New Feature

/ 22 Jul, 2024

SlashID RBAC: Globally-available role-based access control

SlashID RBAC is a globally replicated role-based access control system that allows you to restrict access to resources based on permissions assigned to specific persons.

In this post, we will show you how to use RBAC in SlashID, and how to create permissions, and roles, and assign them to persons.

Robert Laszczak
Robert Laszczak

Ready to start a top-tier security upgrade?

Terms · Privacy · System Status
© 2025 SlashID® Inc. All Rights Reserved.

Products

Identity Protection Identity Management

Resources

Blog Get in touch

We use cookies to improve your experience. Read our cookie policy.