API Security for Configuration

The Akamai Identity Cloud offers a number of configuration endpoints for OpenID Connect (OIDC) that enable you to manage the following resources:

  • OpenID Connect clients and client secrets
  • Login policies
  • Token policies

The OIDC configuration endpoints are similar to other Identity Cloud APIs, but with one important difference. Most Identity Cloud REST APIs require Basic authentication: you need to supply a username (or, more correctly, an API client ID) and a password (an API client secret) in order to access an endpoint.

This isn’t the case with the OIDC configuration endpoints, however. Instead, these endpoints require token-based authentication. To use an OIDC configuration endpoint (for example, to create or delete a login policies) you must first use the /{customer_id}/login/token endpoint to obtain an OAuth 2.0 access token; you then use that access token (instead of a username and password) to call one of the configuration endpoints.

If that seems a little confusing, don’t worry: we’re about to explain just exactly how the process works. But before we do that, we need to provide a quick overview of OAuth clients and configuration clients.

OAuth Clients

As you probably know, OAuth supports two types of clients:

  • Confidential clients. Confidential clients require a client ID and a client secret when being authenticated. And that’s fine:after all, confidential clients typically run on secure TLS networks, which means that they’re fully capable of protect a client secret.
  • Public clients. Public clients have a client ID but don’t have a client secret; this makes them well-suited for use on cell phones and other devices which can’t securely store passwords and client secrets.

But, like we said, you probably already knew that. However, there’s also a third client type that maybe you aren’t familiar with: configuration clients. Unlike confidential clients and public clients, configurations clients have nothing to do with user logins and registrations. Instead, configuration clients are used to acquire the tokens needed to call the other OpenID Connect configuration endpoints.

Configuration Clients

As part of the provisioning process, Akamai provides you with a configuration client, which can then be used to manage OIDC configuration resources. For example, if you need to perform an OpenID Connect administrative task (create a token policy, delete an OIDC client, modify a login policy) you start by using a configuration client to return an access token, then use that token when calling the appropriate OpenID Connect endpoint.

In other words, top call an OpenID Connect configuration endpoint you need to:

  1. Use the client ID and client secret of a configuration client to access the /{customer_id}/login/token endpoint.
  2. Use the access token returned from the token endpoint to call the appropriate OIDC configuration endpoint.

That’s pretty straightforward, although it still leaves us with two questions: how do you know what endpoints you’re getting access to, and how do you what kind of access you’re getting? The answer to those questions lies in the API scope associated with the configuration client.

API Scopes

Configuration clients are authorized to request API scopes based on the allowedScopes property configured in their associated token policy. These API scopes use the following pattern when defining what the access that a client can grant:

behavior:resource

In this syntax, the behavior defines the actions allowed on the specified resource. Valid behaviors are:

API Scopes
Behavior Description

*

Allows owner operations (GET, PUT, PATCH, POST, DELETE) on all resources.

+

Allows mutate operations (GET, POST, PATCH) on resources. Mutate operations enable you to change existing resources, but do not allow you to delete existing resources or create new resources.

.

Allows read-only access (GET) to existing resources.

Meanwhile, the resource part of the API scope syntax specifies the path for which the specified behaviors are allowed. The resource must map to one of the following paths in the configuration API:

  • config
  • config/clients
  • config/loginPolicies
  • config/tokenPolicies

When mapping a resource to a configuration API path, the syntax ** is used to match all the resources contained in that path. For example, the construction config/** provides access to all the resources (client, loginPolicies, and tokenPolicies) found in the config path.

API scopes for a configuration client are specified by using the allowedScopes parameter in the token policy associated with the client (like public and confidential clients, all configuration clients must be associated with a token policy). For example, a token policy will look similar to this:

{
    "accessTokenLifetime": 3600,
    "allowedScopes": [
    	"*:config/**"
    ],
    "refreshTokenLifetime": 28800,
    "title": "Configuration Admin Token Policy"
}

In the preceding policy, you might have noticed that the allowedScopes parameter is set to *:config/**. Based on the syntax we just went over, that grants a client owner operations (*) for all the resource paths that start with config/ (remember, ** means “everything”). In turn, that means that tokens acquired by using this configuration client will have full access to all of the OIDC resources that can be configured by using the OpenID Connect configuration APIs.

Alternatively, you can grant limited access using by using the resource paths mentioned a moment ago. For example:

  • config/clients (limits access to OIDC clients)
  • config/loginPolicies (limits access to login policies)
  • config/tokenPolicies (limits access to token policies)

For example, the scope +:config/clients allows mutate access (+) to the OpenID Connect clients. Similarly, the scope .:config/tokenPolicies enables reads-only access to all the token policies.

Incidentally, the allowedScopes parameter is configured as an array; that means you can specify multiple scopes in a single token policy:

"allowedScopes": [
    	"*:config/tokenPolicies",
     "*:config/loginPolicies"
    ]

If you want to create a configuration client that provides limited access to resources, use your primary configuration client to create the new client and then create a new token policy to associate with that client. Whatever you do, don’t change the token policy associated with your primary configuration client. If you do , you could end up losing owner access to your resources.

Using a Configuration Client to Retrieve an Access Token

In order to call the /{customer_id}/login/token endpoint (and, by extension, to retrieve an access token that can call the other OIDC endpoints), you must have both the client ID and the client secret for your configuration client. These two values are used to configure Basic authentication: the client ID functions as the username and the client ID serves as the password. For example, in Postman your authorization will look similar to this:

If you’re running Curl, your authorization header will look like this:

-H 'Authorization: Basic ajd5d2Z1czZwY2p1dDNraHIyY2RxNWdzODlncHBqeTY6aml1NjJndW45d2trbWk3ZmducXg0cHBlMTQ3NmFtaDk5'

In addition to setting the authorization, you must also configure the request keys grant_type and scope; these keys must be configured as x-www-url-encoded body keys. For example:

The grant_type key must be set to client_credentials; this tells the token endpoint that you are using the client credentials grant to request an access token. That type of grant is used to request access to system resources (such as API endpoints).

Meanwhile, the scope key must be set to the API scopes being requested; note that any scopes assigned to the scope key must also be included in the token policy associated with the configuration client. However, you don’t have to request all the scopes referenced in the token policy. For example, suppose your token policy includes two scopes:

"allowedScopes": [
    	"*:config/tokenPolicies",
     "*:config/loginPolicies"
    ]

If you prefer you can use just one of these scopes (e.g., *:config/tokenPolicies) as the value of the scope key.

Note: So, in the preceding example, could we set *:config/clients as the value of the scope key? Nope. That’s because *:config/clients isn’t specified in the token policy.

A complete Curl command for requesting an access token will look similar to this:

curl -X POST \
  https://v1.api.us.janrain.com/01000000-0000-3000-9000-000000000000/login/token \
  -H 'Authorization: Basic ajd5d2Z1czZwY2p1dDNraHIyY2RxNWdzODlncHBqeTY6aml1NjJndW45d2trbWk3ZmducXg0cHBlMTQ3NmFtaDk5' \
  -F grant_type=client_credentials \
  -F 'scope=*:config/**'

If you successfully connect to the token endpoint you’ll get back a response that includes an access token:

{
    "access_token": " Ki712dpGq5GPQcsxMHY6ShHY7wU_iTs0o9dPx4TEzf5yLIvddjnDVBJxjPDucf5YVB",
    "expires_in": 3600,
    "token_type": "Bearer",
    "scope": "*:config/**"
}

You might have noticed that the preceding response doesn’t include a refresh token. That’s by design: refresh tokens are typically not returned when using the client credentials grant. That’s because the access tokens returned by this grant type are typically used for a specific purpose (such as calling an API endpoint or two) and for a relatively short amount of time. What happens if your access token expires before you finish with your administrative chores? That’s fine: just use the /{customer_id}/login/token endpoint to request a new token.

Using an Access Token to Call the OIDC Configuration APIs

After you have an access token, you can use that token to call the configuration endpoints. To call these endpoints, set the authorization type to Bearer Token, then use your access token as the token value. For example, in Postman your Authorization will look like this:

In Curl, your authorization header will look similar to this:

-H 'Authorization: Bearer Ki712dpGq5GPQcsxMHY6ShHY7wU_iTs0o9dPx4TEzf5yLIvddjnDVBJxjPDucf5YVB'

And what happens if you make your API call and see an error message similar to this:

{
  "errors": "Unable to access TBA endpoints without token!"
}

That usually means that you forgot to set the authorization to Bearer Token. If that’s the problem, then simply changing your authorization to Bearer Token should fix things.