Blog
Integrations

Marc Whitbread

2024/09/17

authentication challengescustom implementationsecurity featuresuser verificationaccount protection

Custom challenges for limiting device access

For some use cases, you may want to create a completely custom challenge for a more seamless experience in your application. For these integrations, you can use the challenge public apis from your back-end to move your users through the challenge process.

The Rupt client SDK

The Rupt client SDK provides a way to attach a device to a user account and listen for incoming challenges. This enables you to create a custom challenge experience for your users. For a detailed explanation of the client SDK setup, see the Rupt documentation related to your platform.

Attach a device using callbacks

To listen for incoming challenges, you can use the on_challenge callback. This callback will be called when a challenge is required for the current user. You can then use your custom challenge experience to resolve the challenge. To disable the default challenge experience, return false from the callback.

import Rupt from "rupt";

const { device_id } = await Rupt.attach({
  client_id: "CLIENT_ID",
  account: "ACCOUNT_ID",
  on_challenge: ({ challenge }) => {
    // Your custom challenge experience
    return false;
  },
  on_current_device_logout: ({ challenge }) => {
    // Your custom logout experience
    return false;
  },
});

The Rupt core SDK

On your server, you can use the Rupt core SDK to convenienty interact with the Rupt API. This SDK provides a way to retrieve challenges/devices, kick devices, and complete challenges. See the Rupt API documentation for specific intallation, setup and usage information.

import RuptCore from "@ruptjs/core";
const Rupt = new RuptCore(SECRET_KEY);

Retrieve the challenge and examine the limit state

To retrieve the challenge, you can use the get challenge endpoint. This endpoint will return the challenge details, including the current limit state. The limit state will indicate if and how many devices are required to resolve the challenge.

const { limit } = await Rupt.getChallenge(CHALLENGE_ID);

When limit.is_exceeded is true, the challenge is in a state where the device limit has been exceeded. You can then use the values of limit.overall, limit.mobile, limit.tablet, and limit.desktop to determine which types and how many of each device are required to be kicked to resolve the challenge.

For example, if limit.overall is 2, you will need to kick any two devices to resolve the challenge.

Retrieve the list of challenge devices

Once the challenge is retrieved, you can use the get challenge devices endpoint to retrieve the list of devices that are currently associated with the challenge. You can use the device type to determine which devices are required to be kicked to resolve the challenge. In addition, The device_id returned from the attach method on the client can be used to identify which device represents a specific user in the challenge device list.

const devices = await Rupt.getChallengeDevices(CHALLENGE_ID);
const currentUserDevice = devices.find(
  ({ _id }) => _id === device_id_from_attach
);

Kick devices to resolve the challenge

To resolve the challenge, you can use the kick challenge device endpoint to kick the required devices. You can use the device_id to kick the device that represents the current user or any other device that is required to resolve the challenge specified in by the limit state.

await Rupt.kickChallengeDevice({
  challenge: CHALLENGE_ID,
  device: DEVICE_ID,
});

It may be helpful to retrieve the challenge limit state again after kicking a device to ensure that the challenge has been resolved and the limit.is_exceeded value is false before proceeding.

Complete the challenge

Once the challenge has been resolved, you can use the complete challenge endpoint to fully complete the challenge and allow the user to continue using your application.

await Rupt.completeChallenge(CHALLENGE_ID);

Conclusion

By using the Rupt client SDK and the Rupt core SDK, you can create a custom challenge experience for your users. This enables you to create a more seamless experience in your application and provide a more tailored solution for your specific use case.