Authsome

Webhooks

Configure webhook endpoints to receive real-time notifications for all 31 Authsome events, with HMAC signing and Relay bridge integration.

Authsome delivers event notifications to your own HTTP endpoints via webhooks. Every significant event -- user creation, sign-in attempts, MFA enrollment, organization changes, environment lifecycle -- fires a webhook. You can configure multiple endpoints and subscribe each one to different event types.

The Webhook model

import "github.com/xraph/authsome/webhook"

type Webhook struct {
    ID          id.ID      `json:"id"`          // awhk_ prefix
    AppID       string     `json:"app_id"`
    EnvID       string     `json:"env_id"`
    OrgID       *id.ID     `json:"org_id,omitempty"` // nil for app-level webhooks
    Name        string     `json:"name"`
    URL         string     `json:"url"`
    Secret      string     `json:"-"`           // HMAC signing secret -- never exposed after creation
    SecretHash  string     `json:"-"`           // stored hash
    Events      []string   `json:"events"`      // subscribed event types
    IsActive    bool       `json:"is_active"`
    Headers     map[string]string `json:"headers,omitempty"` // custom HTTP headers
    MaxRetries  int        `json:"max_retries"` // 0-5, default 3
    TimeoutMs   int        `json:"timeout_ms"`  // delivery timeout, default 5000
    CreatedAt   time.Time  `json:"created_at"`
    UpdatedAt   time.Time  `json:"updated_at"`
}

All event types

Authsome emits 31 event types grouped by category:

User events

EventDescription
user.createdA new user account was created
user.updatedUser profile information was updated
user.deletedA user account was permanently deleted

Session events

EventDescription
session.createdA new session was created (sign-in)
session.revokedA session was revoked (sign-out or forced termination)

Auth events

EventDescription
auth.signinSuccessful sign-in
auth.signin.failedFailed sign-in attempt
auth.signoutUser signed out
auth.password.resetPassword was reset
auth.mfa.enabledMFA was enabled on the account
auth.mfa.enrolledA specific MFA method was enrolled (TOTP, SMS, etc.)
auth.mfa.verifiedAn MFA challenge was successfully verified
auth.mfa.challengedAn MFA challenge was initiated
auth.mfa.disabledMFA was disabled on the account

Passkey events

EventDescription
passkey.registeredA new passkey (WebAuthn credential) was registered
passkey.authenticatedAuthentication via passkey succeeded
passkey.deletedA passkey credential was removed

Social events

EventDescription
social.signinSign-in via a social OAuth provider (Google, GitHub, etc.)
social.signupNew account created via social OAuth

SSO events

EventDescription
sso.signinSign-in via enterprise SSO (OIDC or SAML)
sso.signupNew account created via enterprise SSO

Organization events

EventDescription
org.createdA new organization was created
org.updatedOrganization details were updated
org.member.invitedA user was invited to the organization
org.member.joinedAn invited user accepted and joined
org.member.removedA member was removed from the organization
org.member.role_changedA member's role was changed

Environment events

EventDescription
environment.createdA new environment was created
environment.updatedEnvironment settings were updated
environment.deletedAn environment was deleted
environment.clonedAn environment was cloned from another

API key events

EventDescription
apikey.createdA new API key was created
apikey.revokedAn API key was revoked
EventDescription
consent.grantedA user granted consent
consent.revokedA user revoked a previously granted consent

Creating a webhook

import "github.com/xraph/authsome/webhook"

result, err := auth.Webhooks().Create(ctx, &webhook.CreateInput{
    Name: "My Backend Notifications",
    URL:  "https://api.myapp.com/authsome/events",
    Events: []string{
        "user.created",
        "user.deleted",
        "auth.signin",
        "auth.signin.failed",
        "session.revoked",
    },
    Headers: map[string]string{
        "X-Source": "authsome",
    },
    MaxRetries: 3,
    TimeoutMs:  5000,
})
if err != nil {
    return err
}

// result.Secret is the signing secret -- only available at creation time.
fmt.Printf("webhook created: id=%s secret=%s\n", result.Webhook.ID, result.Secret)

The webhook signing secret is returned only once at creation time and is not stored in plain text. Store it securely (e.g., in your secrets manager). If you lose it, rotate it using auth.Webhooks().RotateSecret(ctx, webhookID).

Listing and managing webhooks

// List all webhooks.
result, err := auth.Webhooks().List(ctx, &webhook.ListInput{Limit: 50})

// Get a specific webhook.
w, err := auth.Webhooks().GetByID(ctx, webhookID)

// Update a webhook (e.g., change subscribed events).
updated, err := auth.Webhooks().Update(ctx, webhookID, &webhook.UpdateInput{
    Events: []string{"user.created", "user.deleted"},
    IsActive: true,
})

// Deactivate (pause delivery without deleting).
err := auth.Webhooks().Deactivate(ctx, webhookID)

// Delete permanently.
err := auth.Webhooks().Delete(ctx, webhookID)

// Rotate the signing secret.
result, err := auth.Webhooks().RotateSecret(ctx, webhookID)
// result.NewSecret is the new secret -- only available here.

Webhook payload

Every webhook request is an HTTP POST with a JSON body:

{
  "id": "awhke_01h455vb4pex5vsknk084sn02q",
  "event": "auth.signin",
  "app_id": "myapp",
  "env_id": "aenv_01h455vb...",
  "occurred_at": "2024-03-15T14:30:00Z",
  "data": {
    "user": {
      "id": "ausr_01h455vb...",
      "email": "alice@example.com",
      "display_name": "Alice Nguyen"
    },
    "session": {
      "id": "ases_01h455vb...",
      "ip": "203.0.113.42",
      "user_agent": "Mozilla/5.0 ..."
    }
  }
}

The data field shape varies by event type. Each event type's data schema is specific to what changed.

HMAC signature verification

Every webhook request includes an X-Authsome-Signature header containing an HMAC-SHA256 signature of the raw request body. Verify this signature on your endpoint before processing the payload:

package webhookhandler

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "io"
    "net/http"
)

func HandleAuthsomeWebhook(secret string) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        body, err := io.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "cannot read body", http.StatusBadRequest)
            return
        }

        // Verify HMAC-SHA256 signature.
        sig := r.Header.Get("X-Authsome-Signature")
        mac := hmac.New(sha256.New, []byte(secret))
        mac.Write(body)
        expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))

        if !hmac.Equal([]byte(sig), []byte(expected)) {
            http.Error(w, "invalid signature", http.StatusUnauthorized)
            return
        }

        // Process the verified payload.
        var payload map[string]any
        json.Unmarshal(body, &payload)
        // ...

        w.WriteHeader(http.StatusOK)
    }
}

Signature headers

HeaderDescription
X-Authsome-Signaturesha256=<hex-encoded HMAC>
X-Authsome-EventThe event type string (e.g. auth.signin)
X-Authsome-DeliveryA unique UUID for this delivery attempt
X-Authsome-TimestampUnix timestamp of delivery (use to reject old replays)

Delivery and retry

When Authsome fires a webhook:

  1. The event payload is built and signed.
  2. An HTTP POST is sent to the endpoint URL with a configurable timeout (TimeoutMs, default 5000ms).
  3. Any 2xx response is considered a success.
  4. On failure (non-2xx, timeout, or connection error), delivery is retried up to MaxRetries times with exponential backoff.
  5. All delivery attempts (including failures) are logged in the webhook delivery log.
// View delivery attempts for a webhook.
deliveries, err := auth.Webhooks().ListDeliveries(ctx, webhookID, &webhook.ListDeliveriesInput{
    Limit: 50,
})

for _, d := range deliveries {
    fmt.Printf("  [%s] attempt=%d status=%d success=%v\n",
        d.CreatedAt.Format(time.RFC3339),
        d.Attempt,
        d.ResponseStatus,
        d.Success,
    )
}

// Manually retry a failed delivery.
err := auth.Webhooks().RetryDelivery(ctx, webhookID, deliveryID)

Relay bridge integration

Relay is the Forge-ecosystem's event bus. When Relay is configured, Authsome publishes every event to Relay in addition to (or instead of) direct HTTP webhook delivery. This lets other services in your platform subscribe to authentication events without going through HTTP.

import (
    "github.com/xraph/authsome"
    "github.com/xraph/authsome/webhook/relay"
)

auth := authsome.New(
    authsome.WithWebhookBridge(relay.NewBridge(
        relay.WithEndpoint("https://relay.internal:8080"),
        relay.WithAPIKey(os.Getenv("RELAY_API_KEY")),
        relay.WithTopic("authsome.events"),
        relay.WithAsync(true),
    )),
)

HTTP API endpoints

MethodPathDescription
POST/webhooksCreate a new webhook
GET/webhooksList all webhooks
GET/webhooks/:webhook_idGet a webhook by ID
PATCH/webhooks/:webhook_idUpdate a webhook
DELETE/webhooks/:webhook_idDelete a webhook
POST/webhooks/:webhook_id/deactivatePause webhook delivery
POST/webhooks/:webhook_id/activateResume webhook delivery
POST/webhooks/:webhook_id/rotate-secretRotate the signing secret
GET/webhooks/:webhook_id/deliveriesList delivery attempts
POST/webhooks/:webhook_id/deliveries/:delivery_id/retryRetry a failed delivery
POST/webhooks/testSend a test event to a webhook URL

On this page