├── .env.example
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── app
├── Comment.php
├── Console
│ ├── Commands
│ │ └── .gitkeep
│ └── Kernel.php
├── Events
│ ├── Event.php
│ └── ExampleEvent.php
├── Exceptions
│ └── Handler.php
├── Http
│ ├── Controllers
│ │ ├── CommentController.php
│ │ ├── Controller.php
│ │ ├── ExampleController.php
│ │ ├── PostCommentController.php
│ │ ├── PostController.php
│ │ └── UserController.php
│ └── Middleware
│ │ ├── Authenticate.php
│ │ ├── Authorize.php
│ │ └── ExampleMiddleware.php
├── Jobs
│ ├── ExampleJob.php
│ └── Job.php
├── Listeners
│ └── ExampleListener.php
├── Post.php
├── Providers
│ ├── AppServiceProvider.php
│ ├── AuthServiceProvider.php
│ └── EventServiceProvider.php
└── User.php
├── artisan
├── bootstrap
└── app.php
├── composer.json
├── config
└── oauth2.php
├── database
├── factories
│ └── ModelFactory.php
├── migrations
│ ├── .gitkeep
│ ├── 2014_04_24_110151_create_oauth_scopes_table.php
│ ├── 2014_04_24_110304_create_oauth_grants_table.php
│ ├── 2014_04_24_110403_create_oauth_grant_scopes_table.php
│ ├── 2014_04_24_110459_create_oauth_clients_table.php
│ ├── 2014_04_24_110557_create_oauth_client_endpoints_table.php
│ ├── 2014_04_24_110705_create_oauth_client_scopes_table.php
│ ├── 2014_04_24_110817_create_oauth_client_grants_table.php
│ ├── 2014_04_24_111002_create_oauth_sessions_table.php
│ ├── 2014_04_24_111109_create_oauth_session_scopes_table.php
│ ├── 2014_04_24_111254_create_oauth_auth_codes_table.php
│ ├── 2014_04_24_111403_create_oauth_auth_code_scopes_table.php
│ ├── 2014_04_24_111518_create_oauth_access_tokens_table.php
│ ├── 2014_04_24_111657_create_oauth_access_token_scopes_table.php
│ ├── 2014_04_24_111810_create_oauth_refresh_tokens_table.php
│ ├── 2016_03_24_182334_create_users_table.php
│ ├── 2016_03_24_221425_create_posts_table.php
│ └── 2016_03_24_221457_create_comments_table.php
└── seeds
│ ├── DatabaseSeeder.php
│ └── OAuthClientSeeder.php
├── phpunit.xml
├── public
├── .htaccess
├── index.php
└── lumen-api-oauth.png
├── resources
└── views
│ └── .gitkeep
├── routes
└── web.php
├── storage
├── app
│ └── .gitignore
├── framework
│ ├── cache
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
└── logs
│ └── .gitignore
└── tests
├── ExampleTest.php
└── TestCase.php
/.env.example:
--------------------------------------------------------------------------------
1 | APP_ENV=local
2 | APP_DEBUG=true
3 | APP_KEY=
4 | APP_TIMEZONE=UTC
5 |
6 | DB_CONNECTION=mysql
7 | DB_HOST=127.0.0.1
8 | DB_PORT=3306
9 | DB_DATABASE=homestead
10 | DB_USERNAME=homestead
11 | DB_PASSWORD=secret
12 |
13 | CACHE_DRIVER=array
14 | QUEUE_DRIVER=array
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /.idea
3 | Homestead.json
4 | Homestead.yaml
5 | .env
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.0
6 | - hhvm
7 |
8 | sudo: false
9 |
10 | install: travis_retry composer install --no-interaction --prefer-source
11 |
12 | script: vendor/bin/phpunit
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Omar El Gabry
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Lumen API OAuth
6 | [](https://travis-ci.org/OmarElGabry/lumen-api-oauth)
7 | [](https://scrutinizer-ci.com/g/OmarElGabry/lumen-api-oauth/?branch=master)
8 | [](https://codeclimate.com/github/OmarElGabry/lumen-api-oauth)
9 | [](https://www.versioneye.com/user/projects/57060d31fcd19a0039f15da4)
10 |
11 | [](https://packagist.org/packages/omarelgabry/lumen-api-oauth)
12 | [](https://packagist.org/packages/omarelgabry/lumen-api-oauth)
13 |
14 | A RESTful API based on Lumen micro-framework with OAuth2. Lumen API OAuth is a simple application, indented for small projects, helps to understand creating RESTful APIs with Lumen and OAuth2, know how to authenticate and authorize, and more.
15 |
16 | The RESTful API for Posts and Comments, where Users can view, create, update, and delete. It provides authorization mechanism to authorize against access tokens using OAuth2, ownership, and non-admin Vs admin users.
17 |
18 | :mega: A full tutorial on building a RESTful API with Lumen and OAuth2 can be found on [Medium](https://medium.com/omarelgabrys-blog/building-restful-apis-with-lumen-and-oauth2-8ba279c6a31).
19 |
20 | ## Index
21 | + [Installation](#installation)
22 | + [Terminology](#terminology)
23 | + [Authorization](#authorization)
24 | + [Routing](#routing)
25 | + [Support](#support)
26 | + [Contribute](#contribute)
27 | + [Dependencies](#dependencies)
28 | + [License](#license)
29 |
30 | ## Installation
31 | Steps:
32 |
33 | 1. Run [Composer](https://getcomposer.org/doc/00-intro.md)
34 |
35 | ```
36 | composer install
37 | ```
38 | 2. Laravel Homestead
39 |
40 | If you are using Laravel Homestead, then follow the [Installation Guide](https://laravel.com/docs/5.2/homestead).
41 |
42 | 3. WAMP, LAMP, MAMP, XAMP Server
43 |
44 | If you are using any of WAMP, LAMP, MAMP, XAMP Servers, then don't forget to create a database, probably a MySQL database.
45 |
46 | 4. Configure the```.env``` file
47 |
48 | Rename ```.env.example``` file to ```.env```, set your application key to a random string with 32 characters long, edit database name, database username, and database password if needed.
49 |
50 | 5. Finally, Run Migrations and Seed the database with fake data.
51 |
52 | ```
53 | php artisan migrate --seed
54 | ```
55 |
56 | ## Terminology
57 | There are some terminologies that will be used on the meaning of the terms used by OAuth 2.0. If you need a refresher, then check [this](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) out.
58 |
59 | ## Authorization
60 | Authorization comes in two layers. The first layer authorize against the access token, and the second one is for checking against ownership, and non-admin Vs admin users.
61 |
62 | By default, user can delete or update a post or a comment **only** if he is the owner. Admins are authorized to view, create, update or delete anything.
63 |
64 | ### Access Tokens
65 | The application implements [Resource owner credentials grant](https://github.com/lucadegasperi/oauth2-server-laravel/blob/master/docs/authorization-server/choosing-grant.md#resource-owner-credentials-grant-section-43), which essentially requires the client to submit 5 fields: ```username```, ```password```, ```client_id```, ```client_secret```, and ```grant_type```.
66 |
67 | The authorization server will then issue access tokens to the client after successfully authenticating the client credentials and presenting authorization grant(user credentials).
68 |
69 | In ```app/Http/routes.php```, A route has been defined for requesting an access token.
70 |
71 | ### Ownership, & non-Admin Vs Admin Users
72 | Now, after validating the access token, we can extend the authorization layers and check if the current user is owner of the requested resource(i.e. post or comment), or is admin. So, _How does it work?_
73 |
74 | **Assign Middleware to controller**
75 | ```php
76 | public function __construct(){
77 |
78 | $this->middleware('oauth', ['except' => ['index', 'show']]);
79 | $this->middleware('authorize:' . __CLASS__, ['except' => ['index', 'show', 'store']]);
80 | }
81 |
82 | ```
83 |
84 | **Order**
85 |
86 | Please note that the middlewares has to be applied in a certain order. The ```oauth``` has to be added before the ```authorize``` Middleware.
87 |
88 | **Override isAuthorized() method**
89 | ```php
90 | public function isAuthorized(Request $request){
91 |
92 | $resource = "posts";
93 | $post = Post::find($this->getArgs($request)["post_id"]);
94 |
95 | return $this->authorizeUser($request, $resource, $post);
96 | }
97 | ```
98 |
99 | In ```app/Providers/AuthServiceProvider.php```, Abilities are defined using ```Gate``` facade.
100 |
101 | ## Routing
102 | These are some of the routes defined in ```app/routes.php```. You can test the API using [Postman](https://www.getpostman.com/)
103 |
104 | | HTTP Method | Path | Action | Fields |
105 | | ----- | ----- | ----- | ------------- |
106 | | GET | /users | index |
107 | | POST | /oauth/access_token | | username, password, client_id, client_secret, and grant_type.
_The ```username``` field is the ```email``` in ```Users``` table_.
_The ```password``` field is **secret**_.
_The ```client_id``` & ```client_secret``` fields are **id0** & **secret0**, or **id1** & **secret1**, ...etc respectively_.
_The ```grant_type``` field is **password**_.
108 | | POST | /posts | store | access_token, title, content
109 | | PUT | /posts/{post_id} | update | access_token, title, content
110 | | DELETE | /posts/{post_id} | destroy | access_token
111 |
112 |
113 | ## Support
114 | I've written this script in my free time during my studies. This is for free, unpaid. If you find it useful, please support the project by spreading the word.
115 |
116 | ## Contribute
117 |
118 | Contribute by creating new issues, sending pull requests on Github or you can send an email at: omar.elgabry.93@gmail.com
119 |
120 | ## Dependencies
121 | + [OAuth2 Server](https://github.com/lucadegasperi/oauth2-server-laravel/)
122 |
123 | ## License
124 | Built under [MIT](http://www.opensource.org/licenses/mit-license.php) license.
125 |
--------------------------------------------------------------------------------
/app/Comment.php:
--------------------------------------------------------------------------------
1 | belongsTo('App\Post');
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/app/Console/Commands/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OmarElgabry/lumen-api-oauth/75a8a1e70d9b8942631bd4d8f505f750d4d4b573/app/Console/Commands/.gitkeep
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | json(['message' => 'Bad Request', 'code' => 400], 400);
57 | }
58 |
59 | if($e instanceof MethodNotAllowedHttpException){
60 | return response()->json(['message' => 'Not Found', 'code' => 404], 404);
61 | }
62 |
63 | return response()->json(['message' => 'Unexpected Error', 'code' => 500], 500);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/Http/Controllers/CommentController.php:
--------------------------------------------------------------------------------
1 | success($comments, 200);
13 | }
14 |
15 | public function show($id){
16 |
17 | $comment = Comment::find($id);
18 |
19 | if(!$comment){
20 | return $this->error("The comment with {$id} doesn't exist", 404);
21 | }
22 |
23 | return $this->success($comment, 200);
24 | }
25 | }
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | json(['data' => $data], $code);
21 | }
22 |
23 | /**
24 | * Return a JSON response for error.
25 | *
26 | * @param array $message
27 | * @param string $code
28 | * @return \Illuminate\Http\JsonResponse
29 | */
30 | public function error($message, $code){
31 | return response()->json(['message' => $message], $code);
32 | }
33 |
34 | /**
35 | * Check if the user is authorized to perform a given action on a resource.
36 | *
37 | * @param \Illuminate\Http\Request $request
38 | * @param array $resource
39 | * @param mixed|array $arguments
40 | * @return boolean
41 | * @see https://lumen.laravel.com/docs/authorization
42 | */
43 | protected function authorizeUser(Request $request, $resource, $arguments = []){
44 |
45 | $user = User::find($this->getUserId());
46 | $action = $this->getAction($request);
47 |
48 | // The ability string must match the string defined in App\Providers\AuthServiceProvider\ability()
49 | $ability = "{$action}-{$resource}";
50 |
51 | // return $this->authorizeForUser($user, "{$action}-{$resource}", $data);
52 | return Gate::forUser($user)->allows($ability, $arguments);
53 | }
54 |
55 | /**
56 | * Check if user is authorized.
57 | *
58 | * This method will be called by "Authorize" Middleware for every controller.
59 | * Controller that needs to be authorized must override this method.
60 | *
61 | * @param \Illuminate\Http\Request $request
62 | * @return bool
63 | */
64 | public function isAuthorized(Request $request){
65 | return false;
66 | }
67 |
68 | /**
69 | * Get current authorized user id.
70 | * This method should be called only after validating the access token using OAuthMiddleware Middleware.
71 | *
72 | * @return boolean
73 | */
74 | protected function getUserId(){
75 | return \LucaDegasperi\OAuth2Server\Facades\Authorizer::getResourceOwnerId();
76 | }
77 |
78 | /**
79 | * Get the requested action method.
80 | *
81 | * @param \Illuminate\Http\Request $request
82 | * @return string
83 | */
84 | protected function getAction(Request $request){
85 | return explode('@', $request->route()[1]["uses"], 2)[1];
86 | }
87 |
88 | /**
89 | * Get the parameters in route.
90 | *
91 | * @param \Illuminate\Http\Request $request
92 | * @return array
93 | */
94 | protected function getArgs(Request $request){
95 | return $request->route()[2];
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/Http/Controllers/ExampleController.php:
--------------------------------------------------------------------------------
1 | middleware('oauth', ['except' => ['index', 'show']]);
15 | $this->middleware('authorize:' . __CLASS__, ['except' => ['index', 'show', 'store']]);
16 | }
17 |
18 | public function index($post_id){
19 |
20 | $post = Post::find($post_id);
21 |
22 | if(!$post){
23 | return $this->error("The post with {$post_id} doesn't exist", 404);
24 | }
25 |
26 | $comments = $post->comments;
27 | return $this->success($comments, 200);
28 | }
29 |
30 | public function store(Request $request, $post_id){
31 |
32 | $post = Post::find($post_id);
33 |
34 | if(!$post){
35 | return $this->error("The post with {$post_id} doesn't exist", 404);
36 | }
37 |
38 | $this->validateRequest($request);
39 |
40 | $comment = Comment::create([
41 | 'content' => $request->get('content'),
42 | 'user_id'=> $this->getUserId(),
43 | 'post_id'=> $post_id
44 | ]);
45 |
46 | return $this->success("The comment with id {$comment->id} has been created and assigned to the post with id {$post_id}", 201);
47 | }
48 |
49 | public function update(Request $request, $post_id, $comment_id){
50 |
51 | $comment = Comment::find($comment_id);
52 | $post = Post::find($post_id);
53 |
54 | if(!$comment || !$post){
55 | return $this->error("The comment with {$comment_id} or the post with id {$post_id} doesn't exist", 404);
56 | }
57 |
58 | $this->validateRequest($request);
59 |
60 | $comment->content = $request->get('content');
61 | $comment->user_id = $this->getUserId();
62 | $comment->post_id = $post_id;
63 |
64 | $comment->save();
65 |
66 | return $this->success("The comment with with id {$comment->id} has been updated", 200);
67 | }
68 |
69 | public function destroy($post_id, $comment_id){
70 |
71 | $comment = Comment::find($comment_id);
72 | $post = Post::find($post_id);
73 |
74 | if(!$comment || !$post){
75 | return $this->error("The comment with {$comment_id} or the post with id {$post_id} doesn't exist", 404);
76 | }
77 |
78 | if(!$post->comments()->find($comment_id)){
79 | return $this->error("The comment with id {$comment_id} isn't assigned to the post with id {$post_id}", 409);
80 | }
81 |
82 | $comment->delete();
83 |
84 | return $this->success("The comment with id {$comment_id} has been removed of the post {$post_id}", 200);
85 | }
86 |
87 | public function validateRequest(Request $request){
88 |
89 | $rules = [
90 | 'content' => 'required'
91 | ];
92 |
93 | $this->validate($request, $rules);
94 | }
95 |
96 | public function isAuthorized(Request $request){
97 |
98 | $resource = "comments";
99 | $comment = Comment::find($this->getArgs($request)["comment_id"]);
100 |
101 | return $this->authorizeUser($request, $resource, $comment);
102 | }
103 | }
--------------------------------------------------------------------------------
/app/Http/Controllers/PostController.php:
--------------------------------------------------------------------------------
1 | middleware('oauth', ['except' => ['index', 'show']]);
14 | $this->middleware('authorize:' . __CLASS__, ['except' => ['index', 'show', 'store']]);
15 | }
16 |
17 | public function index(){
18 |
19 | $posts = Post::all();
20 | return $this->success($posts, 200);
21 | }
22 |
23 | public function store(Request $request){
24 |
25 | $this->validateRequest($request);
26 |
27 | $post = Post::create([
28 | 'title' => $request->get('title'),
29 | 'content'=> $request->get('content'),
30 | 'user_id' => $this->getUserId()
31 | ]);
32 |
33 | return $this->success("The post with with id {$post->id} has been created", 201);
34 | }
35 |
36 | public function show($id){
37 |
38 | $post = Post::find($id);
39 |
40 | if(!$post){
41 | return $this->error("The post with {$id} doesn't exist", 404);
42 | }
43 |
44 | return $this->success($post, 200);
45 | }
46 |
47 | public function update(Request $request, $id){
48 |
49 | $post = Post::find($id);
50 |
51 | if(!$post){
52 | return $this->error("The post with {$id} doesn't exist", 404);
53 | }
54 |
55 | $this->validateRequest($request);
56 |
57 | $post->title = $request->get('title');
58 | $post->content = $request->get('content');
59 | $post->user_id = $this->getUserId();
60 |
61 | $post->save();
62 |
63 | return $this->success("The post with with id {$post->id} has been updated", 200);
64 | }
65 |
66 | public function destroy($id){
67 |
68 | $post = Post::find($id);
69 |
70 | if(!$post){
71 | return $this->error("The post with {$id} doesn't exist", 404);
72 | }
73 |
74 | // no need to delete the comments for the current post,
75 | // since we used on delete cascase on update cascase.
76 | // $post->comments()->delete();
77 | $post->delete();
78 |
79 | return $this->success("The post with with id {$id} has been deleted along with it's comments", 200);
80 | }
81 |
82 | public function validateRequest(Request $request){
83 |
84 | $rules = [
85 | 'title' => 'required',
86 | 'content' => 'required'
87 | ];
88 |
89 | $this->validate($request, $rules);
90 | }
91 |
92 | public function isAuthorized(Request $request){
93 |
94 | $resource = "posts";
95 | $post = Post::find($this->getArgs($request)["post_id"]);
96 |
97 | return $this->authorizeUser($request, $resource, $post);
98 | }
99 | }
--------------------------------------------------------------------------------
/app/Http/Controllers/UserController.php:
--------------------------------------------------------------------------------
1 | middleware('oauth', ['except' => ['index', 'show']]);
15 | $this->middleware('authorize:' . __CLASS__, ['except' => ['index', 'show']]);
16 | }
17 |
18 | public function index(){
19 |
20 | $users = User::all();
21 | return $this->success($users, 200);
22 | }
23 |
24 | public function store(Request $request){
25 |
26 | $this->validateRequest($request);
27 |
28 | $user = User::create([
29 | 'email' => $request->get('email'),
30 | 'password'=> Hash::make($request->get('password'))
31 | ]);
32 |
33 | return $this->success("The user with with id {$user->id} has been created", 201);
34 | }
35 |
36 | public function show($id){
37 |
38 | $user = User::find($id);
39 |
40 | if(!$user){
41 | return $this->error("The user with {$id} doesn't exist", 404);
42 | }
43 |
44 | return $this->success($user, 200);
45 | }
46 |
47 | public function update(Request $request, $id){
48 |
49 | $user = User::find($id);
50 |
51 | if(!$user){
52 | return $this->error("The user with {$id} doesn't exist", 404);
53 | }
54 |
55 | $this->validateRequest($request);
56 |
57 | $user->email = $request->get('email');
58 | $user->password = Hash::make($request->get('password'));
59 |
60 | $user->save();
61 |
62 | return $this->success("The user with with id {$user->id} has been updated", 200);
63 | }
64 |
65 | public function destroy($id){
66 |
67 | $user = User::find($id);
68 |
69 | if(!$user){
70 | return $this->error("The user with {$id} doesn't exist", 404);
71 | }
72 |
73 | $user->delete();
74 |
75 | return $this->success("The user with with id {$id} has been deleted", 200);
76 | }
77 |
78 | public function validateRequest(Request $request){
79 |
80 | $rules = [
81 | 'email' => 'required|email|unique:users',
82 | 'password' => 'required|min:6'
83 | ];
84 |
85 | $this->validate($request, $rules);
86 | }
87 |
88 | public function isAuthorized(Request $request){
89 |
90 | $resource = "users";
91 | // $user = User::find($this->getArgs($request)["user_id"]);
92 |
93 | return $this->authorizeUser($request, $resource);
94 | }
95 | }
--------------------------------------------------------------------------------
/app/Http/Middleware/Authenticate.php:
--------------------------------------------------------------------------------
1 | auth = $auth;
26 | }
27 |
28 | /**
29 | * Handle an incoming request.
30 | *
31 | * @param \Illuminate\Http\Request $request
32 | * @param \Closure $next
33 | * @param string|null $guard
34 | * @return mixed
35 | */
36 | public function handle($request, Closure $next, $guard = null)
37 | {
38 | if ($this->auth->guard($guard)->guest()) {
39 | return response('Unauthorized.', 401);
40 | }
41 |
42 | return $next($request);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Http/Middleware/Authorize.php:
--------------------------------------------------------------------------------
1 | isAuthorized($request)){
19 | return $controller->error("You aren't allowed to perform the requested action", 403);
20 | }
21 |
22 | return $next($request);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Http/Middleware/ExampleMiddleware.php:
--------------------------------------------------------------------------------
1 | hasMany('App\Comment');
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | app['auth']->viaRequest('api', function ($request) {
35 | // if ($request->input('api_token')) {
36 | // return User::where('api_token', $request->input('api_token'))->first();
37 | // }
38 | // });
39 |
40 | // Group & Define simillar Abilities
41 | $this->isOwner([
42 | 'posts' => ['destroy', 'update'],
43 | 'comments' => ['destroy', 'update']
44 | ]);
45 |
46 | $this->isAdmin([
47 | 'users' => ['store', 'destroy', 'update']
48 | ]);
49 | }
50 |
51 | /**
52 | * Define abilities that checks if the current user is the owner of the requested resource.
53 | * In case of admin user, it will return true.
54 | *
55 | * @param array $arguments
56 | * @return boolean
57 | */
58 | private function isOwner($arguments = []){
59 |
60 | foreach ($arguments as $resource => $actions) {
61 | foreach ($actions as $action) {
62 |
63 | // Gate::before(function ($user, $ability) {
64 | // if($user->is_admin){
65 | // return true;
66 | // }
67 | // });
68 |
69 | Gate::define($this->ability($action, $resource), function ($user, $arg) {
70 |
71 | if(is_null($arg)) { return false; }
72 |
73 | return $arg->user_id === $user->id || $user->is_admin;
74 | });
75 | }
76 | }
77 | }
78 |
79 | /**
80 | * Define abilities that checks if the current user is admin.
81 | *
82 | * @param array $arguments
83 | * @return boolean
84 | */
85 | private function isAdmin($arguments){
86 |
87 | foreach ($arguments as $resource => $actions) {
88 | foreach ($actions as $action) {
89 | Gate::define($this->ability($action, $resource), function ($user) {
90 | return $user->is_admin;
91 | });
92 | }
93 | }
94 | }
95 |
96 | /**
97 | * Define ability string.
98 | *
99 | * @param string $action
100 | * @param string $resource
101 | * @return string
102 | */
103 | private function ability($action, $resource){
104 | return "{$action}-{$resource}";
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/app/Providers/EventServiceProvider.php:
--------------------------------------------------------------------------------
1 | [
16 | 'App\Listeners\EventListener',
17 | ],
18 | ];
19 | }
20 |
--------------------------------------------------------------------------------
/app/User.php:
--------------------------------------------------------------------------------
1 | first();
46 |
47 | if($user && Hash::check($password, $user->password)){
48 | return $user->id;
49 | }
50 |
51 | return false;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(
32 | 'Illuminate\Contracts\Console\Kernel'
33 | );
34 |
35 | exit($kernel->handle(new ArgvInput, new ConsoleOutput));
36 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | load();
7 | } catch (Dotenv\Exception\InvalidPathException $e) {
8 | //
9 | }
10 |
11 | /*
12 | |--------------------------------------------------------------------------
13 | | Create The Application
14 | |--------------------------------------------------------------------------
15 | |
16 | | Here we will load the environment and create the application instance
17 | | that serves as the central piece of this framework. We'll use this
18 | | application as an "IoC" container and router for this framework.
19 | |
20 | */
21 |
22 | $app = new Laravel\Lumen\Application(
23 | realpath(__DIR__.'/../')
24 | );
25 |
26 | $app->withFacades();
27 |
28 | $app->withEloquent();
29 |
30 | /*
31 | |--------------------------------------------------------------------------
32 | | Register Container Bindings
33 | |--------------------------------------------------------------------------
34 | |
35 | | Now we will register a few bindings in the service container. We will
36 | | register the exception handler and the console kernel. You may add
37 | | your own bindings here if you like or you can make another file.
38 | |
39 | */
40 |
41 | $app->singleton(
42 | Illuminate\Contracts\Debug\ExceptionHandler::class,
43 | App\Exceptions\Handler::class
44 | );
45 |
46 | $app->singleton(
47 | Illuminate\Contracts\Console\Kernel::class,
48 | App\Console\Kernel::class
49 | );
50 |
51 | /*
52 | |--------------------------------------------------------------------------
53 | | Register Middleware
54 | |--------------------------------------------------------------------------
55 | |
56 | | Next, we will register the middleware with the application. These can
57 | | be global middleware that run before and after each request into a
58 | | route or middleware that'll be assigned to some specific routes.
59 | |
60 | */
61 |
62 | $app->middleware([
63 | // App\Http\Middleware\ExampleMiddleware::class
64 | \LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware::class
65 | ]);
66 |
67 | $app->routeMiddleware([
68 | // 'auth' => App\Http\Middleware\Authenticate::class,
69 | 'oauth' => \LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware::class,
70 | // 'oauth-user'=> \LucaDegasperi\OAuth2Server\Middleware\OAuthUserOwnerMiddleware::class,
71 | 'authorize' => App\Http\Middleware\Authorize::class,
72 | ]);
73 |
74 | /*
75 | |--------------------------------------------------------------------------
76 | | Register Service Providers
77 | |--------------------------------------------------------------------------
78 | |
79 | | Here we will register all of the application's service providers which
80 | | are used to bind services into the container. Service providers are
81 | | totally optional, so you are not required to uncomment this line.
82 | |
83 | */
84 |
85 | // $app->register(App\Providers\AppServiceProvider::class);
86 | $app->register(App\Providers\AuthServiceProvider::class);
87 | // $app->register(App\Providers\EventServiceProvider::class);
88 | $app->register(\LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider::class);
89 | $app->register(\LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider::class);
90 |
91 | /*
92 | |--------------------------------------------------------------------------
93 | | Load The Application Routes
94 | |--------------------------------------------------------------------------
95 | |
96 | | Next we will include the routes file so that they can all be added to
97 | | the application. This will provide all of the URLs the application
98 | | can respond to, as well as the controllers that may handle them.
99 | |
100 | */
101 |
102 | $app->group(['namespace' => 'App\Http\Controllers'], function ($app) {
103 | require __DIR__.'/../routes/web.php';
104 | });
105 |
106 | return $app;
107 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "omarelgabry/lumen-api-oauth",
3 | "description": "A RESTful API based on Lumen micro-framework with OAuth2.",
4 | "keywords": ["lumen", "rest", "api", "oauth", "authentication", "authorization"],
5 | "homepage": "https://github.com/OmarElGabry/lumen-api-oauth",
6 | "license": "MIT",
7 | "type": "project",
8 | "require": {
9 | "php": ">=5.6.4",
10 | "laravel/lumen-framework": "5.4.*",
11 | "vlucas/phpdotenv": "~2.2",
12 | "laravel/homestead": "^5.0",
13 | "lucadegasperi/oauth2-server-laravel": "^5.2"
14 | },
15 | "require-dev": {
16 | "fzaninotto/faker": "~1.4",
17 | "phpunit/phpunit": "~5.0",
18 | "mockery/mockery": "~0.9"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "App\\": "app/"
23 | }
24 | },
25 | "autoload-dev": {
26 | "classmap": [
27 | "tests/",
28 | "database/"
29 | ]
30 | },
31 | "scripts": {
32 | "post-root-package-install": [
33 | "php -r \"copy('.env.example', '.env');\""
34 | ]
35 | },
36 | "minimum-stability": "dev",
37 | "prefer-stable": true
38 | }
39 |
--------------------------------------------------------------------------------
/config/oauth2.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | return [
13 |
14 | /*
15 | |--------------------------------------------------------------------------
16 | | Supported Grant Types
17 | |--------------------------------------------------------------------------
18 | |
19 | | Your OAuth2 Server can issue an access token based on different grant
20 | | types you can even provide your own grant type.
21 | |
22 | | To choose which grant type suits your scenario, see
23 | | http://oauth2.thephpleague.com/authorization-server/which-grant
24 | |
25 | | Please see this link to find available grant types
26 | | http://git.io/vJLAv
27 | |
28 | */
29 |
30 | 'grant_types' => [
31 | 'password' => [
32 | 'class' => '\League\OAuth2\Server\Grant\PasswordGrant',
33 | 'callback' => '\App\User@verify',
34 | 'access_token_ttl' => 3600
35 | ]
36 | ],
37 |
38 | /*
39 | |--------------------------------------------------------------------------
40 | | Output Token Type
41 | |--------------------------------------------------------------------------
42 | |
43 | | This will tell the authorization server the output format for the access
44 | | token and the resource server how to parse the access token used.
45 | |
46 | | Default value is League\OAuth2\Server\TokenType\Bearer
47 | |
48 | */
49 |
50 | 'token_type' => 'League\OAuth2\Server\TokenType\Bearer',
51 |
52 | /*
53 | |--------------------------------------------------------------------------
54 | | State Parameter
55 | |--------------------------------------------------------------------------
56 | |
57 | | Whether or not the state parameter is required in the query string.
58 | |
59 | */
60 |
61 | 'state_param' => false,
62 |
63 | /*
64 | |--------------------------------------------------------------------------
65 | | Scope Parameter
66 | |--------------------------------------------------------------------------
67 | |
68 | | Whether or not the scope parameter is required in the query string.
69 | |
70 | */
71 |
72 | 'scope_param' => false,
73 |
74 | /*
75 | |--------------------------------------------------------------------------
76 | | Scope Delimiter
77 | |--------------------------------------------------------------------------
78 | |
79 | | Which character to use to split the scope parameter in the query string.
80 | |
81 | */
82 |
83 | 'scope_delimiter' => ',',
84 |
85 | /*
86 | |--------------------------------------------------------------------------
87 | | Default Scope
88 | |--------------------------------------------------------------------------
89 | |
90 | | The default scope to use if not present in the query string.
91 | |
92 | */
93 |
94 | 'default_scope' => null,
95 |
96 | /*
97 | |--------------------------------------------------------------------------
98 | | Access Token TTL
99 | |--------------------------------------------------------------------------
100 | |
101 | | For how long the issued access token is valid (in seconds) this can be
102 | | also set on a per grant-type basis.
103 | |
104 | */
105 |
106 | 'access_token_ttl' => 3600,
107 |
108 | /*
109 | |--------------------------------------------------------------------------
110 | | Limit clients to specific grants
111 | |--------------------------------------------------------------------------
112 | |
113 | | Whether or not to limit clients to specific grant types. This is useful
114 | | to allow only trusted clients to access your API differently.
115 | |
116 | */
117 |
118 | 'limit_clients_to_grants' => false,
119 |
120 | /*
121 | |--------------------------------------------------------------------------
122 | | Limit clients to specific scopes
123 | |--------------------------------------------------------------------------
124 | |
125 | | Whether or not to limit clients to specific scopes. This is useful to
126 | | only allow specific clients to use some scopes.
127 | |
128 | */
129 |
130 | 'limit_clients_to_scopes' => false,
131 |
132 | /*
133 | |--------------------------------------------------------------------------
134 | | Limit scopes to specific grants
135 | |--------------------------------------------------------------------------
136 | |
137 | | Whether or not to limit scopes to specific grants. This is useful to
138 | | allow certain scopes to be used only with certain grant types.
139 | |
140 | */
141 |
142 | 'limit_scopes_to_grants' => false,
143 |
144 | /*
145 | |--------------------------------------------------------------------------
146 | | HTTP Header Only
147 | |--------------------------------------------------------------------------
148 | |
149 | | This will tell the resource server where to check for the access_token.
150 | | By default it checks both the query string and the http headers.
151 | |
152 | */
153 |
154 | 'http_headers_only' => false,
155 |
156 | ];
157 |
--------------------------------------------------------------------------------
/database/factories/ModelFactory.php:
--------------------------------------------------------------------------------
1 | define(App\Post::class, function (Faker\Generator $faker) {
15 | return [
16 | 'title' => $faker->sentence(4),
17 | 'content' => $faker->paragraph(4),
18 | 'user_id' => mt_rand(1, 10)
19 | ];
20 | });
21 |
22 | $factory->define(App\Comment::class, function (Faker\Generator $faker) {
23 | return [
24 | 'content' => $faker->paragraph(1),
25 | 'post_id' => mt_rand(1, 50),
26 | 'user_id' => mt_rand(1, 10)
27 | ];
28 | });
29 |
30 | $factory->define(App\User::class, function (Faker\Generator $faker) {
31 |
32 | $hasher = app()->make('hash');
33 |
34 | return [
35 | 'name' => $faker->name,
36 | 'email' => $faker->email,
37 | 'password' => $hasher->make("secret"),
38 | 'is_admin' => mt_rand(0, 1)
39 | ];
40 | });
--------------------------------------------------------------------------------
/database/migrations/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OmarElgabry/lumen-api-oauth/75a8a1e70d9b8942631bd4d8f505f750d4d4b573/database/migrations/.gitkeep
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_110151_create_oauth_scopes_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth scopes table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthScopesTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_scopes', function (Blueprint $table) {
31 | $table->string('id', 40)->primary();
32 | $table->string('description');
33 |
34 | $table->nullableTimestamps();
35 | });
36 | }
37 |
38 | /**
39 | * Reverse the migrations.
40 | *
41 | * @return void
42 | */
43 | public function down()
44 | {
45 | Schema::drop('oauth_scopes');
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_110304_create_oauth_grants_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth grants table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthGrantsTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_grants', function (Blueprint $table) {
31 | $table->string('id', 40)->primary();
32 | $table->nullableTimestamps();
33 | });
34 | }
35 |
36 | /**
37 | * Reverse the migrations.
38 | *
39 | * @return void
40 | */
41 | public function down()
42 | {
43 | Schema::drop('oauth_grants');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_110403_create_oauth_grant_scopes_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth grant scopes table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthGrantScopesTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_grant_scopes', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->string('grant_id', 40);
33 | $table->string('scope_id', 40);
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->index('grant_id');
38 | $table->index('scope_id');
39 |
40 | $table->foreign('grant_id')
41 | ->references('id')->on('oauth_grants')
42 | ->onDelete('cascade');
43 |
44 | $table->foreign('scope_id')
45 | ->references('id')->on('oauth_scopes')
46 | ->onDelete('cascade');
47 | });
48 | }
49 |
50 | /**
51 | * Reverse the migrations.
52 | *
53 | * @return void
54 | */
55 | public function down()
56 | {
57 | Schema::table('oauth_grant_scopes', function (Blueprint $table) {
58 | $table->dropForeign('oauth_grant_scopes_grant_id_foreign');
59 | $table->dropForeign('oauth_grant_scopes_scope_id_foreign');
60 | });
61 | Schema::drop('oauth_grant_scopes');
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_110459_create_oauth_clients_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth client table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthClientsTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_clients', function (BluePrint $table) {
31 | $table->string('id', 40)->primary();
32 | $table->string('secret', 40);
33 | $table->string('name');
34 | $table->nullableTimestamps();
35 |
36 | $table->unique(['id', 'secret']);
37 | });
38 | }
39 |
40 | /**
41 | * Reverse the migrations.
42 | *
43 | * @return void
44 | */
45 | public function down()
46 | {
47 | Schema::drop('oauth_clients');
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_110557_create_oauth_client_endpoints_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth client endpoints table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthClientEndpointsTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_client_endpoints', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->string('client_id', 40);
33 | $table->string('redirect_uri');
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->unique(['client_id', 'redirect_uri']);
38 |
39 | $table->foreign('client_id')
40 | ->references('id')->on('oauth_clients')
41 | ->onDelete('cascade')
42 | ->onUpdate('cascade');
43 | });
44 | }
45 |
46 | /**
47 | * Reverse the migrations.
48 | *
49 | * @return void
50 | */
51 | public function down()
52 | {
53 | Schema::table('oauth_client_endpoints', function (Blueprint $table) {
54 | $table->dropForeign('oauth_client_endpoints_client_id_foreign');
55 | });
56 |
57 | Schema::drop('oauth_client_endpoints');
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_110705_create_oauth_client_scopes_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth client scopes table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthClientScopesTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_client_scopes', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->string('client_id', 40);
33 | $table->string('scope_id', 40);
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->index('client_id');
38 | $table->index('scope_id');
39 |
40 | $table->foreign('client_id')
41 | ->references('id')->on('oauth_clients')
42 | ->onDelete('cascade');
43 |
44 | $table->foreign('scope_id')
45 | ->references('id')->on('oauth_scopes')
46 | ->onDelete('cascade');
47 | });
48 | }
49 |
50 | /**
51 | * Reverse the migrations.
52 | *
53 | * @return void
54 | */
55 | public function down()
56 | {
57 | Schema::table('oauth_client_scopes', function (Blueprint $table) {
58 | $table->dropForeign('oauth_client_scopes_client_id_foreign');
59 | $table->dropForeign('oauth_client_scopes_scope_id_foreign');
60 | });
61 | Schema::drop('oauth_client_scopes');
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_110817_create_oauth_client_grants_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth client grants table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthClientGrantsTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_client_grants', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->string('client_id', 40);
33 | $table->string('grant_id', 40);
34 | $table->nullableTimestamps();
35 |
36 | $table->index('client_id');
37 | $table->index('grant_id');
38 |
39 | $table->foreign('client_id')
40 | ->references('id')->on('oauth_clients')
41 | ->onDelete('cascade')
42 | ->onUpdate('no action');
43 |
44 | $table->foreign('grant_id')
45 | ->references('id')->on('oauth_grants')
46 | ->onDelete('cascade')
47 | ->onUpdate('no action');
48 | });
49 | }
50 |
51 | /**
52 | * Reverse the migrations.
53 | *
54 | * @return void
55 | */
56 | public function down()
57 | {
58 | Schema::table('oauth_client_grants', function (Blueprint $table) {
59 | $table->dropForeign('oauth_client_grants_client_id_foreign');
60 | $table->dropForeign('oauth_client_grants_grant_id_foreign');
61 | });
62 | Schema::drop('oauth_client_grants');
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_111002_create_oauth_sessions_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth sessions table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthSessionsTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_sessions', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->string('client_id', 40);
33 | $table->enum('owner_type', ['client', 'user'])->default('user');
34 | $table->string('owner_id');
35 | $table->string('client_redirect_uri')->nullable();
36 | $table->nullableTimestamps();
37 |
38 | $table->index(['client_id', 'owner_type', 'owner_id']);
39 |
40 | $table->foreign('client_id')
41 | ->references('id')->on('oauth_clients')
42 | ->onDelete('cascade')
43 | ->onUpdate('cascade');
44 | });
45 | }
46 |
47 | /**
48 | * Reverse the migrations.
49 | *
50 | * @return void
51 | */
52 | public function down()
53 | {
54 | Schema::table('oauth_sessions', function (Blueprint $table) {
55 | $table->dropForeign('oauth_sessions_client_id_foreign');
56 | });
57 | Schema::drop('oauth_sessions');
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_111109_create_oauth_session_scopes_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth session scopes table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthSessionScopesTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_session_scopes', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->integer('session_id')->unsigned();
33 | $table->string('scope_id', 40);
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->index('session_id');
38 | $table->index('scope_id');
39 |
40 | $table->foreign('session_id')
41 | ->references('id')->on('oauth_sessions')
42 | ->onDelete('cascade');
43 |
44 | $table->foreign('scope_id')
45 | ->references('id')->on('oauth_scopes')
46 | ->onDelete('cascade');
47 | });
48 | }
49 |
50 | /**
51 | * Reverse the migrations.
52 | *
53 | * @return void
54 | */
55 | public function down()
56 | {
57 | Schema::table('oauth_session_scopes', function (Blueprint $table) {
58 | $table->dropForeign('oauth_session_scopes_session_id_foreign');
59 | $table->dropForeign('oauth_session_scopes_scope_id_foreign');
60 | });
61 | Schema::drop('oauth_session_scopes');
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_111254_create_oauth_auth_codes_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth auth codes table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthAuthCodesTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_auth_codes', function (Blueprint $table) {
31 | $table->string('id', 40)->primary();
32 | $table->integer('session_id')->unsigned();
33 | $table->string('redirect_uri');
34 | $table->integer('expire_time');
35 |
36 | $table->nullableTimestamps();
37 |
38 | $table->index('session_id');
39 |
40 | $table->foreign('session_id')
41 | ->references('id')->on('oauth_sessions')
42 | ->onDelete('cascade');
43 | });
44 | }
45 |
46 | /**
47 | * Reverse the migrations.
48 | *
49 | * @return void
50 | */
51 | public function down()
52 | {
53 | Schema::table('oauth_auth_codes', function (Blueprint $table) {
54 | $table->dropForeign('oauth_auth_codes_session_id_foreign');
55 | });
56 | Schema::drop('oauth_auth_codes');
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_111403_create_oauth_auth_code_scopes_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth code scopes table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthAuthCodeScopesTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_auth_code_scopes', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->string('auth_code_id', 40);
33 | $table->string('scope_id', 40);
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->index('auth_code_id');
38 | $table->index('scope_id');
39 |
40 | $table->foreign('auth_code_id')
41 | ->references('id')->on('oauth_auth_codes')
42 | ->onDelete('cascade');
43 |
44 | $table->foreign('scope_id')
45 | ->references('id')->on('oauth_scopes')
46 | ->onDelete('cascade');
47 | });
48 | }
49 |
50 | /**
51 | * Reverse the migrations.
52 | *
53 | * @return void
54 | */
55 | public function down()
56 | {
57 | Schema::table('oauth_auth_code_scopes', function (Blueprint $table) {
58 | $table->dropForeign('oauth_auth_code_scopes_auth_code_id_foreign');
59 | $table->dropForeign('oauth_auth_code_scopes_scope_id_foreign');
60 | });
61 | Schema::drop('oauth_auth_code_scopes');
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_111518_create_oauth_access_tokens_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth access tokens table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthAccessTokensTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_access_tokens', function (Blueprint $table) {
31 | $table->string('id', 40)->primary();
32 | $table->integer('session_id')->unsigned();
33 | $table->integer('expire_time');
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->unique(['id', 'session_id']);
38 | $table->index('session_id');
39 |
40 | $table->foreign('session_id')
41 | ->references('id')->on('oauth_sessions')
42 | ->onDelete('cascade');
43 | });
44 | }
45 |
46 | /**
47 | * Reverse the migrations.
48 | *
49 | * @return void
50 | */
51 | public function down()
52 | {
53 | Schema::table('oauth_access_tokens', function (Blueprint $table) {
54 | $table->dropForeign('oauth_access_tokens_session_id_foreign');
55 | });
56 | Schema::drop('oauth_access_tokens');
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_111657_create_oauth_access_token_scopes_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth access token scopes table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthAccessTokenScopesTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_access_token_scopes', function (Blueprint $table) {
31 | $table->increments('id');
32 | $table->string('access_token_id', 40);
33 | $table->string('scope_id', 40);
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->index('access_token_id');
38 | $table->index('scope_id');
39 |
40 | $table->foreign('access_token_id')
41 | ->references('id')->on('oauth_access_tokens')
42 | ->onDelete('cascade');
43 |
44 | $table->foreign('scope_id')
45 | ->references('id')->on('oauth_scopes')
46 | ->onDelete('cascade');
47 | });
48 | }
49 |
50 | /**
51 | * Reverse the migrations.
52 | *
53 | * @return void
54 | */
55 | public function down()
56 | {
57 | Schema::table('oauth_access_token_scopes', function (Blueprint $table) {
58 | $table->dropForeign('oauth_access_token_scopes_scope_id_foreign');
59 | $table->dropForeign('oauth_access_token_scopes_access_token_id_foreign');
60 | });
61 | Schema::drop('oauth_access_token_scopes');
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/database/migrations/2014_04_24_111810_create_oauth_refresh_tokens_table.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Illuminate\Database\Migrations\Migration;
13 | use Illuminate\Database\Schema\Blueprint;
14 | use Illuminate\Support\Facades\Schema;
15 |
16 | /**
17 | * This is the create oauth refresh tokens table migration class.
18 | *
19 | * @author Luca Degasperi
20 | */
21 | class CreateOauthRefreshTokensTable extends Migration
22 | {
23 | /**
24 | * Run the migrations.
25 | *
26 | * @return void
27 | */
28 | public function up()
29 | {
30 | Schema::create('oauth_refresh_tokens', function (Blueprint $table) {
31 | $table->string('id', 40)->unique();
32 | $table->string('access_token_id', 40)->primary();
33 | $table->integer('expire_time');
34 |
35 | $table->nullableTimestamps();
36 |
37 | $table->foreign('access_token_id')
38 | ->references('id')->on('oauth_access_tokens')
39 | ->onDelete('cascade');
40 | });
41 | }
42 |
43 | /**
44 | * Reverse the migrations.
45 | *
46 | * @return void
47 | */
48 | public function down()
49 | {
50 | Schema::table('oauth_refresh_tokens', function (Blueprint $table) {
51 | $table->dropForeign('oauth_refresh_tokens_access_token_id_foreign');
52 | });
53 |
54 | Schema::drop('oauth_refresh_tokens');
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/database/migrations/2016_03_24_182334_create_users_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('name');
18 | $table->string('email')->unique();
19 | $table->string('password');
20 | $table->boolean('is_admin');
21 | $table->nullableTimestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | *
28 | * @return void
29 | */
30 | public function down()
31 | {
32 | Schema::drop('users');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/database/migrations/2016_03_24_221425_create_posts_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('title');
18 | $table->string('content');
19 | $table->integer('user_id')->unsigned();
20 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
21 | $table->nullableTimestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | *
28 | * @return void
29 | */
30 | public function down()
31 | {
32 | Schema::drop('posts');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/database/migrations/2016_03_24_221457_create_comments_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('content');
18 |
19 | $table->nullableTimestamps();
20 | $table->integer('user_id')->unsigned();
21 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
22 |
23 | $table->integer('post_id')->unsigned();
24 | $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade')->onUpdate('cascade');
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down()
34 | {
35 | Schema::drop('comments');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/database/seeds/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | create();
27 | factory(Post::class, 50)->create();
28 | factory(Comment::class, 100)->create();
29 |
30 | $this->call('OAuthClientSeeder');
31 |
32 | // Enable it back
33 | DB::statement('SET FOREIGN_KEY_CHECKS = 1');
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/database/seeds/OAuthClientSeeder.php:
--------------------------------------------------------------------------------
1 | truncate();
15 |
16 | for ($i=0; $i < 10; $i++){
17 |
18 | DB::table('oauth_clients')->insert(
19 | [ 'id' => "id$i",
20 | 'secret' => "secret$i",
21 | 'name' => "Test Client $i"
22 | ]
23 | );
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | ./tests
15 |
16 |
17 |
18 |
19 | ./app
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Redirect Trailing Slashes If Not A Folder...
9 | RewriteCond %{REQUEST_FILENAME} !-d
10 | RewriteRule ^(.*)/$ /$1 [L,R=301]
11 |
12 | # Handle Front Controller...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_FILENAME} !-f
15 | RewriteRule ^ index.php [L]
16 |
17 | # Handle Authorization Header
18 | RewriteCond %{HTTP:Authorization} .
19 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
20 |
21 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | run();
29 |
--------------------------------------------------------------------------------
/public/lumen-api-oauth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OmarElgabry/lumen-api-oauth/75a8a1e70d9b8942631bd4d8f505f750d4d4b573/public/lumen-api-oauth.png
--------------------------------------------------------------------------------
/resources/views/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OmarElgabry/lumen-api-oauth/75a8a1e70d9b8942631bd4d8f505f750d4d4b573/resources/views/.gitkeep
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 | get('/', function () use ($app) {
16 | return $app->version();
17 | });
18 |
19 | // Posts
20 | $app->get('/posts','PostController@index');
21 | $app->post('/posts','PostController@store');
22 | $app->get('/posts/{post_id}','PostController@show');
23 | $app->put('/posts/{post_id}', 'PostController@update');
24 | $app->patch('/posts/{post_id}', 'PostController@update');
25 | $app->delete('/posts/{post_id}', 'PostController@destroy');
26 |
27 | // Users
28 | $app->get('/users/', 'UserController@index');
29 | $app->post('/users/', 'UserController@store');
30 | $app->get('/users/{user_id}', 'UserController@show');
31 | $app->put('/users/{user_id}', 'UserController@update');
32 | $app->patch('/users/{user_id}', 'UserController@update');
33 | $app->delete('/users/{user_id}', 'UserController@destroy');
34 |
35 | // Comments
36 | $app->get('/comments', 'CommentController@index');
37 | $app->get('/comments/{comment_id}', 'CommentController@show');
38 |
39 | // Comment(s) of a post
40 | $app->get('/posts/{post_id}/comments', 'PostCommentController@index');
41 | $app->post('/posts/{post_id}/comments', 'PostCommentController@store');
42 | $app->put('/posts/{post_id}/comments/{comment_id}', 'PostCommentController@update');
43 | $app->patch('/posts/{post_id}/comments/{comment_id}', 'PostCommentController@update');
44 | $app->delete('/posts/{post_id}/comments/{comment_id}', 'PostCommentController@destroy');
45 |
46 | // Request an access token
47 | $app->post('/oauth/access_token', function() use ($app){
48 | return response()->json($app->make('oauth2-server.authorizer')->issueAccessToken());
49 | });
50 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/tests/ExampleTest.php:
--------------------------------------------------------------------------------
1 | get('/');
16 |
17 | $this->assertEquals(
18 | $this->app->version(), $this->response->getContent()
19 | );
20 | }
21 |
22 | public function testHomePage()
23 | {
24 |
25 | $response = $this->call('GET', '/');
26 | $this->assertResponseOk();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 |