Sie sind auf Seite 1von 18

Bravo Kernel Home Archives Feed

Search
Jul 19 2015
Apr 20 2015
Improve on Github
How to add JWT Authentication to a CakePHP 3
Tag Cloud
REST API
In this follow-up post to How to prex route a CakePHP 3 REST API we will implement
api apple cakephp cakephp3
JSON Web Token (JWT) authentication.
chef chef-dk cocktails composer
To prevent (yet another) partial/pointless JWT tutorial we will provide you with step- database debian dns dutch
by-step instructions:
elasticsearch fim foc git github ipad

describing a full blown, real world implementation iphone javascript jwt kitchen logstash

usable as drop-in code for (almost) any CakePHP 3 application requiring API microsoft minecraft misc mobile
authentication
mssql nginx octopress
spiced up with background information to help you understand the JWT concept
octostrap3 php phpcs rest
security shellshock time ubuntu
Important: please remember to use SSL/TLS encrypted connections
vagrant validator wheezy xdebug
for ALL API traffic to prevent man in the middle attackers from seeing
and stealing the tokens.

During this tutorial you will:

add some basic user data to the application


enable password hashing
add the JWT plugin
Convert html to pdf online with PDFmyURL
update your prefix route
enable JWT Authentication for API resources
create the API UsersController
implement user registration using the API
sanity check your first JWT token
implement JWT token requests
test JWT protected API resources

Before We Begin

This is part four of the CakePHP 3 REST API tutorial series:

1. How to build a CakePHP 3 REST API in minutes


2. How to use a CakePHP 3 REST API
3. How to prefix route a CakePHP 3 REST API
4. How to add JWT Authentication to a CakePHP 3 REST API

Before starting this tutorial either:

complete the previous posts


start fresh by using these end-state application sources, composer installing and
running the database migration

1. Introduction

The web is already lled with information about JSON Web Token (JWT)
Authentication so we will not duplicate it here but in a nutshell it allows
authenticating users against a single token instead of the more commonly used
username/password.

As a side eect our API will benet from some (very cool) additional JWT functionality
like:

No more need for sessions


Convert html to pdf online with PDFmyURL
No more need to protect our API against Cross-Site Request Forgery (CSRF)
Support for granular security through the use of JWT scopes

2. Adding Users To The Application

Populate the database

Download this CakePHP database migration file to your config/Migrations directory.

Now run the following command inside your applications root directory to create the
users table:

1 bin/cake migrations migrate

Generate the basic controller, entity, table and views

To prepare for testing basic HTML access generate the required controller, entity,
table and views by running the following command inside your applications root
directory:

1 bin/cake bake all Users

Configure Password Hashing

CakePHP 3 comes with a convenient PasswordHasher that will automatically encrypt


user passwords using the very strong bcrypt hashing algorithm. To enable password
hashing for your application make sure to add both the class and the method shown
below to src/Model/Entity/User.php :

1 use Cake
Cake\\Auth
Auth\\DefaultPasswordHasher
DefaultPasswordHasher;;
2
3 protected function _setPassword
_setPassword(($password
$password))

Convert html to pdf online with PDFmyURL


4 {
5 return ((new
new DefaultPasswordHasher)->hash(
DefaultPasswordHasher)->hash($password
$password);
);
6 }

Verify

If things went well you should now be able to:

browse to http://cake3api.app/users
create a new user
see the created user with hashed password

3. Adding the JWT Plugin

Run the following command inside your applications root directory to composer
install the JwtAuth plugin:

1 composer require admad/cakephp-jwt-auth:1.0


admad/cakephp-jwt-auth:1.0.x-dev
.x-dev

Now run the following command to make your application use the plugin:

Convert html to pdf online with PDFmyURL


Now run the following command to make your application use the plugin:

1 bin/cake plugin load ADmad/JwtAuth

4. Updating The Prefix Route

The API prefix route created during the previous tutorial needs updating:

to enable the Api\UsersController for API usage


to connect custom action /register to standard CRUD Plugin action /add
to automatically create routes for the non-standard /register and /token
actions

Pro tip: we connect the /register action so we can simply extend the CRUD Plugin
add() method and benet of already available logic like validation and response
codes instead of having to reinvent the wheel.

Make sure to update the api prefix route in config/routes.php to resemble:

1 Router::prefix('api'
Router::prefix('api',, function ($routes
$routes)) {
2 $routes->extensions([
$routes ->extensions(['json'
'json',, 'xml'
'xml']);
]);
3 $routes->resources(
$routes ->resources('Cocktails'
'Cocktails');
);
4 $routes->resources(
$routes ->resources('Users'
'Users');
);
5 Router::connect('/api/users/register'
Router::connect('/api/users/register',, ['controller'
['controller' => 'Users'
'Users',, 'action' => 'add'
'add',, 'prefix' => 'api'
'api']);
]);
6 $routes->fallbacks(
$routes ->fallbacks('InflectedRoute'
'InflectedRoute');
);
7 });

5. Enabling JWT Authentication

To enable JWT Authentication for all API resources extend the


src/Controller/Api/AppController.php le created during the previous tutorial with the
following initialize method so the file looks similar to:

Convert html to pdf online with PDFmyURL


following initialize method so the file looks similar to:

1 <?php
2 namespace App
App\\Controller
Controller\\Api
Api;;
3
4 use Cake
Cake\\Controller
Controller\\Controller
Controller;;
5 use Cake
Cake\\Event
Event\\Event
Event;;
6
7 class AppController extends Controller
8 {
9
10 use \\Crud
Crud\\Controller
Controller\\ControllerTrait
ControllerTrait;;
11
12 public $components = [
13 'RequestHandler',,
'RequestHandler'
14 'Crud.Crud' => [
15 'actions' => [
16 'Crud.Index',,
'Crud.Index'
17 'Crud.View',,
'Crud.View'
18 'Crud.Add',,
'Crud.Add'
19 'Crud.Edit',,
'Crud.Edit'
20 'Crud.Delete'
21 ],
22 'listeners' => [
23 'Crud.Api',,
'Crud.Api'
24 'Crud.ApiPagination',,
'Crud.ApiPagination'
25 'Crud.ApiQueryLog'
26 ]
27 ]
28 ];
29
30 public function initialize
initialize()
()
31 {
32 $this->loadComponent(
$this ->loadComponent('Auth'
'Auth',, [
33 'authenticate' => [
34 'Form',,
'Form'
35 'ADmad/JwtAuth.Jwt' => [

Convert html to pdf online with PDFmyURL


36 'parameter' => '_token'
'_token',,
37 'userModel' => 'Users'
'Users',,
38 'scope' => ['Users.active'
['Users.active' => 1],
39 'fields' => [
40 'id' => 'id'
41 ]
42 ]
43 ]
44 ]);
45 }
46 }

Note: FormAuthenticate MUST be included here or AuthComponent will not be able


to validate the posted (non-JWT) JSON credentials during the /token action.

Verify Authentication Is Enabled

To verify your API resources now actually require authentication query


http://cake3api.app/api/cocktails.json .

Should return Status Code 401 (Unauthorized) with a JSON response body similar to:

1 {
2 ""success
success":
": false
false,,
3 ""data
data":
": {
4 ""message
message":
": "You are not authorized to access that location.",
location.",
5 ""url
url":
": "\/api\/cocktails.json"
"\/api\/cocktails.json",,
6 ""code
code":
": 401
7 }
8 }

6. Creating the API UsersController

Convert html to pdf online with PDFmyURL


We will now create a UsersController responsible for handling all authentication in
the Api namespace:

using standard AuthComponent allow logic to allow non-authenticated access to


the /add and /token actions
already containing all required use statements required later on

Create new file src/Controller/Api/UsersController with the following code:

1 <?php
2 namespace App
App\\Controller
Controller\\Api
Api;;
3
4 use App
App\\Controller
Controller\\Api
Api\\AppController
AppController;;
5 use Cake
Cake\\Event
Event\\Event
Event;;
6 use Cake
Cake\\Network
Network\\Exception
Exception\\UnauthorizedException
UnauthorizedException;;
7 use Cake
Cake\\Utility
Utility\\Security
Security;;
8
9 class UsersController extends AppController
10 {
11 public function beforeFilter
beforeFilter(Event
(Event $event
$event))
12 {
13 parent::beforeFilter(
parent::beforeFilter($event
$event);
);
14 $this->Auth->allow([
$this ->Auth->allow(['add'
'add',, 'token'
'token']);
]);
15 }
16 }

7. Implementing API User Registration

How it works

User registration through the API does not require JWT authentication and is basically
a matter of posting valid JSON data to the /add action in our UsersController so the
CRUD Plugin can handle validation and creating the user record.

Convert html to pdf online with PDFmyURL


If the user is created succesfully a JSON 201 response (Created) will be returned with
a response body containing:

the id of the new user


a token field containing the new users JWT token

Create the /register action

Because the CRUD plugin normally only returns the id of the new record we will add
the JWT token to the JSON response body by extending the add() method with some
custom CRUD afterSave and serialize logic.

To implement user registration add the following add() method to


src/Controller/Api/UsersController.php :

1 public function add


add()
()
2 {
3 $this->Crud->on(
$this ->Crud->on('afterSave'
'afterSave',, function
function(\Cake\Event\Event
(\Cake\Event\Event $event
$event)) {
4 if (($event
$event->subject->created)
->subject->created) {
5 $this->set(
$this ->set('data'
'data',, [
6 'id' => $event
$event->subject->entity->id,
->subject->entity->id,

Convert html to pdf online with PDFmyURL


7 'token' => $token = \JWT::encode(
8 [
9 'id' => $event
$event->subject->entity->id,
->subject->entity->id,
10 'exp' => time() + 604800
11 ],
12 Security::salt())
13 ]);
14 $this->Crud->action()->config(
$this ->Crud->action()->config('serialize.data'
'serialize.data',, 'data'
'data');
);
15 }
16 });
17 return $this
$this->Crud->execute();
->Crud->execute();
18 }

Note: even though this is not required we are adding the JWT exp claim to the token
payload so the token will expire after one week, eectively forcing the user to
request a new unique token using the /token action.

Verify User Registration

To verify your setup register a new user by posting JSON data to your API using:

URL http://cake3api.app/api/users/register
HTTP Method POST
Accept Header application/json
Content-Type Header application/json
Body data in (absolutely) correct JSON format

Convert html to pdf online with PDFmyURL


Should return Status Code 201 (Created) with a JSON response body containing the
user id and JWT token similar to:

1 {
2 ""success
success":
": true
true,,
3 ""data
data":
": {
4 ""id
id":
": 2,
5 ""token
token":
": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Mn0.q2chPMiKRzwrO3v48fi90HyJPHDLOXtwEKr7EcU3GPk"
6 }
7 }

8. Sanity Checking JWT Tokens

Now that you have received your first JWT token it might be a good time to verify that
your token is valid by:

browsing to http://jwt.io/
pasting your token in the Encoded field
replacing the secret value with the Salt value found in your config/app.php

If things went well you should see a green success message along with the user id
and JWT exp claim as stored in the token:
Convert html to pdf online with PDFmyURL
9. Implementing JWT Token Requests

How it works

Users can request their JWT token by JSON posting their username and password to

Convert html to pdf online with PDFmyURL


the /token action after which AuthComponent will use FormAuthenticate (and thus
not JwtAuth) to validate the credentials.

If validation is successful a JSON 200 response (Success) will be returned with a


response body containing the JWT token.

Create the /token action

To implement token requests add the following token() method to


src/Controller/Api/UsersController.php :

1 public function token


token()
()
2 {
3 $user = $this
$this->Auth->identify();
->Auth->identify();
4 if (!
(!$user
$user)) {
5 throw new UnauthorizedException(
UnauthorizedException('Invalid
'Invalid username or password');
password' );
6 }
7
8 $this->set([
$this ->set([
9 'success' => true
true,,
Convert html to pdf online with PDFmyURL
10 'data' => [
11 'token' => $token = \JWT::encode([
12 'id' => $user
$user[['id'
'id'],
],
13 'exp' => time() + 604800
14 ],
15 Security::salt())
16 ],
17 '_serialize' => ['success'
['success',, 'data'
'data']]
18 ]);
19 }

Verify Token Request

To verify your setup try requesting a token for the newly created user by posting
JSON data to your API using:

URL http://cake3api.app/api/users/token
HTTP Method POST
Accept Header application/json
Content-Type Header application/json
Body data with username and password in (absolutely) correct JSON format

Should return Status Code 200 (Success) with a JSON response body containing only
the JWT token similar to:

Convert html to pdf online with PDFmyURL


the JWT token similar to:

1 {
2 ""success
success":
": true
true,,
3 ""data
data":
": {
4 ""token
token":
": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Mn0.q2chPMiKRzwrO3v48fi90HyJPHDLOXtwEKr7EcU3GPk"
5 }
6 }

10. Testing JWT Authentication

How it works

When accessing an API resource that requires authentication the JWT Plugin will look
for a token in the Authorization header and will validate it using the Salt value
used by your application.

If validation is successful a JSON 200 response (Success) will be returned with


application produced body.

Notes:

Convert html to pdf online with PDFmyURL


there is no need to create extra code, all JWT authentication logic is already
present in the plugin
the JWT Plugin also supports passing the token as a query string parameter
named _token (not described for brevity)
th e Authorization header MUST contain a Bearer Token which is part of the
OAuth V2 standard and should look like:

1 Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Mn0


eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Mn0.q2chPMiKRzwrO3v48fi90HyJPHDLOXtwEKr7EcU3GPk
.q2chPMiKRzwrO3v48fi90HyJPHDLOXtwEKr7EcU3GPk

Verify Authenticated Access

To verify successful authentication is processed as expected retrieve the list of


protected cocktails from your API by using:

URL http://cake3api.app/api/cocktails
HTTP Method GET
Accept Header application/json
Authorization Header containing Bearer {YOUR-JWT-TOKEN}

Should return Status Code 200 (Success) with the familiar JSON cocktails response
body:

1 {
2 ""success
success":
": true
true,,
3 ""data
data":
": [
4 {
5 ""id
id":
": 1,
6 ""name
name":
": "Cosmopolitan"
"Cosmopolitan",,
Convert html to pdf online with PDFmyURL
7 ""description
description":
": "Vodka based"
8 },
9 {
10 ""id
id":
": 2,
11 ""name
name":
": "Margarita"
"Margarita",,
12 ""description
description":
": "Tequila based"
13 },
14 {
15 ""id
id":
": 3,
16 ""name
name":
": "Mojito"
"Mojito",,
17 ""description
description":
": "Rum based"
18 }
19 ]
20 }

Verify Unauthenticated Access

To verify unsuccessful authentication is processed as expected retrieve the list of


protected cocktails by using the exact same query but this time removing the
Authorization header.

Should instantly return Status Code 401 (Unauthorized) with a JSON response body
similar to:

1 {
2 ""success
success":
": false
false,,
3 ""data
data":
": {
4 ""message
message":
": "You are not authorized to access that location.",
location.",
5 ""url
url":
": "\/api\/cocktails.json"
"\/api\/cocktails.json",,
6 ""code
code":
": 401
7 }
8 }

Additional reading
Convert html to pdf online with PDFmyURL
End-state application sources for this tutorial
The CakePHP JWT Plugin on Github
The PHP JWT Library on Github
The CakePHP 3 Book and CakePHP 3 API documentation

Hat tip to CakePHP Core Developer and JWT Plugin creator ADmad for helping
improve this tutorial.

CakePHP
api, cakephp, cakephp3, jwt, rest, security

Support this blog


Developers, Meet Alexa
The Voice Service That Powers Amazon Echo. Learn More.

Comments

2015 bravo-kernel

Convert html to pdf online with PDFmyURL