slashid / php
用于与SlashID API集成的PHP SDK。
Requires
- php: ^8.1
- firebase/php-jwt: ^6.10
- guzzlehttp/guzzle: ^7.2
- psr/cache: ^3.0
Requires (Dev)
- beste/in-memory-cache: ^1.1
- crutch/clock-dev: ^1.0
- friendsofphp/php-cs-fixer: ^3.50
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
This package is not auto-updated.
Last update: 2024-09-19 18:09:18 UTC
README
安装
使用composer安装此库
composer require slashid/php
用法
实例化SDK
首先,创建一个SDK实例,告知以下
$environment
,可以是sandbox
或production
$organizationId
,您的组织ID。您可以在SlashID控制台(生产环境:https://console.slashid.dev/,沙盒环境:https://console.sandbox.slashid.dev/)的“设置”选项卡中找到它,在页面顶部。$apiKey
,您的组织ID。您也可以在SlashID控制台的“设置”选项卡中找到它,在页面底部。
use SlashId\Php\SlashIdSdk; $sdk = new SlashIdSdk(SlashIdSdk::ENVIRONMENT_PRODUCTION, '412edb57-ae26-f2aa-9999-770021ed52d1', 'z0dlY-nluiq8mcvm8YTolSkJV6e9');
发送请求
您可以使用->get
、->post
、->patch
、->put
、->delete
方法直接进行Web服务请求。
GET请求
可以使用get
方法对索引样式端点和获取单个端点。
// GET https://api.slashid.com/persons $response = $sdk->get('/persons'); // $response: [ ['person_id' => '1111-1111-1111', 'handles' => [...]], ['person_id' => '2222-2222-2222', 'handles' => [...]], ]
您可以使用第二个参数指定端点的查询
// GET https://api.slashid.com/persons/1111-1111-1111?fields=handles,groups,attributes $response = $sdk->get('/persons/1111-1111-1111', [ 'fields' => ['handles', 'groups', 'attributes'], ]); // $response: ['person_id' => '1111-1111-1111', 'handles' => [...]]
POST、PATCH、PUT请求
对于POST、PATCH、PUT请求,您需要将正文作为第二个参数传递
// POST https://api.slashid.com/persons $response = $sdk->post('/persons', [ 'active' => true, 'handles' => [ [ 'type' => 'email_address', 'value' => 'user@user.com' ], ], ]); // $response: ['person_id' => '1111-1111-1111', 'handles' => [...]]
// PATCH https://api.slashid.com/persons/1111-1111-1111 $response = $sdk->patch('/persons/1111-1111-1111', [ 'active' => true, 'handles' => [ [ 'type' => 'email_address', 'value' => 'user@user.com' ], ], ]); // $response: ['person_id' => '1111-1111-1111', 'handles' => [...]]
// PUT https://api.slashid.com/persons/1111-1111-1111 $response = $sdk->put('/persons', [ 'active' => true, 'handles' => [ [ 'type' => 'email_address', 'value' => 'user@user.com' ], ], ]); // $response: ['person_id' => '1111-1111-1111', 'handles' => [...]]
DELETE请求
DELETE请求不返回任何内容
// DELETE https://api.slashid.com/persons/1111-1111-1111 $sdk->delete('/persons/1111-1111-1111');
某些端点还需要查询
$sdk->delete('/organizations/webhooks/99999-99999-99999/triggers', [ 'trigger_type' => 'event', 'trigger_name' => 'PersonCreated_v1', ]);
getClient()
$sdk->get()
、$sdk->post()
、$sdk->patch()
、$sdk->put()
和$sdk->delete()
方法期望发送和接收JSON,但某些端点有特殊要求,例如GET /persons/bulk-import
(获取导入CSV模板)将返回CSV并需要Accept: text/csv
头部。
在这种情况下,您可以使用$sdk->getClient()
来检索带有正确凭证预设的底层GuzzlePHP客户端,例如
$response = $sdk->getClient()->request('GET', '/persons/bulk-import', [ 'headers' => [ 'Accept' => 'text/csv', ], ]); $csvContents = (string) $response->getBody();
异常
以下异常可能在连接过程中出现错误时抛出
- 当API返回
400
错误时,抛出\SlashId\Php\Exception\BadRequestException
,表示您发送到请求的数据格式不正确或缺少所需信息。 - 当API返回
401
错误时,抛出\SlashId\Php\Exception\UnauthorizedException
,表示组织ID或API密钥错误。在这种情况下,检查您的凭据。 - 当API返回
403
错误时,抛出\SlashId\Php\Exception\AccessDeniedException
,表示您不允许访问资源。 - 当API返回因您请求的端点不存在而导致
404
错误时,抛出\SlashId\Php\Exception\InvalidEndpointException
。在这种情况下,检查API参考。 - 当API在有效端点返回
404
错误时,抛出\SlashId\Php\Exception\IdNotFoundException
,表示您请求的ID不存在。此异常将在包含ID的URL(如/persons/1111-1111-1111
)的请求中发生。 - 当API返回
409
错误时,抛出\SlashId\Php\Exception\ConflictException
,通常表示您正在尝试创建重复实体(例如,具有已属于现有人员的电子邮件的人员)。在这种情况下,检查API参考以查看是否存在端点的幂等版本。 - 当API返回任何其他
4xx
错误时,抛出\GuzzleHttp\Exception\ClientException
。 - 当API返回任何
5xx
错误时,抛出\GuzzleHttp\Exception\BadResponseException
。 - 如果在与API服务器连接过程中出现其他类型的错误,将实现
\GuzzleHttp\Exception\GuzzleException
。
所有\SlashId\Php\Exception
异常都是\GuzzleHttp\Exception\BadResponseException
的子类,这意味着您可以使用以下方法了解错误原因
// Gets an informative message about the error. $exception->getMessage(); // Gets the request object, with information about the endpoint and the data in the request. $request = $exception->getRequest(); // Gets the response object, with HTTP response code and the response contents. $response = $exception->getResponse(); // Gets the response as text. $responseText = (string) $exception->getResponse()->getBody(); // Gets the response as a parsed array. $responseData = \json_decode((string) $exception->getResponse()->getBody(), true);
\SlashId\Php\PersonInterface
/ \SlashId\Php\Person
类Person
非常适用于表示来自/persons/{id}
端点的个人信息。
您可以使用端点响应实例化一个类,使用Person::fromValues
。
use SlashId\Php\Exception\IdNotFoundException; use SlashId\Php\Person; use SlashId\Php\PersonInterface; function getPerson($identifier): ?PersonInterface try { $response = $this->sdk->get('/persons/' . $identifier, [ 'fields' => ['handles', 'groups', 'attributes'], ]); return Person::fromValues($response); } catch (IdNotFoundException $exception) { return null; } }
有了这些,您就有几个函数可以读取个人的数据
// The ID, such as 9999-9999-9999. It can be null if the $person is created with `new Person()`. $person->getPersonId(); // Whether the person is active. $person->isActive(); // The email addresses associated with the account, such as ['email@example.com', 'email2@example.com']. $person->getEmailAddresses(); // The phone numbers associated with the account, such as ['+199999999', '+44999999999']. $person->getPhoneNumbers(); // The region, one of "us-iowa", "europe-belgium", "asia-japan", "europe-england", "australia-sydney". $person->getRegion(); // The groups of the person, e.g. ['Admin', 'Editor']. $person->getGroups();
我们还有相应的设置器
// Overrides whether the user is active. $person->setActive(false); // Adds one email address to the list. $person->addEmailAddress(string $emailAddress): static // Overrides the full list of email addresses. $person->setEmailAddresses(array $emailAddresses): static // Adds one phone number to the list. $person->addPhoneNumber(string $phoneNumber): static // Overrides the full list of phone numbers. $person->setPhoneNumbers(array $phoneNumbers): static // Overrides the region. $person->setRegion(string $region): static // Overrides the list of groups. $person->setGroups(array $groups): static
⚠️ 注意,本类中的方法将不会更新SlashID服务器中的数据。要执行此操作,您必须发起一个PATCH /persons/:person_id
请求或一个PUT /persons
请求。
属性
SlashID中的人属性存储在桶中。在此SDK中,它们通过以下常量表示。
⚠️ 请注意不要向最终用户暴露“NO_ACCESS”属性。
在Person
中,可以通过以下方法访问属性
// Lists all attributes, grouped by bucket name. $person->getAllAttributes(); // Response: [ PersonInterface::BUCKET_ORGANIZATION_END_USER_READ_WRITE => ['first_name' => 'John'], PersonInterface::BUCKET_ORGANIZATION_END_USER_NO_ACCESS => ['secret_key' => 'aaa-aaa-aaa'], ]; // Gets attributes in a bucket. $person->getBucketAttributes(PersonInterface::BUCKET_ORGANIZATION_END_USER_READ_WRITE); // Response: ['first_name' => 'John']; // Gets one specific attribute. $person->getAttribute(PersonInterface::BUCKET_ORGANIZATION_END_USER_READ_WRITE, 'first_name'); // Response: 'John';
属性也可以设置
// Overrides ALL attributes. $person->setAllAttributes([ PersonInterface::BUCKET_ORGANIZATION_END_USER_READ_WRITE => ['first_name' => 'John'], PersonInterface::BUCKET_ORGANIZATION_END_USER_NO_ACCESS => ['secret_key' => 'aaa-aaa-aaa'], ]); // Overrides attributes in a bucket. $person->setBucketAttributes(PersonInterface::BUCKET_ORGANIZATION_END_USER_NO_ACCESS, ['secret_key' => 'aaa-aaa-aaa']); // Deletes the attributes in a bucket. $person->deleteBucketAttributes(PersonInterface::BUCKET_ORGANIZATION_END_USER_NO_ACCESS); // Overrides one attribute. $person->setAttribute(PersonInterface::BUCKET_ORGANIZATION_END_USER_READ_WRITE, 'last_name', 'Smith'); // Deletes one attribute. $person->deleteAttribute(PersonInterface::BUCKET_ORGANIZATION_END_USER_READ_WRITE, 'first_name');
⚠️ 注意,本类中的方法将不会更新SlashID服务器中的数据。要执行此操作,您必须发起一个PUT /persons/:person_id/attributes
请求或一个/persons/:person_id/attributes/:bucket_name
请求。
组
类Person
也有三个有用的方法来测试个人的组
if ($person->hasGroup('Editor')) { // Do things that only an "Editor" user can do. } if ($person->hasAnyGroup(['Admin', 'Editor', 'Reviewer'])) { // Do things that someone in the group "Admin", OR in the group "Editor", OR // in the group "Reviewer" can do. } if ($person->hasAllGroups(['Admin', 'Editor'])) { // Do things that only someone that is in *both* "Admin" and "Editor" groups // can do. }
迁移抽象
迁移抽象是一个用于批量导入用户的类。
要导入用户,您必须首先创建一个\SlashId\Php\PersonInterface
的数组,然后调用$sdk->migration()->migratePersons($persons);
,例如
$person = (new \SlashId\Php\Person()) ->setRegion('europe-england') ->addEmailAddress('user@example.com') ->addPhoneNumber('+33999999999') ->setGroups(['Admin', 'Editor']) // Define a password hash with one of the supported encryptions. ->setLegacyPasswordToMigrate('$2y$12$YKpfgBJpginFYyUfdAcAHumQKfJsEzJJz9d0oQgg0zoEsRSz6sXty'); $persons = [$person]; $response = $sdk->migration()->migratePersons($persons);
$response
将包含端点POST /persons/bulk-import
的响应,即包含三个键的数组
successful_imports
- 成功导入的人员数量failed_imports
- 导入过程中的失败数量failed_csv
- 报告失败导入的用户以及每行的错误原因的CSV文件
令牌抽象
令牌抽象是一个用于帮助验证和操作用于身份验证的令牌的类。
验证令牌
要验证身份验证令牌,请使用validateToken
方法
if ($sdk->token()->validateToken($token)) { // Token is valid. }
从令牌中获取人员ID
要从令牌中获取人员ID(令牌中的“sub”),请使用getSubFromToken
方法
$personId = $sdk->token()->getSubFromToken($token); // $personId will be something such as "903c1ff9-f2cc-435c-b242-9d8a690fcf0a".
Webhook抽象
Webhook抽象是一个用于帮助处理webhooks的类,用于创建、列出和删除它们,还可以添加和删除触发器。
您可以通过以下方式访问它
$sdk->webhook();
列出webhooks
方法findAll()
列出了组织中所有现有的webhook监听器,包括其他应用程序和环境中的监听器
foreach ($sdk->webhook()->findAll() as $webhook) { // each $webhook: [ 'id' => '065de68b-cce0-7285-ab00-6f34a56b585d', 'name' => 'prod_webhook', 'description' => 'Some description...', 'target_url' => 'https://example.com/slashid/webhook', 'custom_headers' => [ 'X-Extra-Check' => ['Value for the header'], ], 'timeout' => '30s', ] }
方法findById($id)
当您有ID时获取单个webhook。
$webhook = $sdk->webhook()->findById('065de68b-cce0-7285-ab00-6f34a56b585d'); // $webhook: [ 'id' => '065de68b-cce0-7285-ab00-6f34a56b585d', 'name' => 'prod_webhook', 'description' => 'Some description...', 'target_url' => 'https://example.com/slashid/webhook', 'custom_headers' => [ 'X-Extra-Check' => ['Value for the header'], ], 'timeout' => '30s', ]
方法findByUrl($url)
当您有URL时获取单个webhook。
$webhook = $sdk->webhook()->findByUrl('https://example.com/slashid/webhook'); // $webhook: [ 'id' => '065de68b-cce0-7285-ab00-6f34a56b585d', 'name' => 'prod_webhook', 'description' => 'Some description...', 'target_url' => 'https://example.com/slashid/webhook', 'custom_headers' => [ 'X-Extra-Check' => ['Value for the header'], ], 'timeout' => '30s', ]
创建webhooks并设置触发器
register($url, $name, $triggers, $options)
方法是幂等的,即如果该 URL 还未存在,它将创建 webhook;如果已存在具有该 URL 的 webhook,则将其更新。在创建或更新 webhook 之后,它还将为它注册触发器。因此,该方法结合了对 /organizations/webhooks
和 /organizations/webhooks/:webhook_id/triggers
端点的调用。
$webhook = $sdk->webhook()->register('https://example.com/slashid/webhook', 'a_unique_name_for_the_webhook', [ 'PersonCreated_v1', 'PersonDeleted_v1', ]); // $webhook: [ 'id' => '065de68b-cce0-7285-ab00-6f34a56b585d', 'name' => 'a_unique_name_for_the_webhook', 'description' => '', 'target_url' => 'https://example.com/slashid/webhook', 'custom_headers' => [], 'timeout' => '0s', ]
几点说明
$triggers
参数是 webhook 将具有的触发器的列表。完整列表请参见:https://developer.slashid.dev/docs/access/guides/webhooks/introduction- 如果正在更新 webhook,则
$triggers
将覆盖现有触发器。 $options
参数是/organizations/webhooks
端点接收的额外字段列表(请参阅 https://developer.slashid.dev/docs/api/post-organizations-webhooks),例如:
[ 'description' => 'Some description...', 'custom_headers' => [ 'X-Extra-Check' => ['Value for the header'], ], 'timeout' => '30s', ]
- 如果正在更新 webhook,则与
$triggers
不同,$options
将不会覆盖现有值。
一些示例
// Creates a new webhook. $webhook = $sdk->webhook()->register('https://example.com/slashid/webhook', 'a_unique_name_for_the_webhook', [ 'PersonCreated_v1', 'PersonDeleted_v1', ], [ 'timeout' => '18s', ]); // $webhook: [ 'id' => '065de68b-cce0-7285-ab00-6f34a56b585d', 'name' => 'a_unique_name_for_the_webhook', 'description' => '', 'target_url' => 'https://example.com/slashid/webhook', 'custom_headers' => [], 'timeout' => '18s', ] // Lists triggers the webhook has. $triggers = $sdk->webhook()->getWebhookTriggers($webhook['id']); // $triggers: [ 'PersonCreated_v1', 'PersonDeleted_v1', ] // Now we update the webhook, with a different option AND different triggers. $webhook = $sdk->webhook()->register('https://example.com/slashid/webhook', 'a_unique_name_for_the_webhook', [ 'AuthenticationSucceeded_v1', 'PersonCreated_v1', ], [ 'custom_headers' => [ 'X-Custom-Header' => ['Value for the header'], ], ]); // Note that the "custom_header" has been updated, but the value for the "timeout" is unchanged. // $webhook: [ 'id' => '065de68b-cce0-7285-ab00-6f34a56b585d', 'name' => 'a_unique_name_for_the_webhook', 'description' => '', 'target_url' => 'https://example.com/slashid/webhook', 'custom_headers' => [ 'X-Custom-Header' => ['Value for the header'], ], 'timeout' => '18s', ] // As for the triggers, note that "PersonDeleted_v1" is no longer a trigger. $triggers = $sdk->webhook()->getWebhookTriggers($webhook['id']); // $triggers: [ 'AuthenticationSucceeded_v1', 'PersonCreated_v1', ]
设置触发器
您也可以直接处理触发器。
方法 setTriggers($id, $triggers)
将覆盖现有触发器,删除不在 $triggers
列表中的触发器,并添加不在其中的新触发器。
方法 addWebhookTrigger($id, $trigger)
从 webhook 添加单个触发器。
方法 deleteWebhookTrigger($id, $trigger)
从 webhook 中删除单个触发器。
$sdk->webhook()->setWebhookTriggers('065de68b-cce0-7285-ab00-6f34a56b585d', [ 'PersonCreated_v1', 'PersonDeleted_v1', ]); // Triggers in the webhook: PersonCreated_v1, PersonDeleted_v1 $sdk->webhook()->addWebhookTrigger('065de68b-cce0-7285-ab00-6f34a56b585d', 'VirtualPageLoaded_v1'); // Triggers in the webhook: PersonCreated_v1, PersonDeleted_v1, VirtualPageLoaded_v1 $sdk->webhook()->deleteWebhookTrigger('065de68b-cce0-7285-ab00-6f34a56b585d', 'PersonDeleted_v1'); // Triggers in the webhook: PersonCreated_v1, VirtualPageLoaded_v1 $sdk->webhook()->setWebhookTriggers('065de68b-cce0-7285-ab00-6f34a56b585d', []); // Triggers in the webhook: none
删除 webhook
您可以使用 deleteById($id)
或 deleteByUrl($url)
方法中的任何一个来删除 webhook。
$sdk->webhook()->deleteById('065de68b-cce0-7285-ab00-6f34a56b585d'); $sdk->webhook()->deleteByUrl('https://example.com/slashid/webhook');
Webhook 回调
注册 webhook 并添加触发器后,SlashID 服务器将开始向您的端点发送请求。请求将是一个 JWT,您需要使用 JSON Web Signature 标准提供的 API 端点提供的密钥对其进行解码和验证。
由于 JWT Key Set (JWKS) 必须远程下载,因此缓存密钥很重要,以避免不断进行远程请求。因此,在调用 decodeWebhookCall
时,您需要提供 PSR-6 兼容的 \Psr\Cache\CacheItemPoolInterface
对象。
一些框架默认提供 PSR-6 实现
- Laravel:
app('cache.psr6')
- Symfony: 包 symfony/cache
对于其他框架,请查看此列表:https://packagist.org.cn/providers/psr/cache-implementation
要实现 webhook 监听器,从远程请求的体中获取编码的 JWT,然后调用 decodeWebhookCall
以对其进行验证和解码。以下是一个 Laravel 实现示例,它在接收到 webhook 后分发事件。
// The JWT from the request. $encodedJwt = $request->getContent(); // $encodedJwt: // eyJhbGciOiJFUzI1NiIsICJraWQiOiJuTGtxV1EifQ.eyJhdWQiOiI0MTJlZGI1Ny1hZTI2LWYyYWEtMDY5OC03NzAwMjFlZDUyZDEiLCAiZXhwIjx... // Note the use of `app('cache.psr6')` to fetch the cache backend. $decoded = $sdk->webhook()->decodeWebhookCall($encodedJwt, app('cache.psr6')); // Dispatch an event using Laravel's API. WebhookEvent::dispatch( $decoded['trigger_content']['event_metadata']['event_name'], $decoded['trigger_content']['event_metadata']['event_id'], $decoded['trigger_content'], );