forked from auth0/laravel-auth0
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCallbackControllerAbstract.php
146 lines (113 loc) · 5.26 KB
/
CallbackControllerAbstract.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?php
declare(strict_types=1);
namespace Auth0\Laravel\Controllers;
use Auth0\Laravel\Auth\Guard;
use Auth0\Laravel\Entities\CredentialEntityContract;
use Auth0\Laravel\Events\{AuthenticationFailed, AuthenticationSucceeded};
use Auth0\Laravel\Exceptions\ControllerException;
use Auth0\Laravel\Exceptions\Controllers\CallbackControllerException;
use Auth0\Laravel\Guards\GuardAbstract;
use Auth0\Laravel\{Configuration, Events};
use Illuminate\Auth\Events\{Attempting, Authenticated, Failed, Validated};
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
use function is_string;
use function sprintf;
/**
* @api
*/
abstract class CallbackControllerAbstract extends ControllerAbstract
{
/**
* @psalm-suppress InvalidArgument
*
* @param Request $request
*/
public function __invoke(
Request $request,
): Response {
$guard = auth()->guard();
if (! $guard instanceof GuardAbstract) {
logger()->error(sprintf('A request implementing the `%s` controller was not routed through a Guard configured with an Auth0 driver. The incorrectly assigned Guard was: %s', self::class, $guard::class), $request->toArray());
throw new ControllerException(ControllerException::ROUTED_USING_INCOMPATIBLE_GUARD);
}
$code = $request->query('code');
$state = $request->query('state');
$code = is_string($code) ? trim($code) : '';
$state = is_string($state) ? trim($state) : '';
$success = false;
if ('' === $code) {
$code = null;
}
if ('' === $state) {
$state = null;
}
/**
* @var null|string $code
* @var null|string $state
*/
try {
if (null !== $code && null !== $state) {
Events::framework(new Attempting($guard::class, ['code' => $code, 'state' => $state], true));
$success = $guard->sdk()->exchange(
code: $code,
state: $state,
);
}
} catch (Throwable $throwable) {
$credentials = $guard->sdk()->getUser() ?? [];
$credentials['code'] = $code;
$credentials['state'] = $state;
$credentials['error'] = ['description' => $throwable->getMessage()];
Events::framework(new Failed($guard::class, $guard->user(), $credentials));
session()->invalidate();
Events::dispatch($event = new AuthenticationFailed($throwable, true));
if ($event->throwException) {
throw $throwable;
}
}
if (null !== $request->query('error') && null !== $request->query('error_description')) {
// Workaround to aid static analysis, due to the mixed formatting of the query() response:
$error = $request->query('error', '');
$errorDescription = $request->query('error_description', '');
$error = is_string($error) ? $error : '';
$errorDescription = is_string($errorDescription) ? $errorDescription : '';
Events::framework(new Attempting($guard::class, ['code' => $code, 'state' => $state], true));
Events::framework(new Failed($guard::class, $guard->user(), [
'code' => $code,
'state' => $state,
'error' => ['error' => $error, 'description' => $errorDescription],
]));
session()->invalidate();
// Create a dynamic exception to report the API error response
$exception = new CallbackControllerException(sprintf(CallbackControllerException::MSG_API_RESPONSE, $error, $errorDescription));
// Store the API exception in the session as a flash variable, in case the application wants to access it.
session()->flash('auth0.callback.error', sprintf(CallbackControllerException::MSG_API_RESPONSE, $error, $errorDescription));
Events::dispatch($event = new AuthenticationFailed($exception, true));
if ($event->throwException) {
throw $exception;
}
}
if (! $success) {
return redirect()->intended(config(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_LOGIN, '/login'));
}
$credential = ($guard instanceof Guard) ? $guard->find(Guard::SOURCE_SESSION) : $guard->find();
$user = $credential?->getUser();
if ($credential instanceof CredentialEntityContract && $user instanceof Authenticatable) {
Events::framework(new Validated($guard::class, $user));
session()->regenerate(true);
/**
* @var Guard $guard
*/
$guard->login($credential, Guard::SOURCE_SESSION);
Events::dispatch(new AuthenticationSucceeded($user));
// @phpstan-ignore-next-line
if ($user instanceof Authenticatable) {
Events::framework(new Authenticated($guard::class, $user));
}
}
return redirect()->intended(config(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_AFTER_LOGIN, config(Configuration::CONFIG_NAMESPACE_ROUTES . Configuration::CONFIG_ROUTE_INDEX, '/')));
}
}