SDK for Registration

DataDome Account Protect detects account takeover threats and protects you against them.

Account Protect can be integrated into your backend through SDK packages available on multiple platforms.

📘

Prerequisites for Account Protect

Account Protect is separate from bot management and will not be available on your account by default. Please contact your customer success manager to enable it.
This service requires a dedicated API key, which will be available on your dashboard once it is enabled.

Main Concepts

When a user attempts to register on your website, the fraud protection SDK sends data to DataDome's Account protect API:

  • The Account Protect API will respond with a recommendation (to either allow or deny the login).
  • A recommendation means that your application will still make the final decision. You can either invalidate the account or mark it as "downgraded"
Overview of the implementation flow for a registration attempt.

Overview of the implementation flow for a registration attempt.

Installation

The Account protect SDK is distributed on multiple platforms:

You can use one of the commands below to install the relevant package for your application:

npm i @datadome/fraud-sdk-node
dotnet add package DataDome.AspNetCore.Fraud.SDK
<!-- insert in the pom.xml file of the project -->
<dependency>
  <groupId>co.datadome.fraud</groupId>
  <artifactId>fraud-sdk-java</artifactId>
  <!-- <version>1.0.1</version> --> <!-- compatible with Spring Boot 2.x -->
  <version>2.2.2</version>          <!-- compatible with Spring Boot 3.x -->
</dependency>
libraryDependencies += "co.datadome.fraud" % "fraud-sdk-java" % "2.2.1"
pip install datadome-fraud-sdk-python
composer require datadome/fraud-sdk-symfony
# 1. add `datadome/fraud-sdk-laravel` to your project
composer require datadome/fraud-sdk-laravel
# 2. Generate an autoloader
composer dump-autoload
# 3. Edit `config/app.php` to add `DataDomeServiceProvider`
# config/app.php
use DataDome\FraudSdkLaravel\Providers\DataDomeServiceProvider;
[...]
 'providers' => ServiceProvider::defaultProviders()->merge([
 [...]
 DataDomeServiceProvider::class
 
# 4. publish `datadome.php` in the `config` folder
php artisan vendor:publish
gem install datadome_fraud_sdk_ruby

Usage

Using the Account Protect SDK requires changes in your application to send signals regarding the registration and handle the recommendations provided by DataDome's Account Protect API.

Example for a Registration Endpoint

const datadomeClient = new DataDome("FraudAPIKey");

app.post('/register', async function (req, res) {
    const startTime = Date.now();   
    var emailAccount = req.body.email;
    var session = { id: 'sessionId', createdAt: new Date() };
    
    var userAddress = {
        name: 'HQ',
        line1: '22 rue de la Michodiere',
        line2: '2nd floor',
        city: 'Paris',
        countryCode: 'FR',
        country: 'France',
        regionCode: '75',
        zipCode: '75002'
    };
    
    var user = {
        id: 'userId',
        title: 'mrs',
        firstName: 'Data',
        lastName: 'Dome',
        createdAt: new Date(),
        phone: '+33978787878',
        email: emailAccount,
        address: userAddress
    };
    
    var registrationEvent = new RegistrationEvent(emailAccount, session, user);
    
    const datadomeResponse = await datadomeClient.validate(req, registrationEvent);

    if (datadomeResponse?.action == ResponseAction.ALLOW) {
        res.status(200).send(`Successfully created user ${req.body.email}`);
        mocks.registerUser(req.body);
    } else {
        res.status(401).send(`Access denied [by DD] ! ${datadomeResponse?.reasons[0]}`);
    }
});
// 1. Add your API Key in the appsettings.json, or as environment variables
"DataDome": {
    "FraudAPIKey": "----"
}

// 2. In the Program.cs / Startup.cs add the DataDome SDK
using DataDome.AspNetCore.Fraud.SDK;

// Option 1: Passing in a reference to the configuration, for values in appsettings
builder.Services.AddDataDome(builder.Configuration);

// Option 2: Passing in the values directly, for environment variables
builder.Services.AddDataDome(o =>
{
    o.FraudAPIKey = "FraudAPIKey";
});

// 3. Include IDataDomeContext as a dependency injection in the concerned controller
using DataDome.AspNetCore.Fraud.SDK.Model.Shared;

// ...
private readonly SDK.IDataDomeContext _dataDome;
private readonly RegistrationService _registrationService;

public RegistrationController(SDK.IDataDomeContext dataDome, RegistrationService registrationService)
{
    _dataDome = dataDome;
    _registrationService = registrationService;
}

[Route("SubmitRegister")]
[HttpPost]
public async Task<IActionResult> SubmitRegister([FromForm] RegistrationUser user)
{
    if (_registrationService.ValidateRegister(user))
    {
        var address = new Address()
        {
            Name = "HQ",
            Line1 = "55 Rue du Faubourg Saint-Honoré",
            Line2 = "2nd floor",
            City = "Paris",
            CountryCode = "FR",
            RegionCode = "75",
            ZipCode = "75002",
        };

        var newUser = new User()
        {
            Id = "userId",
            Title = "mrs",
            FirstName = "Data",
            LastName = "Dome",
            CreatedAt = DateTime.UtcNow,
            Phone = "+33978787878",
            Email = user.emailAccount,
            Address = address,
        };

        var session = new Session()
        {
            Id = Guid.NewGuid().ToString("D"),
            CreatedAt = DateTime.UtcNow,
        };


        var registrationEvent = new RegistrationEvent(user.emailAccount, LoginStatus.Succeeded, session, newUser);
        var ddResponse = await _dataDome.Validate(Request, registrationEvent);

        if (ddResponse.Action == ResponseAction.Allow)
        {
            if (_authenticationService.RegisterUser(user))
            {
                TempData["SuccessMessage"] = "Registered successfully, you can log in.";
            }
            else
            {
                TempData["ErrorMessage"] = "Error while registering, please try again.";
            }
        }
        else
        {
            TempData["ErrorMessage"] = "Registration blocked.";
        }

    }
    TempData["From"] = "register";
    return RedirectToAction("Index");
}
// 1. Add your FRAUD_API_KEY as an application configuration parameter
// example for resource file "application.properties"
datadome.fraud.api_key=FRAUD_API_KEY

// 2. In an application component add the DataDome Fraud SDK
import co.datadome.fraud.DataDomeFraudService;

// 3. Initialize the DataDome Fraud SDK in a Spring bean
@Bean
public DataDomeFraudService dataDomeFraud(
  @Value("${datadome.fraud.api_key}") 
  String datadomeFraudApiKey) {
  return new DataDomeFraudService(datadomeFraudApiKey);
}

// 4. Use dependency injection to use in it in the controller (here in the constructor)
public RegistrationController(DataDomeFraudService dataDomeFraudService) {
  this.dataDomeFraudService = dataDomeFraudService;
}

// 5. Use it in the registration endpoint of registration controller
@PostMapping(path = "/register")
public ResponseEntity<?> register(ServletRequest request, User user) {
  if(!mock.userExists(user)) {
    
    Address userAddress = Address.newBuilder()
                    .name("HQ")
                    .line1("22 rue de la Michodiere")
                    .line2("2nd floor")
                    .city("Paris")
                    .countryCode("FR")
                    .country("France")
                    .regionCode("75")
                    .zipCode("75002")
                    .build();
    co.datadome.fraud.model.User u = co.datadome.fraud.model.User.newBuilder()
                    .id("userId")
                    .title(user.getTitle())
                    .firstName(user.getFirstName())
                    .lastName(user.getFirstName())
                    .phone(user.getPhone())
                    .email(user.getEmail())
                    .address(userAddress)
                    .build();
    
    DataDomeResponse register = this.dataDomeFraudService.validate(request, 
               RegistrationEvent.newBuilder()
                    .account(user.getEmail())
                    .session(Session.newBuilder().id("sessionId").build())
                    .user(u)
               .build());
    if (!register.getStatus().equals(ResponseStatus.OK)) {
      logger.warn(register);
    }
    if (register.getAction() == ResponseActionType.ALLOW) {
      mock.registerUser(user);
      return new ResponseEntity<>("Successfully created user " + user.getLogin(), HttpStatus.OK);
    } else {
      // Denied
    }
  }
    [...]
 }
// 1. Add these imports
import co.datadome.fraud._
import co.datadome.fraud.api.request.{DataDomeMetadata, RegistrationEvent}
import co.datadome.fraud.model.{Address, User}

// 2. Declare the constant FraudApiKey with the value provided by DataDome

// 3. Initialization of DataDomeFraudService with the FraudApiKey
val dataDomeFraudService = new DataDomeFraudService(FraudApiKey)

// 4. Build data and call the service
val ddm: DataDomeMetadata = requestToDataDomeMetadata(req) // with req coming from your framework
// see below for an example implementation for Finagle

val userAddress: Address = Address.newBuilder()
  .name("home")
  .line1("55 Rue du Faubourg Saint-Honoré")
  .line2("2nd floor")
  .city("Paris")
  .countryCode("FR")
  .country("France")
  .regionCode("75")
  .zipCode("75008")
  .build()
  
val user: User = User.newBuilder()
  .id("userId")
  .email(login)
  .firstName("Data")
  .lastName("Dome")
  .phone("+33978787878")
  .title("mr")
  .address(userAddress)
  .build()

val registrationEvent: RegistrationEvent = RegistrationEvent.newBuilder()
  .user(user)
  .account(login)
  .build()

if (!mock.userExists(user)) {
  // User does not exist - proceed with registration
  /* 
    `validateAsync` and `collectAsync` methods return CompletableFuture<>
    Note that CompletableFuture can throw Exception and must be handled in your code
  */
  val dataDomeResponse: DataDomeResponse = dataDomeFraudService.validate(ddm, registrationEvent)
  
  if (dataDomeResponse.isAllowed) {
    mock.registerUser(user)
  } else {
    // deny
    // [...]
  }
  
} else {
  // User already exists - send data for collection only
  dataDomeFraudService.collect(ddm, registrationEvent)
}
      
// Example implementation to extract the request metadata from a Finagle request
def requestToDataDomeMetadata(req: Request): DataDomeMetadata = {
  val builder = DataDomeMetadata.newBuilder()
    .addr(req.remoteAddress.getHostAddress)
    .method(req.method.name)
    .port(req.remotePort)
    .protocol(req.version.versionString)
    .request(req.uri)

  if (req.accept.nonEmpty) builder.accept(req.accept.mkString(", "))
  
  // retrieve clientId
  req.cookies.get("datadome").foreach(cookie => builder.clientId(cookie.value))

  req.headerMap.get("accept-encoding").foreach(builder.acceptEncoding)
  req.headerMap.get("accept-language").foreach(builder.acceptLanguage)
  req.headerMap.get("connection").foreach(builder.connection)
  req.headerMap.get("from").foreach(builder.from)
  req.headerMap.get("hostname").foreach(builder.serverHostname)
  req.headerMap.get("origin").foreach(builder.origin)
  req.headerMap.get("x-real-ip").foreach(builder.xRealIp)
  req.host.foreach(builder.host)
  req.charset.foreach(builder.acceptCharset)
  req.contentType.foreach(builder.contentType)
  req.referer.foreach(builder.referer)
  req.userAgent.foreach(builder.userAgent)
  req.xForwardedFor.foreach(builder.xForwardedForIp)
  
  builder.build()
}

// 1. Update the .env files with your preferred configuration. 
DATADOME_FRAUD_API_KEY='FRAUD_API_KEY'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'

// 2. Add the required imports in your controller
use DataDome\FraudSdkSymfony\Config\DataDomeOptions;
use DataDome\FraudSdkSymfony\DataDome;
use DataDome\FraudSdkSymfony\Models\Address;
use DataDome\FraudSdkSymfony\Models\LoginEvent;
use DataDome\FraudSdkSymfony\Models\StatusType;
use DataDome\FraudSdkSymfony\Models\RegistrationEvent;
use DataDome\FraudSdkSymfony\Models\Session;
use DataDome\FraudSdkSymfony\Models\User;
use DataDome\FraudSdkSymfony\Models\ResponseAction;

// 3. Create a private DataDome object
$key = $_ENV['DATADOME_FRAUD_API_KEY'];
$timeout = $_ENV['DATADOME_TIMEOUT'];
$endpoint = $_ENV['DATADOME_ENDPOINT'];

$options = new DataDomeOptions($key, $timeout, $endpoint);
$dataDome = new DataDome($options);

// 4. Invoke the validate method as required
$registrationSession = new Session();
// A unique session identifier from your system
$registrationSession->id = "sessionId";

$registrationUser = new User();
// A unique customer identifier from your system. It has to be the same for all other event sent
// This property is required
$registrationUser->id = "userId";

// Provide the below properties only if available
$registrationUser->title = "mrs";
$registrationUser->firstName = "Joy";
$registrationUser->lastName = "Green";
$registrationUser->phone = "+33978787878";
$registrationUser->email = "[email protected]";

$registrationAddress = new Address();
$registrationAddress->name = "Block 17";
$registrationAddress->line1 = "Av. de l'Opéra";
$registrationAddress->line2 = "3rd floor";
$registrationAddress->city = "Paris";
$registrationAddress->countryCode = "FR";
$registrationAddress->zipCode = "75002";

$registrationUser->address = $registrationAddress;

$registrationEvent = new RegistrationEvent(
		"[email protected]",
    StatusType::Succeeded,
    $registrationSession,
    $registrationUser);

$registrationResponse = $dataDome->validate($request, $registrationEvent);

if ($registrationResponse != null && $registrationResponse->action == ResponseAction::Allow) {
  	// Valid registration attempt
    $this->registerUser($request);
} else {
		return new JsonResponse(["Registration denied; " . $registrationResponse->reasons[0] ]);
}
        }
from datadome_fraud_sdk_python import DataDome, ResponseAction, RegistrationEvent,
UserSession, Address, Title, User as datadomeUser

datadome_instance = DataDome("FraudAPIKey")

def signup():
  account = request.form.get('email')
  
  datadomeUserSession = UserSession(session.id)

  userAddress = Address(name="Home",line1="123 Maple Street", line2="2nd floor",
                       city="Anytown", countrycode="FR", country="France",
                       regioncode="PA",zipcode="17101")

  user = datadomeUser(id=account, title=Title.MR, firstname="John",lastname="Doe",
                      createdAt=datetime.now(),
                      phone="+33978787878", email=account, address=userAddress)

  registrationEvent = RegistrationEvent(account, user, datadomeUserSession)

  datadome_response = await datadome_instance.validate(request, registrationEvent)

  if datadome_response.action == ResponseAction.ALLOW:
    flash("You are allowed to register!")
    db.register(user) # register user in your system
    return redirect(url_for('auth.signup'))
  else:
    flash("You are not allowed to register, reason:" + str(datadome_response.reasons[0]))
    return redirect(url_for('auth.login'))
// 1. Update the .env files with your preferred configuration. 
DATADOME_FRAUD_API_KEY='FRAUD_API_KEY'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'

// 2. Add the required imports in your controller
use DataDome\FraudSdkSymfony\Config\DataDomeOptions;
use DataDome\FraudSdkSymfony\DataDome;
use DataDome\FraudSdkSymfony\Models\Address;
use DataDome\FraudSdkSymfony\Models\StatusType;
use DataDome\FraudSdkSymfony\Models\RegistrationEvent;
use DataDome\FraudSdkSymfony\Models\Session;
use DataDome\FraudSdkSymfony\Models\User;
use DataDome\FraudSdkSymfony\Models\ResponseAction;

// 3. Invoke the validate and collect methods as required
[...]
$registrationSession = new Session();
// A unique session identifier from your system
$registrationSession->id = "sessionId";

$registrationUser = new User();
// A unique customer identifier from your system. It has to be the same for all other event sent
// This property is required
$registrationUser->id = "userId";

// Provide the below properties only if available
$registrationUser->title = "mrs";
$registrationUser->firstName = "Joy";
$registrationUser->lastName = "Green";
$registrationUser->phone = "+33978787878";
$registrationUser->email = "[email protected]";

$registrationAddress = new Address();
$registrationAddress->name = "Block 17";
$registrationAddress->line1 = "Av. de l'Opéra";
$registrationAddress->line2 = "3rd floor";
$registrationAddress->city = "Paris";
$registrationAddress->countryCode = "FR";
$registrationAddress->zipCode = "75002";

$registrationUser->address = $registrationAddress;

$registrationEvent = new RegistrationEvent(
		"[email protected]",
    StatusType::Succeeded,
    $registrationSession,
    $registrationUser);

$registrationResponse = app("DataDome")->validate($request, $registrationEvent);

if ($registrationResponse != null && $registrationResponse->action == ResponseAction::Allow) {
  	// Valid registration attempt
    $this->registerUser($request);
} else {
		return new JsonResponse(["Registration denied; " . $registrationResponse->reasons[0] ]);
}
# 1.  Set the value of the DATADOME_FRAUD_API_KEY as an environment variable.
export DATADOME_FRAUD_API_KEY='FRAUD_API_KEY'

# 2. Add the required import in your controller.
require 'datadome_fraud_sdk_ruby'

# 3. Create a DataDome instance.
datadome = DataDome.new

# 4a. If you have access to the HTTP request as `request`:
# Invoke the validate and collect methods.

user_address = DataDomeAddress.new(name:'Home', line1: '123 Maple Street', line2: '2nd floor', city: 'Anytown',country_code: 'FR',region_code: 'PA', zip_code:'17101')
user_datadome = DataDomeUser.new(id: user.id, title: Title::MR, first_name: 'John',last_name: 'Doe', created_at: Time.now.iso8601, phone: '+33978787878', email: params[:user][:email], address: user_address)
datadome_session =  DataDomeSession.new(id: '33')
#user_datadome = DataDomeUser.new(id: params[:user][:email])
if registration.success?
	registration_event = DataDomeRegistrationEvent.new(account: params[:user][:email], user: user_datadome, status: DataDomeStatusType::SUCCEEDED, session: datadome_session )
	datadome_response = datadome.validate(request: request, event: registration_event)
  if datadome_response.action == DataDomeResponseAction::ALLOW
		# implement logic
  else
    # implement logic
  end
else
  registration_event = DataDomeRegistrationEvent.new(account: params[:user][:email], user: user_datadome, status: DataDomeStatusType::FAILED)
	datadome.collect(request: request, event: registration_event)

# 4b. If you can't pass the current HTTP request to our SDK:
# i. Create DataDomeHeaders and DataDomeRequest with request information
datadome_headers = DataDomeHeaders.new(addr: "1.1.1.1", client_ip: "1.1.1.1", content_type: "text", host: "https://example.com", port: 80, x_real_ip: "1.1.1.1", x_forwarded_for_ip: "1.1.1.1", accept_encoding: "gzip, deflate, br", accept_language: "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4", accept: "*/*", method: "POST", protocol: "https", server_hostname: "example.com", referer: "https://example.com", user_agent:"curl", from: "[email protected]", request:"/login", origin: "https://example.com", accept_charset: "utf-8, iso-8859-1;q=0.7", connection: "keep-alive", client_id: "")
datadome_request = DataDomeRequest.new(datadome_headers) 
## ii. Use the code from 4a and replace the request with datadome_request.
datadome_response = datadome.validate(request: datadome_request, event: registration_event)
datadome.collect(request: datadome_request, event: registration_event)

API Reference

RegistrationEvent

The SDK exposes methods for registration validation that require a RegistrationEvent instance to be sent to the Account Protect API along with the client request itself.

Available properties for this event type are listed below:

NameDescriptionDefault ValuePossible ValuesOptional
accountThe unique account identifier used for the login attempt.Any string value.
session.idA unique session identifier from your systemAny string value.Yes
session.createdAtCreation date of the sessionFormat ISO 8601 YYYY-MM-DDThh:mm:ssTZDYes
user.idA unique customer identifier from your system. It has to be the same for all other event sentAny string value.
user.titleTitle of the usermr, mrs, mxYes
user.firstNameFirst name of the userAny string value.Yes
user.lastNameLast name of the userAny string value.Yes
user.createdAtCreation date of the userFormat ISO 8601 YYYY-MM-DDThh:mm:ssTZDYes
user.phonePhone of the userE.164 format including + and a region code
Example : example +33978787878
Yes
user.emailEmail of the userValid email addressYes
user.address.nameName of the addressAny string value.Yes
user.address.line1Line 1 of the addressAny string value.Yes
user.address.line2Line 2 of the addressAny string value.Yes
user.address.cityCity of the addressAny string value.Yes
user.address.countryCodeCountry of the addressFormat ISO-3166-1-alpha-2Yes
user.address.regionCodeRegion codeYes
user.address.zipCodeZip codeYes

Validation Response

Validating a login event should result in a response that can include the following properties:

NameDescriptionPossible ValuesAlways Defined
actionThe recommended action to perform on the login attempt.allow, denyYes
statusThe status of the request to the fraud protection API.ok, failure, timeoutYes
reasonsA list of reasons to support the recommended action.List of reasons (Any string value.)
ipThe IP address detected as the origin of the client request.
messageA description of the error if the status is failure or timeout.Invalid header / Request timed out...
errorsA list of objects representing each error with details.
Each object will have the properties listed below.
errors[i].fieldThe name of the value that triggered the error.
errors[i].errorA short description of the error.

Options

Options can be applied to the SDK during its instantiation.

Option NameDescriptionDefault Value
endpointThe endpoint to call for the fraud protection API.https://account-api.datadome.co
timeoutA timeout threshold in milliseconds.
When an API request times out, the SDK will allow it by default.
1500

You can find usage examples for each platform below:

const instance = new DataDome(apiKey, {
  	timeout: 1500, 
    endpoint: 'https://account-api.datadome.co',
});
// appsettings.json

// The API key is always required, but can also be passed as
// an environment variable named DataDome__FraudAPIKey
"DataDome": {
    "FraudAPIKey": "----",
    "Timeout": 1500,
    "Endpoint": "https://account-api.datadome.co"
}
new DataDomeFraudService(datadomeFraudApiKey, 
                         DataDomeOptions.newBuilder()
                         .endpoint("https://account-api.datadome.co")
                         .timeout(1500)
                         .build()
  );
datadome_instance =  DataDome("FraudAPIKey", timeout=1500, endpoint="https://account-api.datadome.co")
val dataDomeFraudService = new DataDomeFraudService(datadomeFraudApiKey, 
                                                    DataDomeOptions.newBuilder()
                                                    .endpoint("https://account-api.datadome.co")
                                                    .timeout(1500)
                                                    .build()
                                                   )
// .env

DATADOME_FRAUD_API_KEY='----'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'
// .env

DATADOME_FRAUD_API_KEY='----'
DATADOME_TIMEOUT=1500
DATADOME_ENDPOINT='https://account-api.datadome.co'
datadome = DataDome.new(1500, 'https://account-api.datadome.co', config.logger)

FAQ

What happens if there is a timeout on API request?

The SDK has been designed to have minimal impact on the user experience. If the configured timeout is reached, the SDK will cancel its pending operation and allow the application to proceed.

What happens if the API returns an error?

Errors and timeouts are handled the same way by the SDK: it will not interrupt the application and allow it to proceed.

What happens if my API key is incorrect?

Invalid keys are detected when calling the account protect API. The SDK will return an allow response to avoid blocking any login or registration attempt on the application. This response will also have a failure status and a message that describes the problem.