All pages
Powered by GitBook
1 of 5

Events and Hooks

Event are generated and delivered to your hooks. You will be notified when important events such as new user signup happen.

Events are divided into two kinds, Blocking and Non-blocking.

Each event can have multiple hooks. The delivery order of non-blocking events is unspecified. Blocking events are delivered in the order as in the configuration.

Blocking Events

Blocking events are triggered before the operation is performed, such as before user creation. The operation can be aborted by your hooks.

They are delivered to your hooks synchronously, right before changes are persisted to the database.

Your hooks must return a JSON document to indicate whether the operation should continue.

To let the operation proceed, return a JSON document with is_allowed set to true.

{
  "is_allowed": true
}

To abort the operation, return a JSON document with is_allowed set to false, and a non-empty reason and title.

{
  "is_allowed": false,
  "reason": "some reason",
  "title": "some title"
}

If any of your hooks aborts the operation, the operation is aborted. The reason and title will be shown to the end-user as an error message.

Each of your hooks must respond within 5 seconds. All of your hooks must complete within 10 seconds. Otherwise, the delivery will fail to due timeout.

Blocking Event Mutations

Your hooks can optionally apply mutation for certain blocking events. The supported mutation is specific for each type of blocking events. Refer to the Event List to see what mutation is supported.

Mutations by a hook is applied only when the operation is allowed to proceed. Mutations take effect only when all hooks allow the operation to proceed.

The mutations are specified in the hook response. Objects not appearing in mutations are left intact. The mutated objects are NOT merged with the original ones.

The mutated objects are NOT validated, and are propagated along the hook chain. The mutated objects are validated after traversing the hook chain.

Mutations do NOT generate extra events to avoid infinite loops.

Mutations on the user object

When a blocking event supports mutations on the user object, your hooks can respond a JSON document to allow the operation, and specify the mutations you want to apply on the user object.

{
  "is_allowed": true,
  "mutations": {
    "user": {
      "standard_attributes": {
        "name": "John"
      },
      "custom_attributes": {
        "age": 30
      }
    }
  }
}

To mutate the user object, include user inside mutations. Only standard_attributes and custom_attributes of the user object are mutable.

You must include the WHOLE standard_attributes or custom_attributes when you specify the mutations. Otherwise, missing attributes WILL BE deleted.

Mutations on the JWT payload

When a blocking event supports mutations on the JWT payload, your hooks can respond a JSON document to allow the operation, and specify additional fields that you want to include in the JWT payload. However, you MUST NOT change or remove any existing fields in the JWT payload, as they are essential to the validity of the JWT.

{
  "is_allowed": true,
  "mutations": {
    "jwt": {
      "payload": {
        // The original payload you get from the event object.
        "iss": "https://myapp.authgear.cloud",
        "aud": ["YOUR_CLIENT_ID"],
        "sub": "THE_USER_ID",
        // Other essential JWT fields that you MUST retain.

        // Additional fields that you want to add.
        "https://myapp.com": {
          "custom_field": "custom_value"
        }
      }
    }
  }
}

To add additional fields to the JWT payload, include jwt.payload inside mutations. You MUST add your own fields only.

You must include the WHOLE jwt.payload from the event object when you specify the mutations.

Non-blocking Events

Non-blocking events are triggered after the operation is performed.

They are delivered to your hooks asynchronously after the operation is performed.

Your hooks must finish within 60 seconds. Otherwise, the delivery will fail to due timeout.

The response of your hooks is ignored.

Event Shape

Events have the following shape:

{
  "id": "0E1E9537-DF4F-4AF6-8B48-3DB4574D4F24",
  "seq": 435,
  "type": "user.pre_create",
  "payload": { /* ... */ },
  "context": {
    "timestamp": 1670570552,
    "user_id": "f333b70b-4436-4efb-a40b-d9ed7a74d319",
    "preferred_languages": ["en-US", "zh-HK"],
    "language": "en-US",
    "triggered_by": "user"
  }
}
  • id: The ID of the event.

  • seq: A monotonically increasing signed 64-bit integer.

  • type: The type of the event.

  • payload: The payload of the event, varies with type.

  • context: The context of the event.

  • context.timestamp: signed 64-bit UNIX timestamp of when this event is generated. Retried deliveries do not affect this field.

  • context.user_id: The ID of the user associated with the event. It may be absent. For example, the user has not been authenticated yet.

  • context.preferred_languages: User preferred languages, which are inferred from the request. Return values of the ui_locales query if it is provided in the Auth UI, otherwise return languages in the Accept-Language request header.

  • context.language: User locale which is derived based on user's preferred languages and app's languages config.

  • context.triggered_by: Triggered by indicates who triggered the events, values can be user or admin_api. user means it is triggered by the end-user. admin_api means it is triggered by Admin API or admin portal.

Event List

The full list of events

Blocking Events

  • user.pre_create

  • user.profile.pre_update

  • user.pre_schedule_deletion

  • oidc.jwt.pre_create

Non-blocking Events

  • user.created

  • user.profile.updated

  • user.authenticated

  • user.disabled

  • user.reenabled

  • user.anonymous.promoted

  • user.deletion_scheduled

  • user.deletion_unscheduled

  • user.deleted

  • identity.email.added

  • identity.email.removed

  • identity.email.updated

  • identity.email.verified

  • identity.email.unverified

  • identity.phone.added

  • identity.phone.removed

  • identity.phone.updated

  • identity.phone.verified

  • identity.phone.unverified

  • identity.username.added

  • identity.username.removed

  • identity.username.updated

  • identity.oauth.connected

  • identity.oauth.disconnected

  • identity.biometric.enabled

  • identity.biometric.disabled

user.pre_create

Occurs right before the user creation. User can be created by user signup, user signup as an anonymous user, or created by the admin via the Portal or Admin API.

This event supports Mutations on the user object

{
  "type": "user.pre_create",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identities": [
      {
        "id": "239d585d-9b90-4148-9aa2-2e3131b5847a",
        "created_at": "2006-01-02T03:04:05.123456Z",
        "updated_at": "2006-01-02T03:04:05.123456Z",
        "type": "login_id",
        "claims": {
          "email": "user@example.com",
          "https://authgear.com/claims/login_id/key": "email",
          "https://authgear.com/claims/login_id/original_value": "user@example.com",
          "https://authgear.com/claims/login_id/type": "email",
          "https://authgear.com/claims/login_id/value": "user@example.com"
        }
      }
    ]
  }
}

user.profile.pre_update

Occurs right before the update of the user profile.

This event supports Mutations on the user object

{
  "type": "user.profile.pre_update",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "name": "Chris",
        "updated_at": 1136171045
      }
    }
  }
}

user.pre_schedule_deletion

Occurs right before account deletion is scheduled.

This event does not support mutations.

{
  "type": "user.pre_schedule_deletion",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": true,
      "is_deactivated": true,
      "delete_at": "2022-09-30T15:18:19.040081Z",
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    }
  }
}

oidc.jwt.pre_create

Occurs right before the access token is issued. Use this event to add custom fields to the JWT access token.

This event supports Mutations on the JWT payload

{
  "type": "oidc.jwt.pre_create",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": true,
      "is_deactivated": true,
      "delete_at": "2022-09-30T15:18:19.040081Z",
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "jwt": {
      "payload": {
        "iss": "https://myapp.authgear.cloud",
        "aud": ["YOUR_CLIENT_ID"],
        "sub": "338deafa-400b-4589-a922-2c92d670b757"
      }
    }
  }
}

user.created

Occurs after a new user is created. User can be created by user signup, user signup as an anonymous user, or created by the admin via the Portal or Admin API.

{
  "type": "user.created",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identities": [
      {
        "id": "239d585d-9b90-4148-9aa2-2e3131b5847a",
        "created_at": "2006-01-02T03:04:05.123456Z",
        "updated_at": "2006-01-02T03:04:05.123456Z",
        "type": "login_id",
        "claims": {
          "email": "user@example.com",
          "https://authgear.com/claims/login_id/key": "email",
          "https://authgear.com/claims/login_id/original_value": "user@example.com",
          "https://authgear.com/claims/login_id/type": "email",
          "https://authgear.com/claims/login_id/value": "user@example.com"
        }
      }
    ]
  }
}

user.profile.updated

Occurs when the user profile is updated.

{
  "type": "user.profile.updated",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "name": "Chris",
        "updated_at": 1136171045
      }
    }
  }
}

user.authenticated

Occurs after the user logged in.

{
  "type": "user.authenticated",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "session": {
      "id": "6e6e5d9b-7f85-4a8f-a157-2de94694dfea",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "idp",
      "amr": ["pwd"],
      "lastAccessedAt": "2006-01-02T03:04:05.123456Z",
      "createdByIP": "127.0.0.1",
      "lastAccessedByIP": "127.0.0.1",
      "lastAccessedByIPCountryCode": "",
      "lastAccessedByIPEnglishCountryName": "",
      "displayName": "Chrome 104.0.0"
    }
  }
}

user.disabled

Occurs when the user was disabled.

{
  "type": "user.disabled",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": true,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    }
  }
}

user.reenabled

Occurs when the user was re-enabled.

{
  "type": "user.reenabled",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    }
  }
}

user.anonymous.promoted

Occurs whenever an anonymous user is promoted to a normal user.

{
  "type": "user.anonymous.promoted",
  "payload": {
    "anonymous_user": {
      "id": "7a009f88-c636-4245-91ec-7b174dc6a1a1",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "user": {
      "id": "7a009f88-c636-4245-91ec-7b174dc6a1a1",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identities": [
      {
        "id": "1450234b-5e2c-4aa0-b400-f2d9d70a2f45",
        "created_at": "2006-01-02T03:04:05.123456Z",
        "updated_at": "2006-01-02T03:04:05.123456Z",
        "type": "login_id",
        "claims": {
          "email": "user@example.com",
          "https://authgear.com/claims/login_id/key": "email",
          "https://authgear.com/claims/login_id/original_value": "user@example.com",
          "https://authgear.com/claims/login_id/type": "email",
          "https://authgear.com/claims/login_id/value": "user@example.com"
        }
      }
    ]
  }
}

user.deletion_scheduled

Occurs when an account deletion was scheduled.

{
  "type": "user.deletion_scheduled",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": true,
      "is_deactivated": true,
      "delete_at": "2006-01-02T03:04:05.123456Z",
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    }
  }
}

user.deletion_unscheduled

Occurs when an account deletion was unscheduled.

{
  "type": "user.deletion_unscheduled",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    }
  }
}

user.deleted

Occurs when the user was deleted.

{
  "type": "user.deleted",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": false,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": false,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": false,
        "updated_at": 1136171045
      }
    }
  }
}

identity.email.added

Occurs when a new email is added to an existing user. Email can be added by the user in the setting page, added by the admin through the Admin API or Portal.

{
  "type": "identity.email.added",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "phone_number": "+447400123456",
        "phone_number_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "a0c55481-147e-4a58-876e-10dffedfd5cd",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "email": "user@example.com",
        "https://authgear.com/claims/login_id/key": "email",
        "https://authgear.com/claims/login_id/original_value": "user@example.com",
        "https://authgear.com/claims/login_id/type": "email",
        "https://authgear.com/claims/login_id/value": "user@example.com"
      }
    }
  }
}

identity.email.removed

Occurs when an email address is removed from an existing user. Email can be removed by the user in the setting page, removed by admin through admin API or Portal.

{
  "type": "identity.email.removed",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "phone_number": "+447400123456",
        "phone_number_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "239d585d-9b90-4148-9aa2-2e3131b5847a",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "email": "user@example.com",
        "https://authgear.com/claims/login_id/key": "email",
        "https://authgear.com/claims/login_id/original_value": "user@example.com",
        "https://authgear.com/claims/login_id/type": "email",
        "https://authgear.com/claims/login_id/value": "user@example.com"
      }
    }
  }
}

identity.email.updated

Occurs when an email address is updated. Email can be updated by the user on the setting page.

{
  "type": "identity.email.updated",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user3@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "new_identity": {
      "id": "239d585d-9b90-4148-9aa2-2e3131b5847a",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "email": "user3@example.com",
        "https://authgear.com/claims/login_id/key": "email",
        "https://authgear.com/claims/login_id/original_value": "user3@example.com",
        "https://authgear.com/claims/login_id/type": "email",
        "https://authgear.com/claims/login_id/value": "user3@example.com"
      }
    },
    "old_identity": {
      "id": "239d585d-9b90-4148-9aa2-2e3131b5847a",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "email": "user@example.com",
        "https://authgear.com/claims/login_id/key": "email",
        "https://authgear.com/claims/login_id/original_value": "user@example.com",
        "https://authgear.com/claims/login_id/type": "email",
        "https://authgear.com/claims/login_id/value": "user@example.com"
      }
    }
  }
}

identity.email.verified

Occurs when an email address is change from unverified to verified for an existing user. Email can be verified by the user in the setting page, mark verified by admin through admin API or Portal.

{
  "type": "identity.email.verified",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "239d585d-9b90-4148-9aa2-2e3131b5847a",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "email": "user@example.com",
        "https://authgear.com/claims/login_id/key": "email",
        "https://authgear.com/claims/login_id/type": "email",
        "https://authgear.com/claims/login_id/value": "user@example.com"
      }
    }
  }
}

identity.email.unverified

Occurs when an email address is unverified. Email can be unverified using Admin API.

{
  "type": "identity.email.unverified",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": false,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": false,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "239d585d-9b90-4148-9aa2-2e3131b5847a",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "email": "user@example.com",
        "https://authgear.com/claims/login_id/key": "email",
        "https://authgear.com/claims/login_id/type": "email",
        "https://authgear.com/claims/login_id/value": "user@example.com"
      }
    }
  }
}

identity.phone.added

Occurs when a new phone number is added to an existing user. Phone numbers can be added by the user in the setting page, added by admin through admin API or Portal.

{
  "type": "user.phone.added",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "phone_number": "+447400123456",
        "phone_number_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "9fa5668d-a796-4817-93e1-d4096e5966ac",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "phone",
        "https://authgear.com/claims/login_id/original_value": "+447400123456",
        "https://authgear.com/claims/login_id/type": "phone",
        "https://authgear.com/claims/login_id/value": "+447400123456",
        "phone_number": "+447400123456"
      }
    }
  }
}

identity.phone.removed

Occurs when a phone number is removed from an existing user. Phone numbers can be removed by the user on the setting page, removed by admin through admin API or Portal.

{
  "type": "identity.phone.removed",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "9fa5668d-a796-4817-93e1-d4096e5966ac",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "phone",
        "https://authgear.com/claims/login_id/original_value": "+447400123455",
        "https://authgear.com/claims/login_id/type": "phone",
        "https://authgear.com/claims/login_id/value": "+447400123455",
        "phone_number": "+447400123455"
      }
    }
  }
}

identity.phone.updated

Occurs when a phone number is updated. Phone numbers can be updated by the user on the setting page.

{
  "type": "identity.phone.updated",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "phone_number": "+447400123455",
        "phone_number_verified": true,
        "updated_at": 1136171045
      }
    },
    "new_identity": {
      "id": "9fa5668d-a796-4817-93e1-d4096e5966ac",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "phone",
        "https://authgear.com/claims/login_id/original_value": "+447400123455",
        "https://authgear.com/claims/login_id/type": "phone",
        "https://authgear.com/claims/login_id/value": "+447400123455",
        "phone_number": "+447400123455"
      }
    },
    "old_identity": {
      "id": "9fa5668d-a796-4817-93e1-d4096e5966ac",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "phone",
        "https://authgear.com/claims/login_id/original_value": "+447400123456",
        "https://authgear.com/claims/login_id/type": "phone",
        "https://authgear.com/claims/login_id/value": "+447400123456",
        "phone_number": "+447400123456"
      }
    }
  }
}

identity.phone.verified

Occurs when a phone number is change from unverified to verified for an existing user. Phone can be verified by the user in the setting page, mark verified by admin through admin API or Portal.

{
  "type": "identity.phone.verified",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "phone_number": "+447400123455",
        "phone_number_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "9fa5668d-a796-4817-93e1-d4096e5966ac",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "phone",
        "https://authgear.com/claims/login_id/type": "phone",
        "https://authgear.com/claims/login_id/value": "+447400123455",
        "phone_number": "+447400123455"
      }
    }
  }
}

identity.phone.unverified

Occurs when a phone number is unverified. Phone numbers can be unverified using Admin API.

{
  "type": "identity.phone.unverified",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": false,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "phone_number": "+447400123455",
        "phone_number_verified": false,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "9fa5668d-a796-4817-93e1-d4096e5966ac",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "phone",
        "https://authgear.com/claims/login_id/type": "phone",
        "https://authgear.com/claims/login_id/value": "+447400123455",
        "phone_number": "+447400123455"
      }
    }
  }
}

identity.username.added

Occurs when a new username is added to an existing user. Username can be added by the user in setting page, added by admin through Admin API or Portal.

{
  "type": "identity.username.added",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "preferred_username": "user01",
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "38683500-f6ce-477d-b944-1f915a451995",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "username",
        "https://authgear.com/claims/login_id/original_value": "user01",
        "https://authgear.com/claims/login_id/type": "username",
        "https://authgear.com/claims/login_id/value": "user01",
        "preferred_username": "user01"
      }
    }
  }
}

identity.username.removed

Occurs when the username is removed from an existing user. The username can be removed by the user on the setting page, removed by admin through admin API or Portal.

{
  "type": "identity.username.removed",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "38683500-f6ce-477d-b944-1f915a451995",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "username",
        "https://authgear.com/claims/login_id/original_value": "user02",
        "https://authgear.com/claims/login_id/type": "username",
        "https://authgear.com/claims/login_id/value": "user02",
        "preferred_username": "user02"
      }
    }
  }
}

identity.username.updated

Occurs when the username is updated. The username can be updated by the user on the setting page.

{
  "type": "identity.username.updated",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "preferred_username": "user02",
        "updated_at": 1136171045
      }
    },
    "new_identity": {
      "id": "38683500-f6ce-477d-b944-1f915a451995",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "username",
        "https://authgear.com/claims/login_id/original_value": "user02",
        "https://authgear.com/claims/login_id/type": "username",
        "https://authgear.com/claims/login_id/value": "user02",
        "preferred_username": "user02"
      }
    },
    "old_identity": {
      "id": "38683500-f6ce-477d-b944-1f915a451995",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "login_id",
      "claims": {
        "https://authgear.com/claims/login_id/key": "username",
        "https://authgear.com/claims/login_id/original_value": "user01",
        "https://authgear.com/claims/login_id/type": "username",
        "https://authgear.com/claims/login_id/value": "user01",
        "preferred_username": "user01"
      }
    }
  }
}

identity.oauth.connected

Occurs when a user has connected to a new OAuth provider.

{
  "type": "identity.oauth.connected",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "f22425cf-68b2-45f8-936d-3c54c9cdf5c7",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "oauth",
      "claims": {
        "email": "xxx@gmail.com",
        "email_verified": true,
        "family_name": "",
        "given_name": "",
        "https://authgear.com/claims/oauth/profile": {
          "at_hash": "",
          "aud": ["xxx.apps.googleusercontent.com"],
          "azp": "xxx.apps.googleusercontent.com",
          "email": "xxx@gmail.com",
          "email_verified": true,
          "exp": "2006-01-02T03:04:05Z",
          "family_name": "",
          "given_name": "",
          "iat": "2006-01-02T03:04:05Z",
          "iss": "https://accounts.google.com",
          "locale": "en",
          "name": "",
          "nonce": "",
          "picture": "https://lh3.googleusercontent.com/a/xxx",
          "sub": ""
        },
        "https://authgear.com/claims/oauth/provider_alias": "google",
        "https://authgear.com/claims/oauth/provider_type": "google",
        "https://authgear.com/claims/oauth/subject_id": "",
        "locale": "en",
        "name": "",
        "picture": "https://lh3.googleusercontent.com/a/xxx"
      }
    }
  }
}

identity.oauth.disconnected

Occurs when a user is disconnected from an OAuth provider. It can be done by the user disconnecting their OAuth provider in the setting page, or the admin removing the OAuth identity through admin API or Portal.

{
  "type": "identity.oauth.disconnected",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "f22425cf-68b2-45f8-936d-3c54c9cdf5c7",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "oauth",
      "claims": {
        "email": "xxx@gmail.com",
        "email_verified": true,
        "family_name": "",
        "given_name": "",
        "https://authgear.com/claims/oauth/profile": {
          "at_hash": "",
          "aud": ["xxx.apps.googleusercontent.com"],
          "azp": "xxx.apps.googleusercontent.com",
          "email": "xxx@gmail.com",
          "email_verified": true,
          "exp": "2006-01-02T03:04:05Z",
          "family_name": "",
          "given_name": "",
          "iat": "2006-01-02T03:04:05Z",
          "iss": "https://accounts.google.com",
          "locale": "en",
          "name": "",
          "nonce": "",
          "picture": "https://lh3.googleusercontent.com/a/xxx",
          "sub": ""
        },
        "https://authgear.com/claims/oauth/provider_alias": "google",
        "https://authgear.com/claims/oauth/provider_type": "google",
        "https://authgear.com/claims/oauth/subject_id": "",
        "locale": "en",
        "name": "",
        "picture": "https://lh3.googleusercontent.com/a/xxx"
      }
    }
  }
}

identity.biometric.enabled

Occurs when the user enabled biometric login.

{
  "type": "identity.biometric.enabled",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "5cb77960-634b-4c0e-8a0e-6c2c73fb8f47",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "biometric",
      "claims": {
        "https://authgear.com/claims/biometric/device_info": {
          "ios": {
            "NSBundle": {
              "CFBundleDisplayName": "Authgear demo iOS",
              "CFBundleExecutable": "ios_example",
              "CFBundleIdentifier": "com.authgear.exampleapp.ios",
              "CFBundleName": "ios_example",
              "CFBundleShortVersionString": "1.0",
              "CFBundleVersion": "1653565975"
            },
            "NSProcessInfo": {
              "isMacCatalystApp": false,
              "isiOSAppOnMac": false
            },
            "UIDevice": {
              "model": "iPhone",
              "name": "iPhone",
              "systemName": "iOS",
              "systemVersion": "16.1.1",
              "userInterfaceIdiom": "phone"
            },
            "uname": {
              "machine": "iPhone13,3",
              "nodename": "Users-iPhone",
              "release": "22.1.0",
              "sysname": "Darwin",
              "version": "Darwin Kernel Version 22.1.0: Thu Oct  6 19:34:22 PDT 2022; root:xnu-8792.42.7~1/RELEASE_ARM64_T8101"
            }
          }
        },
        "https://authgear.com/claims/biometric/formatted_device_info": "iPhone 12 Pro",
        "https://authgear.com/claims/biometric/key_id": "1CC9D95A-6578-4557-8279-C4D5699D3549"
      }
    }
  }
}

identity.biometric.disabled

Occurs when biometric login is disabled. It will be triggered only when the user disabled it from the settings page or the admin disabled it from the Admin API or portal.

{
  "type": "identity.biometric.disabled",
  "payload": {
    "user": {
      "id": "338deafa-400b-4589-a922-2c92d670b757",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "last_login_at": "2006-01-02T03:04:05.123456Z",
      "is_anonymous": false,
      "is_verified": true,
      "is_disabled": false,
      "is_deactivated": false,
      "can_reauthenticate": true,
      "standard_attributes": {
        "email": "user@example.com",
        "email_verified": true,
        "updated_at": 1136171045
      }
    },
    "identity": {
      "id": "5cb77960-634b-4c0e-8a0e-6c2c73fb8f47",
      "created_at": "2006-01-02T03:04:05.123456Z",
      "updated_at": "2006-01-02T03:04:05.123456Z",
      "type": "biometric",
      "claims": {
        "https://authgear.com/claims/biometric/device_info": {
          "ios": {
            "NSBundle": {
              "CFBundleDisplayName": "Authgear demo iOS",
              "CFBundleExecutable": "ios_example",
              "CFBundleIdentifier": "com.authgear.exampleapp.ios",
              "CFBundleName": "ios_example",
              "CFBundleShortVersionString": "1.0",
              "CFBundleVersion": "1653565975"
            },
            "NSProcessInfo": {
              "isMacCatalystApp": false,
              "isiOSAppOnMac": false
            },
            "UIDevice": {
              "model": "iPhone",
              "name": "iPhone",
              "systemName": "iOS",
              "systemVersion": "16.1.1",
              "userInterfaceIdiom": "phone"
            },
            "uname": {
              "machine": "iPhone13,3",
              "nodename": "Users-iPhone",
              "release": "22.1.0",
              "sysname": "Darwin",
              "version": "Darwin Kernel Version 22.1.0: Thu Oct  6 19:34:22 PDT 2022; root:xnu-8792.42.7~1/RELEASE_ARM64_T8101"
            }
          }
        },
        "https://authgear.com/claims/biometric/formatted_device_info": "iPhone 12 Pro",
        "https://authgear.com/claims/biometric/key_id": "1CC9D95A-6578-4557-8279-C4D5699D3549"
      }
    }
  }
}

Object Reference

The event may contain different objects. You can refer to the below for their attributes.

The user object

"payload":{
  "user": {
    "id": "string",
    "created_at": "timestamp",
    "updated_at": "timestamp",
    "last_login_at": "timestamp",
    "is_anonymous": boolean,
    "is_verified": boolean,
    "is_disabled": boolean,
    "is_deactivated": boolean,
    "can_reauthenticate": boolean,
    "standard_attributes": {
      ...
    },
    "custom_attributes": {
      ...
    }
  }
}

Webhooks

Webhooks is one of the supported hooks to receive events.

To use webhooks you need to:

  1. Deploy a webhook on your server.

  2. Configure Authgear to deliver events to your webhook.

Configure Authgear to deliver events to your webhook

  1. In the portal, go to Advanced > Hooks.

  2. Add your webhooks in Blocking Events and Non-Blocking Events, depending on which event you want to listen to.

  3. Click Save.

hook:
  blocking_handlers:
    - event: "user.pre_create"
      url: 'https://myapp.com/check_user_create'
  non_blocking_handlers:
    # listen to all events and filter events by type in request
    - events: ["*"]
      url: 'https://myapp.com/all_events'
    - events: ["user.created"]
      url: 'https://myapp.com/sync_user_creation'

Protocol

Events are delivered to your webhooks via HTTPS, so your server must support HTTPS.

Events are delivered to your webhooks with POST requests. You webhooks must return a HTTP status code within 2xx range. Other status codes are considered as a failed delivery.

Verifying signature

The request to your webhooks is signed with a secret key shared between Authgear and your hooks. You are RECOMMENDED to verify the signature and reject any requests with invalid signatures. This ensures the request originates from Authgear.

The signature is calculated as the hex encoded value of HMAC-SHA256 of the request body and included in the HTTP header x-authgear-body-signature.

To obtain the secret key, visit the portal and go to Advanced -> Hooks -> Webhook Signature. You may need to reauthenticate yourselves before you can reveal the secret key.

Here is the sample code of how to calculate the signature and verify it.

package main

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

// Obtain the secret in the portal.
const Secret = "SECRET"

// HMACSHA256String returns the hex-encoded string of HMAC-SHA256 code of body using secret as key.
func HMACSHA256String(data []byte, secret []byte) (sig string) {
    hasher := hmac.New(sha256.New, secret)
    _, _ = hasher.Write(data)
    signature := hasher.Sum(nil)
    sig = hex.EncodeToString(signature)
    return
}

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        b, err := io.ReadAll(r.Body)
        if err != nil {
            // Handle the error properly
            panic(err)
        }
        defer r.Body.Close()

        sigInHeader := []byte(r.Header.Get("X-Authgear-Body-Signature"))
        sig := []byte(HMACSHA256String(b, []byte(Secret)))

        // Prefer constant time comparison over == operator.
        if subtle.ConstantTimeCompare(sigInHeader, sig) != 1 {
            // The signature does not match
            // Do NOT trust the content of this webhook!!!
            panic(fmt.Errorf("%v != %v", string(sigInHeader), string(sig)))
        }

        // Continue your logic here.
    })
    http.ListenAndServe(":9999", nil)
}

JavaScript / TypeScript Hooks

JavaScript / TypeScript Hooks is one of the supported hooks to receive events.

JavaScript / TypeScript Hooks are written as a ES2015 module. The module is executed by Deno.

The module MUST have a default export of a function taking 1 argument. The argument is the event. The function can either be synchronous or asynchronous.

If the Hook is registered for a blocking event, the function MUST return a value according to the specification.

The Hooks DO NOT have access to file, or environment. They only have access to external network.

The stdout and the stderr of the Hooks are both ignored. Your hooks MUST NOT assume anything on the arguments and the stdin of the module.

Configure Authgear to deliver events to your Hooks

  1. In the portal, go to Advanced > Hooks.

  2. Add your Hooks in Blocking Events and Non-Blocking Events, depending on which event you want to listen to.

  3. Click Save.

Examples

Here is an example of a Hook for a blocking event.

import { HookEvent, HookResponse } from "https://deno.land/x/authgear_deno_hook@v1.0.0/mod.ts";
export default async function(e: HookEvent): Promise<HookResponse> {
  // This hook simply allows the operation, which is identical to no-op.
  return { is_allowed: true };
}

An example to mutate a JWT token

import {HookResponse, EventOIDCJWTPreCreate } from "https://deno.land/x/authgear_deno_hook@v1.0.0/mod.ts";

export default async function(event: EventOIDCJWTPreCreate): Promise<HookResponse> {
  return { 
    is_allowed: true,
    mutations: {
      jwt:{
        payload:{
          ...event.payload.jwt.payload, //the original payload in the jwt
          "https://myapp.com": {
            "custom_field": "custom_value"
          }
        }
      }
    }
  };
}

Here is an example of a Hook for a non-blocking event.

import { HookEvent } from "https://deno.land/x/authgear_deno_hook@v0.3.0/mod.ts";
export default async function(e: HookEvent): Promise<void> {
  // This hook does nothing, which is identical to no-op.
}

TypeScript Definition

https://deno.land/x/authgear_deno_hook is a TypeScript definition that aids you in writing a Hook. You can see the full definition at https://deno.land/x/authgear_deno_hook/mod.ts

If you are a Visual Studio Code user, you can set up your editor to take full advantage of the definition.

Alternatively, you can edit your hook and use the Deno CLI to typecheck.

$ deno check YOUR_HOOK.ts

Only Allow Signups from Inside the Corporate Network using Hooks

Authgear Webhook is a feature that sends notifications in the form of HTTP requests to an external URL that you specify when certain events occur. For example, Webhook can send the user.pre_create event just before a new user is created.

Webhooks also listen for response from your external services. For blocking events, this response determines if the process that triggered the event should continue to completion or be terminated. In this post, we'll use a blocking event and a custom webhook endpoint to restrict user signup to only devices from inside a corporate network.

How to Prevent or Allow Signups Using Webhooks

Configuring a webhook for the user.pre-created event will prevent Authgear from creating new users if your webhook endpoint returns is_allowed: false in its response. In contrast, a new user will be created successfully when the endpoint returns is_allowed: true in its response.

We will be using this behavior to prevent users from outside a corporate network from signing up by checking their IP address (which is provided in the payload of the webhook HTTP request from Authgear). When the IP address matches a specified allowed IP address, we set the value for is_allowed to true. Otherwise, we return false.

The following tutorial shows detailed steps for allowing signups from inside a corporate network only.

Step 1: Configure Webhook on Authgear Portal

In this step, we'll create a webhook for the User pre-create event in the Authgear Portal. To do that, login to the Authgear Portal and navigate to Advance > Hooks. Next, under Blocking Events section, click on Add, then select Webhook as the type and User pre-create as the Event.

Enter the full URL that points to the page that will be receiving and processing the webhook requests in the Endpoint text field.

Once you're done, click on the Save button at the top.

Step 2: Create an Endpoint

You'll need to create an endpoint in your preferred programming language or framework and host it online. For this tutorial, we'll be creating a basic Express.js endpoint.

Create a new Express.js project and add the following code to app.js:

const express = require("express");
const app = express();
const bodyParser = require('body-parser');
const port = process.env.PORT || 3002;
app.use(bodyParser.json());


app.post('/', (request, response) => {

    response.status(200).send(
        {
            "is_allowed": false,
            "reason": "some reason",
            "title": "some title"
          }
    );
});

app.listen(port, () => {
    console.log("server started!");
});

Let's walk through what the above code will do:

  • Express creates an endpoint that accepts POST requests. (Note that the Authgear webhook only sends POST HTTP requests and your endpoint must support HTTPS).

  • Deploying the above code and pointing your Authgear webhook endpoint to it should prevent any user from signing up.

  • This is because the code returns is_allowed: false as a response to the Webhook. Hence terminating the signup process.

  • You can also state a more descriptive reason for termination in the endpoint response using the reason field.

Step 3: Read Data from Webhook Request

Authgear webhooks send data to the endpoint URL to enable developers to build custom features and more. In this step, we'll read the data from the User pre-created event call and get the IP address for the user trying to register.

To read the request body and output the value to the console add the following code to app.js on a new line just above response.status(200)...

const requestPayload = request.body;
console.log(requestPayload)

Attempting to register a new user again should print JSON data similar to this to your endpoints console:

{
    id: '0000000000022b55',
    seq: 142165,
    type: 'user.pre_create',
    payload: {
      user: {
        id: '413fe869-dba2-43a8-b671-95fd471b7950',
        created_at: '2023-08-14T03:44:36.383367Z',
        updated_at: '2023-08-14T03:44:36.473456Z',
        is_anonymous: false,
        is_verified: true,
        is_disabled: false,
        is_deactivated: false,
        is_anonymized: false,
        can_reauthenticate: true,
        standard_attributes: [Object],
        x_web3: [Object]
      },
      identities: [ [Object] ]
    },
    context: {
      timestamp: 1691984676,
      user_id: '413fe869-dba2-43a8-b671-95fd471b7950',
      triggered_by: 'user',
      audit_context: null,
      preferred_languages: [ 'en-US', 'en' ],
      language: 'en',
      ip_address: '123.456.78.009',
      user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0',
      app_id: 'my_test_app'
    }
  }

The data we're interested in for this example is ip_address in the context object (context.ip_address). We'll use this IP address to determine whether a user is inside the corporate network.

Now, update app.js so that it returns is_allowed: true when the user's IP address is within the corporate network's IP address.

The code for app.js should look like this after the modification:

app.post('/', (request, response) => {

    const requestPayload = request.body;
    console.log(requestPayload)

    const allowedIp = '123.456.78.009'; //replace the value with the corporate network IP address 
    if (requestPayload.context.ip_address == $orgIp) {

        //this user is from the organisation network, continue with sign up.
        response.status(200).type('json').send(
            {
                "is_allowed": true
              }
        );

    } else {
        //user has an IP address outside of the organisation network so disallow sign up.
        response.status(200).type('json').send(
            {
                "is_allowed": false,
                "reason": "You are not allow to sign up to this organisation",
                "title": "Sign-up not allowed!"
              }
        )
    }
    
});

You should replace the value for allowedIp with the IP address you wish to allow.

At this point, if you deploy your endpoint and try signing up from the allowed IP the process should complete without issue, and you've successfully implemented signups only from a corporate network.

Only Allow Signups from Inside the Corporate Network using JavaScript/TypeScript Hooks

Authgear offers an alternative for regular webhooks called the JavaScript/TypeScript hooks. This kind of hook makes it possible to implement our earlier solution without hosting an external endpoint. In the following section, we'll walk through the steps for allowing signups from inside a corporate network only using the Typescript hook.

Step 1: Configure TypeScript Hook on Authgear Portal

Navigate to Advance > Hooks in the Authgear portal. Next, click on Add under the Blocking Events section. Or, modify the previous webhook.

Select TypeScript as the type and User pre-create as the Event.

The third field in a TypeScript hook is different from a regular webhook. The field allows you to enter JavaScript code that the hook can execute when an event is triggered.

You can click on the Edit Script button near the Script field to open the hook script editor.

Step 2: Write the JavaScript for the Hook

Modify the code in the script editor to the following:

export default async function (e: EventUserPreCreate): Promise<HookResponse> {
  // Write your hook with the help of the type definition.
  const allowedIp = "123.456.78.009";
  if (e.context.ip_address == allowedIp) {
    return {
      is_allowed: true,
    };
  } else {
    return {
      is_allowed: false,
      reason: "You are not allow to sign up to this organisation",
      title: "Sign-up not allowed!",
    };
  }
}

Make sure to update the value for allowedIp to the IP address you wish to allow.

Once you're done, click on Finish Editing to return to the Hooks configuration page. Save your new hook by clicking on the Save button at the top of the configuration page.

Step 3: Test the Hook

To test that your TypeScript hook is working properly, try signing up a new user from the IP address you specified earlier. Also, try signing up outside that IP address to see if everything works as expected.