Completed
Push — master ( 857394...4794c5 )
by Mahmoud
03:25
created

ApplicationAuthentication   A

Complexity

Total Complexity 7

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 3
Bugs 1 Features 1
Metric Value
wmc 7
c 3
b 1
f 1
lcom 1
cbo 4
dl 0
loc 115
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
B handle() 0 50 6
1
<?php
2
3
namespace App\Containers\Application\Middlewares;
4
5
use App\Containers\Application\Exceptions\AuthenticationFailedException;
6
use App\Containers\Application\Exceptions\UserNotPermittedException;
7
use App\Containers\Application\Tasks\FindApplicationByIdTask;
8
use App\Containers\Authentication\Adapters\JwtAuthAdapter;
9
use Closure;
10
use Dingo\Api\Auth\Auth as Authentication;
11
use Dingo\Api\Routing\Router;
12
use Illuminate\Auth\AuthManager;
13
use Illuminate\Support\Facades\App;
14
15
/**
16
 * Class ApplicationAuthentication
17
 *
18
 * @author  Mahmoud Zalt  <[email protected]>
19
 */
20
class ApplicationAuthentication
21
{
22
23
    /**
24
     * Router instance.
25
     *
26
     * @var \Dingo\Api\Routing\Router
27
     */
28
    protected $router;
29
30
    /**
31
     * Authenticator instance.
32
     *
33
     * @var \Dingo\Api\Auth\Auth
34
     */
35
    protected $auth;
36
37
    /**
38
     * @var  \App\Containers\Application\Tasks\FindApplicationByIdTask
39
     */
40
    private $findApplicationByIdTask;
41
42
    /**
43
     * @var  \App\Containers\Authentication\Adapters\JwtAuthAdapter
44
     */
45
    private $jwtAuthAdapter;
46
47
    /**
48
     * @var  \Illuminate\Auth\AuthManager
49
     */
50
    private $authManager;
51
52
    /**
53
     * ApplicationAuthentication constructor.
54
     *
55
     * @param \Dingo\Api\Routing\Router                                 $router
56
     * @param \Dingo\Api\Auth\Auth                                      $auth
57
     * @param \App\Containers\Application\Tasks\FindApplicationByIdTask $findApplicationByIdTask
58
     * @param \App\Containers\Authentication\Adapters\JwtAuthAdapter    $jwtAuthAdapter
59
     * @param \Illuminate\Auth\AuthManager                              $authManager
60
     */
61
    public function __construct(
62
        Router $router,
63
        Authentication $auth,
64
        FindApplicationByIdTask $findApplicationByIdTask,
65
        JwtAuthAdapter $jwtAuthAdapter,
66
        AuthManager $authManager
67
    ) {
68
        $this->router = $router;
69
        $this->auth = $auth;
70
        $this->findApplicationByIdTask = $findApplicationByIdTask;
71
        $this->jwtAuthAdapter = $jwtAuthAdapter;
72
        $this->authManager = $authManager;
73
    }
74
75
    /**
76
     * @param          $request
77
     * @param \Closure $next
78
     *
79
     * @return  mixed
80
     * @throws \App\Containers\Application\Exceptions\UserNotPermittedException
81
     * @throws \App\Containers\Application\Exceptions\AuthenticationFailedException
82
     */
83
    public function handle($request, Closure $next)
84
    {
85
        // NOTE: this ApplicationAuthentication `app.auth` works on top of the `api.auth` provided by dingo.
86
        // Some endpoints can be accessed by 2 types of tokens (User & App). Laravel doesn't support
87
        // middleware fallback: (if not first middleware try the second): `'middleware' => ['app.auth|api.auth']`
88
        // So this middleware `app.auth` will be used on the endpoints that are allowed to be accessed by Apps.
89
        // It check first if the token contain Application ID in its payload and if so it tries to authenticate
90
        // the (owner) user of that App. BUT if the token doesn't have an Application ID that means he is using
91
        // his own token from my client App (Admin/User front-end) so will try to authenticate him normally
92
        // using the `api.auth` (this is the fallback to the original auth middleware).
93
94
        $token = str_replace('Bearer ', '', $request->header('authorization'));
95
96
        if(!$token){
97
            throw new AuthenticationFailedException('Empty Token!');
98
        }
99
100
        // get App ID from the token payload custom claim `ApplicationId`
101
        if ($applicationId = $this->jwtAuthAdapter->getPayload($token)->get('ApplicationId')) {
102
103
            // find that App in the database
104
            $application = $this->findApplicationByIdTask->run($applicationId);
105
106
            if (!$application || !$user = $application->user) {
107
                throw new AuthenticationFailedException();
108
            }
109
110
            // add the application on the request object
111
            $request->merge([
112
                'application' => $application
113
            ]);
114
115
            // NOTE: You can remove this condition of you are not using roles for this purpose.
116
            // Allow Access only for users with valid developer account
117
            if (!$user->hasRole('developer')) {
118
                throw new UserNotPermittedException();
119
            }
120
121
        }else{
122
            return (App::make(\Dingo\Api\Http\Middleware\Auth::class))->handle($request, $next);
123
            // another way to do handle this is by calling `$user = $this->jwtAuthAdapter->toUser($token);`
124
            // and continuing the execution of this code till the last return, but to maintain consistency
125
            // I'm calling the same auth middleware used by all other endpoints.
126
        }
127
128
        // make the user accessible on the requests objects when using `$request->user()`
129
        $this->authManager->setUser($user);
130
131
        return $next($request);
132
    }
133
134
}
135