gitlab-it/okta-sdk

此软件包已被废弃且不再维护。作者建议使用 provisionesta/okta-api-client 软件包。

Okta API SDK for Laravel

维护者

详细信息

gitlab.com/gitlab-it/okta-sdk

3.0.1 2023-02-27 16:12 UTC

This package is auto-updated.

Last update: 2023-12-30 21:51:39 UTC


README

[[目录]]

概述

Okta SDK 是由 GitLab IT Engineering 开发的一个开源 Composer 包,用于在内部 Laravel 应用中连接到 Okta,以便进行用户、组、应用和其他相关功能的配置和取消配置。

免责声明:这不是 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,它可以对您在 Okta API 文档 中找到的任何端点执行 GETPOSTPUTDELETE 请求,并为您处理 API 响应、错误处理和分页。

此功能基于由Guzzle HTTP客户端(见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', [
    'profile' => [
        '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, [
    'profile' => [
        '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中的配置数组,允许您配置Base URL、API Token .env变量名以及Okta API每个连接的日志通道,并为该连接提供一个唯一的名称。

每个连接都关联着不同的Base URL和API token。

我们已经预先配置了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中添加额外的连接键,使用您选择的名称,并使用其他连接作为示例创建新的Base URL和API token变量。

Base URL

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

https://mycompany.okta.com

如果您可以访问Okta的预览沙箱/测试/预发布实例,您也可以在preview连接键中配置Base URL和API token。

https://mycompany.oktapreview.com

如果您有免费的Okta开发者帐户,您也可以在dev键中配置Base URL和API token。

https://dev-12345678.okta.com

API Tokens

有关创建API token的说明,请参阅Okta文档。请注意,API token使用它所属用户的权限,因此对于生产应用程序用例,最好创建一个服务帐户(机器人)用户。任何30天内没有API调用的活动token将自动过期。

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

内部开发者说明:初始化SDK时,API密钥会自动以SSWS前缀。

默认全局连接

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

如果您刚开始使用,建议保留此设置为dev,以便您可以使用此SDK与您的Okta开发者账户一起使用。当您的应用程序部署到预发布或生产环境时,您可以稍后将其更改为previewprod

OKTA_DEFAULT_CONNECTION="dev"

默认连接密钥变量

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

简单地保留任何未使用的连接为空,或从您的.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提供connection key。这允许您在不硬编码连接密钥的情况下构建应用程序,只需更新.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调用使用特定的连接

如果您想使用与OKTA_DEFAULT_CONNECTION .env变量不同的特定connection key时使用ApiClient,可以将已在config/okta-sdk.php中配置的任何connection key作为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 Base URL多次添加到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调用动态变量连接

API请求

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

内联使用

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

GET请求

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

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

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

$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', [
    'profile' => [
        'name' => 'Hack the Planet Engineers',
        'description' => 'This group contains engineers that have proven they are elite enough to hack the Gibson.'
    ]
]);

PUT请求

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

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

// Update a group
// https://developer.okta.com/docs/reference/api/groups/#update-group
$group_id = '0oa1ab2c3D4E5F6G7h8i';
$record = $okta_api->put('/groups/' . $group_id, [
    'profile' => [
        '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