Inline Challenges

This chapter assumes that you have completed the Baseline Integration.

Challenging suspicious logins

If you are using Castle's Adaptive Authentication API to implement a dynamic in-line challenge flow, this chapter will describe the events you will need to track around your challenge flow. If you are not familiar with Castle's Adaptive Authentication API, please refer to our Baseline Integration Guideline before proceeding.

Whenever a verdict of challenge is returned from the Authentication endpoint, redirect the user to a second page and present them with a verification flow, such as a PIN code sent over email.

Track $challenge.requested to tell Castle that the challenge process has started.

castle.track(
  event: '$challenge.requested',
  user_id: user.id
)
castle.track(
    {
        'event': '$challenge.requested',
        'user_id': user.id
    }
)
<?
Castle::track(
  array(
    'event' => '$challenge.requested',
    'user_id' => $user->id
  )
);
castle.track("$challenge.requested", user_id);
curl https://api.castle.io/v1/track \
  -u ":YOUR-API-SECRET" \
  -H "Content-Type: application/json" \
  -d '
  {
    "event": "$challenge.requested",
    "user_id": "1234",
    "context": {
      "client_id": "a97b492d-dcc3-4fc1-87d6-65682955afa5",
      "ip": "37.46.187.90",
      "headers": {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
        "Accept": "text/html",
        "Accept-Language": "en-us,en;q=0.5"
      }
    }
  }'

When the user completes the challenge, track $challenge.succeeded to lower the risk for the current session.

castle.track(
  event: '$challenge.succeeded',
  user_id: user.id
)
castle.track(
    {
        'event': '$challenge.succeeded',
        'user_id': user.id
    }
)
<?
Castle::track(
  array(
    'event' => '$challenge.succeeded',
    'user_id' => $user->id
  )
);
CastleContext context = Castle.contextBuilder()
    .fromHttpServletRequest(req)
    .build();

Castle.client().track(CastleMessage.builder("$challenge.succeeded")
    .context(context)
    .userId(userId)
    .build()
);
curl https://api.castle.io/v1/track \
  -u ":YOUR-API-SECRET" \
  -H "Content-Type: application/json" \
  -d '
  {
    "event": "$challenge.succeeded",
    "user_id": "1234",
    "context": {
      "client_id": "a97b492d-dcc3-4fc1-87d6-65682955afa5",
      "ip": "37.46.187.90",
      "headers": {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
        "Accept": "text/html",
        "Accept-Language": "en-us,en;q=0.5"
      }
    }
  }'

Alternatively, track $challenge.failed to increase the risk for similar contexts.

castle.track(
  event: '$challenge.failed',
  user_id: user.id
)
castle.track(
    {
        'event': '$challenge.failed',
        'user_id': user.id
    }
)
<?
Castle::track(
  array(
    'event' => '$challenge.failed',
    'user_id' => $user->id
  )
);
CastleContext context = Castle.contextBuilder()
    .fromHttpServletRequest(req)
    .build();

Castle.client().track(CastleMessage.builder("$challenge.failed")
    .context(context)
    .userId(userId)
    .build()
);
curl https://api.castle.io/v1/track \
  -u ":YOUR-API-SECRET" \
  -H "Content-Type: application/json" \
  -d '
  {
    "event": "$challenge.failed",
    "user_id": "1234",
    "context": {
      "client_id": "a97b492d-dcc3-4fc1-87d6-65682955afa5",
      "ip": "37.46.187.90",
      "headers": {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
        "Accept": "text/html",
        "Accept-Language": "en-us,en;q=0.5"
      }
    }
  }'