Created
October 1, 2021 03:41
-
-
Save kmuenkel/ce39bdec364344491398d5cbec9311cd to your computer and use it in GitHub Desktop.
Passport scopes with abstracted route names
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Models\Data; | |
use BadMethodCallException; | |
use Symfony\Component\Routing\Exception\RouteNotFoundException; | |
/** | |
* @method static string accountRetrieval() | |
* @method static string accountCreation() | |
*/ | |
class AccountScopes | |
{ | |
/** | |
* @var string | |
*/ | |
protected static $accountRetrieval = 'auth.account.create'; | |
/** | |
* @var string | |
*/ | |
protected static $accountCreation = 'auth.account.read'; | |
/** | |
* @var array | |
*/ | |
protected static $scopes = []; | |
/** | |
* @return string[] | |
*/ | |
public static function all(): array | |
{ | |
static::make(); | |
return static::$scopes; | |
} | |
/** | |
* @return string[] | |
*/ | |
public static function keys(): array | |
{ | |
static::make(); | |
return array_keys(static::$scopes); | |
} | |
protected static function make() | |
{ | |
static::$scopes = [ | |
static::accountCreation() => 'Retrieve an account', | |
static::accountRetrieval() => 'Create an account' | |
]; | |
} | |
/** | |
* @param string $name | |
* @param array $arguments | |
* @return string | |
*/ | |
public static function __callStatic(string $name, array $arguments = []) | |
{ | |
if (property_exists(static::class, $name)) { | |
try { | |
return route(static::$$name); | |
} catch (RouteNotFoundException $exception) { | |
return '{'.static::$$name.'}'; | |
} | |
} | |
throw new BadMethodCallException('Method '.static::class."::$name does not exist"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Providers; | |
use Laravel\Passport\Passport; | |
use Illuminate\Routing\Router; | |
use App\Models\Data\AccountScopes; | |
use Illuminate\Support\Collection; | |
use Laravel\Passport\Http\Middleware as PassportMiddleware; | |
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; | |
class AuthServiceProvider extends ServiceProvider | |
{ | |
/** | |
* The policy mappings for the application. | |
* | |
* @var array | |
*/ | |
protected $policies = [ | |
// 'App\Models\Model' => 'App\Policies\ModelPolicy', | |
]; | |
/** | |
* Register any authentication / authorization services. | |
* | |
* @return void | |
*/ | |
public function boot() | |
{ | |
$this->registerPolicies(); | |
$this->bootPassport(); | |
} | |
protected function bootPassport() | |
{ | |
!$this->app->routesAreCached() && Passport::routes(); | |
$this->resoldRouteScopes(); | |
/** @var Router $router */ | |
$router = $this->app->make('router'); | |
$router->aliasMiddleware('user_scope.all', PassportMiddleware\CheckScopes::class) | |
->aliasMiddleware('user_scope.any', PassportMiddleware\CheckForAnyScope::class) | |
->aliasMiddleware('client_scope.all', PassportMiddleware\CheckClientCredentials::class) | |
->aliasMiddleware('client_scope.any', PassportMiddleware\CheckClientCredentialsForAnyScope::class); | |
Passport::tokensExpireIn(now()->addDays(15)); | |
Passport::refreshTokensExpireIn(now()->addDays(30)); | |
Passport::personalAccessTokensExpireIn(now()->addMonths(6)); | |
} | |
/** | |
* Referencing routs by name only works after the RouteServiceProvider boots, which is after this Provider | |
*/ | |
protected function resoldRouteScopes() | |
{ | |
/** @var RouteServiceProvider $routeProvider */ | |
$routeProvider = current($this->app->getProviders(RouteServiceProvider::class)); | |
optional($routeProvider)->booted(function () { | |
Passport::tokensCan(AccountScopes::all()); | |
foreach (app('router')->getRoutes()->getRoutes() as $route) { | |
$action = $route->getAction(); | |
$action['middleware'] = array_map(function (string $middleware) { | |
return $this->resolveRouteScope($middleware); | |
}, (array)($action['middleware'] ?? [])); | |
$route->setAction($action); | |
} | |
}); | |
} | |
/** | |
* @param string $middleware | |
* @return string | |
*/ | |
protected function resolveRouteScope(string $middleware): string | |
{ | |
[$alias, $scopes] = array_pad(explode(':', $middleware, 2), 2, ''); | |
if ($this->getPassportAliases()->contains($alias)) { | |
$scopes = array_map(function (string $scope) { | |
return preg_match('/^{(.+?)}$/', $scope, $matches) ? route($matches[1]) : $scope; | |
}, explode(',', $scopes)); | |
$middleware = $alias.':'.implode(',', $scopes); | |
} | |
return $middleware; | |
} | |
/** | |
* @return Collection<string> | |
*/ | |
protected function getPassportAliases(): Collection | |
{ | |
static $aliases = null; | |
return $aliases ?: $aliases = collect(app('router')->getMiddleware()) | |
->map(function (string $className, string $alias) { | |
return in_array($className, [ | |
PassportMiddleware\CheckScopes::class, | |
PassportMiddleware\CheckForAnyScope::class, | |
PassportMiddleware\CheckClientCredentials::class, | |
PassportMiddleware\CheckClientCredentialsForAnyScope::class | |
]) ? $alias : null; | |
})->filter(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use App\Models\Data\AccountScopes; | |
use Illuminate\Support\Facades\Route; | |
/* | |
|-------------------------------------------------------------------------- | |
| Web Routes | |
|-------------------------------------------------------------------------- | |
| | |
| Here is where you can register web routes for your application. These | |
| routes are loaded by the RouteServiceProvider within a group which | |
| contains the "web" middleware group. Now create something great! | |
| | |
*/ | |
Route::get('/', function () { | |
return view('welcome'); | |
}); | |
Route::group(['prefix' => 'auth', 'as' => 'auth.'], function () { | |
Route::group(['as' => 'account.'], function () { | |
Route::get('account', ['as' => 'create', 'uses' => function () { | |
return response(AccountScopes::all()[AccountScopes::accountCreation()]); | |
}]); | |
Route::get('account.readonly', ['as' => 'read', 'uses' => function () { | |
return response(AccountScopes::all()[AccountScopes::accountRetrieval()]); | |
}]); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment