Skip to content

Commit

Permalink
Adding client credentials support (#775)
Browse files Browse the repository at this point in the history
### Changes

- Support for Default Organisation in clients

a. Added new field default_organization to GET/PATCH endpoints
/api/v2/clients/{id}
b. Added new field default_organization to POST endpoints
/api/v2/clients/

- Support query param q for GET /api/v2/clients

    a. q=client_grant.organization_id:org_<uniq_id>
    b. q=client_grant.allow_any_organization:true

- Support query param grant_ids for GET
/api/v2/organizations/{org_id}/client-grants

- Support query param identifiers for GET /api/v2/resource-servers

### References

[Management API
Documentation](https://sus.auth0.com/docs/api/management/v2/organizations/post-organizations)
[Internal
Reference](https://oktawiki.atlassian.net/wiki/spaces/IAMPS/pages/3052704289/Organizations+for+Client+Credentials+GA+SDK+Changes)

### Testing

Added test cases for testing above end-points.
Tested the implementation manually according to this [internal
document](https://oktawiki.atlassian.net/wiki/spaces/IAMPS/pages/3028125865/Demo+-+Default+Organizations)


- [x] This change adds unit test coverage

- [] This change adds integration test coverage

- [x] This change has been tested on the latest version of the
platform/language or why not

### Contributor Checklist

- [x] I agree to adhere to the [Auth0 General Contribution
Guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md).
- [x] I agree to uphold the [Auth0 Code of
Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md).
  • Loading branch information
kishore7snehil authored Nov 5, 2024
1 parent 02f9c63 commit a16a81d
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/API/Management/Clients.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ public function getAll(

/** @var array<null|int|string> $parameters */

// If the 'q' parameter is provided, ensure it's correctly passed in the query
if (isset($parameters['q'])) {
[$parameters['q']] = Toolkit::filter([$parameters['q']])->string()->trim();
}

return $this->getHttpClient()
->method('get')
->addPath(['clients'])
Expand Down
2 changes: 2 additions & 0 deletions src/API/Management/Organizations.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public function getByName(
public function getClientGrants(
string $id,
?RequestOptions $options = null,
?string $grantIds = null,
): ResponseInterface {
[$id] = Toolkit::filter([$id])->string()->trim();

Expand All @@ -299,6 +300,7 @@ public function getClientGrants(

return $this->getHttpClient()
->method('get')->addPath(['organizations', $id, 'client-grants'])
->withParams(['grant_ids' => $grantIds])
->withOptions($options)
->call();
}
Expand Down
6 changes: 6 additions & 0 deletions src/API/Management/ResourceServers.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,16 @@ public function get(

public function getAll(
?RequestOptions $options = null,
?array $parameters = null,
): ResponseInterface {
[$parameters] = Toolkit::filter([$parameters])->array()->trim();

/** @var array<null|int|string> $parameters */

return $this->getHttpClient()
->method('get')
->addPath(['resource-servers'])
->withParams($parameters)
->withOptions($options)
->call();
}
Expand Down
6 changes: 4 additions & 2 deletions src/Contract/API/Management/OrganizationsInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,17 @@ public function getByName(
* Get client grants associated to an organization.
* Required scope: `read:organization_client_grants`.
*
* @param string $id Organization (by ID) that the connection is associated with
* @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these.)
* @param string $id Organization (by ID) that the connection is associated with
* @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these.)
* @param string $grantIds Client Grant (by ID) to associate with the organization.
*
* @throws \Auth0\SDK\Exception\ArgumentException when an invalid `id` or `connectionId` are provided
* @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error
*/
public function getClientGrants(
string $id,
?RequestOptions $options = null,
?string $grantIds = null,
): ResponseInterface;

/**
Expand Down
4 changes: 3 additions & 1 deletion src/Contract/API/Management/ResourceServersInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,16 @@ public function get(
* Get all Resource Servers, by page if desired.
* Required scope: `read:resource_servers`.
*
* @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.)
* @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.)
* @param null|int[]|null[]|string[] $parameters Optional. Additional query parameters to pass with the API request. See @see for supported options.
*
* @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error
*
* @see https://auth0.com/docs/api/management/v2#!/Resource_Servers/get_resource_servers
*/
public function getAll(
?RequestOptions $options = null,
?array $parameters = null,
): ResponseInterface;

/**
Expand Down
70 changes: 70 additions & 0 deletions tests/Unit/API/Management/ClientsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,73 @@
$body = $this->api->getRequestBodyAsString();
expect($body)->toEqual(json_encode((object) $mockRequestBody));
});

test('getAll() issues an appropriate request with organization queries', function(): void {
$this->endpoint->getAll(['q' => 'allow_any_organization=true']);

expect($this->api->getRequestMethod())->toEqual('GET');
expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/clients');

expect($this->api->getRequestQuery(null))->toEqual('q=' . rawurlencode('allow_any_organization=true'));
});

test('create() issues an appropriate request with default_organization', function(): void {
$mock = (object) [
'name' => uniqid(),
'body' => [
'default_organization' => [
'flows' => ["client_credentials"],
'organization_id' => "org_" . uniqid()
]
]
];

$this->endpoint->create($mock->name, $mock->body);

expect($this->api->getRequestMethod())->toEqual('POST');
expect($this->api->getRequestUrl())->toEndWith('/api/v2/clients');

$body = $this->api->getRequestBody();

$this->assertArrayHasKey('name', $body);
expect($body['name'])->toEqual($mock->name);

// Check for default_organization
$this->assertArrayHasKey('default_organization', $body);
expect($body['default_organization']['organization_id'])->toEqual($mock->body['default_organization']['organization_id']);
expect($body['default_organization']['flows'])->toEqual($mock->body['default_organization']['flows']);

// Ensure the request body matches the expected format
expect($this->api->getRequestBodyAsString())->toEqual(json_encode(array_merge(['name' => $mock->name], $mock->body)));
});

test('update() issues an appropriate request with organization queries', function(): void {
$clientId = uniqid();
$mock = (object) [
'body' => [
'default_organization' => [
'flows' => ["client_credentials"],
'organization_id' => "org_" . uniqid()
]
]
];

$this->endpoint->update($clientId, $mock->body);

expect($this->api->getRequestMethod())->toEqual('PATCH');
expect($this->api->getRequestUrl())->toEndWith('/api/v2/clients/' . $clientId);

$body = $this->api->getRequestBody();

// Check for default_organization
$this->assertArrayHasKey('default_organization', $body);
expect($body['default_organization']['organization_id'])->toEqual($mock->body['default_organization']['organization_id']);
expect($body['default_organization']['flows'])->toEqual($mock->body['default_organization']['flows']);

// Ensure the request body matches the expected format
expect($this->api->getRequestBodyAsString())->toEqual(json_encode($mock->body));
});




15 changes: 14 additions & 1 deletion tests/Unit/API/Management/OrganizationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@
test('getClientGrants() issues an appropriate request', function(): void {
$organization = 'org_' . uniqid();

$this->endpoint->getClientGrants($organization);
$this->endpoint->getClientGrants($organization, null , null);

expect($this->api->getRequestMethod())->toEqual('GET');
expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/organizations/' . $organization . '/client-grants');
Expand All @@ -508,3 +508,16 @@
expect($this->api->getRequestMethod())->toEqual('DELETE');
expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/organizations/' . $organization . '/client-grants/' . $grant);
});

test('getClientGrants() issues an appropriate request with grant_ids', function (): void {
$orgId = uniqid();
$grantIds = 'cgr_12345,cgr_67890'; // Comma-separated grant IDs

$this->endpoint->getClientGrants($orgId, null, $grantIds);

expect($this->api->getRequestMethod())->toEqual('GET');
expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/organizations/' . $orgId . '/client-grants');

$query = $this->api->getRequestQuery(null);
expect($query)->toContain('grant_ids=' . rawurlencode($grantIds));
});
24 changes: 24 additions & 0 deletions tests/Unit/API/Management/ResourceServersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,27 @@
})->with(['mocked id' => [
fn() => uniqid(),
]]);

test('getAll() issues an appropriate request with identifiers array', function(): void {
$identifiers = ['https://tst.api.com/api/v1/', 'https://tst.api.com/api/v2/'];

$this->endpoint->getAll(null, ['identifiers' => $identifiers]);

expect($this->api->getRequestMethod())->toEqual('GET');
expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/resource-servers');

expect($this->api->getRequestUrl())->toContain(rawurlencode('identifiers[0]') .'='. rawurlencode('https://tst.api.com/api/v1/'));
expect($this->api->getRequestUrl())->toContain(rawurlencode('identifiers[1]') .'='. rawurlencode('https://tst.api.com/api/v2/'));
});

test('getAll() supports checkpoint pagination parameters', function(): void {
$this->paginatedRequest->setFrom('indexIdentifier')->setTake(50);

$this->endpoint->getAll($this->requestOptions, null);

expect($this->api->getRequestMethod())->toEqual('GET');
expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/resource-servers');

expect($this->api->getRequestQuery())->toContain('&from=indexIdentifier');
expect($this->api->getRequestQuery())->toContain('&take=50');
});

0 comments on commit a16a81d

Please sign in to comment.