PHP code example of boldlygrow / okta-api-client

1. Go to this page and download the library: Download boldlygrow/okta-api-client library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

boldlygrow / okta-api-client example snippets


use BoldlyGrow\Okta\ApiClient;

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

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

// Search for users with a specific
// This example uses positional arguments
// https://developer.okta.com/docs/reference/api/users/#list-users-with-search
$users = ApiClient::get('users', [
    'search' => 'profile.firstName eq "Dade"'
]);

// Get a specific record
// https://developer.okta.com/docs/reference/api/groups/#get-group
$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');

// {
//     +"id": "0og1ab2c3D4E5F6G7h8i",
//     +"created": "2023-01-01T00:00:00.000Z",
//     +"lastUpdated": "2023-02-01T00:00:00.000Z",
//     +"lastMembershipUpdated": "2023-03-15T00:00:00.000Z",
//     +"type": "OKTA_GROUP",
//     +"profile": {
//         +"name": "Hack the Planet Engineers",
//         +"description": "This group contains engineers that have proven they are elite enough to hack the Gibson.",
//     },
// }

$group_name = $group->data->profile->name;
// Hack the Planet Engineers

// Create a group
// https://developer.okta.com/docs/reference/api/groups/#add-group
// This example uses named arguments
$group = ApiClient::post(
    uri: 'groups',
    data: [
        '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
// This example uses named arguments
$group_id = '00g1ab2c3D4E5F6G7h8i';
$group = ApiClient::put(
    uri: 'groups/' . $group_id,
    data: [
        '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 = '00g1ab2c3D4E5F6G7h8i';
ApiClient::delete('groups/' . $group_id);

OKTA_API_URL="https://mycompany.okta.com"
OKTA_API_CLIENT_ID="0oaExampleClientId"
OKTA_API_KEY_ID="the-registered-kid"

# Provide the signing key one of two ways (see Private Key Storage):
# a path to a PEM file (local development or a mounted secret) ...
OKTA_API_PRIVATE_KEY_PATH="/var/secrets/okta/private-key.pem"
# ... or an inline PEM string (often resolved from a secrets manager in code).
# OKTA_API_PRIVATE_KEY=

OKTA_API_URL="https://mycompany.okta.com"
OKTA_API_TOKEN="S3cr3tK3yG03sH3r3"

OKTA_API_URL="https://mycompany.okta.com"

OKTA_API_URL="https://mycompany.oktapreview.com"

OKTA_API_URL="https://dev-12345678.okta.com"

use BoldlyGrow\Okta\PublicKeyJwk;

$jwk = PublicKeyJwk::fromPemFile('okta-public-key.pem');
// or
$jwk = PublicKeyJwk::fromPem($publicKeyPemString);

use BoldlyGrow\Okta\ApiClient;

// Requests only okta.users.read for this call
$users = ApiClient::get(uri: 'users', scope: 'okta.users.read')->data;

// Requests only okta.groups.read for this call
$groups = ApiClient::get(uri: 'groups', scope: 'okta.groups.read')->data;

$response = ApiClient::get(uri: 'users/' . $id . '/appLinks', scope: 'okta.users.read okta.apps.read')->data;

use BoldlyGrow\Okta\ApiClient;
use Google\Cloud\SecretManager\V1\Client\SecretManagerServiceClient;
use Google\Cloud\SecretManager\V1\AccessSecretVersionRequest;

// Fetch the PEM in your own code. Swap this for Vault, AWS Secrets Manager,
// a mounted file, an HTTP call, etc. Cache it as appropriate for your app.
$client = new SecretManagerServiceClient();
$name = $client->secretVersionName('my-gcp-project', 'okta-mycompany-private-key', 'latest');
$privateKey = $client->accessSecretVersion(
    (new AccessSecretVersionRequest())->setName($name)
)->getPayload()->getData();

$connection = [
    'url' => 'https://mycompany.okta.com',
    'client_id' => '0oaExampleClientId',
    'key_id' => 'the-registered-kid',
    'private_key' => $privateKey,
];

$users = ApiClient::get(uri: 'users', scope: 'okta.users.read', connection: $connection)->data;

OKTA_API_PRIVATE_KEY_PATH="/var/secrets/okta/private-key.pem"

// config/okta-api-client.php (after php artisan vendor:publish --tag=okta-api-client)
'private_key' => app(\App\Support\OktaKeyResolver::class)->pem(),

OKTA_API_TOKEN="S3cr3tK3yG03sH3r3"

$connection = [
    'url' => 'https://mycompany.okta.com',
    'client_id' => '0oaExampleClientId',
    'key_id' => 'the-registered-kid',
    'private_key' => $privateKeyPem, // an inline PEM string (see Private Key Storage)
    // or, instead of private_key:
    // 'private_key_path' => '/var/secrets/okta/private-key.pem',
];

$connection = [
    'url' => 'https://mycompany.okta.com',
    'token' => 'S3cr3tK3yG03sH3r3',
];

use BoldlyGrow\Okta\ApiClient;

class MyClass
{
    private array $connection;

    public function __construct($connection)
    {
        $this->connection = $connection;
    }

    public function getGroup($group_id)
    {
        return ApiClient::get(
            connection: $this->connection,
            uri: 'groups/' . $group_id,
            scope: 'okta.groups.read'
        )->data;
    }
}

use BoldlyGrow\Okta\ApiClient;

class MyClass
{
    public function getGroup($group_id)
    {
        return ApiClient::get('groups/' . $group_id)->data;
    }
}

class MyClass
{
    public function getGroup($group_id)
    {
        return \BoldlyGrow\Okta\ApiClient::get('groups/' . $group_id)->data;
    }
}

ApiClient::get('groups');
ApiClient::post('groups', []);
ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');
ApiClient::put('groups/00g1ab2c3D4E5F6G7h8i', []);
ApiClient::delete('groups/00g1ab2c3D4E5F6G7h8i');

// Named Arguments
ApiClient::get(
    uri: 'groups'
);

// Positional Arguments
ApiClient::get('groups');

ApiClient::get('groups');

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

// Use variable for endpoint
$endpoint = 'groups';
$records = ApiClient::get($endpoint);

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

// 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 `0og1ab2c3D4E5F6G7h8i`.
$okta_group = \App\Models\OktaGroup::where('id', $id)->firstOrFail();
$record = ApiClient::get('groups/' . $okta_group->api_group_id);

// Named Arguments
$records = ApiClient::get(
    uri: 'groups',
    data: ['search' => 'profile.name eq "Hack the Planet Engineers"']
);

// Positional Arguments
$records = ApiClient::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

$records = ApiClient::get(
    uri: 'users',
    data: ['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

$records = ApiClient::get(
    uri: 'users',
    data: ['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

// Create a group
// https://developer.okta.com/docs/reference/api/groups/#add-group
$record = ApiClient::post(
    uri: 'groups',
    data: [
        '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 = '00g1ab2c3D4E5F6G7h8i';
$record = ApiClient::put(
    uri: 'groups/' . $group_id,
    data: [
        'profile' => [
            'description' => 'This group contains engineers that have liberated the garbage files.'
        ]
    ]
);

// Update a group
// https://developer.okta.com/docs/reference/api/groups/#update-group
$group_id = '00g1ab2c3D4E5F6G7h8i';
$record = ApiClient::put(
    uri: 'groups/' . $group_id,
    data: [
        'profile' => [
            'name' => 'Hack the Planet Engineers',
            'description' => 'This group contains engineers that have revealed to the world their elite skills.'
        ]
    ]
);

// Delete a group
// https://developer.okta.com/docs/reference/api/groups/#remove-group
$group_id = '00g1ab2c3D4E5F6G7h8i';
$record = ApiClient::delete('groups/' . $group_id);



use BoldlyGrow\Okta\ApiClient;
use BoldlyGrow\Okta\Exceptions\NotFoundException;

class OktaGroupService
{
    private $connection;

    public function __construct(array $connection = [])
    {
        // If connection is null, use the environment variables
        $this->connection = !empty($connection) ? $connection : config('okta-api-client');
    }

    public function listGroups($query = [])
    {
        $groups = ApiClient::get(
            connection: $this->connection,
            uri: 'groups',
            data: $query
        );

        return $groups->data;
    }

    public function getGroup($id, $query = [])
    {
        try {
            $group = ApiClient::get(
                connection: $this->connection,
                uri: 'groups/' . $id,
                data: $query
            );
        } catch (NotFoundException $e) {
            // Custom logic to handle a record not found. For example, you could
            // redirect to a page and flash an alert message.
        }

        return $group->data;
    }

    public function storeGroup($request_data)
    {
        $group = ApiClient::post(
            connection: $this->connection,
            uri: 'groups',
            data: $request_data
        );

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

        // To return the ID of the newly created group
        // return $group->data->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 data, headers, and status
        // return $group;
    }

    public function updateGroup($id, $request_data)
    {
        try {
            $group = ApiClient::put(
                connection: $this->connection,
                uri: 'groups/' . $id,
                data: $request_data
            );
        } catch (NotFoundException $e) {
            // Custom logic to handle a record not found. For example, you could
            // redirect to a page and flash an alert message.
        }

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

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

    public function deleteGroup($id)
    {
        try {
            $group = ApiClient::delete(
                connection: $this->connection,
                uri: 'groups/' . $id
            );
        } catch (NotFoundException $e) {
            // Custom logic to handle a record not found. For example, you could
            // redirect to a page and flash an alert message.
        }

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

// API Request
$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');

// API Response
$group->data; // object
$group->headers; // array
$group->status; // object
$group->status->code; // int (ex. 200)
$group->status->ok; // bool (is 200 status)
$group->status->successful; // bool (is 2xx status)
$group->status->failed; // bool (is 4xx/5xx status)
$group->status->clientError; // bool (is 4xx status)
$group->status->serverError; // bool (is 5xx status)

$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');
$group->data;

$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i')->data;

$group_name = $group->profile->name;
// Hack the Planet Engineers

$groups = ApiClient::get('groups')->data;

foreach($groups as $group) {
    dd($group->profile->name);
    // Hack the Planet Engineers
}

use Illuminate\Support\Facades\Cache;
use BoldlyGrow\Okta\ApiClient;

$groups = Cache::remember('okta_groups', now()->addHours(12), function () {
    return ApiClient::get('groups')->data;
});

foreach($groups as $group) {
    dd($group->profile->name);
    // Hack the Planet Engineers
}

$group_id = '00g1ab2c3D4E5F6G7h8i';

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

$created_date = Carbon::parse($group->data->created)->format('Y-m-d');
// 2023-01-01

$created_age_days = Carbon::parse($group->data->created)->diffInDays();
// 265

$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');
$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; 

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

$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');
$group->status;

{
  +"code": 200 // int (ex. 200)
  +"ok": true // bool (is 200 status)
  +"successful": true // bool (is 2xx status)
  +"failed": false // bool (is 4xx/5xx status)
  +"serverError": false // bool (is 4xx status)
  +"clientError": false // bool (is 5xx status)
}

$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');

$status_code = $group->status->code;
// 200

GET 404 https://example.okta.com/api/v1/users/00u1ab2c3D4E5F6G7h8i (Reason) E0000007 Not found: Resource not found: 00u1ab2c3D4E5F6G7h8i (User)

// {"errorCode":"E0000001","errorSummary":"Api validation failed: profile","errorCauses":[{"errorSummary":"firstName: The field cannot be left blank"},{"errorSummary":"email: Does not match 

// {"error":"invalid_client","error_description":"The client secret is invalid."}
POST 400 https://example.okta.com/oauth2/v1/token (Reason) invalid_client The client secret is invalid.

GET 400 https://example.okta.com/api/v1/users?filter=profile.email+eq+%22user%40example.com%22 (Reason) E0000031 Invalid filter (Decoded) https://example.okta.com/api/v1/users?filter=profile.email eq "[email protected]"

use BoldlyGrow\Okta\Exceptions\NotFoundException;

try {
    $group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');
} catch (NotFoundException $e) {
    // Group is not found. You can create a log entry, throw an exception, or handle it another way.
    Log::error('Okta group could not be found', ['okta_group_id' => $group_id]);
}

OKTA_API_EXCEPTIONS=false

$users = ApiClient::get('users');

$user_collection = collect($users->data)->where('profile.department', 'Security')->toArray();

// This will return an array of users that belong to the Security department based on their profile attribute

$users = collect(ApiClient::get('users')->data)
    ->where('profile.department', 'Security')
    ->toArray();

// Get an array with email addresses
$user_job_titles = collect(ApiClient::get('users')->data)
    ->pluck('profile.email')
    ->toArray();

// [
//     0 => '[email protected]',
//     1 => '[email protected]',
//     2 => '[email protected]',
// ]

// Get an array with email address keys and job title values
$user_job_titles = collect(ApiClient::get('users')->data)
    ->pluck('profile.title', 'profile.email')
    ->toArray();

// [
//     '[email protected]' => 'Senior Frontend Engineer',
//     '[email protected]' => 'Professional Services Engineer',
//     '[email protected]' => 'Frontend Engineer',
// ]

// Get an array of unique job titles

// Option 1
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->unique('profile.title')
    ->pluck('profile.title')
    ->toArray();

// Option 2 (marginally faster)
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->pluck('profile.title')
    ->unique()
    ->toArray();

// [
//     236 => 'Professional Services Engineer',
//     511 => 'Senior Frontend Engineer',
//     988 => 'Frontend Engineer',
// ]

// Get an array of unique job titles

// Option 1
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->unique('profile.title')
    ->pluck('profile.title')
    ->values()
    ->toArray();

// Option 2
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->pluck('profile.title')
    ->unique()
    ->values()
    ->toArray();

// [
//     0 => 'Professional Services Engineer',
//     1 => 'Senior Frontend Engineer',
//     2 => 'Frontend Engineer',
// ]

// Get an array of unique job titles

// Option 1
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->sortBy('profile.title')
    ->unique('profile.title')
    ->pluck('profile.title')
    ->values()
    ->toArray();

// Option 2
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->pluck('profile.title')
    ->unique()
    ->sort()
    ->values()
    ->toArray();

// [
//     0 => 'Frontend Engineer',
//     1 => 'Professional Services Engineer',
//     2 => 'Senior Frontend Engineer',
// ]

// Get an array with email address keys and job title values
$user_job_titles = collect(ApiClient::get('users')->data)
    ->pluck('profile.title', 'profile.email')
    ->sortKeys()
    ->toArray();

// [
//     '[email protected]' => 'Professional Services Engineer',
//     '[email protected]' => 'Senior Frontend Engineer',
//     '[email protected]' => 'Frontend Engineer',
// ]

// Get a count of unique job titles
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->pluck('profile.title')
    ->unique()
    ->count();

// 376

// Get a count of unique job titles
$unique_job_titles = collect(ApiClient::get('users')->data)
    ->countBy('profile.title')
    ->sortKeys()
    ->toArray();

// [
//     'Frontend Engineer' => 8,
//     'Professional Services Engineer' => 4,
//     'Senior Frontend Engineer' => 44,
// ]

// Disclaimer: This is anonymized fake data.
[
    {
      +"id": "00ue2xov9e5xiQmuL5d7",
      +"status": "ACTIVE",
      +"created": "2023-12-23T16:49:49.000Z",
      +"activated": "2023-12-23T16:49:50.000Z",
      +"statusChanged": "2023-12-23T16:49:50.000Z",
      +"lastLogin": null,
      +"lastUpdated": "2023-12-23T16:49:50.000Z",
      +"passwordChanged": "2023-12-23T16:49:50.000Z",
      +"type": {
        +"id": "otye2ebqn49728Yfb5d7",
      },
      +"profile": {
        +"lastName": "Howe",
        +"costCenter": "Sales",
        +"displayName": "Angelica Howe",
        +"secondEmail": null,
        +"managerId": "5f9632",
        +"hire_date": "2020-12-19",
        +"title": "Senior Channel Sales Manager",
        +"login": "[email protected]",
        +"employeeNumber": "aee562",
        +"division": "Sales",
        +"firstName": "Angelica",
        +"management_level": "Individual Contributor",
        +"mobilePhone": null,
        +"department": "Channel Sales",
        +"email": "[email protected]",
      },
    },
    {
      +"id": "00ue2xp1yybaQEE2o5d7",
      +"status": "ACTIVE",
      +"created": "2023-12-23T16:49:12.000Z",
      +"activated": "2023-12-23T16:49:12.000Z",
      +"statusChanged": "2023-12-23T16:49:12.000Z",
      +"lastLogin": null,
      +"lastUpdated": "2023-12-23T16:49:12.000Z",
      +"passwordChanged": "2023-12-23T16:49:12.000Z",
      +"type": {
        +"id": "otye2ebqn49728Yfb5d7",
      },
      +"profile": {
        +"lastName": "O'Kon",
        +"costCenter": "Sales",
        +"displayName": "Earlene O'Kon",
        +"secondEmail": null,
        +"managerId": "2410f0",
        +"hire_date": "2019-03-01",
        +"title": "Manager, Deal Desk",
        +"login": "eo'[email protected]",
        +"employeeNumber": "0561bc",
        +"division": "Sales",
        +"firstName": "Earlene",
        +"management_level": "Manager",
        +"mobilePhone": null,
        +"department": "Sales Operations",
        +"email": "eo'[email protected]",
      },
    },
  ]

// Get all Okta users
$users = collect(ApiClient::get('users')->data)
    ->transform(function($item) {
        return [
            'id' => $item->id,
            'displayName' => $item->profile->displayName,
            'email' => $item->profile->email,
            'title' => $item->profile->title,
            'department' => $item->profile->department
        ];
    })->toArray();

// [
//     "id" => "00ue2xov9e5xiQmuL5d7",
//     "displayName" => "Angelica Howe",
//     "email" => "[email protected]",
//     "title" => "Senior Channel Sales Manager",
//     "department" => "Channel Sales",
// ],
// [
//     "id" => "00ue2xp1yybaQEE2o5d7",
//     "displayName" => "Earlene O'Kon",
//     "email" => "eo'[email protected]",
//     "title" => "Manager, Deal Desk",
//     "department" => "Sales Operations",
// ],

$users = collect(ApiClient::get('users')->data)
    ->transform(function($item) {
        return [
            'id' => $item->id,
            'displayName' => $item->profile->displayName,
            'email' => $item->profile->email,
            'title' => isset($item->profile->title) ? $item->profile->title : null,
            'department' => isset($item->profile->department) ? $item->profile->department : null
        ];
    })->toArray();

$users = collect(ApiClient::get('users')->data)
    ->transform(fn($item) => [
        'id' => $item->id,
        'displayName' => $item->profile->displayName,
        'email' => $item->profile->email,
        'title' => isset($item->profile->title) ? $item->profile->title : null,
        'department' => isset($item->profile->department) ? $item->profile->department : null
    ])->toArray();

use Carbon\Carbon;

$users = collect(ApiClient::get('users')->data)
    ->transform(function($item) {
        // Calculate dates using Carbon (https://carbon.nesbot.com/docs/)
        $created_date = Carbon::parse($item->created)->format('Y-m-d');
        $created_date_age = Carbon::parse($item->created)->diffInDays();

        // It is recommended to use match statements instead of if/else statements for string matching use cases
        $elevated_permissions = match($item->profile->department) {
            'Infrastructure' => true,
            'IT' => true,
            'Security' => true,
            default => false
        };

        return [
            'id' => $item->id,
            'displayName' => $item->profile->displayName,
            'email' => $item->profile->email,
            'title' => isset($item->profile->title) ? $item->profile->title : null,
            'department' => isset($item->profile->department) ? $item->profile->department : null,
            'created_date' => $created_date,
            'new_user' => ($created_date_age < 60 ? true : false),
            'elevated_permissions' => $elevated_permissions
        ];
    })->toArray();

// [
//     "id" => "00ue2xov9e5xiQmuL5d7",
//     "displayName" => "Angelica Howe",
//     "email" => "[email protected]",
//     "title" => "Senior Channel Sales Manager",
//     "department" => "Channel Sales",
//     "created_date" => "2023-12-23",
//     "new_user" => true,
//     "elevated_permissions" => false,
// ],
// [
//     "id" => "00ue2xp1yybaQEE2o5d7",
//     "displayName" => "Earlene O'Kon",
//     "email" => "eo'[email protected]",
//     "title" => "Manager, Deal Desk",
//     "department" => "Sales Operations",
//     "created_date" => "2023-12-23",
//     "new_user" => true,
//     "elevated_permissions" => false,
// ],

$users = collect(ApiClient::get('users')->data)
    ->transform(fn($item) => [
        'id' => $item->id,
        'displayName' => $item->profile->displayName,
        'email' => $item->profile->email,
        'title' => isset($item->profile->title) ? $item->profile->title : null,
        'department' => isset($item->profile->department) ? $item->profile->department : null
    ])->groupBy('department')
    ->toArray();

// "Channel Sales" => [
//     [
//         "id" => "00ue2xov9e5xiQmuL5d7",
//         "displayName" => "Angelica Howe",
//         "email" => "[email protected]",
//         "title" => "Senior Channel Sales Manager",
//         "department" => "Channel Sales",
//     ],
// ],
// "Sales Operations" => [
//     [
//         "id" => "00ue2xp1yybaQEE2o5d7",
//         "displayName" => "Earlene O'Kon",
//         "email" => "eo'[email protected]",
//         "title" => "Manager, Deal Desk",
//         "department" => "Sales Operations",
//     ],
//     [
//         "id" => "00ue2xpoh6h5rfN315d7",
//         "displayName" => "Rylee Veum",
//         "email" => "[email protected]",
//         "title" => "Senior Program Manager, Customer Programs",
//         "department" => "Sales Operations",
//     ],
// ],
plain
php artisan vendor:publish --tag=okta-api-client
plain
php artisan okta:jwk --generate --out=okta-private-key.pem
plain
php artisan okta:jwk okta-public-key.pem
plain
[YYYY-MM-DD HH:II:SS] local.DEBUG: ApiClient::put Success {"event_type":"okta.api.put.success","method":"BoldlyGrow\\Okta\\ApiClient::put","event_ms":287,"metadata":{"okta_request_id":"REDACTED","rate_limit_remaining":"49","url":"https://dev-12345678.okta.com/api/v1/groups/00g1b2c3d4e5f6g7h8i9"}}
plain
[YYYY-MM-DD HH:II:SS] local.ERROR: ApiClient::get Client Error {"event_type":"okta.api.get.error.unauthorized","method":"BoldlyGrow\\Okta\\ApiClient::get","errors":{"error_code":"E0000011","error_message":"Invalid token provided","status_code":401},"event_ms":261,"metadata":{"okta_request_id":"REDACTED","rate_limit_remaining":null,"url":"https://dev-12345678.okta.com/api/v1/org"}}