Buy

Client Credentials

Client Credentials

Meet Brent. He’s the hardworking, beard-growing, kale-munching type who has a coop of the nicest, smartest, and best egg-laying chickens this side o’ the Mississippi! But feeding his chickens and doing other things around the farm has always taken a lot of time.

But great news! The brand new “Chicken Oversight Operations Platform”, or COOP site has just launched! With COOP, you can login to the site and collect your chicken eggs, unlock the barn, and do all kinds of other things just by clicking a button.

Noticing that COOP has an API, Brent wonders if he could write a little script that would collect his eggs automatically. Yea, if he had a script that made an API request on his behalf, he could run it on a CRON job daily and sleep in!

So COOP is real, sort of. You can find this make-believe website by going to http://coop.apps.knpuniversity.com. Go ahead and create an account, and start controlling your virtual farm. It’s the future!

Starting our Command-line Script

COOP’s API is simple, with just a few endpoints, including the one we want for our little command-line script: eggs-collect.

I’ve already made a cron/ directory with a script called collect_eggs.php that’ll get us started:

// collect_eggs.php
include __DIR__.'/vendor/autoload.php';
use Guzzle\Http\Client;

// create our http client (Guzzle)
$http = new Client('http://coop.apps.knpuniversity.com', array(
    'request.options' => array(
        'exceptions' => false,
    )
));

Tip

Code along with us! Click the Download button on this page to get the starting point of the project, and follow the readme to get things setup.

It doesn’t do anything except create a Client object that’s pointing at the COOP website. Since we’ll need to make HTTP requests to the COOP API, we’ll use a really nice PHP library called Guzzle. Don’t worry if you’ve never used it, it’s really easy.

Before we start, we need to use Composer to download Guzzle. Download Composer into the cron/ directory and then install the vendor libraries:

php composer.phar install

Note

New to Composer? Do yourself a favor and master it for free: The Wonderful World of Composer.

Let’s try making our first API request to /api/2/eggs-collect. The 2 is our COOP user ID, since we want to collect eggs from our farm. Your number will be different:

// collect_eggs.php
// ...

$request = $http->post('/api/2/eggs-collect');
$response = $request->send();
echo $response->getBody();

echo "\n\n";

Try it by executing the script from the command line:

php collect_eggs.php

Not surprisingly, this blows up!

{
  "error": "access_denied",
  "error_description": "an access token is required"
}

OAuth Applications

But before we think about getting a token, we need to create an application on COOP. The application represents the external app or website that we want to build. In our case, it’s the little command-line script. In OAuth-speak, it’s this application that will actually ask for access to a user’s COOP account.

Give it a name like “Brent’s Lazy CRON Job”, a description, and check only the box for “Collect Eggs from Your Chickens”. These are “scopes”, or basically the permissions that your app will have if a token is granted from COOP.

When we finish, we now have a Client ID and an auto-generated “Client Secret”. These are a sort of username and password for the application. One tricky thing is that the terms “application” and “client” are used interchangeably in OAuth. And both are used to refer to the application we just registered and the actual app you’re building, like the CRON script or your website. I’ll try to clarify along the way.

Now, let’s get an access token!

Client Credentials Grant Type

The first OAuth grant type is called Client Credentials, which is the simplest of all the types. It involves only two parties, the client and the server. For us, this is our command-line script and the COOP API.

Using this grant type, there is no “user”, and the access token we get will only let us access resources under the control of the application. When we make API requests using this access token, it’s almost like we’re logging in as the application itself, not any individual user. I’ll explain more in a second.

If you visit the application you created earlier, you’ll see a nice “Generate a Token” link that when clicked will fetch one. Behind the scenes, this uses client credentials, which we’ll see more closely in a second.

http://coop.apps.knpuniversity.com/token
    ?client_id=Your+Client+Name
    &client_secret=abcdefg
    &grant_type=client_credentials

But for now, we can celebrate by using this token immediately to take actions on behalf of the application!

Access Tokens in the API

Exactly how to do this depends on the API you’re making requests to. One common method, and the one COOP uses, is to send it via an Authorization Bearer header.

GET /api/barn-unlock HTTP/1.1
Host: coop.apps.knpuniversity.com
Authorization: Bearer ACCESSTOKENHERE

Update the script to send this header:

// collect-eggs.php
// ...

$accessToken = 'abcd1234def67890';

$request = $http->post('/api/2/eggs-collect');
$request->addHeader('Authorization', 'Bearer '.$accessToken);
$response = $request->send();
echo $response->getBody();

echo "\n\n";

When we run the script again, start celebrating, because it works! And now we have enough eggs to make an omlette :)

{
  "action": "eggs-collect",
  "success": true,
  "message": "Hey look at that, 3 eggs have been collected!",
  "data": 3
}

Trying to Collect Someone Else’s Eggs

Notice that this collects the eggs for our user becase we’re including our user ID in the URL. What happens if we change id to be for a different user?

/api/3/eggs-collect

If you try it, it fails!

{
  "error": "access_denied",
  "error_description": "You do not have access to take this action"
}

Technically, with a token from client credentials, we’re making API requests not on behalf of a user, but on behalf of an application. This makes client credentials perfect for making API calls that edit or get information about the application itself, like a count of how many users it has.

We decided to build COOP so that the application also has access to modify the user that created the application. That’s why we are able to collect our user’s eggs, but not our neighbor’s.

Getting the Token via Client Credentials

Put the champagne away: we’re not done yet. Typically, access tokens don’t last forever. COOP tokens last for 24 hours, which means that tomorrow, our script will break.

Letting the website do the client-credentials work for us was nice for testing, but we need to do it ourselves inside the script. Every OAuth server has an API endpoint used to request access tokens. If we look at the COOP API Authentication docs, we can see the URL and the POST parameters it needs:

http://coop.apps.knpuniversity.com/token

Parameters:
client_id client_secret grant_type

Let’s update our script to first make this API request. Fill in the client_id, client_secret and grant_type POST parameters:

// collect-eggs.php
// ...

// run this code *before* requesting the eggs-collect endpoint
$request = $http->post('/token', null, array(
    'client_id'     => 'Brent\'s Lazy CRON Job',
    'client_secret' => 'a2e7f02def711095f83f2fb04ecbc0d3',
    'grant_type'    => 'client_credentials',
));

// make a request to the token url
$response = $request->send();
$responseBody = $response->getBody(true);
var_dump($responseBody);die;
// ...

With any luck, when you run it, you should see a JSON response with an access token and a few other details:

{
  "access_token": "fa3b4e29d8df9900816547b8e53f87034893d84c",
  "expires_in": 86400,
  "token_type": "Bearer",
  "scope": "chickens-feed"
}

Let’s use this access token instead of the one we pasted in there:

// collect-eggs.php
// ...

// step1: request an access token
$request = $http->post('/token', null, array(
    'client_id'     => 'Brent\'s Lazy CRON Job',
    'client_secret' => 'a2e7f02def711095f83f2fb04ecbc0d3',
    'grant_type'    => 'client_credentials',
));

// make a request to the token url
$response = $request->send();
$responseBody = $response->getBody(true);
$responseArr = json_decode($responseBody, true);
$accessToken = $responseArr['access_token'];

// step2: use the token to make an API request
$request = $http->post('/api/2/eggs-collect');
$request->addHeader('Authorization', 'Bearer '.$accessToken);
$response = $request->send();
echo $response->getBody();

echo "\n\n";

Now, it still works and since we’re getting a fresh token each time, we’ll never have an expiration problem. Once Brent sets up a CRON job to run our script, he’ll be sleeping in ‘til noon!

Why, What and When: Client Credentials

Every grant type eventually uses the /token endpoint to get a token, but the details before that differ. Client Credentials is a way to get a token directly. One limitation is that it requires your client secret, which is ok now because our script is hidden away on some server.

But on the web, we won’t be able to expose the client secret. And that’s where the next two grant types become important.

Leave a comment!

  • 2016-11-30 Souhail Merroun

    weaverryan a pleasure :)

  • 2016-11-30 weaverryan

    Thanks for sharing Souhail Merroun!

  • 2016-11-30 Souhail Merroun

    Hey folks,
    For those who have the error "The grant type was not specified in the request".
    The video is done with an old Guzzle version.
    Here is a snippet with a new one.

    https://gist.github.com/souhai...

  • 2016-10-07 Victor Bocharsky

    Hey Serdan1689 ,

    Please, check out Ryan's explanation on the similar comment below: https://knpuniversity.com/screencast/oauth/client-credentials#comment-2802524257 . What REST client are you using here?

    Cheers!

  • 2016-10-06 Serdan1689

    $request = $client->post('/token', null, array(
    'grant_type' => 'client_credentials',
    'client_id' => "Daniel's CRON Job",
    'client_secret' => 'KEY',
    ));

    Is there something wrong with this request? I keep getting a 400 response:

    {"error":"invalid_request","error_description":"The grant type was not specified in the request"}

    When I comment that section out it works fine. I'm not sure what's going wrong.

  • 2016-08-12 weaverryan

    Hey Andjii!

    We don't talk about FOSOAuthServerbundle on any tutorials, but that might be great idea for a tutorial! Is the documentation unclear? Or are you stuck on anything specifically? This tutorial talks entirely about the *client* side of OAuth (not the server) - but it should help make OAuth in general a lot more clear (which is important for using OAuth as a client or a server).

    Cheers!

  • 2016-08-12 Andjii

    How to use it with FOSOAuthServerBundle for symfony 3? I couldn't find any explanations or tutorials... Could you, please, provide some help?

  • 2016-07-25 weaverryan

    Hi Raghu!

    Yes, using a standalone REST client is a perfectly valid thing to do :).

    In this case, those 3 parameters should be sent as "form encoded body". Or, this may be called other things - like "form parameters" or "x-www-form-urlencoded" encoded data. This is actually the *most* traditional way to send data in a request, and it's not exactly *any* of the three options that you see in your REST client :/. What REST client are you using?

    Cheers!

  • 2016-07-22 Raghu Sodha

    I am not much familiar with PHP, So I thought of learning and following tutorial with a stand-alone REST client.

    I this PHP piece of code to get access token.

    $request = $http->post('/token', null, array(
    'client_id' => 'Brent\'s Lazy CRON Job',
    'client_secret' => 'a2e7f02def711095f83f2fb04ecbc0d3',
    'grant_type' => 'client_credentials',
    ));

    My question is, How do I pass these parameters in stand alone REST
    client? Options I have are 1. Query Parameter 2. Headers 3. a JSON post
    data.

    I tried all three and get following error for all three.
    {"error":"invalid_request","error_description":"The grant type was not specified in the request"}

    Could you please suggest what could be going wrong here.

  • 2016-07-20 weaverryan

    Thanks for posting that Sébastien Rosset! You're absolutely right - and fortunately, version 3 of Guzzle is still available (but getting quite old). But yes - if you are ok re-working some of your Guzzle code (just like you did), then feel free to use the new version :).

    Cheers!

  • 2016-07-20 Sébastien Rosset

    For information, the code given in this tutorial works with GuzzleHTTP 3 or earlier. On later version, it is very different.

    Here's what I did to make it work:

    include __DIR__.'/vendor/autoload.php';
    use GuzzleHttp\Client;

    $client = new Client( [

    'base_uri' => 'http://coop.apps.knpuniversity...',
    'defaults' => [
    'exceptions' => false,

    ]
    ]);

    $response = $client->post( 'api/2/eggs-collect', [
    'headers' => [
    'Authorization' => 'Bearer tokensdcsdcsdc66561'
    ]
    ]);

    echo $response->getBody();

  • 2016-05-25 weaverryan

    Hi Rex!

    Yep, that's one way - glad you figured that out :). There's also a download link on the upper right of this page if you're a subscriber.

    Cheers!

  • 2016-05-23 Rex Tsao

    I guess I got the start project from Github, thanks!

  • 2016-05-23 Rex Tsao

    Hi, where can I download the start project? I can't find it.

  • 2016-05-04 weaverryan

    Yep, unfortunately - Guzzle came out with a flurry of releases in a short time (4, 5 & 6). Fortunately, you an still install version 3 without (most) conflicts - because they renamed the library afterwards and still maintain those docs. And (also fortunately) the HTTP client itself isn't the central focus of the tutorial.

    That being said, I *love* this tutorial - so it's on our list to update it someday to make it easier to use Guzzle :).

    Cheers!

  • 2016-05-04 Rui Peng

    Guzzle seems to have changed a lot. The current version is Guzzle 6. The way it is used has also changed. Would you please add more information on how to use Guzzle 6? It is just a little confusing. I am looking for some information on OAuth2 for Nodejs.

  • 2015-09-03 weaverryan

    Hey Mattia!

    I haven't used FOSOAuthServerBundle yet, so I won't be a big help to you :/. But, it looks like FOSOAuthBundle supplies much of what you need already, right? If you revoke a token, I would imagine that FOSOAuthServerBundle already denies access for revoked tokens. And if you want to use tokens in your JavaScript, you'll just need to setup a way for your JavaScript to get an access token - either via the implicit flow, or possibly just by having the user login with a username/password and then returning an access token (which should be pretty easy).

    Sorry I can't be more help - I'm just not very familiar with FOSOAuthServerBundle. Related to authentication, KnpU Guard might be useful: https://knpuniversity.com/scre... - or everything may already be handled by the OAuth bundle :).

    Good luck!

  • 2015-08-31 Mattia Mariselli

    Can someone help me?

  • 2015-08-28 Mattia Mariselli

    Hi, I want to use FOSOAuthBundle to manage all the login in my Application. I’d like that when a user log in, Symfony doesn’t store the UsernamePasswordToken but store, instead, the Access Token provided by FOSOAuth bundle. Doing this I’d like that when an Access Token is revoked, the user logged will be logged out. I also would like that in the frontend of my website, the Javascript use the same API that the mobile application use. I hope i did explain well my problem.

  • 2015-07-09 weaverryan

    Will do - it's not smooth yet - I probably would have had the same question as you :)

  • 2015-07-09 Pierre de LESPINAY

    It sure does, thanks. Maybe you should mention the fact that one needs to subscribe to get the files, for those coming from a Google search like me.

  • 2015-07-09 weaverryan

    Hey Pierre!

    Yes, we do start with some files - if you're a subscriber, there's a Download button on this page that has the stuff you'll need. That download comes with a start directory and a finish directory - we're working in the start.

    If you don't have a subscription, you can still get the start code directly form the repository: https://github.com/knpuniversi....

    Does that help?

    Cheers!

  • 2015-07-08 Pierre de LESPINAY

    You are talking about installing the vendors with composer, but I can't find your requirements.
    I have the impression you start from existing files, where can I find them if it is the case ?

  • 2015-05-17 weaverryan

    Hi Chris!

    You're exactly right: that's up to your business logic code. So basically, with client credentials, the token you're using means you're authenticating "on behalf of the application" that the token belongs to. Then, in your business code, if you wanted, you could say "since UserA is the owner of Application X, if someone sends a token for Application X, then they have access to things on UserA's account". So, purely up to you :).

    Great question!

  • 2015-05-15 Chris Peng

    I am trying to build a oauth-based api server with client credential grant type, I find that I don't know how does oauth know '/api/2/egg' and '/api/3/egg' belongs to different token, when a client get a token with client credential grant, it passed my oauth layer and gets everything, is this logic implemented by additional business code?