slashid/php

用于与SlashID API集成的PHP SDK。

1.1.0 2024-05-30 16:40 UTC

This package is not auto-updated.

Last update: 2024-09-19 18:09:18 UTC


README

安装

使用composer安装此库

composer require slashid/php

用法

实例化SDK

首先,创建一个SDK实例,告知以下

  • $environment,可以是sandboxproduction
  • $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',
]

几点说明

  1. $triggers 参数是 webhook 将具有的触发器的列表。完整列表请参见:https://developer.slashid.dev/docs/access/guides/webhooks/introduction
  2. 如果正在更新 webhook,则 $triggers 将覆盖现有触发器。
  3. $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',
]
  1. 如果正在更新 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 实现

对于其他框架,请查看此列表: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'],
);