CloudObjects / Blog / Adding lightweight authentication to your API with CloudObjects shared secrets

Adding lightweight authentication to your API with CloudObjects shared secrets

Do you have an API that you’d like to share with developers? You don’t need or want to build an extensive onboarding process to hand out API keys, but you don’t feel comfortable cutting out authentication completely? In that case, you’ve come to the right place because CloudObjects shared secrets is a lightweight authentication mechanism that you can add to your API.

What are CloudObjects shared secrets?

CloudObjects Core organizes all objects into namespaces, identified by domains, or hostnames. For each combination of two namespaces, CloudObjects creates a shared secret that APIs, applications, and other implementations of objects in these namespaces can use to authenticate against each other. At the time of writing, shared secrets are permanent, but we’re planning to extend them with revoking, regenerating, and rotating mechanisms in the future.

Developers can retrieve the shared secret between two namespaces through the CloudObjects CLI if they’re a member of at least one of them by running the following command:

cloudobjects domain-providers:secret DOMAIN1 DOMAIN2

If you have a CloudObjects account, added a domain, and installed the CLI tool, you could try running the command to get the shared secret between your domain and cloudobjects.io.

What is shared secret authentication?

CloudObjects suggests using shared secrets combined with HTTP Basic authentication to secure Web APIs and other forms of HTTP exchanges. It goes without saying that you should only ever do this over TLS-encrypted connections (HTTPS). HTTP Basic authentication requires a username and password, for which you should use the following values:

  • The username is your domain name.
  • The password is the shared secret between your domain and the namespace to that the object representing the endpoint belongs.

We use this approach for our Object API that provides access to the objects stored in CloudObjects Core. Why not try this on your command line now, replacing MYDOMAIN with a domain you control?

SHARED_SECRET=`cloudobjects domain-providers:secret MYDOMAIN cloudobjects.io`
curl -u "MYDOMAIN:$SHARED_SECRET" https://api.cloudobjects.net/cloudobjects.io/object.xml

(In this particular case, for technical reasons, the endpoint is located at api.cloudobjects.net, but since the Object API belongs to the namespace cloudobjects.io, you need the latter for the shared secret.)

By the way, if you have seen our phpMAE launch tutorial, the public phpMAE instances use the same method of authentication.

Implementing shared secret authentication as an API provider

If you are an API provider whose backend runs on PHP, you can use the CloudObjects PHP SDK to add client authentication. If you don’t use PHP, I’ll explain further down how the process works internally.

To get started, add the SDK using composer:

composer require cloudobjects/sdk

To use the SDK to access CloudObjects Core, you need to get the shared secret between your domain and cloudobjects.io, which, incidentally, I showed you in the previous section. You provide your namespace domain and the shared secret as constructor parameters for an ObjectRetriever. While you’re at it, I recommend adding a cache to improve performance, such as a basic filesystem cache. Here’s the code to initialize the ObjectRetriever:

$retriever = new CloudObjects\SDK\ObjectRetriever([
    'auth_ns' => 'MYDOMAIN',
    'auth_secret' => 'SHARED_SECRET',
    'cache_provider' => 'file',
    'cache_provider.file.directory' => __DIR__.'/cache'
]);

Next, you need the SharedSecretAuthentication helper, which requires the previously initialized ObjectRetriever as a constructor parameter:

$authHelper = new CloudObjects\SDK\Helpers\SharedSecretAuthentication($retriever);

For every API request, you need to check whether the username is a valid CloudObjects namespace and the password matches the shared secret between their namespace and your namespace. To do so, you pass the username and password to the verify() function:

$authResult = $authHelper->verify($username, $password);
if ($authResult == CloudObjects\SDK\Helpers\SharedSecretAuthentication::RESULT_OK) {
    // TODO: Handle API request ...
} else {
    // TODO: Report authentication error
}

Of course, the specific integration is different depending on the framework that you use for your API. I’ll show you an example using Mika Tuupola’s HTTP Basic Authentication middleware, which works with frameworks supporting PSR-7/PSR-15 style middlewares, such as Slim:

$app = new Slim\App;
$app->add(new Tuupola\Middleware\HttpBasicAuthentication([
    'authenticator' => function ($arguments) use ($authHelper) {
        return ($authHelper->verify($arguments['user'], $arguments['password'])
            == CloudObjects\SDK\Helpers\SharedSecretAuthentication::RESULT_OK);
    }
]));

That’s all you need to get started!

CloudObjects directory integration

When you look at the directory page for our Object API, you’ll see that the API supports HTTP Basic Authentication with CloudObjects shared secrets. There is also a short set of instructions mimicking the explanations in this article. There’s another excellent feature. When you are signed in to CloudObjects, you can open one of the API operations, switch to the “Try it” tab, and click on Configure shared secret authentication. A popup opens where you can select any domain of which you are a member. The directory page retrieves the shared secret and includes it into the web-based API test client. In other words, API consumers can find your API in the directory, immediately create the credentials required to try the API, and make requests straight from their browser!

As an API provider, you can submit your API as a wa:WebAPI object. I’ve previously written a tutorial for creating them from scratch, and an OpenAPI importer is currently being implemented. To indicate in your API that it supports HTTP Basic Authentication with CloudObjects shared secrets, you can add the property wa:supportsAuthenticationMechanism with the value wa:SharedSecretAuthenticationViaHTTPBasic to your object, as shown in the following example:

@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix wa: <coid://webapi.cloudobjects.io/> .

<MYAPICOID> rdf:type wa:WebAPI ;
      wa:supportsAuthenticationMechanism wa:SharedSecretAuthenticationViaHTTPBasic .

How does it work?

This section is only for those curious to know how shared secret authentication works under the hood, e.g. because you want to implement the SDK’s behavior in a different language. So, here we go:

Whenever the SDK verifies username and password, it attempts to fetch the username’s namespace as an object from CloudObjects Core using the Object API. Whenever the API retrieves a request for a namespace object other than the one authenticating against the API, it includes the shared secret as an RDF triple with the response. You can try this with the Object API in the directory by opening “GET /{namespace}/object.{extension}” and going to the “Try it” tab. Click Configure shared secret authentication and select your domain, then confirm with the Use these credentials button. Enter a public namespace such as “phpmae.cloudobjects.io” for the namespace parameter and set the extension to “jsonld”. After you hit Send request, you’ll find a co:SharedSecret entry in the response with co:hasTokenValue set to the shared secret between your domain and “phpmae.cloudobjects.io”. Voilá, that’s all the magic there is!

Get started!

I invite you to play around with shared secret authentication and implement it in your next API. Feel free to contact us when you need any help setting this up or get your API into our directory.

by Lukas Rosenstock

Show Disqus Comment Thread
Privacy Notice: Your IP address will be sent to Disqus when you enable comments.