glamstack/okta-sdk

此包已被弃用且不再维护。作者建议使用 gitlab-it/okta-sdk 包。

Okta API SDK for Laravel

维护者

详细信息

gitlab.com/glamstack/okta-sdk


README

[[目录]]

概览

Okta SDK 是 GitLab IT Engineering 为在内部 Laravel 应用程序中连接到 Okta 以进行用户、组、应用程序和其他相关功能的配置和取消配置而创建的开源 Composer 包。

免责声明:这不是由 GitLab 或 Okta 产品和开发团队维护的官方包。这是一个我们在 GitLab IT 部门使用的内部工具,我们将其开源作为我们公司价值观的一部分。

请自行承担风险,并为我们遇到的任何错误创建合并请求。

我们不维护社区功能请求路线图,但我们邀请您贡献力量,我们很高兴审查您的合并请求。

v2 到 v3 升级指南

3.0.版本中有几个重大变更。请参阅 v3.0 变更日志 了解更多信息。

更改内容

  • glamstack/okta-sdk 已被弃用,并已重命名为 gitlab-it/okta-sdk。
  • config/glamstack-gitlab.php 已重命名为 config/gitlab-sdk.php。没有进行数组更改。
  • 命名空间从 Glamstack\Okta 更改为 GitlabIt\Okta。
  • 从修改版的 Calendar Versioning (CalVer) 更改为使用 Semantic Versioning (SemVer)
  • 许可证从 Apache 2.0 更改为 MIT。

迁移步骤

  1. 从 composer.json 中删除 glamstack/okta-sdk,并添加 "gitlab-it/okta-sdk": "^3.0",然后运行 composer update。
  2. 导航到您的 config 目录,并将 glamstack-okta.php 重命名为 okta-sdk.php。
  3. 在整个代码库中执行从 Glamstack\Okta 到 GitlabIt\Okta 的查找和替换。
  4. 执行从 config('glamstack-okta.) 到 config('okta-sdk.) 的查找和替换。

维护者

名称GitLab 处理
Dillon Wheeler@dillonwheeler
Jeff Martin@jeffersonmartin

如何工作

您的 Okta 实例的 URL(例如 https://mycompany.okta.com)和 API 令牌在 config/okta-sdk.php 中指定,使用从 .env 文件继承的变量。

如果您的连接配置存储在数据库中并需要动态提供,则可以在 SDK 初始化期间通过传递数组到 connection_config 参数来覆盖 config/okta-sdk.php 配置文件。有关更多信息,请参阅 每个 API 调用的动态变量连接

我们不是为 API 文档中的每个端点提供一个方法,而是通过提供一个通用的 ApiClient,该客户端可以执行 GET、POST、PUT 和 DELETE 请求到您在 Okta API 文档 中找到的任何端点,并为您处理 API 响应、错误处理和分页。

此功能基于由Guzzle HTTP客户端驱动的Laravel HTTP客户端的简洁性,为Okta API响应提供“代码解析的最后一步”,从而提升开发者体验。

以下示例是入门指南。有关更多详细信息,请参阅下面的API请求API响应部分。

// Option 1. Initialize the SDK using the default connection
$okta_api = new \GitlabIt\Okta\ApiClient();

// Option 2. Initialize the SDK using a specific hard coded connection
$okta_api = new \GitlabIt\Okta\ApiClient('prod');

// Get a list of records
// https://developer.okta.com/docs/reference/api/groups/#list-groups
$groups = $okta_api->get('/groups');

// Search for records with a specific name
// https://developer.okta.com/docs/reference/core-okta-api/#filter
// https://developer.okta.com/docs/reference/api/groups/#list-groups-with-search
$groups = $okta_api->get('/groups', [
    'search' => 'profile.name eq "Hack the Planet Engineers"'
]);

// https://developer.okta.com/docs/reference/api/users/#list-users-with-search
$users = $okta_api->get('/users', [
    'search' => 'profile.firstName eq "Dade"'
]);

// Get a specific record
// https://developer.okta.com/docs/reference/api/groups/#get-group
$record = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');

// Create a group
// https://developer.okta.com/docs/reference/api/groups/#add-group
$record = $okta_api->post('/groups', [
    'name' => 'Hack the Planet Engineers',
    'description' => 'This group contains engineers that have proven they are elite enough to hack the Gibson.'
]);

// Update a group
// https://developer.okta.com/docs/reference/api/groups/#update-group
$group_id = '0oa1ab2c3D4E5F6G7h8i';
$record = $okta_api->put('/groups/' . $group_id, [
    'description' => 'This group contains engineers that have liberated the garbage files.'
]);

// Delete a group
// https://developer.okta.com/docs/reference/api/groups/#remove-group
$group_id = '0oa1ab2c3D4E5F6G7h8i';
$record = $okta_api->delete('/groups/' . $group_id);

安装

要求

要求版本
PHP^8.0, ^8.1, ^8.2
Laravel^8.0, ^9.0, ^10.0

添加Composer包

仍在使用glamstack/okta-sdk(v2.x)?请参阅v3.0升级指南以了解升级到gitlab-it/okta-sdk:^3.0的说明。

composer require gitlab-it/okta-sdk:^3.0

如果您正在为此包做出贡献,请参阅CONTRIBUTING.md以了解配置本地Composer包(带符号链接)的说明。

发布配置文件

此命令应在您第一次使用此包时运行。如果您再次运行此命令且未备份旧配置文件,则将覆盖任何自定义配置。

php artisan vendor:publish --tag=okta-sdk

环境配置

环境变量

要开始,请将以下变量添加到您的.env文件中。您可以在文件的任何位置添加这些变量(每行一个),也可以将它们添加到文件底部(由您选择)。

# .env

OKTA_DEFAULT_CONNECTION="dev"
OKTA_DEV_BASE_URL=""
OKTA_DEV_API_TOKEN=""
OKTA_PREVIEW_BASE_URL=""
OKTA_PREVIEW_API_TOKEN=""
OKTA_PROD_BASE_URL=""
OKTA_PROD_API_TOKEN=""

连接密钥

我们使用连接密钥(也称为实例密钥)的概念,它引用config/okta-sdk.php中的配置数组,允许您为每个连接到Okta API的连接配置基本URL、API令牌.env变量名称和日志通道,并为该连接提供一个唯一的名称。

每个连接都关联有不同的基本URL和API令牌。

我们已经预配置了devpreviewprod密钥,以便您无需修改config/okta-sdk.php文件即可快速入门。

# config/okta-sdk.php

'connections' => [
    'prod' => [
        'base_url' => env('OKTA_PROD_BASE_URL'),
        'api_token' => env('OKTA_PROD_API_TOKEN'),
        'log_channels' => ['single'],
    ],
    'preview' => [
       'base_url' => env('OKTA_PREVIEW_BASE_URL'),
       'api_token' => env('OKTA_PREVIEW_API_TOKEN'),
       'log_channels' => ['single'],
    ],
    'dev' => [
       'base_url' => env('OKTA_DEV_BASE_URL'),
       'api_token' => env('OKTA_DEV_API_TOKEN'),
       'log_channels' => ['single'],
    ],
]

如果您有特殊用例,需要额外的Okta实例或超出预配置范围的最小权限服务帐户,您可以在config/okta-sdk.php中添加额外的连接密钥(您选择的名称),并使用其他连接作为示例创建新的基本URL和API令牌变量。

基本URL

每个Okta客户都为其公司提供了一个子域。这有时被称为租户或API文档中的${yourOktaDomain}。这应在prod连接密钥或使用.env变量中配置(见下文)。

https://mycompany.okta.com

如果您可以访问Okta预览沙盒/测试/预发布实例,您还可以在preview连接密钥中配置基本URL和API令牌。

https://mycompany.oktapreview.com

如果您拥有免费的Okta开发者帐户,您可以在dev密钥中配置基本URL和API令牌。

https://dev-12345678.okta.com

API令牌

请参阅Okta文档以创建API令牌。请注意,API令牌使用它所属用户的权限,因此对于生产应用程序用例,最好创建服务帐户(机器人)用户。任何30天内未进行API调用的令牌都会自动过期。

安全警告:请勿将API令牌添加到config/okta-sdk.php文件中,以避免将秘密提交到您的仓库(秘密泄露)。所有API令牌应定义在.env文件中,该文件包含在.gitignore中,并且不会被提交到您的仓库。对于高级用例,您可以将变量存储在CI/CD变量或秘密保险库中(此处未提供文档)。

内部开发人员备注:初始化SDK时,API密钥将自动以SSWS前缀。

默认全局连接

默认情况下,SDK将使用dev连接密钥处理您应用程序中所有的API调用,除非您使用OKTA_DEFAULT_CONNECTION变量覆盖默认连接到不同的连接密钥

如果您刚开始使用,建议您将其保持为dev,这样您就可以使用您的Okta开发者帐户使用此SDK。在将应用程序部署到预发布或生产环境时,您可以稍后将其更改为previewprod

OKTA_DEFAULT_CONNECTION="dev"

默认连接密钥变量

您可以使用任何组合的prodpreviewdev连接密钥。请确保用您的URL替换mycompany

只需保留任何未使用的连接为空或从您的.env文件中删除它们。您以后可以随时添加它们。

# .env

OKTA_DEFAULT_CONNECTION="dev"
OKTA_DEV_BASE_URL="https://dev-12345678.okta.com"
OKTA_DEV_API_TOKEN="YourDevT0k3nG0esH3r3"
OKTA_PREVIEW_BASE_URL="https://mycompany.oktapreview.com"
OKTA_PREVIEW_API_TOKEN="YourPreviewT0k3nG0esH3r3"
OKTA_PROD_BASE_URL="https://mycompany.okta.com"
OKTA_PROD_API_TOKEN="YourProdT0k3nG0esH3r3"

初始化API连接

要使用默认连接,您不需要向ApiClient提供连接密钥。这允许您在不硬编码连接密钥的情况下构建应用程序,并简单地更新.env变量。

// Initialize the SDK
$okta_api = new \GitlabIt\Okta\ApiClient();

// Get a list of records
// https://developer.okta.com/docs/reference/api/groups/#list-groups
$groups = $okta_api->get('/groups');

按API调用使用特定连接

如果您在使用ApiClient时想要使用与OKTA_DEFAULT_CONNECTION .env变量不同的特定连接密钥,则可以将已配置在config/okta-sdk.php中的任何连接密钥作为ApiClient的第一个构造参数传递。

// Initialize the SDK
$okta_api = new \GitlabIt\Okta\ApiClient('preview');

// Get a list of records
// https://developer.okta.com/docs/reference/api/groups/#list-groups
$groups = $okta_api->get('/groups');

如果您遇到错误,请确保API令牌已添加到您的.env文件中的OKTA_{CONNECTION_KEY}_API_TOKEN变量。请注意,Okta API令牌在30天不活动后自动过期,因此您可能已经一段时间没有运行devpreview API调用了,并将收到未授权的错误消息。

按API调用动态变量连接

如果未在config/okta-sdk.php配置文件中使用连接密钥,则可以将包含自定义连接配置的数组作为第二个参数传递。

// Initialize the SDK
$okta_api = new \GitlabIt\Okta\ApiClient(null, [
    'base_url' => 'https://mycompany.okta.com',
    'api_token' => '00fJq-ABCDEFGhijklmn0pQrsTu-Vw-xyZ12345678'
    'log_channels' => ['single', 'okta-sdk']
]);

安全警告:不要将硬编码的API令牌提交到代码库中。这只应在使用存储在数据库中的动态变量时使用。

以下是如何使用您自己的Eloquent模型存储Okta实例并向SDK提供它们的示例。您可以选择是否在应用程序逻辑中提供动态日志通道,或者硬编码在应用程序中使用SDK配置的通道。

// The $okta_instance_id is provided dynamically in the controller or service request

// Get Okta Instance
// Disclaimer: This is an example and is not a feature of the SDK.
$okta_instance = \App\Models\OktaInstance::query()
    ->where('id', $okta_instance_id)
    ->firstOrFail();

// Initialize the SDK
$okta_api = new \GitlabIt\Okta\ApiClient(null, [
    'base_url' => $okta_instance->api_base_url,
    'api_token' => decrypt($okta_instance->api_token),
    'log_channels' => ['single', 'okta-sdk']
]);

日志配置

默认情况下,我们使用配置在应用程序的config/logging.php文件中的single通道处理所有日志。这会将所有Okta API日志消息发送到storage/logs/laravel.log文件。

您可以在config/okta-sdk.php中配置此SDK的日志通道。您可以在auth.log_channels中配置初始身份验证的日志通道。您还可以在connections.{connection_key}.log_channels中为每个连接配置日志通道。

// config/okta-sdk.php

   'auth' => [
        'log_channels' => ['single'],
    ],

    'connections' => [
        'prod' => [
            // ...
            'log_channels' => ['single'],
        ],
        'preview' => [
            // ...
            'log_channels' => ['single'],
        ],
        'dev' => [
            // ...
            'log_channels' => ['single'],
        ],
    ]

自定义日志通道

如果您想将Okta API日志单独保存到一个更容易进行故障排除的日志文件中,而不包含无关的日志消息,您可以创建一个自定义日志通道。例如,我们建议使用okta-sdk的值,但您可以选择任何您喜欢的名称。您还可以为您的每个Okta连接创建一个日志通道(例如okta-sdk-prodokta-sdk-preview)。

将自定义日志通道添加到config/logging.php

// config/logging.php

    'channels' => [

        // Add anywhere in the `channels` array

        'okta-sdk' => [
            'name' => 'okta-sdk',
            'driver' => 'single',
            'level' => 'debug',
            'path' => storage_path('logs/okta-sdk.log'),
        ],
    ],

更新channels.stack.channels数组,包括您自定义通道的数组键(例如okta-sdk)。请确保将okta-sdk添加到现有的数组值中,而不是替换现有的值。

// config/logging.php

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['single','slack', 'okta-sdk'],
            'ignore_exceptions' => false,
        ],
    ],

最后,更新config/okta-sdk.php配置。

// config/okta-sdk.php

   'auth' => [
        'log_channels' => ['okta-sdk'],
    ],

您可以通过重复这些配置步骤来自定义任何连接键。

安全最佳实践

不共享令牌

不要使用您已经为其他目的创建的API令牌。您应该为每个用例生成一个新的API令牌。

这在安全事件期间非常有用,当需要在受损害的系统上吊销密钥时,您不希望使用相同用户或服务账户的其他系统受到影响,因为它们使用的是未吊销的不同密钥。

API令牌存储

不要将您的API令牌添加到config/okta-sdk.php文件中,以避免将其提交到您的仓库(泄露机密)。

所有API令牌都应定义在.env文件中,该文件包含在.gitignore中,并且不会提交到您的仓库。

建议将每个API令牌的副本存储在您首选的密码管理器(例如1Password,LastPass等)和/或密钥保管库(例如HashiCorp Vault,Ansible等)中。

API令牌权限

不同的Okta API操作需要不同的管理员权限级别。API令牌继承创建它们的管理员账户的权限级别。因此,创建一个用于创建API令牌的服务账户是良好的实践,这样您就可以将特定的权限级别分配给令牌。有关管理员账户类型和每个的特定权限,请参阅管理员文档

来源:Okta文档 - 创建API令牌

最小权限

如果您需要使用不同的API密钥来实现最小权限的网络安全,您可以通过使用任何符合您需求的名称(例如prod_scope1prod_scope2prod_scope3)将相同的Okta基本URL多次添加到config/okta-sdk.php中,并使用不同的连接键来自定义config/okta-sdk.php

您可以根据需要自定义.env变量名称。SDK使用config/okta-sdk.php文件中的值,并不直接使用任何.env变量。

'prod_read_only' => [
    'base_url' => env('OKTA_PROD_BASE_URL'),
    'api_token' => env('OKTA_PROD_READ_ONLY_API_TOKEN'),
    'log_channels' => ['single']
],

'prod_super_admin' => [
    'base_url' => env('OKTA_PROD_BASE_URL'),
    'api_token' => env('OKTA_PROD_SUPER_ADMIN_API_TOKEN'),
    'log_channels' => ['single']
],

'prod_group_admin' => [
    'base_url' => env('OKTA_PROD_BASE_URL'),
    'api_token' => env('OKTA_PROD_GROUP_ADMIN_API_TOKEN'),
    'log_channels' => ['single']
],

您只需在调用SDK时提供连接键即可。

$okta_api = new \GitlabIt\Okta\ApiClient('prod_read_only');
$groups = $okta_api->get('/groups')->object();

如果您需要更多灵活性,请使用动态变量连接

API请求

您可以向Okta REST API文档中的任何资源端点发出API请求。

内联使用

// Initialize the SDK
$okta_api = new \GitlabIt\Okta\ApiClient('prod');

GET请求

端点在/api/v1之后以/开头。Okta API文档提供了完整的端点,因此复制粘贴端点时请删除/api/v1

以下示例的参考请参见列出所有组 API文档。

使用SDK时,您可以使用端点/groups作为第一个参数,使用get()方法。

$okta_api->get('/groups');

您还可以使用变量或数据库模型获取数据,用于构建您的端点。

$endpoint = '/groups';
$records = $okta_api->get($endpoint);

以下是使用端点的更多示例。

// Get a list of records
// https://developer.okta.com/docs/reference/api/groups/#list-groups
$records = $okta_api->get('/groups');

// Get a specific record
// https://developer.okta.com/docs/reference/api/groups/#get-group
$record = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');

// Get a specific record using a variable
// This assumes that you have a database column named `api_group_id` that
// contains the string with the Okta ID `0oa1ab2c3D4E5F6G7h8i`.
$okta_group = App\Models\OktaGroup::where('id', $id)->firstOrFail();
$api_group_id = $okta_group->api_group_id;
$record = $okta_api->get('/groups/' . $api_group_id);

带有查询字符串参数的GET请求

get() 方法的第二个参数是一个可选的参数数组,该数组由 SDK 和 Laravel HTTP 客户端 解析,并自动添加 ?& 转换为查询字符串。

// Search for records with a specific name
// https://developer.okta.com/docs/reference/api/groups/#list-groups-with-search
$records = $okta_api->get('/groups', [
    'search' => 'profile.name eq "Hack the Planet Engineers"'
]);

// This will parse the array and render the query string
// https://mycompany.okta.com/api/v1/groups?search=profile.name+eq+%22Hack%20the&%20Planet%20Engineers%22
// List all deprovisioned users
// https://developer.okta.com/docs/reference/api/users/#list-users-with-search
$records = $okta_api->get('/users', [
    'search' => 'status eq "DEPROVISIONED"'
]);

// This will parse the array and render the query string
// https://mycompany.okta.com/api/v1/groups?search=status+eq+%22DEPROVISIONED%22
// Get all users for a specific department
// https://developer.okta.com/docs/reference/api/users/#list-users-with-search
$records = $okta_api->get('/users', [
    'search' => 'profile.department eq "Engineering"'
]);

// This will parse the array and render the query string
// https://mycompany.okta.com/api/v1/groups?search=profile.department+eq+%22Engineering%22

POST 请求

post() 方法与 get() 请求几乎相同,都使用参数数组,但是参数通过 application/json 内容类型作为表单数据传递,而不是在 URL 中作为查询字符串。这是行业标准,并非特定于 SDK。

您可以在 Laravel HTTP 客户端文档 中了解更多关于请求数据的信息。

// Create a group
// https://developer.okta.com/docs/reference/api/groups/#add-group
$record = $okta_api->post('/groups', [
    'name' => 'Hack the Planet Engineers',
    'description' => 'This group contains engineers that have proven they are elite enough to hack the Gibson.'
]);

PUT 请求

put() 方法用于更新现有记录(类似于 PATCH 请求)。您需要确保您想要更新的记录的 ID 已提供在第一个参数(URI)中。

在大多数应用程序中,这将是从您的数据库或其他位置获取的变量,而不是硬编码的。

// Update a group
// https://developer.okta.com/docs/reference/api/groups/#update-group
$group_id = '0oa1ab2c3D4E5F6G7h8i';
$record = $okta_api->put('/groups/' . $group_id, [
    'description' => 'This group contains engineers that have liberated the garbage files.'
]);

DELETE 请求

delete() 方法用于基于您提供的 ID 销毁资源的操作。

请注意,delete() 方法的状态码可能会根据供应商的不同而不同(例如,200、201、202、204 等)。Okta 的 API 将返回 204 状态码以表示成功删除的资源。您应该使用 $response->status->successful 布尔值来检查结果。

// Delete a group
// https://developer.okta.com/docs/reference/api/groups/#remove-group
$group_id = '0oa1ab2c3D4E5F6G7h8i';
$record = $okta_api->delete('/groups/' . $group_id);

类方法

上面的示例展示了适用于大多数用例的基本内联用法。如果您更喜欢使用类和构造函数,下面的示例将提供有用的示例。

<?php

use GitlabIt\Okta\ApiClient;

class OktaGroupService
{
    protected $okta_api;

    public function __construct($connection_key = null)
    {
        // If connection key is null, use the default connection key
        if(! $connection_key) {
            $connection_key = config('okta-sdk.auth.default_connection');
        }

        $this->$okta_api = new ApiClient($connection_key);
    }

    public function listGroups($query = [])
    {
        $groups = $this->$okta_api->get('/groups', $query);

        return $groups->object;
    }

    public function getGroup($id, $query = [])
    {
        $group = $this->$okta_api->get('/groups/'.$id, $query);

        return $group->object;
    }

    public function storeGroup($request_data)
    {
        $group = $this->$okta_api->post('/groups', $request_data);

        // To return an object with the newly created group
        return $group->object;

        // To return the ID of the newly created group
        // return $group->object->id;

        // To return the status code of the form request
        // return $group->status->code;

        // To return a bool with the status of the form request
        // return $group->status->successful;

        // To throw an exception if the request fails
        // throw_if(!$group->status->successful, new \Exception($group->error->message, $group->status->code));

        // To return the entire API response with the object, json, headers, and status
        // return $group;
    }

    public function updateGroup($id, $request_data)
    {
        $group = $this->$okta_api->put('/groups/'.$id, $request_data);

        // To return an object with the updated group
        return $group->object;

        // To return a bool with the status of the form request
        // return $group->status->successful;
    }

    public function deleteGroup($id)
    {
        $group = $this->$okta_api->delete('/groups/'.$id);

        return $group->status->successful;
    }
}

API 响应

此 SDK 使用 GitLab IT 标准 API 响应格式。

// API Request
$group = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');

// API Response
$group->headers; // array
$group->json; // json
$group->object; // object
$group->status; // object
$group->status->code; // int (ex. 200)
$group->status->ok; // bool
$group->status->successful; // bool
$group->status->failed; // bool
$group->status->serverError; // bool
$group->status->clientError; // bool

API 响应头

由于键使用连字符,可能会与访问键和值的语法冲突,因此将头返回为数组而不是对象。

$group = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');
$group->headers;
[
    "Date" => "Sun, 30 Jan 2022 01:11:44 GMT",
    "Content-Type" => "application/json",
    "Transfer-Encoding" => "chunked",
    "Connection" => "keep-alive",
    "Server" => "nginx",
    "Public-Key-Pins-Report-Only" => "pin-sha256="REDACTED="; pin-sha256="REDACTED="; pin-sha256="REDACTED="; pin-sha256="REDACTED="; max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly"",
    "Vary" => "Accept-Encoding",
    "x-okta-request-id" => "A1b2C3D4e5@f6G7H8I9j0k1L2M3",
    "x-xss-protection" => "0",
    "p3p" => "CP="HONK"",
    "x-rate-limit-limit" => "1000",
    "x-rate-limit-remaining" => "998",
    "x-rate-limit-reset" => "1643505155",
    "cache-control" => "no-cache, no-store",
    "pragma" => "no-cache",
    "expires" => "0",
    "content-security-policy" => "default-src 'self' mycompany.okta.com *.oktacdn.com; connect-src 'self' mycompany.okta.com mycompany-admin.okta.com *.oktacdn.com *.mixpanel.com *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com mycompany.kerberos.okta.com https://oinmanager.okta.com data:; script-src 'unsafe-inline' 'unsafe-eval' 'self' mycompany.okta.com *.oktacdn.com; style-src 'unsafe-inline' 'self' mycompany.okta.com *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com; frame-src 'self' mycompany.okta.com mycompany-admin.okta.com login.okta.com; img-src 'self' mycompany.okta.com *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com data: blob:; font-src 'self' mycompany.okta.com data: *.oktacdn.com fonts.gstatic.com",
    "expect-ct" => "report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0",
    "x-content-type-options" => "nosniff",
    "Strict-Transport-Security" => "max-age=315360000; includeSubDomains",
    "set-cookie" => "sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ autolaunch_triggered=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ JSESSIONID=E07ED763D2ADBB01B387772B9FB46EBF; Path=/; Secure; HttpOnly"
]

API 响应特定头

$content_type = $group->headers['Content-Type'];
application/json

API 响应 JSON

$group = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');
$group->json;
"{"id":0oa1ab2c3D4E5F6G7h8i,"name":"Hack the Planet Engineers","state":"ACTIVE"}"

API 响应对象

$group = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');
$group->object;
{
  +"id": "0oa1ab2c3D4E5F6G7h8i"
  +"description": "This group contains engineers that have proven they are elite enough to hack the Gibson."
  +"name": "Hack the Planet Engineers"
  +"state": "ACTIVE"
}

访问单个记录值

您可以使用对象符号访问这些变量。这是处理 API 响应最常见的用例。

$group = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i')->object;

dd($group->name);
Hack the Planet Engineers

遍历记录

如果您有一个包含多个对象的数组,您可以通过记录进行遍历。

$groups = $okta_api->get('/groups')->object;

foreach($groups as $group) {

    dd($group->name);
}
Hack the Planet Engineers

缓存响应

SDK 不使用缓存,以避免您在控制可以缓存的端点时遇到任何约束。

您可以在进行 API 调用时将端点包裹在缓存外观中。您可以在 Laravel 缓存 文档中了解更多信息。

use Illuminate\Support\Facades\Cache;

$okta_api = new \GitlabIt\Okta\ApiClient($connection_key);

$groups = Cache::remember('okta_groups', now()->addHours(12), function () use ($okta_api) {
    return $okta_api->get('/groups')->object;
});

foreach($groups as $group) {
    dd($group->name);
}

当获取特定 ID 或传递其他参数时,请确保将变量传递给 use($var1, $var2)

$okta_api = new \GitlabIt\Okta\ApiClient($connection_key);
$group_id = '0oa1ab2c3D4E5F6G7h8i';

$groups = Cache::remember('okta_group_' . $group_id, now()->addHours(12), function () use ($okta_api, $group_id) {
    return $okta_api->get('/groups/' . $group_id)->object;
});

API 响应状态

请参阅 Laravel HTTP 客户端文档 了解有关不同状态布尔值的更多信息。

$group = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');
$group->status;
{
  +"code": 200
  +"ok": true
  +"successful": true
  +"failed": false
  +"serverError": false
  +"clientError": false
}

API 响应状态码

$group = $okta_api->get('/groups/0oa1ab2c3D4E5F6G7h8i');
$group->status->code;
200

错误处理

所有 Okta API 响应都会从 SDK 返回到您的应用程序,如果发生错误则不会抛出异常。如果您需要检查成功的结果,请使用 $response->status->successful 布尔值。

Okta 错误代码

如果请求未成功,将在对象中返回标准的 Okta 错误代码 响应。

+"object": {#1344
    +"errorCode": "E0000003"
    +"errorSummary": "The request body was not well-formed."
    +"errorLink": "E0000003"
    +"errorId": "<string>"
    +"errorCauses": []
}
+"status": {#1369
    +"code": 400
    +"ok": false
    +"successful": false
    +"failed": true
    +"serverError": false
    +"clientError": true
}

捕获状态码

$group = $this->$okta_api->post('/groups', $request_data);

您可以通过返回布尔值或HTTP状态码来获取表单请求的状态。请参阅API响应状态了解可以返回的其他属性。

return $group->status->successful;
return $group->status->code;

您还可以使用\Exception或您应用中的自定义异常来抛出异常。

throw_if(!$group->status->successful, new \Exception($group->error->message, $group->status->code));

您还可以使用条件语句来处理成功和失败的响应。

if($group->status->successful) {
    dd('Group Saved - ID ' . $group->object->id);
} else {
    dd('Group Could Not Be Saved - Reason: ' . $group->object->errorSummary);
}

如果Guzzle抛出\Illuminate\Http\Client\RequestException异常,则不会返回objectjson属性,并将包含更多异常细节的错误对象包含在内,以便您能够优雅地处理错误。

{
+"error": {
    +"code": "<string>"
    +"message": "<string>"
    +"reference": "<string>"
}
+"status": {
    +"code": 400
    +"ok": false
    +"successful": false
    +"failed": true
    +"serverError": false
    +"clientError": true
}

如果存在SDK配置或认证错误,将抛出一个包含错误消息的新\Exception

请参阅下面的日志输出了解SDK如何处理配置错误。

请参阅Okta API错误代码文档了解可以返回的代码。更多关于每个资源端点的信息可以在相应的API文档页面找到。

日志输出

错误消息的输出显示在README中,以便搜索引擎可以索引这些消息以支持开发者调试。

连接配置和认证

当首次调用ApiClient类时,将对Okta连接的/org端点执行API连接测试。此端点需要认证,因此这验证了API令牌是否有效。

$okta_api = new \GitlabIt\Okta\ApiClient('prod');
$okta_api->get('/groups');

有效的API令牌

[2022-01-31 23:38:56] local.INFO: GET 200 https://gitlab.okta.com/api/v1/org {"api_endpoint":"https://gitlab.okta.com/api/v1/org","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"prod","message":"GET 200 https://gitlab.okta.com/api/v1/org","event_type":"okta-api-response-info","okta_request_id":"YfhzENHYyWivKath4UvZhAAAAt8","rate_limit_remaining":"998","status_code":200}

[2022-01-31 23:38:56] local.INFO: GET 200 https://gitlab.okta.com/api/v1/groups {"api_endpoint":"https://gitlab.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"prod","message":"GET 200 https://gitlab.okta.com/api/v1/groups","event_type":"okta-api-response-info","okta_request_id":"YfhzEC100RhpyNJdV3sEiAAABmQ","rate_limit_remaining":"499","status_code":200}

缺少API令牌

[2022-01-31 23:40:26] local.CRITICAL: The API token for this Okta connection key is not defined in your `.env` file. The variable name for the API token can be found in the connection configuration in `config/okta-sdk.php`. Without this API token, you will not be able to performed authenticated API calls. {"event_type":"okta-api-config-missing-error","class":"GitlabIt\\Okta\\ApiClient","status_code":"501","message":"The API token for this Okta connection key is not defined in your `.env` file. The variable name for the API token can be found in the connection configuration in `config/okta-sdk.php`. Without this API token, you will not be able to performed authenticated API calls.","connection_key":"prod"}

无效的API令牌

[2022-01-31 23:41:01] local.NOTICE: GET 401 https://gitlab.okta.com/api/v1/org {"api_endpoint":"https://gitlab.okta.com/api/v1/org","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"prod","event_type":"okta-api-response-client-error","message":"GET 401 https://gitlab.okta.com/api/v1/org","okta_request_id":"Yfhzjforta34Ho5ON3SqeQAADlY","okta_error_causes":[],"okta_error_code":"E0000011","okta_error_id":"oaepVpdl1ZQQO-U7Ki-e_-wHQ","okta_error_link":"E0000011","okta_error_summary":"Invalid token provided","rate_limit_remaining":null,"status_code":401}

ApiClient构造API令牌

$okta_api = new \GitlabIt\Okta\ApiClient('prod', '00fJq-A1b2C3d4E5f6G7h8I9J0-Kl-mNoPqRsTuVwx');
$okta_api->get('/groups');
[2022-01-31 23:42:04] local.NOTICE: The Okta API token for these API calls is using an API token that was provided in the ApiClient construct method. The API token that might be configured in the `.env` file is not being used. {"event_type":"okta-api-config-override-notice","class":"GitlabIt\\Okta\\ApiClient","status_code":"203","message":"The Okta API token for these API calls is using an API token that was provided in the ApiClient construct method. The API token that might be configured in the `.env` file is not being used.","okta_connection":"prod"}

[2022-01-31 23:42:04] local.INFO: GET 200 https://gitlab.okta.com/api/v1/org {"api_endpoint":"https://gitlab.okta.com/api/v1/org","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"prod","message":"GET 200 https://gitlab.okta.com/api/v1/org","event_type":"okta-api-response-info","okta_request_id":"YfhzzDq5PIe70D1-C8HRHwAACdg","rate_limit_remaining":"999","status_code":200}

[2022-01-31 23:42:05] local.INFO: GET 200 https://gitlab.okta.com/api/v1/groups {"api_endpoint":"https://gitlab.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"prod","message":"GET 200 https://gitlab.okta.com/api/v1/groups","event_type":"okta-api-response-info","okta_request_id":"YfhzzK6LrJwm1XbvpcPnGwAAA6g","rate_limit_remaining":"499","status_code":200}

缺少连接配置

[2022-01-31 23:43:03] local.CRITICAL: The Okta connection key is not defined in `config/okta-sdk.php` connections array. Without this array config, there is no URL or API token to connect with. {"event_type":"okta-api-config-missing-error","class":"GitlabIt\\Okta\\ApiClient","status_code":"501","message":"The Okta connection key is not defined in `config/okta-sdk.php` connections array. Without this array config, there is no URL or API token to connect with.","connection_key":"test"}

缺少基础URL

[2022-01-31 23:44:04] local.CRITICAL: The Base URL for this Okta connection key is not defined in `config/okta-sdk.php` or `.env` file. Without this configuration (ex. `https://mycompany.okta.com`), there is no URL to perform API calls with. {"event_type":"okta-api-config-missing-error","class":"GitlabIt\\Okta\\ApiClient","status_code":"501","message":"The Base URL for this Okta connection key is not defined in `config/okta-sdk.php` or `.env` file. Without this configuration (ex. `https://mycompany.okta.com`), there is no URL to perform API calls with.","connection_key":"test"}

速率限制

大多数速率限制是由于具有大响应的分页(例如,/users端点)而触发的。如果您有大量数据集,您可能需要考虑使用search查询来过滤结果以减少结果数量。

如果Okta端点的速率限制超过,将抛出一个包含消息“Okta API速率限制超过”的新\Exception。日志将显示当速率限制剩余10%时的速率限制警告。

大多数速率限制是每分钟一次,因此您可能需要在代码中添加sleep(#)来减慢请求速度,或者考虑更高效的API请求方式。一些端点允许您设置自定义每页limit值。最大值可以在端点的Okta API文档中找到(根据端点而异)。

[2023-02-26 18:04:18] local.INFO: GET 200 https://dev-12345678.okta.com/api/v1/groups {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","message":"GET 200 https://dev-12345678.okta.com/api/v1/groups","event_type":"okta-api-response-info","okta_request_id":"<string>","rate_limit_remaining":"5","status_code":200}
[2023-02-26 18:04:18] local.WARNING: 10 percent of Okta API rate limit remaining {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","event_type":"okta-api-rate-limit-approaching","message":"10 percent of Okta API rate limit remaining","okta_request_id":"<string>","okta_rate_limit_remaining":"5","okta_rate_limit_limit":"50","okta_rate_limit_percent":10.0,"status_code":200}

[2023-02-26 18:04:18] local.INFO: GET 200 https://dev-12345678.okta.com/api/v1/groups {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","message":"GET 200 https://dev-12345678.okta.com/api/v1/groups","event_type":"okta-api-response-info","okta_request_id":"<string>","rate_limit_remaining":"4","status_code":200}
[2023-02-26 18:04:18] local.WARNING: 8 percent of Okta API rate limit remaining {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","event_type":"okta-api-rate-limit-approaching","message":"8 percent of Okta API rate limit remaining","okta_request_id":"<string>","okta_rate_limit_remaining":"4","okta_rate_limit_limit":"50","okta_rate_limit_percent":8.0,"status_code":200}

[2023-02-26 18:04:19] local.INFO: GET 200 https://dev-12345678.okta.com/api/v1/groups {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","message":"GET 200 https://dev-12345678.okta.com/api/v1/groups","event_type":"okta-api-response-info","okta_request_id":"<string>","rate_limit_remaining":"3","status_code":200}
[2023-02-26 18:04:19] local.WARNING: 6 percent of Okta API rate limit remaining {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","event_type":"okta-api-rate-limit-approaching","message":"6 percent of Okta API rate limit remaining","okta_request_id":"<string>","okta_rate_limit_remaining":"3","okta_rate_limit_limit":"50","okta_rate_limit_percent":6.0,"status_code":200}

[2023-02-26 18:04:19] local.INFO: GET 200 https://dev-12345678.okta.com/api/v1/groups {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","message":"GET 200 https://dev-12345678.okta.com/api/v1/groups","event_type":"okta-api-response-info","okta_request_id":"<string>","rate_limit_remaining":"2","status_code":200}
[2023-02-26 18:04:19] local.WARNING: 4 percent of Okta API rate limit remaining {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","event_type":"okta-api-rate-limit-approaching","message":"4 percent of Okta API rate limit remaining","okta_request_id":"<string>","okta_rate_limit_remaining":"2","okta_rate_limit_limit":"50","okta_rate_limit_percent":4.0,"status_code":200}

[2023-02-26 18:04:20] local.INFO: GET 200 https://dev-12345678.okta.com/api/v1/groups {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","message":"GET 200 https://dev-12345678.okta.com/api/v1/groups","event_type":"okta-api-response-info","okta_request_id":"<string>","rate_limit_remaining":"1","status_code":200}
[2023-02-26 18:04:20] local.WARNING: 2 percent of Okta API rate limit remaining {"api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","api_method":"GET","class":"GitlabIt\\Okta\\ApiClient","connection_key":"dev","event_type":"okta-api-rate-limit-approaching","message":"2 percent of Okta API rate limit remaining","okta_request_id":"<string>","okta_rate_limit_remaining":"1","okta_rate_limit_limit":"50","okta_rate_limit_percent":2.0,"status_code":200}

[2023-02-26 18:04:20] local.ERROR: Okta API rate limit exceeded {"event_type":"okta-api-rate-limit-exceeded","class":"GitlabIt\\Okta\\ApiClient","status_code":200,"message":"Okta API rate limit exceeded","api_method":"GET","api_endpoint":"https://dev-12345678.okta.com/api/v1/groups","connection_key":"dev","okta_request_id":"<string>","okta_rate_limit_remaining":"1","okta_rate_limit_limit":"50"}

本例中的异常与最后一个日志条目Okta API速率限制超过同时抛出。

问题跟踪和错误报告

免责声明:这不是由 GitLab 或 Okta 产品和开发团队维护的官方包。这是一个我们在 GitLab IT 部门使用的内部工具,我们将其开源作为我们公司价值观的一部分。

请自行承担风险,并为我们遇到的任何错误创建合并请求。

我们不维护社区功能请求路线图,但我们邀请您贡献力量,我们很高兴审查您的合并请求。

对于GitLab团队成员,请在此处创建问题:gitlab-it/okta-sdk(公开)或gitlab-com/it/dev/issue-tracker(机密)。

贡献

请参阅CONTRIBUTING.md了解如何贡献的更多信息。