Protecting the profile update

🚧

For ideal performance, you should complete the guide on Protecting the login before you implement this guide.

The$profile_update activity is sent to the Risk API whenever a user updates their account information on your platform, and provides additional information for detecting account related fraud after the actual registration or login. The inline response can then be used to determine when to step up verification.

The activity can be sent at different stages of the profile update, which is denoted by setting the status field:

  • $attempted – The form was posted but hasn't yet been validated by your backend. Useful when you want to use the Castle response to verify the user before the user profile update persists
  • $succeeded –  The user profile was updated
  • $failed – The user profile was not updated, likely because of validation errors
castle = ::Castle::Client.new

begin
  token = request.params['castle_request_token']
  context = Castle::Context::Prepare.call(request)

  res = castle.risk(
    type: '$profile_update',
    status: '$succeeded',
    request_token: token,
    context: {
      ip: context[:ip],
      headers: context[:headers]
    },
    user: {
      id: 'ca1242f498', # Required. A unique, persistent user identifier
      email: '[email protected]' # Required
    },
    properties: {
      email_updated: true,
      password_updated: true
    }
  )

  if res[:risk] > 0.9
    # IMPLEMENT: Deny attempt
  end

rescue Castle::Error => e
  # Handle error
end
// NOTE: See the Ruby example for a more comprehensive set of parameters

try {
  $token = $_POST['castle_request_token'];

  $res = Castle::risk([
    'type' => '$profile_update',
    'status' => '$succeeded',
    'request_token' => $token,
    'context' => [
      'ip' => Castle_RequestContext::extractIp(),
      'headers' => Castle_RequestContext::extractHeaders()
    ],
    'user' => [
      'id' => $user->id,
      'email' => $user->email
    ]
  ]);

  if ($res->risk > 0.9) {
    // IMPLEMENT: Deny attempt
  }

} catch (Castle_Error $e) {
  // Handle error
}
# NOTE: See the Ruby example for a more comprehensive set of parameters

try:
    token = request.form['castle_request_token'] # Using Flask
    context = ContextPrepare.call(request)
    client = Client()

    res = client.risk({
        'type': '$profile_update',
        'status': '$succeeded',
        'request_token': token,
        'context': {
          'ip': context['ip'],
          'headers': context['headers']
        },
        'user': {
          'id': user.id,
          'email': user.email
        }
    })

    if res['risk'] > 0.9:
        # IMPLEMENT: Deny attempt

except CastleError as e:
     # Handle error
// NOTE: See the Ruby example for a more comprehensive set of parameters

String token = request.getParameter("castle_request_token");

Castle castle = Castle.initialize();
CastleContextBuilder context = castle.contextBuilder().fromHttpServletRequest(request)

try {
  CastleResponse response = castle.client().risk(ImmutableMap.builder()
    .put("type", "$profile_update")
    .put("status", "$succeeded")
    .put(Castle.KEY_CONTEXT, ImmutableMap.builder()
      .put(Castle.KEY_IP, context.getIp())
      .put(Castle.KEY_HEADERS, context.getHeaders())
      build()
    )
    .put(Castle.KEY_USER, ImmutableMap.builder()
      .put(Castle.KEY_USER_ID, user.getId())
      .put(Castle.KEY_EMAIL, user.getEmail())
      .put("username", user.getUsername())
      .build()
    )
    .put(Castle.KEY_REQUEST_TOKEN, token)
    .build()
  );
} catch (CastleRuntimeException runtimeException) {
    // Handle errors
}

float risk = response.json()
   .getAsJsonObject()
   .get("risk")
   .getAsFloat();

if (risk > 0.9) {
  // IMPLEMENT: Deny attempt
};
// NOTE: See the Ruby example for a more comprehensive set of parameters

try {
  const token = request.body["castle_request_token"]; // Using Express

  const castle = new Castle({ apiSecret: 'YOUR SECRET HERE' });
  const context = ContextPrepareService.call(request, {}, castle.configuration);

  const res = castle.risk({
    type: '$profile_update',
    status: '$succeeded',
    request_token: token,
    user: {
      id: user.id,
      email: user.email
    },
    context: {
      ip: context.ip,
      headers: context.headers
    }
  });

  if (res.risk > 0.9) {
    // IMPLEMENT: Deny attempt
  }
} catch (e) {
  console.error(e);
}

📘

Tip: Use the properties object to highlight more granular information about the profile update, for instance which specific user properties that were updated. This helps the risk analyst better understand the severity of a suspicious profile update.

Taking action

The response from the API call to Risk can then be used to take different actions. Typically, for profile updates, you'd want to ask the user to either re-authenticate using their password, or preferably by stepping up the authentication to a stronger factor.


Did this page help you?