Skip to content

Commit 8af2f17

Browse files
authored
Merge pull request #52 from delamux/jwt-implement
Jwt implement
2 parents d9f6caf + 9b46bba commit 8af2f17

File tree

4 files changed

+562
-3
lines changed

4 files changed

+562
-3
lines changed

docs/Documentation/Auth/jwt.md

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
## Json Web Token Authentication tutorial
2+
3+
* I take the JWT auth of [Admad cakephp-jwt-auth](https://github.com/ADmad/cakephp-jwt-auth) JwtAuthenticate.php file
4+
and modified some lines to adapter for this plugin, The good reason for do this is for to be used in
5+
Frontend JavaScript Frameworks like, [VueJS](https://vuejs.org/v2/guide/), [Angular](https://angular.io/docs),
6+
[React](https://reactjs.org/) and others.
7+
8+
### Configuration
9+
10+
1. First before do this, I hope that you read obout [Installation](../installation.md) and [configuration](../configuration.md).
11+
The first step is modified the config file in ``config/api.php``
12+
13+
```php
14+
<?
15+
return [
16+
'Users' => [
17+
// token expiration,
18+
// - 3600 seconds
19+
// - 24 hours
20+
// - 31 days
21+
'Token' => [
22+
'expiration' => 3600 * 24 * 31
23+
],
24+
'Api' => [
25+
// if service class is not defined we use crud fallback service
26+
'ServiceFallback' => '\\CakeDC\\Api\\Service\\FallbackService',
27+
// response rendered as JSend
28+
'renderer' => 'CakeDC/Api.JSend',
29+
// Data parse from cakephp request object
30+
'parser' => 'CakeDC/Api.Form',
31+
32+
//routes inflector: specify underscore, dasherize, or false for neither/no inflection
33+
'routesInflectorMethod' => false,
34+
35+
// version is not used
36+
'useVersioning' => false,
37+
'versionPrefix' => 'v',
38+
39+
// auth permission uses require auth strategy
40+
'Auth' => [
41+
'Crud' => [
42+
'default' => 'auth'
43+
],
44+
],
45+
46+
'Service' => [
47+
'default' => [
48+
'options' => [],
49+
'Action' => [
50+
'default' => [
51+
//auth configuration
52+
'Auth' => [
53+
'storage' => 'Memory',
54+
'authorize' => [
55+
'CakeDC/Api.SimpleRbac'
56+
],
57+
'authenticate' => [
58+
'CakeDC/Api.Jwt' => [
59+
'userModel' => 'CakeDC/Users.Users',
60+
'require_ssl' => false,
61+
],
62+
],
63+
],
64+
// default app extensions
65+
'Extension' => [
66+
// allow request from other domains
67+
'CakeDC/Api.Cors',
68+
// enable sort
69+
'CakeDC/Api.Sort',
70+
// load Hateoas
71+
'CakeDC/Api.CrudHateoas',
72+
// enable relations
73+
'CakeDC/Api.CrudRelations',
74+
]
75+
],
76+
// all index actions configuration
77+
'Index' => [
78+
'Extension' => [
79+
// enable pagination for index actions
80+
'CakeDC/Api.Paginate',
81+
],
82+
],
83+
],
84+
],
85+
],
86+
'Log' => [
87+
'className' => 'File',
88+
'scopes' => ['api'],
89+
'levels' => ['error', 'info'],
90+
'file' => 'api.log',
91+
]
92+
]
93+
];
94+
```
95+
96+
2. Extend the **AuthService** in `src/Service/Action/.php`
97+
````php
98+
<?
99+
namespace App\Service;
100+
101+
use App\Service\Action\Auth\LoginAction;
102+
use CakeDC\Api\Service\AuthService as ApiAuthService;
103+
104+
/**
105+
* Class AuthService
106+
*
107+
* @package CakeDC\Api\Service
108+
*/
109+
class AuthService extends ApiAuthService
110+
{
111+
112+
/**
113+
* @inheritdoc
114+
* @return void
115+
*/
116+
public function initialize()
117+
{
118+
parent::initialize();
119+
$methods = ['method' => ['POST'], 'mapCors' => true];
120+
$this->mapAction('login', LoginAction::class, $methods);
121+
}
122+
}
123+
````
124+
125+
3. Extend and Rewriting the **LoginAction** in `src/Service/Action/Auth/LoginAction.php`
126+
```php
127+
<?
128+
namespace App\Service\Action\Auth;
129+
130+
use CakeDC\Api\Service\Action\Auth\LoginAction as ApiLogin;
131+
use CakeDC\Users\Controller\Component\UsersAuthComponent;
132+
use CakeDC\Users\Exception\UserNotFoundException;
133+
use Cake\Core\Configure;
134+
use Cake\Utility\Security;
135+
use Firebase\JWT\JWT;
136+
137+
class LoginAction extends ApiLogin
138+
{
139+
/**
140+
* Login JWT action rewrite
141+
*
142+
* @return mixed|void
143+
*/
144+
public function execute()
145+
{
146+
$socialLogin = false;
147+
$event = $this->dispatchEvent(UsersAuthComponent::EVENT_BEFORE_LOGIN);
148+
if (is_array($event->result)) {
149+
$user = $this->_afterIdentifyUser($event->result);
150+
} else {
151+
$user = $this->Auth->identify();
152+
$user = $this->_afterIdentifyUser($user, $socialLogin);
153+
}
154+
if (empty($user)) {
155+
throw new UserNotFoundException(__d('CakeDC/Api', 'User not found'), 401);
156+
}
157+
158+
$result = [
159+
'success' => true,
160+
'data' => [
161+
'token' => JWT::encode(
162+
[
163+
'username' => $user['username'],
164+
'email' => $user['email'],
165+
'name' => $user['first_name'],
166+
'sub' => $user['id'],
167+
'exp' => time() + Configure::read('Users.Token.expiration')
168+
],
169+
Security::getSalt()
170+
)
171+
],
172+
'_serialize' => ['success', 'data']
173+
];
174+
175+
return $result;
176+
}
177+
178+
}
179+
```
180+
4. Extend and Rewriting the **RegisterAction** in `src/Service/Action/Auth/RegisterAction.php`
181+
```php
182+
<?
183+
namespace App\Service\Action\Auth;
184+
185+
use CakeDC\Api\Exception\ValidationException;
186+
use CakeDC\Api\Service\Action\Auth\RegisterAction as ApiRegister;
187+
use CakeDC\Users\Controller\Component\UsersAuthComponent;
188+
use Cake\Core\Configure;
189+
use Cake\Datasource\EntityInterface;
190+
use Cake\Utility\Security;
191+
use Firebase\JWT\JWT;
192+
193+
class RegisterAction extends ApiRegister
194+
{
195+
/**
196+
* {@inheritdoc}
197+
*/
198+
public function execute()
199+
{
200+
$usersTable = $this->getUsersTable();
201+
$user = $usersTable->newEntity();
202+
$options = $this->_registerOptions();
203+
$requestData = $this->getData();
204+
$event = $this->dispatchEvent(UsersAuthComponent::EVENT_BEFORE_REGISTER, [
205+
'usersTable' => $usersTable,
206+
'options' => $options,
207+
'userEntity' => $user,
208+
]);
209+
210+
if ($event->result instanceof EntityInterface) {
211+
if ($userSaved = $usersTable->register($user, $event->result->toArray(), $options)) {
212+
return $this->_afterRegister($userSaved);
213+
}
214+
}
215+
if ($event->isStopped()) {
216+
return false;
217+
}
218+
$userSaved = $usersTable->register($user, $requestData, $options);
219+
if (!$userSaved) {
220+
throw new ValidationException(__d('CakeDC/Api', 'The user could not be saved'), 0, null, $user->getErrors());
221+
}
222+
223+
return $this->_afterRegister($userSaved);
224+
}
225+
226+
/**
227+
* Prepare flash messages after registration, and dispatch afterRegister event
228+
*
229+
* @param EntityInterface $userSaved User entity saved
230+
* @return EntityInterface
231+
*/
232+
protected function _afterRegister(EntityInterface $userSaved)
233+
{
234+
$validateEmail = (bool)Configure::read('Users.Email.validate');
235+
$message = __d('CakeDC/Api', 'You have registered successfully, please log in');
236+
if ($validateEmail) {
237+
$message = __d('CakeDC/Api', 'Please validate your account before log in');
238+
}
239+
$event = $this->dispatchEvent(UsersAuthComponent::EVENT_AFTER_REGISTER, [
240+
'user' => $userSaved
241+
]);
242+
if ($event->result instanceof EntityInterface) {
243+
$userSaved = $event->result;
244+
}
245+
246+
$event = $this->dispatchEvent('Action.Auth.onRegisterFormat', ['user' => $userSaved]);
247+
if ($event->result) {
248+
$userSaved = $event->result;
249+
}
250+
251+
$result = [
252+
'message' => $message,
253+
'success' => true,
254+
'data' => [
255+
'token' => JWT::encode(
256+
[
257+
'username' => $userSaved['username'],
258+
'email' => $userSaved['email'],
259+
'name' => $userSaved['first_name'],
260+
'sub' => $userSaved['id'],
261+
'exp' => time() + Configure::read('Users.Token.expiration')
262+
],
263+
Security::getSalt()
264+
)
265+
],
266+
'_serialize' => ['message', 'success', 'data']
267+
];
268+
269+
return $result;
270+
}
271+
}
272+
```
273+
274+
275+
276+
277+

docs/Documentation/actions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ Let's define it here:
4747

4848

4949
```php
50-
namespace App\Api\Service\Action\Auth;
50+
namespace App\Api\Service\Action\Stats;
5151

5252
use CakeDC\Api\Exception\ValidationException;
5353
use CakeDC\Api\Service\Action\Action;
5454

55-
class LoginAction extends Action
55+
class StatsAction extends Action
5656
{
5757

5858
public function validates()

docs/Documentation/auth.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,7 @@ So global Auth configuration one should put into `Api.Service.default.Action.de
7373
]
7474
```
7575
* If no rule allowed = true is matched for a given user role and url, default return value will be false
76-
76+
77+
### JWT (Json Web Token Authentication).
78+
79+
* [Example tutorial](Documentation/Auth/jwt.md)

0 commit comments

Comments
 (0)