My Profile Photo

MSc. Alban Afmeti


Information Technology is my passion, especially software development. I am trying to grow professionally every day with hard work, with practice and online courses. My objective is clear. Everything is possible if we devote to that. All the persons with a higher position than me are there because the life has bring to them some opportunities, not because they are smarter than me. Let's find these opportunities and let's thank the god that we have a good healthy life.


Using Laravel Passport with an Angular 2 client app

OAuth is an open standard for authorization, commonly used as a way for Internet users to authorize websites or applications to access their information on other websites but without giving them the passwords. This mechanism is used, for example, by Google, Facebook, Microsoft, Twitter, etc. to permit the users to share information about their accounts with third party applications or websites.
OAuth is basically a protocol that supports authorization workflows. What this means is that it gives you a way to ensure that a specific user has permissions to do something.

In this article we are going to explain how to use Laravel Passport which is native OAuth 2 server for Laravel apps. We are going to create a client project (with Angular 2) which will make some API calls to another server-side project built with Laravel. This calls need to be secure so we will use Passport in such way that every API call need to sent an authorization header with an access token so can get a valid response.

Introduction

We need to install two projects to implement Laravel Passport, a server-side Laravel 5.3 project which serves some API, and a client-side Angular 2 project which will make some API calls to server-side.
You can host this projects in localhost with different ports, but in my case I am going to use my VPS server where I have created a project in a subdomain server.techalin.com and another project angular2.techalin.com so they have different origins. You don’t have to be worried because is the same thing.

We can install the Laravel project running in the terminal composer create-project --prefer-dist laravel/laravel project_root

Then we install the Angular 2 project running the command git clone https://github.com/angular/quickstart.git project_root. After going to project root directory we install the dependencies with the command npm install.

So our projects are ready in their initial state. Given that our projects are hosted with different subdomains they have different origins and so during the API calls we can get an error to the browser’s console, in my case as follows:

XMLHttpRequest cannot load http://server.techalin.com/api/users. 
Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://angular2.techalin.com' is therefore not allowed access.

This is called the CORS (Cross-Origin Resource Sharing) problem and to solve this I am going to show a fast way because it’s not the topic of this article.
What we need to do is to implement a middleware in server-side which is going to add some headers to the request we make. This headers are going to allow our subdomain to continue with the request and to get a valid response.

Let’s create our middleware called Cors. If we run in the terminal the command php artisan make:middleware Cors a new file will be generated to app/Http/Middleware called Cors.php which is a PHP class containing a handle() function.
The content of the middleware will be as following:

<?php

namespace App\Http\Middleware;

use Closure;
use App\Domain;

class Cors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {

         //Here we put our client domains
        $trusted_domains = ["http://angular2.techalin.com:3000", "http://another-domain.com"];
        
        if(isset($request->server()['HTTP_ORIGIN'])) {
            $origin = $request->server()['HTTP_ORIGIN'];

            if(in_array($origin, $trusted_domains)) {
                header('Access-Control-Allow-Origin: ' . $origin);
                header('Access-Control-Allow-Headers: Origin, Content-Type');
            }
        }

        return $next($request);
    }
}

We set the header Access-Control-Allow-Origin: $origin which tells the browser that the content of this page is accessible to the current $origin. Access-Control-Allow-Headers is a comma separated list of acceptable headers where we have put Origin, Content-Type. In this case Origin header indicates the origin of the cross-site access request or preflight request.

We need to register the middleware to the Kernel so go to Kernel.php located to app/Http directory. In the property $middleware we add an array element like below:

<?php

    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \App\Http\Middleware\Cors::class //added by us
    ];

Now everything is ready and we can continue explaining our main topic.

Laravel Project

Firstly we need to setup our project. We need to configure the database because Passport requires it, and we are going to use sqlite. We create a sqlite file inside database directory called database.sqlite. In the .env environment file we set DB_CONNECTION=sqlite and clear all other information about the database connection. Inside database.php in the config directory we set the sqlite database database.sqlite like the code below:

<?php

        'sqlite' => [
            'driver' => 'sqlite',
            'database' => env('DB_DATABASE', database_path('database.sqlite')),
            'prefix' => '',
        ],

We need a user in the database so we create a seeder with the command php artisan make:seeder UsersTableSeeder and put to the generated file the following content:

<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use App\User;

class UsersTableSeeder extends Seeder
{
    public function run()
    {
        $user = [
            'name' => 'Alban Afmeti',
            'email' => 'albanafmeti@gmail.com',
            'password' => Hash::make('password')
        ];

        User::create($user);
    }
}

After saving we run to the terminal php artisan migrate which is going to create the users table into the database. And then we populate the table running php artisan db:seed --class=UsersTableSeeder.

Now we are going to install Laravel Passport package so go to terminal and run composer require laravel/passport. Once we have the package we go to config/app.php, and add Laravel\Passport\PassportServiceProvider to our providers list.
We can then do php artisan migrate again which is going to install a few additional tables necessary for Passport. Next we need to run the command php artisan passport:install which will create encryption keys (local files) to generate secure access tokens and personal/password grant tokens (inserted into our database).

If we load our database in a sqlite object browser we are going to see the following tables:

Sqlite Tables

The content of the table oauth_clients will be as below:

Oauth Clients

Then we go to our User model class and import the trait Laravel\Passport\HasApiTokens. User model has the following content:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{

    //Add HasApiTokens to your User model
    use HasApiTokens, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

Then we need to add the routes which are available with Passport so go to AuthServiceProvider and import Laravel\Passport\Passport, then inside the boot() method add Passport::routes();. In config/auth.php, guards.api.driver change the api guard to use passport driver instead of token:

<?php

return [
    ...
    'guards' => [
        ...
        'api' => [
            'driver' => 'passport', // was previously 'token'
            'provider' => 'users'
        ]
    ]
];

In the back-end we are going to offer a route, which the clients can get a list of current users from our database so we need to add it in routes/api.php file:

<?php

use Illuminate\Http\Request;

Route::get('/users', function (Request $request) {
    return response()->json(\App\User::all());
})->middleware('auth:api');

Make sure to put ->middleware('auth:api') so our route will be protected and needs authentication to access it.
Now everything is ready in the back-end part, we only need to test it at front-end part we are going to talk below.

Angular 2 Project

To understand this you need to have a little knowledge of Angular 2 apps, the main fundamentals of it how we can use it, what are components, services and some knowledge of Http service. You can learn more about this in this link. We are going to use Angular with Typescript (a superset of JavaScript) in our example.

The request that we are going to make to the back-end part is accessing the list of current users with this url http://server.techalin.com/api/users.
If we run this url to our browser we will see a NotFoundHttpException error because this route is protected, and we need authentication to access it.

Now we will use Angular Http service to make a call to back-end API.

In the app.component.ts file we put the following content:

import {Component} from '@angular/core';

@Component({
    selector: 'my-app',
    template: `
            <h3>Angular 2 Client Project</h3>
            <hr>
            <users></users>
    `
})
export class AppComponent {
}

So, with the Component decorator we are defining our selector my-app which is an element in index.html file. In that tag is going to be printed all the body of our app. Above we see in the template property we have put some html. This is what is going to print to <my-app></my-app> tag of index.html file. Than we see a customized element <users></users> that we haven’t seen before in html. Of course, because I named it like this and there will be printed the template content of another component I am going to create below.

Let’s create a model for our application called User (create a new file user.ts inside app directory) with the following content:

export class User {
    constructor(public id: number,
                public name: string,
                public email: string,
                public created_at: Date,
                public updated_at: Date,) {
    }
}

Now we create a service for our application which will contain two functions named getAccessToken() and getUsers(). Then we will use this functions inside the component to get the list of the users.

Create a file user.service.ts which contains the following content:

import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Rx';
import {Http, Headers, Response} from '@angular/http';
import {User} from './user';


@Injectable()
export class UserService {

    constructor(private http: Http) {
    }

    private oauthUrl = "http://server.techalin.com/oauth/token";
    private usersUrl = "http://server.techalin.com/api/users";

    getAccessToken() {
        var headers = new Headers({
            "Content-Type": "application/json",
            "Accept": "application/json"
        });

        let postData = {
            grant_type: "password",
            client_id: 2,
            client_secret: "RGNmOzt7WQ8SdNiCcJKKDoYrsFqI2tudopFjOJU3",
            username: "albanafmeti@gmail.com",
            password: "password",
            scope: ""
        }

        return this.http.post(this.oauthUrl, JSON.stringify(postData), {
            headers: headers
        })
            .map((res: Response) => res.json())
            .catch((error: any) => Observable.throw(error.json().error || 'Server error'));
    }

    getUsers(accessToken: string): Observable<User[]> {

        var headers = new Headers({
            "Accept": "application/json",
            "Authorization": "Bearer " + accessToken,
        });

        return this.http.get(this.usersUrl, {
            headers: headers
        })
            .map((res: Response) => res.json())
            .catch((error: any) => Observable.throw(error.json().error || 'Server error'));
    }
}

The function getAccessToken() serves to make an Http request to the API to get the access token, which we need to authorize the other requests. So we create headers Content-Type and Accept properties with the value application/json because we are going to accept a response with json data.
We need to send some POST parameters so we create a json object:

let postData = {
    grant_type: "password",
    client_id: 2,
    client_secret: "RGNmOzt7WQ8SdNiCcJKKDoYrsFqI2tudopFjOJU3",
    username: "albanafmeti@gmail.com",
    password: "password",
    scope: ""
}

These parameters are necessary for the Laravel Passport. grant_type: "password" means we are going to send a username and a password with the POST request. Regarding to client_id: 2 and client_secret: "RGNmOzt7WQ8SdNiCcJKKDoYrsFqI2tudopFjOJU3" we need to take this values from the table oauth_clients in the sqlite database we have explained above. We get these values from Laravel Password Grant Client row. We could create another client in that table, but in this example we are going to use this.
Set the username and the password of our user, and for this tutorial we let the scope empty. Scope is a permission to access certain data, or perform a certain action, which you can learn more about it in Laravel\Passport docs.

Then we make an HTTP POST request where we pass the URL, JSON.stringify(postData) and an object which contains headers.

return this.http.post(this.oauthUrl, JSON.stringify(postData), {
    headers: headers
})
    .map((res: Response) => res.json())
    .catch((error: any) => Observable.throw(error.json().error || 'Server error'));

The getUsers(accessToken: string) function gets an accessToken input parameter which is going to send with a header Authorization while making the GET request to get the list of users.

var headers = new Headers({
    "Accept": "application/json",
    "Authorization": "Bearer " + accessToken,
});

When the request is made with this type of header "Bearer " + valid access token, the request will be authorized and we are going to get a successful response.

It’s required that before using some Services (like Http) and classes we have used above we need to declare them in app.module.ts file as below:

import {NgModule}      from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {HttpModule}    from '@angular/http';

import {AppComponent}  from './app.component';
import {UsersComponent} from "./users.component";

@NgModule({
    imports: [
        BrowserModule,
        HttpModule
    ],
    declarations: [AppComponent, UsersComponent],
    bootstrap: [AppComponent]
})
export class AppModule {
}

Let’s see the component user.component.ts that we create inside app directory.

import {Component} from '@angular/core';
import {UserService} from './user.service';
import {User} from './user';

@Component({
    selector: 'users',
    template: `
            <h2>List of users:</h2>
            <ul>
                <li *ngFor="let user of users"></li>
            </ul>
        `,
    providers: [UserService]
})
export class UsersComponent {
    users: User[];

    constructor(private userService: UserService) {
        this.userService.getAccessToken()
            .subscribe(data => {
                this.getUsers(data.access_token)
            });
    }

    getUsers(accessToken: string) {
        this.userService.getUsers(accessToken)
            .subscribe(
                users => {
                    this.users = users;
                    console.log(users);
                });
    }
}

We have imported Component decorator, UserService and User model and then inside the @Component we have passed an object with some properties. Selector is set to users, a tag we mentioned above in the template of app.component.ts. Template contains some html code with a <h2> title and an <ul><li> list where we have used *ngFor directive to print the list of users. In the providers property we have declared an array with an element UserService which we are going to use in this class. In the constructor is bound an object of type UserService.

Inside the constructor, after getting the access token, we call this.getUsers(data.access_token).

        this.userService.getAccessToken()
            .subscribe(data => {
                this.getUsers(data.access_token)
            });

getUsers() method calls the UserService method with the same name and gets the list of users. We need to pass an access token as an input parameter. We pass an array of users to the users property of the class this.users = users and print it to browser’s console. After this inside the template we see <li *ngFor="let user of users"></li> where is going to print the list of the items which are found in users property.

After writing all this code we need to test our application so we need to run it. Go to your terminal in the root directory of the client project and run the command npm start. After making some processing, if you are working in localhost you can see the output to http://localhost:3000, in my case I see the output to http://angular2.techalin.com:3000

In the browser’s console we are going to see the following output:

Success Response

If we pass an invalid access token the list of users will not be printed and we will get to the browser’s console the following error:

Unauthenticated

Conclusion

In this article I have explained how to use Laravel\Passport which adds a new “passport” driver we can use in our app to make certain routes OAuth2 authed. Passport not only simplify things, it also adds a load of new features to our apps. To learn more about it you ca go to the Laravel Official Documentation for Passport.

comments powered by Disqus