Securing public access

Learn how to secure the access to your content using the Delivery API.

Premium feature

Secure access to the Delivery API requires a Professional plan or higher. See Pricing for more details.

When using the public Delivery API, you cannot use it to manage the user roles and their access to particular content in your application. With secure access enabled, you can protect your content by requiring an API key with each request and configuring content authorization in your app.

Secure access to the Delivery API allows you to use two concurrent API keys, Primary and Secondary.

Note: For continuous use, we recommend using the Primary key to authenticate your API requests and only using the Secondary key when revoking the Primary key to prevent your site from any downtime. For more information, see the Revoking the API keys section of this tutorial.

Primary vs. Secondary key

The following instructions work equally for both the Primary key and the Secondary key.

Enabling secure access

Secure access to the Delivery API is disabled by default for new projects and you need to activate it. By activating the secure access, the system will also generate new API keys, Primary and Secondary.

  1. In Kentico Cloud, choose a project.
  2. From the app menu, choose Project settings.
  3. Under Development, choose API keys.
  4. In the Delivery API box, click the switch to activate secure access.
Delivery API with secure access enabled.

Delivery API with secure access enabled.

You will mainly use the Primary key to authenticate your requests. Both API keys are generated per project and have no expiration date. For more information, see secure access in our API reference.

Getting API key

Every request to the Delivery API with secure access enabled must be authenticated with an API key. This key is unique to each project in Kentico Cloud.

To get the key:

  1. In Kentico Cloud, choose a project.
  2. From the app menu, choose Project settings.
  3. Under Development, choose API keys.
  4. In the Delivery API box, click Copy to clipboard to copy the API key.

You can now use the key to authenticate your requests to the API.

Authenticating requests

Every request you make must come with an API key in the Authorization header. The Authorization header uses the following format:

Authorization: Bearer <YOUR_API_KEY>

Retrieving secured content

To retrieve a specific content item from Kentico Cloud via API, you need to use the project ID and the content item's codename in your request. See Getting content to find out how you can get these two values.

The Delivery API uses the following URI to retrieve the published content:

https://deliver.kenticocloud.com/<YOUR_PROJECT_ID>/

Once you have the project ID and content item codename, you can retrieve the published content item.

For example, to retrieve the content of a published article named "On Roasts" from the sample project, you can use the following request.

curl --request GET \
  --url https://deliver.kenticocloud.com/975bf280-fd91-488c-994c-2f04416e5ee3/items/on_roasts \
  --header 'authorization: Bearer <YOUR_API_KEY>'
using KenticoCloud.Delivery;

// Initializes a secured content delivery client
IDeliveryClient client = DeliveryClientBuilder
    .WithOptions(builder => builder
        .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3")
        .UseSecuredProductionApi("<YOUR_API_KEY>")
        .Build())
    .Build();

// Gets a specific content item
// Tip: Generate strongly typed models via https://github.com/Kentico/cloud-generators-net
DeliveryItemResponse<object> response = await client.GetItemAsync<object>("on_roasts");

var items = response.Items;
const KenticoCloud = require('kentico-cloud-delivery');

// Create strongly typed models according to https://github.com/Kentico/kentico-cloud-js/blob/master/doc/delivery.md#creating-models
class Article extends KenticoCloud.ContentItem {
    constructor() {
        super();
    }
}

const deliveryClient = new KenticoCloud.DeliveryClient({
    projectId: '975bf280-fd91-488c-994c-2f04416e5ee3',
    enableSecuredMode: true,
    securedApiKey: "<YOUR_API_KEY>",
    typeResolvers: [
        new KenticoCloud.TypeResolver('article', () => new Article)
    ]
});

deliveryClient.item('on_roasts')
    .getObservable()
    .subscribe(response => console.log(response));
import { ContentItem, DeliveryClient, Fields, TypeResolver } from 'kentico-cloud-delivery';

// Create strongly typed models according to https://github.com/Kentico/kentico-cloud-js/blob/master/doc/delivery.md#creating-models
export class Article extends ContentItem {
    public title: Fields.TextField;
    public summary: Fields.TextField;
    public post_date: Fields.DateTimeField;
    public teaser_image: Fields.AssetsField;
    public related_articles: Article[];
}

const deliveryClient = new DeliveryClient({
    projectId: '975bf280-fd91-488c-994c-2f04416e5ee3',
    enableSecuredMode: true,
    securedApiKey: "<YOUR_API_KEY>",
    typeResolvers: [
        new TypeResolver('article', () => new Article)
    ]
});

deliveryClient.item<Article>('on_roasts')
    .getObservable()
    .subscribe(response => console.log(response));
import com.kenticocloud.delivery;

DeliveryOptions options = new DeliveryOptions();
options.setProjectId("975bf280-fd91-488c-994c-2f04416e5ee3");
options.setProductionApiKey("<YOUR_API_KEY>");

DeliveryClient client = new DeliveryClient(options);

ContentItemResponse item = client.getItem("on_roasts");
import com.kenticocloud.delivery_core.*;
import com.kenticocloud.delivery_rx.*;

import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;

// Prepares the DeliveryService configuration object
String projectId = "975bf280-fd91-488c-994c-2f04416e5ee3";
String secureApiKey = "<YOUR_API_KEY>";
IDeliveryConfig config = DeliveryConfig.newConfig(projectId)
    .withSecuredApiKey(secureApiKey);

// Gets the latest version of a content item using a simple request
ContentItem item = deliveryService.<ContentItem>item("on_roasts")
    .get()
    .getItem();

// Gets the latest version of a content item using RxJava2
deliveryService.<ContentItem>item("on_roasts")
    .getObservable()
    .subscribe(new Observer<DeliveryItemResponse<ContentItem>>() {
        @Override
        public void onSubscribe(Disposable d) {
        }

        @Override
        public void onNext(DeliveryItemResponse<ContentItem> response) {
            // Gets the content item
            ContentItem item = response.getItem();
        }

        @Override
        public void onError(Throwable e) {
        }

        @Override
        public void onComplete() {
        }
    });
<?php

// Defined by Composer to include required libraries
require __DIR__ . '/vendor/autoload.php';

use KenticoCloud\Delivery\DeliveryClient;

$client = new DeliveryClient('975bf280-fd91-488c-994c-2f04416e5ee3', '<YOUR_API_KEY>');

$item = $client->getItem('on_roasts');
import KenticoCloud

let client = DeliveryClient.init(projectId: "975bf280-fd91-488c-994c-2f04416e5ee3", secureApiKey: "<YOUR_API_KEY>")

client.getItem(modelType: Article.self, itemName: "on_roasts") { (isSuccess, deliveryItem, error) in
     if isSuccess {
        if let article = deliveryItem.item {
            // Use your item here
        }
    } else {
        if let error = error {
            print(error)
        }
    }

After performing the request, you receive a single content item in the JSON format.

{
    "item": {
        "system": {
            "id": "f4b3fc05-e988-4dae-9ac1-a94aba566474",
            "name": "On Roasts",
            "codename": "on_roasts",
            "language": "en-US",
            "type": "article",
            "sitemap_locations": [
                "articles"
            ],
            "last_modified": "2017-11-02T09:11:00.1067922Z"
        },
        "elements": {
            "personas": {
                "type": "taxonomy",
                "name": "Personas",
                "taxonomy_group": "personas",
                "value": [
                    {
                        "name": "Barista",
                        "codename": "barista"
                    },
                    {
                        "name": "Coffee blogger",
                        "codename": "coffee_blogger"
                    }
                ]
            },
            "title": {
                "type": "text",
                "name": "Title",
                "value": "On roasts"
            },
            "teaser_image": {
                "type": "asset",
                "name": "Teaser image",
                "value": [
                    {
                        "name": "on-roasts-1080px.jpg",
                        "type": "image/jpeg",
                        "size": 121946,
                        "description": "Coffee Roastery",
                        "url": "https://assets-us-01.kc-usercontent.com/e1a09831-6a84-4f18-89da-7199f37ee3e3/f6daed1f-3f3b-4036-a9c7-9519359b9601/on-roasts-1080px.jpg"
                    }
                ]
            },
            "post_date": {
                "type": "date_time",
                "name": "Post date",
                "value": "2014-11-07T00:00:00Z"
            },
            "summary": {
                "type": "text",
                "name": "Summary",
                "value": "Roasting coffee beans can take from 6 to 13 minutes. Different roasting times produce different types of coffee, with varying concentration of caffeine and intensity of the original flavor."
            },
            "body_copy": {
                "type": "rich_text",
                "name": "Body Copy",
                "images": {},
                "links": {},
                "modular_content": [],
                "value": "<p>There’s nothing complicated about roasting. It’s as easy as baking a pie, maybe even simpler as there’s just one ingredient. What’s complicated is fine-tuning the whole process to perfection. ...</p>"
            },
            "related_articles": {
                "type": "modular_content",
                "name": "Related articles",
                "value": [
                    "coffee_processing_techniques",
                    "origins_of_arabica_bourbon"
                ]
            },
            "meta_keywords": {
                "type": "text",
                "name": "Meta keywords",
                "value": "roasts, coffee"
            },
            "meta_description": {
                "type": "text",
                "name": "Meta description",
                "value": "Roasting coffee beans can take from 6 to 13 minutes. Different roasting times produce different types of coffee."
            },
            "url_pattern": {
                "type": "url_slug",
                "name": "URL pattern",
                "value": "on-roasts"
            },
          	...
        }
    },
  	"modular_content": {
    	...
    }
}

Note: Modular content (content linked using Rich text or Linked items elements) was omitted from the response for brevity.

You can limit the retrieved data, for example, retrieve only specific elements, by using optional query parameters. See the API reference for the Delivery API to learn more about the available options and methods.

Revoking the API keys

In certain situations, you may need to revoke one of the API keys and generate a new one. For example, when you suspect unauthorized key usage or when a developer with access to the API key has left your company.

For the reasons above, one or both of the API keys can be regenerated. Activating a new key will immediately replace the old key, making it useless. Requests made with a revoked API Key will then receive a 401 Unauthorized HTTP status code in the response.

Note: First you need to have the secure access for the Delivery API enabled in the UI.

  1. In Kentico Cloud, choose a project.
  2. From the app menu, choose Project settings.
  3. Under Development, choose API keys.
  4. Regenerate the Secondary key as this ensures it's new and secure.
  5. Change all applications using the secured Delivery API to use the newly regenerated Secondary key.
  6. Validate all applications using the Secondary key are functioning correctly.
  7. Regenerate the Primary key to make sure any unauthorized users cannot use this key to access the application.
  8. (Optional) Switch back to using the regenerated Primary key in all of your applications.

The last step is optional as switching back to the regenerated Primary key might seem unnecessarily complicated. The reason behind this is simple – you can easily keep track of the API key you are currently using for your application. If you only use the Secondary key to prevent downtime when revoking the Primary key, it can keep things simple in the long run.

Security tips

Here are some quick tips to help you while using the secure access to the Delivery API:

  • Only regenerate one key at a time to prevent downtime.
  • Do not store API Keys in the source code.
  • Encrypt the key when storing it.
  • Regenerate your API keys periodically.
  • The older a key is, the higher the probability it could have been compromised.