├── .env.example ├── .gitignore ├── app ├── Author.php ├── Console │ ├── Commands │ │ └── .gitkeep │ └── Kernel.php ├── Events │ ├── Event.php │ └── ExampleEvent.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── AuthorController.php │ │ ├── Controller.php │ │ └── ExampleController.php │ └── Middleware │ │ ├── Auth0Middleware.php │ │ ├── Authenticate.php │ │ └── ExampleMiddleware.php ├── Jobs │ ├── ExampleJob.php │ └── Job.php ├── Listeners │ └── ExampleListener.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ └── EventServiceProvider.php └── User.php ├── artisan ├── bootstrap └── app.php ├── composer.json ├── composer.lock ├── database ├── factories │ └── ModelFactory.php ├── migrations │ ├── .gitkeep │ └── 2017_12_18_203510_create_authors_table.php └── seeds │ └── DatabaseSeeder.php ├── phpunit.xml ├── public ├── .htaccess └── index.php ├── readme.md ├── 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 | AUTH0_DOMAIN=https://your-domain.auth0.com/ 7 | AUTH0_AUD=https://authorsapi.com 8 | 9 | DB_CONNECTION=mysql 10 | DB_HOST=127.0.0.1 11 | DB_PORT=3306 12 | DB_DATABASE=homestead 13 | DB_USERNAME=homestead 14 | DB_PASSWORD=secret 15 | 16 | CACHE_DRIVER=file 17 | QUEUE_DRIVER=sync 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /.idea 3 | Homestead.json 4 | Homestead.yaml 5 | .env 6 | -------------------------------------------------------------------------------- /app/Author.php: -------------------------------------------------------------------------------- 1 | json(Author::all()); 14 | } 15 | 16 | public function showOneAuthor($id) 17 | { 18 | return response()->json(Author::find($id)); 19 | } 20 | 21 | public function create(Request $request) 22 | { 23 | $this->validate($request, [ 24 | 'name' => 'required', 25 | 'email' => 'required|email|unique:authors', 26 | 'location' => 'required|alpha' 27 | ]); 28 | 29 | $author = Author::create($request->all()); 30 | 31 | return response()->json($author, 201); 32 | } 33 | 34 | public function update($id, Request $request) 35 | { 36 | $author = Author::findOrFail($id); 37 | $author->update($request->all()); 38 | 39 | return response()->json($author, 200); 40 | } 41 | 42 | public function delete($id) 43 | { 44 | Author::findOrFail($id)->delete(); 45 | return response('Deleted Successfully', 200); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | bearerToken(); 24 | 25 | if(!$token) { 26 | return response()->json('No token provided', 401); 27 | } 28 | 29 | $this->validateAndDecode($token); 30 | $decodedToken = $this->validateAndDecode($token); 31 | if ($scopeRequired && !$this->tokenHasScope($decodedToken, $scopeRequired)) { 32 | return response()->json(['message' => 'Insufficient scope'], 403); 33 | } 34 | return $next($request); 35 | } 36 | 37 | public function validateAndDecode($token) 38 | { 39 | try { 40 | $jwksUri = env('AUTH0_DOMAIN') . '.well-known/jwks.json'; 41 | 42 | $jwksFetcher = new JWKFetcher(null, [ 'base_uri' => $jwksUri ]); 43 | $signatureVerifier = new AsymmetricVerifier($jwksFetcher); 44 | $tokenVerifier = new TokenVerifier(env('AUTH0_DOMAIN'), env('AUTH0_AUD'), $signatureVerifier); 45 | 46 | return $tokenVerifier->verify($token); 47 | } 48 | catch(InvalidTokenException $e) { 49 | throw $e; 50 | }; 51 | } 52 | 53 | /** 54 | * Check if a token has a specific scope. 55 | * 56 | * @param \stdClass $token - JWT access token to check. 57 | * @param string $scopeRequired - Scope to check for. 58 | * 59 | * @return bool 60 | */ 61 | protected function tokenHasScope($token, $scopeRequired) 62 | { 63 | if (empty($token['scope'])) { 64 | return false; 65 | } 66 | 67 | $tokenScopes = explode(' ', $token['scope']); 68 | 69 | return in_array($scopeRequired, $tokenScopes); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /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/ExampleMiddleware.php: -------------------------------------------------------------------------------- 1 | app['auth']->viaRequest('api', function ($request) { 34 | if ($request->input('api_token')) { 35 | return User::where('api_token', $request->input('api_token'))->first(); 36 | } 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 16 | 'App\Listeners\EventListener', 17 | ], 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /app/User.php: -------------------------------------------------------------------------------- 1 | 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 | // ]); 65 | 66 | // $app->routeMiddleware([ 67 | // 'auth' => App\Http\Middleware\Authenticate::class, 68 | // ]); 69 | 70 | $app->routeMiddleware([ 71 | 'auth' => App\Http\Middleware\Auth0Middleware::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 | 89 | /* 90 | |-------------------------------------------------------------------------- 91 | | Load The Application Routes 92 | |-------------------------------------------------------------------------- 93 | | 94 | | Next we will include the routes file so that they can all be added to 95 | | the application. This will provide all of the URLs the application 96 | | can respond to, as well as the controllers that may handle them. 97 | | 98 | */ 99 | 100 | $app->router->group([ 101 | 'namespace' => 'App\Http\Controllers', 102 | ], function ($router) { 103 | require __DIR__.'/../routes/web.php'; 104 | }); 105 | 106 | return $app; 107 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/lumen", 3 | "description": "The Laravel Lumen Framework.", 4 | "keywords": ["framework", "laravel", "lumen"], 5 | "license": "MIT", 6 | "type": "project", 7 | "require": { 8 | "php": "^7.2.5", 9 | "auth0/auth0-php": "^7.1", 10 | "laravel/lumen-framework": "^7.0" 11 | }, 12 | "require-dev": { 13 | "fzaninotto/faker": "~1.4", 14 | "phpunit/phpunit": "~6.0", 15 | "mockery/mockery": "~0.9" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "App\\": "app/" 20 | } 21 | }, 22 | "autoload-dev": { 23 | "classmap": [ 24 | "tests/", 25 | "database/" 26 | ] 27 | }, 28 | "scripts": { 29 | "post-root-package-install": [ 30 | "php -r \"copy('.env.example', '.env');\"" 31 | ] 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true, 35 | "optimize-autoloader": true 36 | } 37 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | define(App\User::class, function (Faker\Generator $faker) { 15 | return [ 16 | 'name' => $faker->name, 17 | 'email' => $faker->email, 18 | ]; 19 | }); 20 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0-blog/lumen-api-auth/c3883c792ef3f51fdf9f3d3055012b20a03fb74c/database/migrations/.gitkeep -------------------------------------------------------------------------------- /database/migrations/2017_12_18_203510_create_authors_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->string('github'); 21 | $table->string('twitter'); 22 | $table->string('location'); 23 | $table->string('latest_article_published'); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('authors'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/seeds/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call('UsersTableSeeder'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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(); 30 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | This repo demonstrates how to create a simple API with Lumen, require an access token to access endpoints, and add permission requirements using Auth0. 2 | 3 | For instructions, please [follow the tutorial here]( 4 | https://auth0.com/blog/developing-restful-apis-with-lumen). -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0-blog/lumen-api-auth/c3883c792ef3f51fdf9f3d3055012b20a03fb74c/resources/views/.gitkeep -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | get('/', function () use ($router) { 15 | return $router->app->version(); 16 | }); 17 | 18 | $router->group(['prefix' => 'api', 'middleware' => 'auth'], function () use ($router) { 19 | $router->get('authors', ['uses' => 'AuthorController@showAllAuthors']); 20 | 21 | $router->get('authors/{id}', ['uses' => 'AuthorController@showOneAuthor']); 22 | 23 | $router->post('authors', ['uses' => 'AuthorController@create']); 24 | 25 | $router->delete('authors/{id}', ['uses' => 'AuthorController@delete']); 26 | 27 | $router->put('authors/{id}', ['uses' => 'AuthorController@update']); 28 | }); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 |