In laravel Sanctum is possible to modify the 'auth:sanctum' middleware to record the user agent and ip when the last_used_at timestamp is updated ?
I created a middlware called SaveIpAndUserAgentInEveryRequest , and it is working , but the database is writen twice, one for the last_used_at and then another for the SaveIpAndUserAgentInEveryRequest .
Code.
Api Routes
api.php
Route::middleware(['auth:sanctum'])->group(function () {
Route::get('/me', function (Request $request) {
return $request->user();
});
});
Kernel:
'api' => [
//\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SaveIpAndUserAgentInEveryRequest::class,
],
SaveIpAndUserAgentInEveryRequest
<?php
namespace App\Http\Middleware;
use App\Models\PersonalAccessToken;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class SaveIpAndUserAgentInEveryRequest
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param \Closure(Request): (Response) $next
* @return Response
*/
public function handle(Request $request, Closure $next): Response
{
// Check if the request is authenticated
if ($request->user()) {
// Access the authenticated user
$user = $request->user();
// Access the user's token
$token = $user->currentAccessToken();
// Update token data if a token exists
if ($token instanceof PersonalAccessToken) {
$token->saveIpAndUserAgent();
}
}
return $next($request);
}
}
Table:
array:12 [ // app/Http/Middleware/SaveIpAndUserAgentInEveryRequest.php:33
"id" => 15
"tokenable_type" => "App\Models\User"
"tokenable_id" => "9b699bb2-aafa-4281-b...."
"name" => "api-token"
"abilities" => array:1 [
0 => "*"
]
"ip_address" => "192.168.65.1"
"user_agent" => "PostmanRuntime/7.36.3"
"last_used_at" => "2024-02-24T13:30:09.000000Z"
"expires_at" => null
"created_at" => "2024-02-24T13:08:45.000000Z"
"updated_at" => "2024-02-24T13:30:09.000000Z"
"tokenable" => array:8 [
"id" => "9b699bb...."
"name" => "My name"
"email" => "e....."
"phone" => null
"role" => "user"
"email_verified_at" => null
"created_at" => "2024-02-24T12:47:40.000000Z"
"updated_at" => "2024-02-24T12:47:40.000000Z"
]
]
Migration
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id();
//$table->morphs('tokenable');
$table->uuidMorphs('tokenable');
$table->string('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->text('ip_address')->nullable();
$table->text('user_agent')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
});
WORKAROUND SO FAR:
<?php
namespace App\Models;
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
class PersonalAccessToken extends SanctumPersonalAccessToken
{
protected $fillable = [
'name',
'token',
'abilities',
'expires_at',
'ip_address',
'user_agent',
'last_used_at',
];
public function saveIpAndUserAgent() :void
{
$this->update(['ip_address' => request()->ip(), 'user_agent' => request()->userAgent()]);
}
/**
* WORKAROUND , when there is a save method called in this model, check if last_used_at is the only key to be updated, if so, add ip_address & user_agent too.
* @param array $options
* @return bool
*/
public function save(array $options = []): bool
{
$changes = $this->getDirty();
if (!array_key_exists('last_used_at', $changes) || count($changes) > 2) {
return parent::save();
}
// Update last_used_at with ip_address and user_agent
$this->ip_address = request()->ip();
$this->user_agent = request()->userAgent();
return parent::save();
}
}