Integrate OAuth2 Server into Lumen to secure your RESTful API with Access Tokens

Category: Joomla
Integrate OAuth2 Server into Lumen to secure your RESTful API with Access Tokens

This is the third article in the series of Create Your First RESTful Web Service for Joomla! 3 With Lumen. In the previous article we covered configuration of Lumen, creation of Models, Controllers and Routes. Basically your API is functioning but it has no security, meaning that everyone having URLs can make a GET request and get data. Now we are going to add a security layer between users and your API resources so that only allowed users we have access.

We are going to use OAuth2 server for protection purposes that will help us generate access tokens for users with credentials.

If you are not familiar with OAuth2 I would recommend you to take a look at The OAuth 2.0 Authorization Framework standards published by the Internet Engineering Task Force (IETF). In short:

“The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf”.

Enough of introduction, let’s get to it!

Installation of OAuth2 into Lumen is pretty straightforward as with any other dependencies via composer. If you are unfamiliar with composer please take a look at my article – Installing Latest Version Of Magento 2 1.0.0-Beta. In that article I’ve described how to install composer and make it available globally. Just in case if you are coming from Swift, and you want to create a simple API for your iOS project, think about composer as CocoaPods. They both are meant for dependency management.

In my case I’m running Lumen on a shared server. I will connect to my server via SSH and will install composer there. I have written the step-by-step guide on establishing SSH connection in Installing Magento Patches SUPEE-5344, SUPEE-1533 – Using SSH Connection and Running SH Script. As soon as you are connected to your server via SSH navigate to the directory where Lumen is installed and issue the following commands one at a time:

php -r "readfile('https://getcomposer.org/installer');" > composer-setup.php
php -r "if (hash_file('SHA384', 'composer-setup.php') === '7228c001f88bee97506740ef0888240bd8a760b046ee16db8f4095c0d8d525f2367663f22a46b48d072c816e7fe19959') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

These four lines will:

  1. Download the installer to the current directory
  2. Verify the installer SHA-384
  3. Run the installer
  4. Remove the installer

Now that you have composer installed on your remote server you can proceed with OAuth2 installation via composer.

If you check the documentation of OAuth2 Server Laravel project for Lumen at GitHub you will find that installation process is very simple.

First you need to edit composer.json file that should be located in the root directory of Lumen. You need to add "lucadegasperi/oauth2-server-laravel": "^5.0" to the require section that is located at about line 7 (it might vary though). By that piece of code we are telling composer that we require dependency of that particular library into our project and the requested version is greater than, or equals to - 5.0 and is less than 6.0.

Now you can simply open terminal, navigate to the root directory of the Lumen installation and run composer update. Composer will automatically download all dependencies.

NOTE: When you run composer update command you might think that nothing is happening but give it some time and the download process will start.

When the download process is finished, under the vendor folder you will find lucadegasperi directory:

Now let’s setup OAuth2 and make it ready to be used to secure our API.

First step is to copy oauth2.php file from lucadegasperi -> oauth2-server-laravel -> config into your config folder that you should create in the root directory of Lumen. Then copy all database migration files from lucadegasperi -> oauth2-server-laravel -> database -> migrations into root directory -> database -> migrations.

Second step is to register OAuth2 in Lumen.

In your bootstrap/app.php register service providers (somewhere at line 80):

$app->register(\LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider::class);
$app->register(\LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider::class);

In the same file you should register the middleware (somewhere at line 60):

$app->middleware([
    \LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware::class
]);

The initial code is commented out, so you can either paste this whole piece or uncomment the beginning and the end and insert only \LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware::class.

What this middleware does is that it checks for OAuth2 exceptions and handles them. Basically it transforms exceptions/errors related to OAuth2 into JSON.

Now you should add this code class_alias('Illuminate\Support\Facades\Config', 'Config'); to the top of bootstrap/app.php before $app->withFacades(); (somewhere at line 26). You should also uncomment $app->withFacades(); temporarily to import the migrations, just in case you haven’t done this already. If you recall we uncommented this line in the previous article, when we were setting up Lumen.

We are almost done with the setup.

In the previous article I mentioned that you could generate database for your API with the help of migrations and database seeding, but I omitted that part. I’m pretty sure that you don’t need to generate database for your API because as my case is, I’m building webservice for an existing website that has contents that is already stored in the database.

What’s Database Migration?

Migrations are a type of version control for your database. They allow a team to modify the database schema and stay up to date on the current schema state.

What’s Database Seeding?

It’s a simple way to seed your database with test data using seed classes. All seed classes are stored in database/seeds. Seed classes may have any name you wish, but probably should follow some sensible convention, such as UserTableSeeder, etc. By default, a DatabaseSeeder class is defined for you. From this class, you may use the call method to run other seed classes, allowing you to control the seeding order.

We should create a table in the database for OAuth2 to register clients and store access tokens. We will do this with the above mentioned database migrations and seeds.

Let’s get to it.

If you navigate to database/migrations where we just copied migration files from OAuth2, you will find a file 2014_04_24_110459_create_oauth_clients_table.php. Using this migration you can create oauth_clients table in our databse. This new table will have four columns. We need only three of them: id, secret and name.

Now navigate to database/seeds folder where you will find DatabaseSeeder.php file. Make a clone of it and rename it to anything you prefer. In order to follow Lumen’s conventions I will call it OAuthClientSeeder.php.

The new file looks like this:

<?php

use Illuminate\Database\Seeder;

class OAuthClientSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
      
        DB::table('oauth_client')->insert(
         [
           'id' => '100001',
           'secret' => 'myUniqueSecret34234234',
           'name' => 'theRealMrGott'
         ]
        );
          
    }
}

If you take a closer look at the code you will notice that we run insert method on the table oauth_client and insert new entry into it. With this code I just created one user. If you want to you can make use of for loop to create several users.

Now you should edit DatabaseSeeder.php. Inside the public function run paste $this->call('OAuthClientSeeder');. This line will call for the class that we created now.

Next step is to open up Terminal, navigate to your Lumen installation directory and run the following commands:

composer dumpautoload //this line will tell composer to discover new files
php artisan migrate --seed //this command will run the migration and seeding process

In order to receive access token for a particular user, he/she should make a call to some URL on our webservice. We have to set up new route. In the root directory -> app -> Http -> routes.php file configure new route.

$app->post('oauth/access_token', function() {
    return response()->json(Authorizer::issueAccessToken());
});

If you take a closer look at the newly defined route, first you will notice that the method we are using here is POST (in the previous cases we used only GET). After a user will post the data OAuth2 will make a record in the database and return an access token for a particular user. To do that in our route definition we are making use of the Authorizer class that is part of OAuth2 Server. In order to make it work we need to make a class reference. Inside our bootstrap/app.php add (somewhere at line 27):

class_alias(\LucaDegasperi\OAuth2Server\Facades\Authorizer::class, 'Authorizer');

Next we need to configure OAuth2 itself. Edit root directory -> config -> ouath2.php file. Somewhere on line 30 find grant_types and add the following:

    'grant_types' => [
    'client_credentials' => [
        'class' => '\League\OAuth2\Server\Grant\ClientCredentialsGrant',
        'access_token_ttl' => 0
      ]
    ],

If you would like to learn more on grant types in OAuth2 Server visit thephpleague.com and Choosing a Grant.

Let’s get the access token!

Open Postman. If you don’t have it, find it at Chrome Web Store.

Postman parameters to get access token

By now our OAuth is configured and ready to work. We have generated the access token and finally we can use OAuth2 in Lumen. To protect our API’s URLs, we need to first enable route middlewares. Setting middlewares will let us verify that the request is made by an authorized user and then grant access to the actions/methods defined in our controllers.

To enable route middlewares add the code into bootstrap/app.php(somewhere on line 78):

$app->routeMiddleware([
    'check-authorization-params' => \LucaDegasperi\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware::class,
    'csrf' => \Laravel\Lumen\Http\Middleware\VerifyCsrfToken::class,
    'oauth' => \LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware::class,
    'oauth-client' => \LucaDegasperi\OAuth2Server\Middleware\OAuthClientOwnerMiddleware::class,
    'oauth-user' => \LucaDegasperi\OAuth2Server\Middleware\OAuthUserOwnerMiddleware::class,
]);

If you try accessing any resource in you API you will be directly presented with the requested data. Now we should add a __construct function to our controllers so before processing the request OAuth2 will check whether the request contains access token and in case it’s valid controller will process the request and return JSON data.

__construct function:

  public function __construct(){
    $this->middleware('oauth');
  }

With this oauth middleware call OAuth2 will check for access token for all actions/methods of the controller. If you would like to remove “protection” from any of the methods you could use except or include, just like this:

$this->middleware('oauth', ['except' => ['METHOD_NAME_1', 'METHOD_NAME_2']]);

Now you can try directly accessing your resource. For ex. http://yourwebservice.com/article/3. If you do it with Postman you will get status: 400 Bad Request and a message telling that you are missing access token parameter.

To successfully make a request and get the data you have to pass access token:

http://yourwebservice.com/article/3?access_token=YOUR_ACCESS_TOKEN

Conclusion

Finally we have reached the end. Now your API is more than functional. You are successfully making the request and protecting your resources, so that only authorized users will have access. I hope everything was easy to follow and now you are excited to have built protected API. Just in case you encountered any issues or some of the implementation parts remain obscure please feel free to contact me. And at last, but not least, the following article will be dedicated to error handling and optimization of your API. Stay tuned!