Client Credentials Flow

Introduction

This page describes how to implement the Client Credentials Flow in Centrify. For an overview of this flow see OAuth Clients.

Overview

Five steps are required to enable your client applications to use OAuth 2.0 when accessing Centrify endpoints:
Step 1: Create an OAuth 2.0 Client.
Step 2: Configure the newly created OAuth 2.0 Client.
Step 3: Create scopes.
Step 4: (Optional) Create a Confidential Client
Step 5: Develop a client application.

Enabling a Client Application Access to Centrify

The steps below are to be used for web applications that require OAuth2 authentication but are not in the Application Catalog.

Step 1: Create a New OAuth App in the Admin Portal

The first step in enabling your client applications to use OAuth 2 when accessing Centrify endpoints, is to create a new OAuth Client on the Admin Portal:

  1. Log in to your tenant.
  2. Navigate to your user name at the top right corner and select Switch to Admin Portal from the dropdown:
951
  1. Click Skip on the Quickstart splash screen if it appears.
  2. Click Apps in the Dashboard.
  3. Click Add Web Apps and select the Custom tab on the Add Web Apps popup.
  4. Locate OAuth2 Client in the list and click Add. This creates an OAuth2 Client for use with the Centrify APIs.
  5. Click Yes on the Add Web App popup that appears.
  6. Click Close on the Add Web Apps popup. The app configuration screen is displayed.

Step 2. Configure the new OAuth 2.0 Client

  1. In the app configuration screen (left side), select categories (listed below).
  2. For each category, enter the appropriate data in the fields.
  3. Click Save.
724

Note: You can create multiple OAuth 2.0 clients on the Admin Portal and configure each of them differently to serve different clients. For example, you can configure the supported OAuth 2.0 flows and scopes differently.

Description:

  • Application ID: the tenant portion of the URL for OAuth endpoints. Note that this value cannot contain spaces.
  • Application Name: a descriptive name for the application.
  • Application Description: a description of the application (not seen by users).
  • Category: the default grouping for the app on the Admin Portal.

General Usage: specifies the types of credentials that can be used to authorize with this client:

  • Client ID Type:
    • Anything: allows for authorization in any client where authorization is granted by the user (e.g. in a popup screen). Select this setting when using the Authorization Code flow.
    • List: - specifies a list of clients that are allowed access. Click Add and then enter the application name of your client. Select this setting when the application uses the Authorization Code flow.
    • Confidential: requires an OAuth client to send a client ID and secret. Selecting Confidential is recommended for all flows in which the user provides their username and password and the client sends a client ID and secret. If you select this option, populate the remaining configuration elements (listed below) and then proceed to Create a Confidential Client.

Issuer: the URL of the server issuing access tokens. Can be left as default.

Allow Redirects: specifies the redirects that should be trusted when redirection occurs during the Authorization Code and Implicit flows. Not applicable for the Client Credential and Resource Owner flows.

Tokens:

  • Token Type: specifies the type of token to issue (JwtRS256 or opaque). JwtRS256 is a JSON Web Token (JWT) composed of Base64-encoded user and claim information. An opaque token contains no information about the user. To obtain user and claim information for an opaque token an introspection URL must be used by passing the token.
  • Auth Methods: specifies the authentication flow(s) for which the specified token type should be issued. Ensure that Client Creds is selected for the client credentials flow.
  • Token Lifetime: specifies the duration of the token's lifespan.
  • Issue refresh tokens: when enabled, allows clients to request a refresh token that can be exchanged for a new access token. Not applicable for the Resource Owner flow.

Scope:

  • User must confirm authorization request: when enabled, this setting requires that the client display a popup where the user must select and approve the scope(s) to allow for the client.
  • Scope Definitions: allows one or more scopes to be specified for authorization by the client. Scopes must first be defined through the scope configuration screen as described here.

For more information see Create Scopes below.

User Access: specifies the system role(s) that the user—a user represents a confidential client— containing the credentials must be included in, in order for a client to successfully authorize with those credentials (see Step 4 below for information on creating a user to represent a confidential client).

Changelog: lists changes made to the client.

Step 3. Create Scopes

A scope is a named entity which defines the endpoint(s) that a client may access. Scopes are used in flows where the user is prompted to grant scope authorization, as well as for confidential clients where there is no popup for the user to approve authorization. Defining scopes through the Admin Portal lowers the risk associated with traditional service accounts by scoping the authorization to specific APIs. This ensures the application will only be able to access resources for the scopes assigned to the client through the Admin Portal.

Note: the user must be in a role that gives them access to use the API’s that the server is scoped to.

Use the following steps to define scopes for an OAuth2 Client:

Note: this step may be performed before or during the configuration of the OAuth 2 Client.

  1. Navigate to Apps and select the application to set the scope(s) for.
  2. Click on Scope:
1008
  1. Click Add.
  2. Enter a unique name for the new scope, and optionally a description.
  3. Click Add and enter the endpoint suffix. Note that the wildcard ".*" character can be used to specify multiple endpoints. Repeat this step to add additional entries:
501

Note: to add a scope for all APIs, enter .* for the REST Regex value.

  1. Click Save to save the new scope definition and close the popup.

Step 4. Create a Confidential Client

Note: the user interface and workflow described in this section will change significantly in the near future.

If you selected Confidential as the web application Client ID Type during configuration, continue with this procedure. To authorize a confidential client (that is, a client that provides a client ID and client secret), you must create a user entity representing the confidential client.

  1. Navigate to Core Services > Users to display the Create Centrify Directory User screen.
  2. Click Add User.
  3. Enter the application's client ID into the Login Name field.
  4. Enter values into the Email Address and Display Name fields. Confidential clients do not use these values, but they are required in order to satisfy the required fields of the user form.
  5. Enter the application's client secret into the Password and Confirm Password fields.
  6. Navigate to the Status section at the bottom and enable Is OAuth confidential client.
  7. Click Create User. A confidential client who specifies the client ID and secret can now authorize against your Centrify Tenant.

Step 5. Develop a Client

This section illustrates one way to approach client application development. It draws on key components of the Centrify.Samples.PowerShell example client application, available on GitHub. The following procedure and code samples provide a brief walk through of the example project and the key files to examine.

Behind the Scenes

Before examining the PowerShell script, it's useful to understand the underlying Centrify APIs that will be invoked by PowerShell to perform the Client Credentials Flow.

The first API that is invoked is /token/. The header is set to Authorization Basic and is followed by a Base64-encoded string constructed from the client ID and secret separated by a ":" character:

Header: Authorization Basic <Client ID:Client Secret (Base64 encoded)>

Note: Your request header must contain X-CENTRIFY-NATIVE-CLIENT:true to indicate that an application is invoking the Centrify endpoint, and Content-Type: application/json to indicate that the body is in JSON format.

The body of the request specifies a grant_type of client_credentials, and optionally, a scope:

https://<yourtenant>/oauth2/token/<your app ID>

Header: Authorization Basic <Client ID:Client Secret (Base64 encoded)>

{
	"grant_type":"client_credentials",
	"scope":"myscope"
}

The response contains an access_token for use in subsequent API calls, as well as information about the token's expiration time, the scope for which access was granted, and the type of token issued:

{
	access_token = "abc1234asdf9823...",
	expires_in=18000,
	scope="myscope",
	token_type="Bearer"
}

The token can then be used in subsequent API calls by including it in the Authorization header along with the type of token. For example:

https://<yourtenant>/cdirectoryservice/createuser

Header: Authorization Bearer abc1234asdf9823...

{
	"Name":"John",
	...
}

Prepare the PowerShell Script

  1. Download the example client application from GitHub.

  2. Navigate to the centrify-samples-powershell folder and open Centrify.Samples.PowerShell.Example.ps1 in a text editor.

  3. Comment out the line which makes the username mandatory as well as $username and modify the $endpoint parameter to point to your tenant's base URL and save the file:

...
[CmdletBinding()]
param(
    #[Parameter(Mandatory=$true)] <-comment out this
    #[string]$username = "",	<-comment out this
    [string]$endpoint = "https://cloud.centrify.com" <- modify this.
)
...
  1. Set the Appid, Clientid, Clientsecret, and Scope parameters in the call to Centrify-OAuth-ClientCredentials based on how they were set on the Admin Portal, and save the file:
#$token = Centrify-OAuth-ClientCredentials -Endpoint $endpoint -Appid "applicationId" -Clientid "client@domain" -Clientsecret "clientSec" -Scope "scope" -Verbose:$enableVerbose    
	$token = Centrify-OAuth-ClientCredentials -Endpoint $endpoint -Appid "JamesTest" -Clientid "[email protected]" -Clientsecret "MyTest1" -Scope "Accounts" -Verbose:$enableVerbose
  1. Navigate to the module subfolder and open Centrify.Samples.PowerShell.psm1.

  2. Locate the Centrify-OAuth-ClientCredentials function, modify $endpoint to point to your tenant's base URL, and save the file:

function Centrify-OAuth-ClientCredentials
{
    [CmdletBinding()]
    param(
        [string] $endpoint = "https://cloud.centrify.com", <- modify this
        [Parameter(Mandatory=$true)]
        [string] $appid, 
        [Parameter(Mandatory=$true)]
        [string] $clientid,
        [Parameter(Mandatory=$true)]
        [string] $clientsecret,
        [Parameter(Mandatory=$true)]
        [string] $scope
        )
     ...
  1. Open Powershell and navigate to the centrify-samples-powershell folder.

  2. Execute the Powershell script Centrify.Samples.PowerShell.Example.ps1 using the following command:

./Centrify.Samples.PowerShell.Example.ps1
  1. The response contains the following elements:
  • access_token: the access token for use in subsequent requests.
  • token_type: the type of token. Will be set to Bearer.
  • expires_in: the number of seconds when the access token will expire.
  • scope: the name of the scope specified in the request.

The following is an example response:

{
	"access_token" : "123434JhbGc_83g....",
	"token_type" : "Bearer",
	"expires_in" : 18000,
	"scope":"Accounts"
}

In the PowerShell script, the access token is stored in $token which can then be passed to subsequent requests.

$token = Centrify-OAuth-ClientCredentials -Endpoint $endpoint ...

A Closer Look at Centrify-OAuth-ClientCredentials

The steps above used the function Centrify-OAuth-ClientCredentials to perform OAuth2 authentication which returned an access token. That function is defined in \centrify-samples-powershell-master\module\Centrify.Samples.PowerShell.psm1 and contains all of the functionality to invoke the /oauth/token endpoint.

This section takes a closer look at how that function works.

The function starts with the parameter definitions which include a default endpoint, and mandatory parameters for the application ID, client ID, client secret, and the name of the desired scope to access:

function Centrify-OAuth-ClientCredentials
{
    [CmdletBinding()]
    param(
        #[string] $endpoint = "https://cloud.centrify.com"
        [Parameter(Mandatory=$true)]
        [string] $appid, 
        [Parameter(Mandatory=$true)]
        [string] $clientid,
        [Parameter(Mandatory=$true)]
        [string] $clientsecret,
        [Parameter(Mandatory=$true)]
        [string] $scope
        )
        ...

The function then sets $verbosePreference to Continue which ensures that all output is displayed on screen. This is followed by construction of the full path to the endpoint which is stored in $api, and then construction of the JSON request body in $bod which sets the grant type to client_credentials for the client credentials flow, followed by the name of the scope:

...
$verbosePreference = "Continue"
$api = "$endpoint/oauth2/token/$appid"
$bod = @{}
$bod.grant_type = "client_credentials"
$bod.scope = $scope
...

Centrify-OAuth-ClientCredentials then places the client ID and secret into $basic which will then be passed in the headers of the request:

$basic = Centrify-InternalMakeClientAuth $clientid $clientsecret

The function calls Invoke-RestMethod to invoke the REST endpoint using the parameters and headers set above, and the response from the request is stored in $restResult:

...
$restResult = Invoke-RestMethod -Method Post -Uri $api -Headers $basic -Body $bod
...

The function then constructs $finalResult which will be returned from the function using the Write-Output construct. It is populated with the endpoint as well as the access token which is extracted from $restResult.access_token:

...
$finalResult = @{}
$finalResult.Endpoint = $endpoint    
$finalResult.BearerToken = $restResult.access_token

Write-Output $finalResult
...

Use the Access Token in Subsequent Requests

Once you verify that an access token can be successfully returned with the PowerShell script, you can then uncomment subsequent requests in the script that will use the access token. For example, to use the Createuser function which creates a new user, uncomment the following lines in Centrify.Samples.PowerShell.Example.ps1:

$newUserUUID = CreateUser -Endpoint $token.Endpoint -BearerToken $token.BearerToken -Username "apitest@co" -Password "new1234F@!%^"
Write-Host "Create user result: " $newUserUUID

CreateUser takes in the endpoint and the access token that was obtained by invoking the Centrify-OAuth-ClientCredentials function, as well as the desired username and password to assign to the user being created. The function returns the UUID of the newly created user in newUserUUID. Write-Host then writes this UUID to the screen. CreateUser is described in more detail below.

A Closer Look at CreateUser

CreateUser is defined in \centrify-samples-powershell-master\functions\Centrify.Samples.PowerShell.CreateUser.ps1 and contains all of the functionality to invoke the /CDirectoryService/CreateUser endpoint.

The function begins by defining the required parameters to take in the endpoint, access token, user name and password:

function CreateUser {
    param(
        [Parameter(Mandatory=$true)]
        $endpoint,
        [Parameter(Mandatory=$true)]
        $bearerToken,
        [Parameter(Mandatory=$true)]
        $username,
        [Parameter(Mandatory=$true)]
        $password        
    )
    ...

The function then constructs the JSON body of the request in $restArg. It sets the Name and Mail elements both to the user name passed in, and Password to the password passed in:

...
$restArg = @{}
$restArg.Name = $username
$restArg.Mail = $username
$restArg.Password = $password    
...

Centrify-InvokeREST then invokes the request on the endpoint. Centrify-InvokeREST is defined in \centrify-samples-powershell-master\module\Centrify.Samples.PowerShell.psm1 and performs a REST call against the CIS platform. It is more specialized than PowerShell's built-in Invoke-RestMethod function (which is used in other places throughout Centrify.Samples.PowerShell) because it sets up Centrify-specific elements.

Centrify-InvokeREST is passed the endpoint, access token, and the JSON body, and the result is stored in $restResult:

$restResult = Centrify-InvokeREST -Method "/cdirectoryservice/createuser" -Endpoint $endpoint -Token $bearerToken -ObjectContent $restArg -Verbose:$enableVerbose

restResult contains the boolean element success. If its value is not true then the function throws an exception, otherwise, the JSON response is returned through $restResult/Result:

if($restResult.success -ne $true)
{
	throw "Server error: $($restResult.Message)"
}     

return $restResult.Result

The caller of CreateUser in Centrify.Samples.PowerShell.Example.ps1, receives the UUID and stores it in $newUserUUID. This UUID can then be used in subsequent requests when managing the user:

$newUserUUID = CreateUser -Endpoint $token.Endpoint -BearerToken $token.BearerToken -Username "apitest@contoso" -Password "newP@3651awdF@!%^"

Inspect the Client's Traffic in Fiddler

Using a network inspection tool is an excellent way to view the OAuth 2 calls being made behind the scenes.

This section provides a brief walkthrough of viewing the network traffic of Centrify's Centrify.Samples.PowerShell example client application using Fiddler, a popular network traffic analysis tool. See Using Trace Tools for more information about using Fiddler to analyze network traffic between a Centrify client and tenant.

Note: this section assumes that you have modified the PowerShell scripts as described in the previous section to successfully obtain an access token from your tenant and to create a new user.

  1. Run Fiddler.
  2. Open PowerShell and run \centrify-samples-powershell-master\Centrify.Samples.PowerShell.Example.ps1.
  3. Return to Fiddler and select the /oauth2/token/<your application> call in the left pane. This was the first call made by the client application to obtain an access token.
  4. Select the Raw tab for the request in the upper region of the right pane, and the JSON tab for the response in the lower region of the right pane.
1183

In the screenshot above, the request's authorization header includes the Base64-encoded client ID and secret while the body includes the grant_type (client_credentials flow) and scope.

The response contains an access token that was returned by Centrify-OAuth-ClientCredentials for use in subsequent API calls.

  1. Select the /cdirectoryservice/createuser call in the left pane. This API was invoked after the /oauth2/token endpoint using the access token from that endpoint:
1183

In the screenshot above, the request's Authorization header specifies Bearer to indicate that it is the client who possesses the access token. The header also includes the access token itself.

The Host header specifies the base tenant URL on which to execute the endpoint.

The response contains a value of true for the success field indicating that the call to the /cdirectoryservice/createuser endpoint was successful.