We use cookies on this site to enhance your user experience. By clicking "OK, I Agree" or using our site, you consent to the use of cookies unless you have disabled them.
View our cookie policy to learn more.

Suddenly, Brent is jolted awake at noon to the sound of farmer Scott driving
his eggs to the market and screaming:

Haha, Brent! My chickens lay way more eggs than yours!

But in reality, Brent knows that his chickens are way better egg-making hens
than Scott's... but how to prove it?

Then it hits him! The COOP API has an endpoint to see how many eggs have
been collected from a user's farm each day. Brent decides to create a new
website that will use this endpoint to count how many total eggs a COOP user's
farm has collected. He'll call it: Top Cluck! Fantasy Chicken League, or FCL for short.
To call the /api/eggs-count endpoint on behalf of each user, the site
will use OAuth to collect an access token for every farmer that signs up.

Once again, the question is: how can each user give FCL an access token that
allows it to count eggs on their behalf?

Let's check out the FCL app, which Brent has already started building. It
lives in the client/ directory of the code download. I'll use the built-in
PHP web server to run this site:

cd client/web

php -S localhost:9000

Tip

Code along with us! Click the Download link on this page to get the starting
point of the project.

That command starts a built-in PHP webserver, and it'll just sit right there
until we're ready to turn it off. This project also uses Composer, so let's
copy the composer.phar file we used earlier into this directory and use
install to download some outside libraries the project uses.

Tip

If this doesn't work and PHP simply shows you its command-line options,
check your PHP version. The built-in web server requires PHP 5.4 or higher.

Put the URL to the site into your browser and load it up. Welcome to Top Cluck!
We already have a leaderboard and basic registration. Go ahead and create an
account, which automatically logs us in.

The site is fully-functional, with database tables ready to keep track of
how many eggs each farmer has collected. The only missing piece is OAuth:
getting the access token for each user so that we can make an API request
to count their eggs.

The first step of the authorization code grant type is to redirect the user
to a specific URL on COOP. From here the user will authorize our app.
According to COOP's API Authentication page, we need to redirect
the user to /authorize and send several query parameters.

The response_type type is code because we're using the Authorization
Code flow. The other valid value is token, which is for a grant type
called implicit flow. We'll see that later.

For scopes, we're using profile and eggs-count so that once we're
authorized, we can get some profile data about the COOP user and, of course,
count their eggs.

To get a client_id, let's go to COOP and create a new application that
represents TopCluck. The most important thing is to check the 2 boxes for
profile information and collecting the egg count. I'll show you why in a second.

Tip

If there is already an application with the name you want, just choose
something different and use that as your client_id.

Copy the client_id into our URL. Great! The last piece is the redirect_uri,
which is a URL on our site that COOP will send the user to after granting
or denying our application access. We're going to do all kinds of important
things once that happens.

Let's set that URL to be /coop/oauth/handle, which is just another page
that's printing a message. The code for this is right inside the same file,
a little further down:

Let's try it! Go back to the homepage and click the "Authorize" link. This
takes us to our code, which then redirects us to COOP. We're already logged
in, so it gets straight to asking us to authorize the app. Notice that the
scopes that we included in the URL are clearly communicated. Let's authorize
the app. Later, we'll see what happens if you don't.

When we click the authorization button, we're sent back to the redirect_uri
on TopCluck! Nothing has really happened yet. COOP didn't set any cookies
or anything else. But the URL does include a code query parameter.

This query parameter is called the authorization code, and it's unique
to this grant type. It's not an access token, which is really what we want,
but it's the key to getting that. The authorization code is our temporary
proof that the user said that our application can have an access token.

Let's start by copying the code from the collect_eggs.php script and
pasting it here. Go ahead and change the client_id and client_secret
to be from the new client or application we created for TopCluck:

If we look back at the COOP API Authentication docs, we'll see that /token
has 2 other parameters that are used with the authorization grant type: code
and redirect_uri. I'm already retrieving the code query parameter, so
let's fill these in. Make sure to also change the grant_type to
authorization_code like it describes in the docs. Finally, dump the
$responseBody to see if this request works:

The key to this flow is the code parameter. When COOP receives our request,
it will check that the authorization code is valid. It also knows which user
the code belongs to, so the access token it returns will let us make API requets
on behalf of that user.

But what about the redirect_uri? This parameter is absolutely necessary
for the API request to work, but isn't actually used by COOP. It's a security
measure, and it must exactly equal the original redirect_uri that we
used when we redirected the user.

Ok, let's try it! When we refresh, the API actually gives us an error:

The authorization code has a very short lifetime, typically measured in seconds.
We normally exchange it immediately for an access token, so that's ok! Let's
start the whole process from the homepage again.

Tip

Usually, an OAuth server will remember that a user already authorized an
app and immediately redirect the user back to your app. COOP doesn't do this
only to make things easier to understand.

This time, the API request to /token returns an access_token. Woot!
Let's also set expires_in to a variable, which is the number of seconds
until this access token expires:

Just like in our CRON script, let's use the access token to make an API request!
One of the endpoints is /api/me, which returns information about the user
that is tied to the access token. Let's make a GET request to this endpoint,
setting the access token on the Authorization header, just like we did
before:

Try it by going back to the homepage and clicking "Authorize". Simply refreshing
the page won't work here, as the authorization code will have already expired.
With any luck, you'll see a JSON response with information about the user:

This works of course because we're sending an access token that is tied to
Brent's account. This also works because when we redirect the user, we're
asking for the profile scope.

And with that, we've seen the key parts of the authorization code grant type
and how to use an access token in our application. But where should we store
the token and what if the user denies our application access? We'll look
at these next.

Leave a comment!

2020-04-23Vladimir Sadicov

Yep Thanks for suggestion, I think it will be really great!!!

Cheers!

2020-04-22bandroidx

I would suggest putting a notice on the start of the tutorial that you must use the new URL so others dont go crazy like I did....

2020-04-22bandroidx

YES! it works! OMG I was tearing my hair out because for some reason the POST's all work fine with the redirect but the GET's didnt.

Can you try to change uri of COOP app to http://coop.apps.symfonycas... I guess it can work not stable due to redirect to the new domain, however it should work. If it doesn't help let me know here and we'll try to investigate it.

Cheers!

2020-04-22bandroidx

Is coop still be working in 2020? I am going out of my mind trying to follow this tutorial and i am doing it using the new HttpClient and I cant get it to work and I am starting to wonder if the issue is with Coop (no idea if its still maintained) because i started getting some weird 400 errors too. I have this and it keeps telling me i need an access token (but if i post to pick eggs that works fine):

Sorry for the slow reply! For some reason, your comment ended up in the Disqus Spam :(.

I this the entire error? It looks like some sort of MySQL error, but I don't actually see the error. It's possible that some of the columns that are being set to null are required, but I'm not sure! If you have a screenshot of the error, that would be perfect :).

i got this error why this happend when i register ? I didnt change anything from that script.

2017-07-10Victor Bocharsky

Hey Todd,

Good debugging! So there're 2 possible good ways I think:- Start using asset() Twig function: https://silex.sensiolabs.or...- Or you can cheat a bit with homepage URL, for example: "{{ path('homepage') }}/css/bootstrap.css", where homepage route always is a correct address of the web/ folder, so you just need to add the path to assets *relative* to the web/.

Cheers!

2017-07-09Todd Jackson

Hey Victor,I've tried debugging 'app.request.basePath', yes this is the cause of the problem, as it returns empty.My document root for it's nginx config points to the web/ folder. All the bootstrap css & js files reside in the correct path.I can navigate to other pages such as login & register etc..but css & js path's don't get resolved.

Cheers.

2017-07-05Victor Bocharsky

Hey Todd,

Could you debug what is your "app.request.basePath" path is? Is it a path to web/ directory where your index.php is? Do you configure web/ folder as a document root in Nginx config? Are you sure that bootstrap.css file exists in path/to/web/css/bootstrap.css? Can you access any other files in document root except index.php? Also, could you get a page other than homepage? Does it works the same way?

Cheers!

2017-07-05Todd Jackson

PS - I'm using the Silex conf setup for nginx as provided on Silex setup page

2017-07-05Todd Jackson

I'm running nginx, and have created the conf file for this 'client' site, I'm using a port rather than socket for php-fpm in nginx conf file for 'client' site i.e. 127.0.0.1:9000, and added it server name to local hosts file.

I get the home page but all the css & js not found, i.e. {{ app.request.basePath }}/css/bootstrap.css }} doesn't resolve properly.

I've done the composer install, is there anything else I'm missing?

2017-06-14Victor Bocharsky

Hey Debra,

First of all, do you use Silex framework as we do in this screencast? If no, then generateUrl() won't work until you create it. But if you use Silex as we, let's fix the problem with this generateUrl(), but I need to know what exactly error do you have with it to help you fix it.

About hard-coded the redirect_uri - no problem, I don't think the problem in it, of course, if you hard-coded the correct URL ;)

And, in any case, how could the receiveAuthorizationCode() even run if COOP is redirecting to /web/handle.php and the function that needs to fire is inside /src/OAuth2Demo/Client/Controllers/CoopOAuthController.php?

That's a good question. In shorts, you need a front-controller, an entry point for every request to your application - it's web/index.php file in out case. Then with router system you point a route (a specific URL) to a specific controller, e.g. you set up "receiveAuthorizationCode()" to match the "/coop/oauth/handle" route, so when you go to the"/coop/oauth/handle" URL - the receiveAuthorizationCode() method will be fired up. We configure these routes in "CoopOAuthController::addRotues()". But if you're not using Silex, it's almost pointless for you - you need to implement this in your application somehow. Actually, it's basics of any framework.

What about replacing redirect() with a more standard PHP redirect code, it depends on what framework do you use. If you're on Silex, then you better use this redirect() built-in function. But if you're write withut any framework, of course this redirect() won't work since most probably you don't have this function. Then you need to use native PHP support to make redirects:

Yes, you can use it for sure - they are pretty stable now. Btw, those libs are from a guy who works at Google ;) There's another one cool lib I could advice you: https://github.com/thephple... . However, if you want to match our screencasts - bshaffer's libs are a good choice I think.

Cheers!

2017-06-09Debra Gitterman

In the "Authorization Code Grant Type" video, where we are "Exchanging the Authorization Code for an Access Token",

I can't get the $responseBody to var_dump in the receiveAuthorizationCode() function (copied below). Instead of the expected $responseBody, I see the "Hello World" string that my redirect_uri file /web/handle.php prints out.

And, in any case, how could the receiveAuthorizationCode() even run if COOP is redirecting to /web/handle.php and the function that needs to fire is inside /src/OAuth2Demo/Client/Controllers/CoopOAuthController.php?

What have I done wrong?

Also, I'm wondering if I need to replace this line with more standard PHP since the course says that that function is customized for the tutorial's app. (although I'm not sure what standard PHP redirect code is).

In server/composer.json I see the following two components"require": {..."bshaffer/oauth2-server-php": "dev-develop","bshaffer/oauth2-server-httpfoundation-bridge": "dev-master",...}

I need to implement an OAuth2 server. Would you recommend to use these libraries for my purposes, or are there better alternatives. I'm not done with this course and haven't done much research yet, that's why I wanted to ask your suggestion.Thanks,

2017-06-05weaverryan

Thanks Vlad! And yep, the content of this tutorial is holding strong... but some of the tools that we used are aging. If you use download the "start" course code, it all should work - that uses all the original versions of the libraries. For the most part, those tools are really secondary to what we're learning anyways.

But, btw, if anyone has any issues, definitely let us know :).

Cheers!

2017-06-03Vlad

I had to add downgrade to Symfony2 by adding the following line:"require": { "symfony/symfony": "2.*", ...

to client/composer.json in order to follow this tutorial. Otherwise you'll get errors due to deprecation and removal errors.

2016-12-31Ramon Felipe

Thanks!

2016-12-30Victor Bocharsky

Hey Ramon,

The "OAuth2 in 8 Steps" is just a name! :) So $12 is a price of the whole course, which contains of 11 chapters.

Cheers!

2016-12-30Ramon Felipe

Question on the course payment ($12). Is that for the entire OAuth course, the complete 8 steps, or just this step? Thanks.

2016-02-28luowen

very usefull

2016-02-25weaverryan

Hey Nathan! I'm glad you got it sorted out - I was just looking through your first comment when I saw you had already fixed it. Awesome! Good luck!

2016-02-24Nathan B.

I figured out what I was doing wrong. I was using $this instead of $user when setting the coopAccessToken, coopUserId, and coopAccessExpiresAt properties. Changing the end of my code to the following worked:

Near the end of video "Authorization Code Grant Type", I first receive the access token and then use that access token to connect to the /api/me endpoint with an "Authorization" header. I get the proper json response including my Coop user_id and then try to save to my local db which fails. I've stepped through the code with Xdebug to verify this and it appears to try and create a new user in the sqlite db instead of updating. My code is below. Thank you for your help:

Hey Folks thanks for this tutorial!I've seen the composer get broken by version incompatibility while versions climbs higher... The easiest way around this, is to go fix the version numbers.In composer.json adjust every version according to this pattern: "1.2.3"So remove every "~"-sign and if the the version is composed out of two parts (major and minor), then add a third part (the build) ".0" to it. So for example you get from "~1.5" to "1.5.0".Safe the file, do 'composer update' and your good to go.

2015-04-18Peter Couture

Thank you for the tutorial. I've been staring at the RPC spec docs for 3 weeks and my eyes were sore.

2015-01-25nglaser power

If you are getting a pdodriver not found exception you may have to install sqlite and php5-sqlite. I am running on vagrant box ubuntu 14.04.

Here are the commands if you are ubuntu flavored linux

sudo apt-get install sqlitesudo apt-get install php5-sqlite

2015-01-23weaverryan

Hey Saad!

Right now, the download button doesn't show up unless you own this screencast (or have the all-access pass). I want to fix that, but for now, you can also get the starting state of the project right form GitHub: https://github.com/knpunive...