dodgeball / dodgeball-sdk-server
该库为基于PHP的服务器应用程序提供执行检查点和跟踪Dodgeball平台事件的函数。
Requires
- guzzlehttp/guzzle: ^7.0
- ramsey/uuid: ^4.2.3
Requires (Dev)
- phpunit/phpunit: ^9.6.13
README
目录
目的
Dodgeball 允许开发人员将安全逻辑从应用程序代码中分离出来。这有多个好处,包括
- 切换和比较安全服务的能力,如欺诈引擎、多因素认证(MFA)、KYC和机器人预防。
- 更快地应对新的攻击。当威胁演变和发现新的漏洞时,无需更改代码行即可更新应用程序的安全逻辑。
- 在关注产品开发的同时,为未来的安全改进预留空间的能力。
- 一种将所有应用程序安全逻辑可视化的方式。
Dodgeball Server Trust SDK for PHP使与Dodgeball API的集成变得简单,并由Dodgeball团队维护。
先决条件
您需要从 Dodgeball开发者中心 获取应用程序的API密钥。
相关
查看 Dodgeball Trust Client SDK 了解如何将Dodgeball集成到前端应用程序中。
安装
使用 composer
安装Dodgeball模块
composer require dodgeball/dodgeball-sdk-server
使用
<?php use Dodgeball\DodgeballSdkServer\Dodgeball; $dodgeball = new Dodgeball('secret-api-key...'); $checkpointResponse = $dodgeball->checkpoint([ 'checkpointName' => 'PLACE_ORDER', 'event' => (object) [ 'ip' => $_SERVER['REMOTE_ADDR'], // Make sure this is the real IP address of the client 'data' => (object) [ 'key' => 'value', 'nested' => (object) [ 'key' => 'nestedValue', ], ], ], 'sourceToken' => $_SERVER['HTTP_X_DODGEBALL_SOURCE_TOKEN'], 'sessionId' => $currentSessionId, 'userId' => $currentUserId, 'useVerificationId' => $_SERVER['HTTP_X_DODGEBALL_VERIFICATION_ID'] ])
API
配置
该软件包需要将秘密API密钥作为构造函数的第一个参数。
const dodgeball = new Dodgeball("secret-api-key...");
可选地,您可以将多个配置选项传递给构造函数
const dodgeball = new Dodgeball("secret-api-key...", { // Optional configuration (defaults shown here) 'apiVersion' => "v1", 'apiUrl' => "https://api.dodgeballhq.com", 'isEnabled' => true });
调用检查点
检查点代表应用程序中的关键风险时刻,是Dodgeball工作的核心。检查点可以代表任何被认为有风险的任何活动。一些常见的例子包括:登录、下单、兑换优惠券、发表评论、更改银行账户信息、捐款、转账、创建列表。
const $checkpointResponse = $dodgeball->checkpoint({ 'checkpointName': "CHECKPOINT_NAME", 'event' => (object) [ 'ip' => $_SERVER['REMOTE_ADDR'], // Make sure this is the real IP address of the client 'data' => (object) [ 'transaction' => (object) [ 'amount' => 100, 'currency' => 'USD', ], 'paymentMethod' => (object) [ 'token' => 'ghi789' ] ], ], 'sourceToken' => 'abc123...', // Obtained from the Dodgeball Client SDK, represents the device making the request 'sessionId' => 'session_def456', // The current session ID of the request 'userId' => 'user_12345', // When you know the ID representing the user making the request in your database (ie after registration), pass it in here. Otherwise leave it blank. 'useVerificationId' => 'def456' // Optional, if you have a verification ID, you can pass it in here });
解析检查点响应
调用检查点会在Dodgeball中创建一个验证。验证的状态和结果决定应用程序应该如何进行。继续到可能的检查点响应以获取对可能的状况和结果组合以及如何解析它们的全面解释。
$checkpointResponse = [ 'success' => boolean, 'errors' => [ [ 'code' => int, 'message' => string ] ], 'version' => string, 'verification': [ 'id': string, 'status': string, 'outcome': string ] ];
验证状态
验证结果
可能的检查点响应
批准
$checkpointResponse = (object) [ 'success' => true, 'errors' => [], 'version' => 'v1', 'verification' => (object) [ 'id' => 'def456', 'status' => 'COMPLETE', 'outcome' => 'APPROVED', ], ];
当请求被允许继续时,验证的 状态
将是 完成
,而 结果
将是 批准
。
拒绝
$checkpointResponse = (object) [ 'success' => true, 'errors' => [], 'version' => 'v1', 'verification' => (object) [ 'id' => 'def456', 'status' => 'COMPLETE', 'outcome' => 'DENIED', ], ];
当请求被拒绝时,验证的 状态
将是 完成
,而 结果
将是 拒绝
。
挂起
$checkpointResponse = (object) [ 'success' => true, 'errors' => [], 'version' => 'v1', 'verification' => (object) [ 'id' => 'def456', 'status' => 'PENDING', 'outcome' => 'PENDING', ], ];
如果验证仍在处理中,则 状态
将是 挂起
,而 结果
将是 挂起
。
阻塞
$checkpointResponse = (object) [ 'success' => true, 'errors' => [], 'version' => 'v1', 'verification' => (object) [ 'id' => 'def456', 'status' => 'BLOCKED', 'outcome' => 'PENDING', ], ];
阻塞的验证需要在继续之前从用户那里获取额外的输入。当请求被阻塞时,验证的 状态
将是 阻塞
,而 结果
将是 挂起
。
未决定
$checkpointResponse = (object) [ 'success' => true, 'errors' => [], 'version' => 'v1', 'verification' => (object) [ 'id' => 'def456', 'status' => 'COMPLETE', 'outcome' => 'PENDING', ], ];
如果验证已完成,但没有就如何继续作出决定,则验证的 状态
将是 完成
,而 结果
将是 挂起
。
错误
$checkpointResponse = (object) [ 'success' => false, 'errors' => [ (object) [ 'code' => 503, 'message' => '[Service Name]: Service is unavailable', ], ], 'version' => 'v1', 'verification' => (object) [ 'id' => 'def456', 'status' => 'FAILED', 'outcome' => 'ERROR', ], ];
如果在处理验证过程中遇到错误(例如第三方服务不可用),则success
标志将为false。验证的status
将为FAILED
,而outcome
将为ERROR
。errors
数组将至少包含一个包含描述发生错误的code
和message
的对象。
实用方法
有几个实用方法可用于帮助解释检查点响应。强烈建议使用它们,而不是直接解释检查点响应。
checkpointResponse->isAllowed()
isAllowed
方法返回true
,如果请求被允许继续进行。
checkpointResponse->isDenied()
isDenied
方法返回true
,如果请求被拒绝且不应被允许继续进行。
checkpointResponse->isRunning()
isRunning
方法返回true
,如果没有就如何继续做出决定。验证应返回到前端应用程序以从用户那里收集更多输入。有关使用方法和端到端示例的更多详细信息,请参阅useVerification部分。
checkpointResponse->isUndecided()
isUndecided
方法返回true
,如果验证已完成,但没有就如何继续做出决定。有关更多详细信息,请参阅undecided。
checkpointResponse->hasError()
hasError
方法返回true
,如果它包含错误。
checkpointResponse->isTimeout()
isTimeout
方法返回true
,如果验证已超时。此时,应用程序必须决定如何继续。
useVerification
有时在做出如何继续的决定之前,需要从用户那里获取更多输入。例如,如果用户在允许继续之前需要执行2FA,则检查点响应将包含一个status
为BLOCKED
且结果为PENDING
的验证。在这种情况下,您希望将验证返回到您的前端应用程序。在前端应用程序内部,您可以直接将返回的验证传递给dodgeball.handleVerification()
方法来自动收集用户的其他输入。继续我们的2FA示例,用户将被提示选择一个电话号码并输入发送到该号码的代码。一旦收到额外的输入,前端应用程序应简单地发送执行验证的ID到您的API。通过将验证ID传递给useVerification
选项,将允许使用该验证而不是创建新的验证来完成此检查点。这防止了对用户执行重复的验证。
重要说明:为了防止重放攻击,每个验证ID只能传递给useVerification
一次。
跟踪事件
您可以通过从您的服务器提交跟踪事件来跟踪有关用户旅程的更多信息。这些信息将被添加到用户的个人资料中,并对检查点可用。
$dodgeball->event([ 'event' => [ 'type' => 'EVENT_NAME', // Can be any string you choose 'data' => [ // Arbitrary data to track... 'transaction' => [ 'amount' => 100, 'currency' => 'USD', ], 'paymentMethod' => [ 'token' => 'ghi789', ], ], ], 'sourceToken' => 'abc123...', // Obtained from the Dodgeball Client SDK, represents the device making the request 'sessionId' => 'session_def456', // The current session ID of the request 'userId' => 'user_12345', // When you know the ID representing the user making the request in your database (ie after registration), pass it in here. Otherwise leave it blank. ]);
端到端示例
// In your frontend application... const placeOrder = async (order, previousVerificationId = null) => { const sourceToken = await dodgeball.getSourceToken(); const endpointResponse = await axios.post("/api/orders", { order }, { headers: { "x-dodgeball-source-token": sourceToken, // Pass the source token to your API "x-dodgeball-verification-id": previousVerificationId // If a previous verification was performed, pass it along to your API } }); dodgeball.handleVerification(endpointResponse.data.verification, { onVerified: async (verification) => { // If an additional check was performed and the request is approved, simply pass the verification ID in to your API await placeOrder(order, verification.id); }, onApproved: async () => { // If no additional check was required, update the view to show that the order was placed setIsOrderPlaced(true); }, onDenied: async (verification) => { // If the action was denied, update the view to show the rejection setIsOrderDenied(true); }, onError: async (error) => { // If there was an error performing the verification, display it setError(error); setIsPlacingOrder(false); } }); }
use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; // In your API... Route::post('/api/orders', function(Request $request) { // In moments of risk, call a checkpoint within Dodgeball to verify the request is allowed to proceed $order = $request->input('order'); $checkpointResponse = $dodgeball->checkpoint([ 'checkpointName' => 'PLACE_ORDER', 'event' => [ 'ip' => $request->ip(), 'data' => [ 'order' => $order, ], ], 'sourceToken' => $request->header('x-dodgeball-source-token'), 'sessionId' => $request->session()->getId(), 'userId' => $request->session()->get('userId'), 'useVerificationId' => $request->header('x-dodgeball-verification-id'), ]); if ($checkpointResponse->isAllowed()) { // Proceed with placing the order... $placedOrder = app('database')->createOrder($order); return response()->json([ 'order' => $placedOrder, ]); } else if ($checkpointResponse->isRunning()) { // If the outcome is pending, send the verification to the frontend to do additional checks (such as MFA, KYC) return response()->json([ 'verification' => $checkpointResponse->verification, ], 202); } else if ($checkpointResponse->isDenied()) { // If the request is denied, you can return the verification to the frontend to display a reason message return response()->json([ 'verification' => $checkpointResponse->verification, ], 403); } else { // If the checkpoint failed, decide how you would like to proceed. You can return the error, choose to proceed, retry, or reject the request. return response()->json([ 'message' => $checkpointResponse->errors, ], 500); } });
运行测试
此包使用PHPUnit进行测试。要运行测试,请从项目根目录运行以下命令
./vendor/bin/phpunit --display-warnings --display-deprecations tests