Skip to content

Commit c798ab0

Browse files
committed
Initial commit.
0 parents  commit c798ab0

10 files changed

+631
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/composer.lock
2+
/phpunit.xml
3+
/vendor

.travis.yml

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
language: php
2+
3+
php:
4+
- 5.4
5+
- 5.5
6+
- 5.6
7+
8+
env:
9+
matrix:
10+
- DB=mysql db_class='Cake\Database\Driver\Mysql' db_dsn='mysql:host=0.0.0.0;dbname=cakephp_test' db_database='cakephp_test' db_login='travis' db_password=''
11+
global:
12+
- DEFAULT=1
13+
14+
matrix:
15+
fast_finish: true
16+
17+
include:
18+
- php: 5.4
19+
env: DB=pgsql db_class='Cake\Database\Driver\Postgres' db_dsn='pgsql:host=127.0.0.1;dbname=cakephp_test' db_database="cakephp_test" db_login='postgres' db_password=''
20+
21+
- php: 5.4
22+
env: DB=sqlite db_class='Cake\Database\Driver\Sqlite' db_dsn='sqlite::memory:'
23+
24+
- php: 5.4
25+
env: PHPCS=1 DEFAULT=0
26+
27+
before_script:
28+
- composer self-update
29+
- composer install --prefer-dist --no-interaction --dev
30+
31+
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi"
32+
33+
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi"
34+
35+
- sh -c "if [ '$PHPCS' = '1' ]; then pear channel-discover pear.cakephp.org; fi"
36+
- sh -c "if [ '$PHPCS' = '1' ]; then pear install --alldeps cakephp/CakePHP_CodeSniffer; fi"
37+
38+
- phpenv rehash
39+
- set +H
40+
41+
script:
42+
- sh -c "if [ '$DEFAULT' = '1' ]; then phpunit --stderr; fi"
43+
- sh -c "if [ '$PHPCS' = '1' ]; then phpcs -p --extensions=php --standard=CakePHP --ignore=vendor --ignore=docs . ; fi"
44+
45+
notifications:
46+
email: false

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2013 A. Sarela, aka ADmad
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# CakePHP JWT Authenticate plugin
2+
3+
[![Build Status](https://travis-ci.org/ADmad/cakephp-jwt-auth.png?branch=master)](https://travis-ci.org/ADmad/cakephp-jwt-auth)
4+
5+
Plugin containing AuthComponent's authenticate class for authenticating using
6+
[JSON Web Tokens](http://jwt.io/). You can read about it's specification in
7+
detail [here](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-27).
8+
9+
## Requirements
10+
11+
* CakePHP 3.0+
12+
13+
## Installation
14+
15+
_[Composer]_
16+
17+
run: `composer require admad/cakephp-jwt-auth:1.0.x-dev`.
18+
19+
## Usage
20+
21+
In your app's `config/bootstrap.php` add: `CakePlugin::load('ADmad/JwtAuth')`;
22+
23+
## Configuration:
24+
25+
Setup the authentication class settings:
26+
27+
```php
28+
//in $components
29+
public $components = [
30+
'Auth' => [
31+
'authenticate' => [
32+
'ADmad/JwtAuth.Jwt' => [
33+
'parameter' => '_token',
34+
'userModel' => 'Users',
35+
'scope' => ['Users.active' => 1],
36+
'fields' => [
37+
'id' => 'id'
38+
]
39+
]
40+
]
41+
]
42+
];
43+
44+
//Or in beforeFilter()
45+
$this->Auth->config('authenticate', [
46+
'ADmad/JwtAuth.Jwt' => [
47+
'parameter' => '_token',
48+
'userModel' => 'Users',
49+
'scope' => ['Users.active' => 1],
50+
'fields' => [
51+
'id' => 'id'
52+
]
53+
]
54+
]);
55+
```

composer.json

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "admad/jwt-auth",
3+
"type": "cakephp-plugin",
4+
"description": "CakePHP plugin for authenticating using JSON Web Tokens",
5+
"keywords":[
6+
"cakephp",
7+
"authenticate",
8+
"authentication",
9+
"jwt"
10+
],
11+
"homepage": "http://github.com/ADmad/cakephp-jwt-auth",
12+
"authors": [
13+
{
14+
"name":"ADmad",
15+
"role":"Author",
16+
"homepage":"https://github.com/ADmad"
17+
}
18+
],
19+
"license": "MIT",
20+
"support": {
21+
"source":"https://github.com/ADmad/cakephp-jwt-auth",
22+
"issues":"https://github.com/ADmad/cakephp-jwt-auth/issues"
23+
},
24+
"require": {
25+
"cakephp/cakephp": "3.0.*-dev",
26+
"cakephp/plugin-installer": "*",
27+
"firebase/php-jwt": "~1.0"
28+
},
29+
"autoload": {
30+
"psr-4": {
31+
"ADmad\\JwtAuth\\": "src"
32+
}
33+
},
34+
"autoload-dev": {
35+
"psr-4": {
36+
"ADmad\\JwtAuth\\Test\\": "tests"
37+
}
38+
},
39+
"extra": {
40+
"branch-alias": {
41+
"dev-master": "1.0.x-dev"
42+
}
43+
}
44+
}

phpunit.xml.dist

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit
3+
colors="true"
4+
processIsolation="false"
5+
stopOnFailure="false"
6+
syntaxCheck="false"
7+
bootstrap="./tests/bootstrap.php"
8+
>
9+
<php>
10+
<ini name="memory_limit" value="-1"/>
11+
</php>
12+
13+
<!-- Add any additional test suites you want to run here -->
14+
<testsuites>
15+
<testsuite name="All Tests">
16+
<directory>./tests/TestCase</directory>
17+
</testsuite>
18+
<!-- Add plugin test suites here. -->
19+
</testsuites>
20+
21+
<!-- Setup a listener for fixtures -->
22+
<listeners>
23+
<listener
24+
class="\Cake\TestSuite\Fixture\FixtureInjector"
25+
file="./vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php">
26+
<arguments>
27+
<object class="\Cake\TestSuite\Fixture\FixtureManager" />
28+
</arguments>
29+
</listener>
30+
</listeners>
31+
32+
<php>
33+
<!-- Postgres
34+
<env name="db_class" value="Cake\Database\Driver\Postgres"/>
35+
<env name="db_database" value="cake_test"/>
36+
<env name="db_login" value=""/>
37+
<env name="db_password" value=""/>
38+
-->
39+
<!-- Mysql
40+
<env name="db_class" value="Cake\Database\Driver\Mysql"/>
41+
<env name="db_dsn" value="mysql:host=localhost;dbname=cake_test"/>
42+
<env name="db_database" value=""/>
43+
<env name="db_login" value=""/>
44+
<env name="db_password" value=""/>
45+
-->
46+
</php>
47+
</phpunit>

src/Auth/JwtAuthenticate.php

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
namespace ADmad\JwtAuth\Auth;
3+
4+
use Cake\Auth\BaseAuthenticate;
5+
use Cake\Controller\ComponentRegistry;
6+
use Cake\Network\Request;
7+
use Cake\Network\Response;
8+
use Cake\ORM\TableRegistry;
9+
use Cake\Utility\Security;
10+
use \JWT;
11+
12+
/**
13+
* An authentication adapter for authenticating using JSON Web Tokens.
14+
*
15+
* {{{
16+
* $this->Auth->config('authenticate', [
17+
* 'ADmad/JwtAuth.Jwt' => [
18+
* 'parameter' => '_token',
19+
* 'userModel' => 'Users',
20+
* 'scope' => ['User.active' => 1]
21+
* 'fields' => [
22+
* 'id' => 'id'
23+
* ],
24+
* ]
25+
* ]);
26+
* }}}
27+
*
28+
* @copyright 2014 A. Sarela aka ADmad
29+
* @license MIT
30+
* @see http://jwt.io
31+
* @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token
32+
*/
33+
class JwtAuthenticate extends BaseAuthenticate {
34+
35+
/**
36+
* Constructor.
37+
*
38+
* Settings for this object.
39+
*
40+
* - `parameter` - The url parameter name of the token. Defaults to `_token`.
41+
* First $_SERVER['HTTP_AUTHORIZATION'] is checked for token value.
42+
* It's value should be of form "Bearer <token>". If empty this query string
43+
* paramater is checked.
44+
* - `userModel` - The model name of the User, defaults to `Users`.
45+
* - `fields` - Has key `id` whose value contains primary key field name.
46+
* Defaults to ['id' => 'id'].
47+
* - `scope` - Additional conditions to use when looking up and authenticating
48+
* users, i.e. `['Users.is_active' => 1].`
49+
* - `contain` - Extra models to contain.
50+
* - `unauthenticatedException` - Fully namespaced exception name. Exception to
51+
* throw if authentication fails. Set to false to do nothing.
52+
* Defaults to '\Cake\Network\Exception\UnauthorizedException'.
53+
*
54+
* @param \Cake\Controller\ComponentRegistry $registry The Component registry
55+
* used on this request.
56+
* @param array $config Array of config to use.
57+
*/
58+
public function __construct(ComponentRegistry $registry, $config) {
59+
$this->config([
60+
'parameter' => '_token',
61+
'fields' => ['id' => 'id'],
62+
'unauthenticatedException' => '\Cake\Network\Exception\UnauthorizedException'
63+
]);
64+
65+
parent::__construct($registry, $config);
66+
}
67+
68+
/**
69+
* Unused, since this is a stateless authentication provider.
70+
*
71+
* @param Request $request The request object.
72+
* @param Response $response response object.
73+
* @return bool Always false.
74+
*/
75+
public function authenticate(Request $request, Response $response) {
76+
return false;
77+
}
78+
79+
/**
80+
* Get token information from the request.
81+
*
82+
* @param \Cake\Network\Request $request Request object.
83+
* @return bool|array Either false or an array of user information
84+
*/
85+
public function getUser(Request $request) {
86+
$token = $request->env('HTTP_AUTHORIZATION');
87+
if ($token) {
88+
$token = explode(' ', $token);
89+
if (!empty($token[1])) {
90+
return $this->_findUser($token[1]);
91+
}
92+
}
93+
94+
if (!empty($this->_config['parameter']) &&
95+
$token = $request->query($this->_config['parameter'])
96+
) {
97+
return $this->_findUser($token);
98+
}
99+
100+
return false;
101+
}
102+
103+
/**
104+
* Find a user record.
105+
*
106+
* @param string $token The token identifier.
107+
* @param string $password Unused password.
108+
* @return bool|array Either false on failure, or an array of user data.
109+
*/
110+
protected function _findUser($token, $password = null) {
111+
$token = JWT::decode($token, Security::salt());
112+
113+
// Token has full user record.
114+
if (isset($token->record)) {
115+
// Trick to convert object of stdClass to array. Typecasting to
116+
// array doesn't convert property values which are themselves objects.
117+
return json_decode(json_encode($token->record), true);
118+
}
119+
120+
$userModel = $this->_config['userModel'];
121+
list($plugin, $model) = pluginSplit($userModel);
122+
$fields = $this->_config['fields'];
123+
124+
$conditions = [$model . '.' . $fields['id'] => $token->id];
125+
if (!empty($this->_config['scope'])) {
126+
$conditions = array_merge($conditions, $this->_config['scope']);
127+
}
128+
$table = TableRegistry::get($userModel)->find('all');
129+
if ($this->_config['contain']) {
130+
$table = $table->contain($contain);
131+
}
132+
133+
$result = $table
134+
->where($conditions)
135+
->hydrate(false)
136+
->first();
137+
138+
if (empty($result)) {
139+
return false;
140+
}
141+
142+
unset($result[$fields['password']]);
143+
return $result;
144+
}
145+
146+
/**
147+
* Handles an unauthenticated access attempt. Depending on value of config
148+
* `unauthenticatedException` either throws the specified exception or returns
149+
* null.
150+
*
151+
* @param \Cake\Network\Request $request A request object.
152+
* @param \Cake\Network\Response $response A response object.
153+
* @return void
154+
* @throws \Cake\Network\Exception\UnauthorizedException
155+
*/
156+
public function unauthenticated(Request $request, Response $response) {
157+
if (!$this->_config['unauthenticatedException']) {
158+
return;
159+
}
160+
161+
$exception = $this->_config['unauthenticatedException'];
162+
// @codingStandardsIgnoreStart
163+
throw new $exception($this->_registry->Auth->_config['authError']);
164+
// @codingStandardsIgnoreEnd
165+
}
166+
167+
}

0 commit comments

Comments
 (0)