1.2 Understanding and Using OAuth in Laravel
In this lesson, we’ll dive into the low-level details of an OAuth implementation. We’ll examine the actual PHP code that makes it happen, and look over endpoints, requests, and responses in detail. It’s an integration between two applications, so we’ll be jumping back and forth between two codebases as we go through the flow.
1.2 Understanding and Using OAuth in Laravel
[SOUND] Hello and welcome to this Tuts+ Coffee Break Course on stepping through a simple OAuth implementation. We've got a lot of ground to cover, so we'll jump right into it. Let's start on the main home page of WormRate with a user logged in. The goal here is to have an integration between WormRate and MyBookshelf so that data from MyBookshelf flows down into WormRate. That way a user who happens to use both applications can see the data from both apps correlated together in one interface. In this case, WormRate will look at the books you have in MyBookshelf, and let you know if there's a book that you own over there that you haven't written a review for over here. If we wanna activate the integration, all we have to do is click this button right here that says, Integrate MyBookshelf. When we click that, we get kicked over to the MyBookshelf app and we get this screen which is an OAuth login screen. If you notice here, we've got the name of the application that's requesting access and we also have a list of roles right here that the app is asking for. It knows which app is asking for what because we have WormRate passed up here as the client ID, and we've also got a redirect URI right here which we'll be using later on. The response_type is code because we're using the authorization code grants, which means that WormRate will receive an authorization code from MyBookshelf, and then activate it to receive an access_token. That's the whole goal of the OAuth flow, so that WormRate will have an access_token so that they can make calls against MyBookshelf API. Let's go into the code from MyBookshelf and check out what's powering this login screen right here. To see that login screen, we're hitting this route right here. It's a get request to OAuth/authorize, which takes us to the login screen action on the OAuth controller. If we go into the OAuth controller, this is one file that gives us all the actions related to the OAuth integration. If we go here into the login screen route, we identify which app is requesting permission through the client ID field, and then we look up some information from the database to see what information we need to display. Here's where we're fetching the scopes that you saw in that bulleted list, and then we're rendering it onto a blade template which looks like this. Here's where the name of the app gets displayed, here's where the permissions get displayed. And when all is said and done, the user will either approve or deny the authorization. So when we submit this form, what we expect to happen is that an authorization code will get generated. It'll be grafted onto a redirect URI and then will redirect the user to that redirect URI. The redirect URI is going to be endpoints on WormRate that says, okay, we've approved the authorization, so here's what you need to proceed. From there, WormRate will activate the authorization code, and then they'll have their first access_token, and the integration will be complete. Now I could just click Approve right now and finish the whole thing for you. I think we'd do better to go through it manually with Postman, so that you can see all the different API calls that need to be made during this process. So let's check out the code for what happens when we do submit this. The form gets submitted to this route called oauth.authorize.post, and when we look at that here in the routes file, that goes to the login submit action on the OAuth controller. So if we go down into that action we see that we're detecting, okay, who submitted it, and whether or not they approved or denied the request. If they denied it, we're not really gonna do anything. We'll just send the user back where they came from. But if they approved it, we're gonna generate an auth code. The auth code is gonna be part of this redirectUri right here and then we actually perform the redirect to the redirectUri. But since we're gonna be doing this in Postman and not letting the application code handle everything, let's go ahead and put in some debug statements. All we need for right now is the authorization code, so we'll echo that out and then put a die. And now if I hit approve, there is our redirect URI with the auth code grafted on to the end right there. This is where we would have been redirected to, and if I wanted to, I could just paste this whole thing into the browser and do the redirect myself. So let's go over to the WormRate code and have a look at the endpoint that we would be hitting if we did visit this URL. We've got a route here on the WormRate app called /mybookshelf that takes us to the MyBookshelf action on the HomeController. So if we go over there, what we're doing here is we're creating a new instance of the MyBookshelfClient class. So we've got a client object now and we pass in the currently logged in user as an argument. We've got a handy method here on the MyBookshelfClient class called activateAuthorizationCode. All you have to do is pass in the code as an argument and that'll take care of activating it. So if we go into the MyBookshelfClient class and we go down to the activateAuthorizationCode method, you'll see here that we're using a library called Guzzle. Guzzle is an HTTP client, just like Curl or Postman, but it's actually for PHP. So you write out what the API call is supposed to look like in PHP code and you can make your API calls programmatically inside your application. So all we do is fill in the appropriate parameters according to the OAuth specification. We pass in all the variables that we need, which are the authorization code, the client's ID, and secret, which are constants that we have up here at the top of the file. And once we've got it activated, we're gonna convert the JSON response to an array, and we're gonna store the access_token, and refresh_token that come back onto the user's database record. Now that we've gone through the code on the WormRate side, let's check out the PHP code back over on the MyBookshelf side at the endpoint that receives the request to activate the authorization code. That route is gonna be this one right here which is opposed to oauth/access_token. Instead of routing over to a controller, I just pass in an anonymous function right here. This call that you see right here is part of, a composer package for Laravel l that I'm using, which sets up a lot of the plumbing that you need to make an app into an OAuth provider. We can pretty much just trust that library that it's doing its job. So to better illustrate that call for you, let's go ahead and call it using Postman so that you can see what the request and response look like. I've got a handy preset saved already for this call, Activate Authorization Code to get First Access Token. So if I go over to the body, we've got a few different fields that we wanna pass in as params. The grant_type is gonna be authorization _code, note the client_id and client_secret from those constants that we have in the MyBookshelfClient class. And then for extra security, we're also required to include the redirect URI which is the base redirect URI, not with the code or any of those params grafted onto it. So all we need to do now is grab our authorization code and paste it in right here. So if we paste that in and we submit this, and in the response we get an access_token and a refresh_token. What you see right here where it says expires in 7200, that's 7200 seconds. So an access_token has a limited life span. It's a security feature of OAuth. That way if an access_token gets leaked, well, at least they didn't have unauthorized access for very long. Now normally WormRate would take this access_token and refresh_token and save it onto the user record inside the WormRate database, but if we look in the database, we see that those values are not filled in. Well, that's because we made the call manually using Postman. It would have been WormRate's job to actually do this. But for these fields to be saved, we need to actually have WormRate make this call to activate the authorization code. We can't do it ourselves in Postman. So what we can do now is remove those debug statements, run through the OAuth flow again, and we should see an access_token and refresh_token saved here.l So I'll go ahead and remove these, and let's give this a try. So now when we chose to approve the authorization, it kicked us over to the redirect URI on the WormRate app. Here's our code right here, WormRate did its thing of activating the auth code, saving the access_token and refresh_token. And if we go to the home page, we see that data from MyBookshelf is now coming through. These are three books that I own according to the data in MyBookshelf, but I haven't written a review for them inside of WormRate. And now if we have a look in the database, we see that an access_token and a refresh_token have been saved onto my user's record. Now that we've got an access_token, the integration is complete. Let's use one of my presets to pull down a list of books. You just go over to the Header section under Authorization and paste the token here after the word Bearer. Submit it and there we go, a list of books in JSON format. That's how that data is ending up on the home page we just saw. When your access_token expires, you'll wanna get a new one. That's what your refresh_token is for. You pass it into an API call, which I've got another preset for right here. The grant_type is refresh_token. So you paste in the refresh_token right here, submit. And this is the response that you get, a new access_token and a new refresh_token. Just now we did that manually but WormRate does it automatically. Here on the MyBookshelf client we've got a method called useRefreshToken which does the same job. You'll also see getBooksForUser which pulls down the same list of books we just got, but it automatically refreshes the token if a call comes back saying the access_token was expired. I hope that gives you a solid understanding of what an OAuth implementation might look like from a developer's perspective. Thanks for watching and good luck with your API integration.