Skip to content

Commit bd37978

Browse files
committed
Initial pass at implementing Laravel Sanctum for authorization on the API
1 parent e313dff commit bd37978

File tree

13 files changed

+324
-220
lines changed

13 files changed

+324
-220
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Pterodactyl\Extensions\Laravel\Sanctum;
4+
5+
use Pterodactyl\Models\ApiKey;
6+
use Laravel\Sanctum\NewAccessToken as SanctumAccessToken;
7+
8+
/**
9+
* @property \Pterodactyl\Models\ApiKey $accessToken
10+
*/
11+
class NewAccessToken extends SanctumAccessToken
12+
{
13+
/**
14+
* NewAccessToken constructor.
15+
*
16+
* @noinspection PhpMissingParentConstructorInspection
17+
*/
18+
public function __construct(ApiKey $accessToken, string $plainTextToken)
19+
{
20+
$this->accessToken = $accessToken;
21+
$this->plainTextToken = $plainTextToken;
22+
}
23+
}

app/Http/Kernel.php

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

33
namespace Pterodactyl\Http;
44

5-
use Pterodactyl\Models\ApiKey;
65
use Illuminate\Auth\Middleware\Authorize;
76
use Illuminate\Auth\Middleware\Authenticate;
87
use Illuminate\Http\Middleware\TrustProxies;
@@ -16,7 +15,6 @@
1615
use Illuminate\Routing\Middleware\ThrottleRequests;
1716
use Pterodactyl\Http\Middleware\LanguageMiddleware;
1817
use Illuminate\Foundation\Http\Kernel as HttpKernel;
19-
use Pterodactyl\Http\Middleware\Api\AuthenticateKey;
2018
use Illuminate\Routing\Middleware\SubstituteBindings;
2119
use Illuminate\Session\Middleware\AuthenticateSession;
2220
use Illuminate\View\Middleware\ShareErrorsFromSession;
@@ -25,13 +23,13 @@
2523
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
2624
use Pterodactyl\Http\Middleware\Api\AuthenticateIPAccess;
2725
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
28-
use Pterodactyl\Http\Middleware\Api\HandleStatelessRequest;
2926
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
3027
use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate;
3128
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
3229
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
3330
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
3431
use Pterodactyl\Http\Middleware\Api\Client\SubstituteClientBindings;
32+
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
3533
use Pterodactyl\Http\Middleware\Api\Application\AuthenticateApplicationUser;
3634

3735
class Kernel extends HttpKernel
@@ -67,29 +65,19 @@ class Kernel extends HttpKernel
6765
RequireTwoFactorAuthentication::class,
6866
],
6967
'api' => [
70-
HandleStatelessRequest::class,
7168
IsValidJson::class,
72-
StartSession::class,
73-
AuthenticateSession::class,
74-
VerifyCsrfToken::class,
69+
EnsureFrontendRequestsAreStateful::class,
70+
'auth:sanctum',
71+
RequireTwoFactorAuthentication::class,
72+
AuthenticateIPAccess::class,
7573
],
7674
'application-api' => [
7775
SubstituteBindings::class,
78-
'api..key:' . ApiKey::TYPE_APPLICATION,
7976
AuthenticateApplicationUser::class,
80-
AuthenticateIPAccess::class,
81-
],
82-
'client-api' => [
83-
SubstituteClientBindings::class,
84-
'api..key:' . ApiKey::TYPE_ACCOUNT,
85-
AuthenticateIPAccess::class,
86-
// This is perhaps a little backwards with the Client API, but logically you'd be unable
87-
// to create/get an API key without first enabling 2FA on the account, so I suppose in the
88-
// end it makes sense.
89-
//
90-
// You just wouldn't be authenticating with the API by providing a 2FA token.
91-
RequireTwoFactorAuthentication::class,
9277
],
78+
// TODO: don't allow an application key to use the client API, but do allow a client
79+
// api key to access the application API.
80+
'client-api' => [SubstituteClientBindings::class],
9381
'daemon' => [
9482
SubstituteBindings::class,
9583
DaemonAuthenticate::class,
@@ -112,7 +100,5 @@ class Kernel extends HttpKernel
112100
'bindings' => SubstituteBindings::class,
113101
'recaptcha' => VerifyReCaptcha::class,
114102
'node.maintenance' => MaintenanceMiddleware::class,
115-
// API Specific Middleware
116-
'api..key' => AuthenticateKey::class,
117103
];
118104
}

app/Http/Middleware/Api/AuthenticateIPAccess.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use IPTools\IP;
77
use IPTools\Range;
88
use Illuminate\Http\Request;
9+
use Laravel\Sanctum\TransientToken;
910
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
1011

1112
class AuthenticateIPAccess
@@ -20,14 +21,19 @@ class AuthenticateIPAccess
2021
*/
2122
public function handle(Request $request, Closure $next)
2223
{
23-
$model = $request->attributes->get('api_key');
24+
/** @var \Laravel\Sanctum\TransientToken|\Pterodactyl\Models\ApiKey $token */
25+
$token = $request->user()->currentAccessToken();
2426

25-
if (is_null($model->allowed_ips) || empty($model->allowed_ips)) {
27+
// If this is a stateful request just push the request through to the next
28+
// middleware in the stack, there is nothing we need to explicitly check. If
29+
// this is a valid API Key, but there is no allowed IP restriction, also pass
30+
// the request through.
31+
if ($token instanceof TransientToken || empty($token->allowed_ips)) {
2632
return $next($request);
2733
}
2834

2935
$find = new IP($request->ip());
30-
foreach ($model->allowed_ips as $ip) {
36+
foreach ($token->allowed_ips as $ip) {
3137
if (Range::parse($ip)->contains($find)) {
3238
return $next($request);
3339
}

app/Http/Middleware/Api/AuthenticateKey.php

Lines changed: 0 additions & 109 deletions
This file was deleted.

app/Http/Middleware/Api/HandleStatelessRequest.php

Lines changed: 0 additions & 35 deletions
This file was deleted.

app/Http/Middleware/VerifyCsrfToken.php

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

33
namespace Pterodactyl\Http\Middleware;
44

5-
use Closure;
6-
use Pterodactyl\Models\ApiKey;
75
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
86

97
class VerifyCsrfToken extends BaseVerifier
@@ -16,31 +14,4 @@ class VerifyCsrfToken extends BaseVerifier
1614
* @var string[]
1715
*/
1816
protected $except = ['remote/*', 'daemon/*'];
19-
20-
/**
21-
* Manually apply CSRF protection to routes depending on the authentication
22-
* mechanism being used. If the API request is using an API key that exists
23-
* in the database we can safely ignore CSRF protections, since that would be
24-
* a manually initiated request by a user or server.
25-
*
26-
* All other requests should go through the standard CSRF protections that
27-
* Laravel affords us. This code will be removed in v2 since we have switched
28-
* to using Sanctum for the API endpoints, which handles that for us automatically.
29-
*
30-
* @param \Illuminate\Http\Request $request
31-
*
32-
* @return mixed
33-
*
34-
* @throws \Illuminate\Session\TokenMismatchException
35-
*/
36-
public function handle($request, Closure $next)
37-
{
38-
$key = $request->attributes->get('api_key');
39-
40-
if ($key instanceof ApiKey && $key->exists) {
41-
return $next($request);
42-
}
43-
44-
return parent::handle($request, $next);
45-
}
4617
}

0 commit comments

Comments
 (0)