Skip to content

Commit 9f31abc

Browse files
committed
Implementing authentication as middleware instead of filter.
Signed-off-by: Jason Lewis <[email protected]>
1 parent d8a2735 commit 9f31abc

File tree

9 files changed

+449
-244
lines changed

9 files changed

+449
-244
lines changed

src/ApiServiceProvider.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php namespace Dingo\Api;
22

33
use Closure;
4+
use Dingo\Api\Auth\Shield;
45
use Dingo\Api\Http\Response;
56
use Dingo\Api\Routing\Router;
67
use Dingo\Api\Auth\ProviderManager;
@@ -35,11 +36,6 @@ public function boot()
3536
$formats = array_map('value', $this->app['config']['api::formats']);
3637

3738
Response::setFormatters($formats);
38-
39-
$this->app['router']->filter('api', function($route, $request)
40-
{
41-
$this->app['dingo.api.auth']->authenticate();
42-
});
4339
}
4440

4541
/**
@@ -57,6 +53,8 @@ public function register()
5753

5854
$this->registerAuthentication();
5955

56+
$this->registerMiddlewares();
57+
6058
// We'll also register a booting event so that we can set our exception handler
6159
// instance, default API version and the API vendor on the router.
6260
$this->app->booting(function($app)
@@ -152,8 +150,18 @@ protected function registerAuthentication()
152150
$providers[$provider] = $app['dingo.api.auth.manager']->driver($provider)->setOptions($options);
153151
}
154152

155-
return new Authentication($app['router'], $app['auth'], $providers);
153+
return new Shield($app['auth'], $providers);
156154
});
157155
}
158156

157+
/**
158+
* Register the middlewares.
159+
*
160+
* @return void
161+
*/
162+
protected function registerMiddlewares()
163+
{
164+
$this->app->middleware('Dingo\Api\Http\Middleware\Authentication');
165+
}
166+
159167
}

src/Authentication.php renamed to src/Auth/Shield.php

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
1-
<?php namespace Dingo\Api;
1+
<?php namespace Dingo\Api\Auth;
22

33
use Exception;
4+
use Illuminate\Http\Request;
45
use Dingo\Api\Http\Response;
56
use Dingo\Api\Routing\Router;
67
use Illuminate\Routing\Route;
78
use Illuminate\Auth\AuthManager;
89
use Dingo\Api\Http\InternalRequest;
910
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
1011

11-
class Authentication {
12-
13-
/**
14-
* API router instance.
15-
*
16-
* @var \Dingo\Api\Routing\Router
17-
*/
18-
protected $router;
12+
class Shield {
1913

2014
/**
2115
* Illuminate auth instance.
@@ -48,14 +42,12 @@ class Authentication {
4842
/**
4943
* Create a new Dingo\Api\Authentication instance.
5044
*
51-
* @param \Dingo\Api\Routing\Router $router
5245
* @param \Illuminate\Auth\AuthManager $auth
5346
* @param array $providers
5447
* @return void
5548
*/
56-
public function __construct(Router $router, AuthManager $auth, array $providers)
49+
public function __construct(AuthManager $auth, array $providers)
5750
{
58-
$this->router = $router;
5951
$this->auth = $auth;
6052
$this->providers = $providers;
6153
}
@@ -65,20 +57,8 @@ public function __construct(Router $router, AuthManager $auth, array $providers)
6557
*
6658
* @return null|\Dingo\Api\Http\Response
6759
*/
68-
public function authenticate()
60+
public function authenticate(Request $request, Route $route)
6961
{
70-
$request = $this->router->getCurrentRequest();
71-
72-
if ($request instanceof InternalRequest or ! is_null($this->user))
73-
{
74-
return null;
75-
}
76-
77-
if ( ! $route = $this->router->getCurrentRoute() or ! $this->routeIsProtected($route))
78-
{
79-
return null;
80-
}
81-
8262
$exceptionStack = [];
8363

8464
// Spin through each of the registered authentication providers and attempt to
@@ -112,30 +92,16 @@ public function authenticate()
11292
throw $exception;
11393
}
11494

115-
/**
116-
* Determine if a route is protected.
117-
*
118-
* @param \Illuminate\Routing\Route $route
119-
* @return bool
120-
*/
121-
protected function routeIsProtected(Route $route)
122-
{
123-
$action = $route->getAction();
124-
125-
return in_array('protected', $action, true) or (isset($action['protected']) and $action['protected'] === true);
126-
}
127-
12895
/**
12996
* Get the authenticated user.
13097
*
13198
* @return \Illuminate\Auth\GenericUser|\Illuminate\Database\Eloquent\Model
13299
*/
133100
public function getUser()
134101
{
135-
if ($this->user)
136-
{
137-
return $this->user;
138-
}
102+
if ($this->user) return $this->user;
103+
104+
if ( ! $this->userId) return null;
139105

140106
if ( ! $this->auth->check())
141107
{

src/Dispatcher.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use Closure;
44
use RuntimeException;
5+
use Dingo\Api\Auth\Shield;
56
use Illuminate\Http\Request;
67
use Illuminate\Http\Response;
78
use Dingo\Api\Routing\Router;
@@ -36,11 +37,11 @@ class Dispatcher {
3637
protected $router;
3738

3839
/**
39-
* API authentication instance.
40+
* API authentication shield instance.
4041
*
41-
* @var \Dingo\Api\Authentication
42+
* @var \Dingo\Api\Auth\Shield
4243
*/
43-
protected $auth;
44+
protected $shield;
4445

4546
/**
4647
* Original request input array.
@@ -83,15 +84,15 @@ class Dispatcher {
8384
* @param \Illuminate\Http\Request $request
8485
* @param \Illuminate\Routing\UrlGenerator $url
8586
* @param \Dingo\Api\Routing\Router $router
86-
* @param \Dingo\Api\Authentication $auth
87+
* @param \Dingo\Api\Auth\Shield $shield
8788
* @return void
8889
*/
89-
public function __construct(Request $request, UrlGenerator $url, Router $router, Authentication $auth)
90+
public function __construct(Request $request, UrlGenerator $url, Router $router, Shield $shield)
9091
{
9192
$this->request = $request;
9293
$this->url = $url;
9394
$this->router = $router;
94-
$this->auth = $auth;
95+
$this->shield = $shield;
9596
}
9697

9798
/**
@@ -107,7 +108,7 @@ public function be($user)
107108
throw new RuntimeException('User must be an instance of either Illuminate\Database\Eloquent\Model or Illuminate\Auth\GenericUser.');
108109
}
109110

110-
$this->auth->setUser($user);
111+
$this->shield->setUser($user);
111112

112113
return $this;
113114
}
@@ -360,7 +361,7 @@ protected function refreshRequestStack()
360361
{
361362
if ( ! $this->persistAuthenticationUser)
362363
{
363-
$this->auth->setUser(null);
364+
$this->shield->setUser(null);
364365

365366
$this->persistAuthenticationUser = true;
366367
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php namespace Dingo\Api\Http\Middleware;
2+
3+
use Dingo\Api\Http\Response;
4+
use Illuminate\Routing\Route;
5+
use Dingo\Api\Http\InternalRequest;
6+
use Symfony\Component\HttpFoundation\Request;
7+
use Symfony\Component\HttpKernel\HttpKernelInterface;
8+
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
9+
10+
class Authentication implements HttpKernelInterface {
11+
12+
/**
13+
* The wrapped kernel implementation.
14+
*
15+
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
16+
*/
17+
protected $app;
18+
19+
/**
20+
* Create a new authentication middleware instance.
21+
*
22+
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $app
23+
* @return void
24+
*/
25+
public function __construct(HttpKernelInterface $app)
26+
{
27+
$this->app = $app;
28+
}
29+
30+
/**
31+
* Handle a given request and return the response.
32+
*
33+
* @param \Symfony\Component\HttpFoundation\Request $request
34+
* @param int $type
35+
* @param bool $catch
36+
* @return \Symfony\Component\HttpFoundation\Response
37+
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
38+
*/
39+
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
40+
{
41+
// Our middleware needs to ensure that Laravel is booted before we
42+
// can do anything. This gives us access to all the booted
43+
// service providers and other container bindings.
44+
$this->app->boot();
45+
46+
if ($request instanceof InternalRequest or $this->app->make('dingo.api.auth')->user())
47+
{
48+
return $this->app->handle($request, $type, $catch);
49+
}
50+
51+
$router = $this->app->make('router');
52+
53+
$response = null;
54+
55+
// If a collection exists for the request and we can match a route
56+
// from the request then we'll check to see if the route is
57+
// protected and, if it is, we'll attempt to authenticate.
58+
if ($collection = $router->getApiRouteCollectionFromRequest($request) and $route = $collection->match($request))
59+
{
60+
if ($this->routeIsProtected($route))
61+
{
62+
$response = $this->authenticate($request, $route);
63+
}
64+
}
65+
66+
return $response ?: $this->app->handle($request, $type, $catch);
67+
}
68+
69+
/**
70+
* Authenticate the request for the given route.
71+
*
72+
* @param \Symfony\Component\HttpFoundation\Request $request
73+
* @param \Illuminate\Routing\Route $route
74+
* @return null|\Dingo\Api\Http\Response
75+
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
76+
*/
77+
protected function authenticate(Request $request, Route $route)
78+
{
79+
try
80+
{
81+
$this->app->make('dingo.api.auth')->authenticate($request, $route);
82+
}
83+
catch (UnauthorizedHttpException $exception)
84+
{
85+
$router = $this->app->make('router');
86+
87+
$response = $router->handleException($exception);
88+
89+
return Response::makeFromExisting($response)->morph($router->getRequestedFormat());
90+
}
91+
}
92+
93+
/**
94+
* Determine if a route is protected.
95+
*
96+
* @param \Illuminate\Routing\Route $route
97+
* @return bool
98+
*/
99+
protected function routeIsProtected(Route $route)
100+
{
101+
$action = $route->getAction();
102+
103+
return in_array('protected', $action, true) or (isset($action['protected']) and $action['protected'] === true);
104+
}
105+
106+
}

src/Routing/Router.php

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,6 @@ protected function addRoute($methods, $uri, $action)
218218
*/
219219
protected function addApiRoute($route)
220220
{
221-
$route->before('api');
222-
223221
// Since the groups action gets merged with the routes we need to make
224222
// sure that if the route supplied its own protection that we grab
225223
// that protection status from the array after the merge.
@@ -402,9 +400,7 @@ public function requestTargettingApi($request = null)
402400
return true;
403401
}
404402

405-
$collection = array_first($this->api, function($k, $c) use ($request) { return $c->matchesRequest($request); }, false);
406-
407-
return $collection instanceof ApiRouteCollection;
403+
return $this->getApiRouteCollectionFromRequest($request) instanceof ApiRouteCollection;
408404
}
409405

410406
/**
@@ -425,6 +421,17 @@ protected function parseAcceptHeader($request)
425421
}
426422
}
427423

424+
/**
425+
* Get a matching API route collection from the request.
426+
*
427+
* @param \Illuminate\Http\Request $request
428+
* @return null|\Dingo\Api\Routing\ApiRouteCollection
429+
*/
430+
public function getApiRouteCollectionFromRequest(Request $request)
431+
{
432+
return array_first($this->api, function($k, $c) use ($request) { return $c->matchesRequest($request); }, null);
433+
}
434+
428435
/**
429436
* Get an API route collection for a given version.
430437
*
@@ -559,6 +566,26 @@ public function getVendor()
559566
return $this->vendor;
560567
}
561568

569+
/**
570+
* Get the requested version.
571+
*
572+
* @return string
573+
*/
574+
public function getRequestedVersion()
575+
{
576+
return $this->requestedVersion;
577+
}
578+
579+
/**
580+
* Get the requested format.
581+
*
582+
* @return string
583+
*/
584+
public function getRequestedFormat()
585+
{
586+
return $this->requestedFormat;
587+
}
588+
562589
/**
563590
* Get a controller inspector instance.
564591
*

0 commit comments

Comments
 (0)