Laravel Sanctum Token CRUD
Route ::group (['middleware ' => 'auth:sanctum ' ], function (){
Route ::resource ('tokens ' ,'TokenController ' );
});
<?php
namespace App \Http \Controllers ;
use App \Models \ApiToken ;
use Illuminate \Http \Request ;
use Illuminate \Http \Response ;
use Illuminate \Validation \ValidationException ;
use Illuminate \Auth \Access \AuthorizationException ;
class TokenController extends Controller
{
public function __construct ()
{
$ this ->authorizeResource (ApiToken ::class, 'token ' );
}
/**
* Display a listing of the resource.
* @param Request $request
* @return SearchableResource
*/
public function index (Request $ request )
{
return response ([
'resource ' => $ request ->user ()->tokens ()->with ('tokenable ' )->paginate (8 )
]);
}
/**
* Show the form for creating a new resource.
* @return Response
*/
public function create (): Response
{
return response ([
'entity ' => [
'name ' => '' ,
'abilities ' => [],
]
]);
}
/**
* Display the specified resource.
* @param ApiToken $token
* @return Response
*/
public function show (ApiToken $ token ): Response
{
return response ([
'entity ' => $ token ->loadMissing ('tokenable ' )
]);
}
/**
* Display the specified resource.
* @param ApiToken $token
* @return Response
*/
public function edit (ApiToken $ token ): Response
{
return response ([
'entity ' => $ token ->loadMissing ('tokenable ' )
]);
}
/**
* Store a newly created resource in storage.
* @param Request $request
* @return Response
* @throws AuthorizationException|ValidationException
*/
public function store (Request $ request ): Response
{
$ this ->authorize ('create ' , [ApiToken ::class]);
$ request ->validate (ApiToken ::validationRules ());
$ newAccessToken = $ request ->user ()->createToken (
$ request ->get ('name ' ),
$ request ->get ('abilities ' )
);
return response ([
'message ' => 'Entity Created ' ,
'entity ' => array_merge ($ newAccessToken ->accessToken ->toArray (), [
'token ' => $ newAccessToken ->plainTextToken
]),
]);
}
/**
* Update the specified resource in storage.
* @param Request $request
* @param ApiToken $token
* @return Response
*/
public function update (Request $ request , ApiToken $ token ): Response
{
$ request ->validate (ApiToken ::validationRules ($ token ));
return response ([
'message ' => 'Entity Updated ' ,
'entity ' => tap ($ token )->update ($ request ->only (['name ' , 'abilities ' ]))
]);
}
/**
* Remove the specified resource from storage.
* @param ApiToken $token
* @return Response
* @throws \Exception
*/
public function destroy (ApiToken $ token ): Response
{
$ token ->delete ();
return response ([
'message ' => 'Entity Destroyed ' ,
'entity ' => $ token ->only ('id ' )
]);
}
}
<?php
namespace App \Models ;
use Illuminate \Validation \Rule ;
use Illuminate \Support \Collection ;
use Illuminate \Support \Facades \Cache ;
use Laravel \Sanctum \PersonalAccessToken ;
class ApiToken extends PersonalAccessToken
{
public $ table = 'personal_access_tokens ' ;
public static function validationRules (?User $ user = null ): array
{
return [
'name ' => 'required|string|max:255 ' ,
'abilities ' => 'required|array|min:1 ' ,
'abilities.* ' => ['string ' , Rule ::in ([
// ...AllowedGrants
//'tokens:viewAny',
//'tokens:view',
//'tokens:create',
//'tokens:update',
//'tokens:delete',
//'tokens:forceDelete',
//'tokens:restore',
//etc...
])],
];
}
public static function abilitiesInUse (): Collection
{
return Cache ::remember ('airlock:abilities ' , 120 , fn()=>static ::query ()
->pluck ('abilities ' )
->flatten (1 )
->unique ()
->values ());
}
public static function boot ()
{
parent ::boot ();
static ::saved (fn()=>Cache ::forget ('airlock:abilities ' ));
static ::deleted (fn()=>Cache ::forget ('airlock:abilities ' ));
}
}
<?php
namespace App \Policies ;
use App \Models \User ;
use App \Models \ApiToken ;
use Illuminate \Auth \Access \HandlesAuthorization ;
class ApiTokenPolicy
{
use HandlesAuthorization ;
/**
* Determine whether the user can view any tokens.
* @param User $user
* @return bool
*/
public function viewAny (User $ user ): bool
{
return $ user ->tokenCan ('tokens:viewAny ' );
}
/**
* Determine whether the user can view the token.
* @param User $user
* @param ApiToken $token
* @return bool
*/
public function view (User $ user , ApiToken $ token ): bool
{
return ($ user ->is ($ token ->tokenable ) && $ user ->tokenCan ('tokens:view ' ));
}
/**
* Determine whether the user can create tokens.
* @param User $user
* @return bool
*/
public function create (User $ user ): bool
{
return $ user ->tokenCan ('tokens:create ' );
}
/**
* Determine whether the user can update the token.
* @param User $user
* @param ApiToken $token
* @return bool
*/
public function update (User $ user , ApiToken $ token ): bool
{
return ($ user ->is ($ token ->tokenable ) && $ user ->tokenCan ('tokens:update ' ));
}
/**
* Determine whether the user can delete the token.
* @param User $user
* @param ApiToken $token
* @return bool
*/
public function delete (User $ user , ApiToken $ token ): bool
{
return ($ user ->is ($ token ->tokenable ) && $ user ->tokenCan ('tokens:delete ' ));
}
/**
* Determine whether the user can restore the token.
* @param User $user
* @param ApiToken $token
* @return bool
*/
public function restore (User $ user , ApiToken $ token ): bool
{
return ($ user ->is ($ token ->tokenable ) && $ user ->tokenCan ('tokens:restore ' ));
}
/**
* Determine whether the user can permanently delete the token.
* @param User $user
* @param ApiToken $token
* @return bool
*/
public function forceDelete (User $ user , ApiToken $ token ): bool
{
return ($ user ->is ($ token ->tokenable ) && $ user ->tokenCan ('tokens:forceDelete ' ));
}
}