Sanctum es un paquete que nos ayuda a manejar la auntenticación para una API o una aplicación SPA
A continuación vamos a ver un ejemplo de uso del paquete
Para empezar
Instalar el paquete con composer
composer require laravel/sanctum
Publicar la configuración y crear las tablas para su funcionamiento
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Agregar trait al modelo USER
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
}
Registrar usuario
A continuación se especifica cómo se puede registrar un usuario
Agregar Test
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class RegisterControllerTest extends TestCase
{
use RefreshDatabase;
use WithFaker;
/** @test */
public function itCanRegisterAnUser(): void
{
$data = [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'password' => $this->faker->password()
];
$response = $this->post(route('api.register'), $data, [
'Accept' => 'application/json'
]);
$response->assertJsonMissingValidationErrors();
$response->assertStatus(201);
$response->assertJsonFragment([
'message' => trans('auth.user_created_message'),
'user' => [
'name' => $data['name'],
'email' => $data['email'],
]
]);
$this->assertDatabaseCount('users', 1);
$this->assertDatabaseHas('users', [
'name' => $data['name'],
'email' => $data['email'],
]);
}
/** @test */
public function itCanValidatedWhenRegisterAnUser(): void
{
$user = User::factory()->create();
$data = [
'name' => '',
'email' => $user->email,
'password' => ''
];
$response = $this->post(route('api.register'), $data, [
'Accept' => 'application/json'
]);
$response->assertJsonValidationErrors(['name', 'email', 'password']);
$response->assertStatus(422);
}
}
Agregar Form Request
<?php
namespace App\Domain\Auth\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'name' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required',
];
}
}
Agregar Controller
<?php
namespace App\Domain\Auth\Controllers;
use App\Domain\Auth\Requests\RegisterRequest;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\JsonResponse;
class RegisterController extends Controller
{
public function __invoke(RegisterRequest $request): JsonResponse
{
$user = User::query()->create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'password' => bcrypt($request->get('password')),
]);
return response()->json([
'message' => trans('auth.user_created_message'),
'user' => $user->only(['name', 'email']),
], 201);
}
}
Agregar Ruta
Route::post('register', RegisterController::class)->name('api.register');
Realizar el Login
A continuación se especifica cómo se usa sanctum para realizar la autenticación
Agregar Test
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class LoginControllerTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function itCanLogin(): void
{
$user = User::factory()->create();
$response = $this->post(route('api.login'), [
'email' => $user->email,
'password' => 'password'
], ['accept' => 'application/json']);
$response->assertStatus(200);
$response->assertJsonMissingValidationErrors(['email', 'password']);
$response->assertJsonStructure([
'access_token'
]);
}
/** @test */
public function itCanValidatedPasswordWrongWhenLogin(): void
{
$user = User::factory()->create();
$response = $this->post(route('api.login'), [
'email' => $user->email,
'password' => 'passwordwrong'
], ['accept' => 'application/json']);
$response->assertStatus(422);
$response->assertJsonFragment([
'message' => trans('auth.failed'),
]);
}
/** @test */
public function itCanValidatedWhenLogin(): void
{
$response = $this->post(route('api.login'), [
'email' => 'email@',
'password' => ''
], ['accept' => 'application/json']);
$response->assertStatus(422);
$response->assertJsonValidationErrors(['email', 'password']);
}
}
Agregar Form Request
<?php
namespace App\Domain\Auth\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LoginRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'email' => 'required|email',
'password' => 'required',
];
}
}
Agregar Controller
<?php
namespace App\Domain\Auth\Controllers;
use App\Domain\Auth\Requests\LoginRequest;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class LoginController extends Controller
{
public function __invoke(LoginRequest $request): JsonResponse
{
if (!Auth::attempt($request->only('email', 'password'))) {
return response()->json([
'message' => trans('auth.failed'),
], 422);
}
$user = User::query()->where('email', $request->get('email'))->first();
$token = $user->createToken(Str::random())->plainTextToken;
return response()->json([
'access_token' => $token,
]);
}
}
Agregar Ruta
Route::post('login', LoginController::class)->name('api.login');
Proteger una ruta
El paquete nos brinda el middleware auth:sanctum
para restringir el acceso a partes del sistema
Route::middleware('auth:sanctum')->get('route-private', function () {
return $request->user();
});
Usar el token que se obtiene al realizar el login
Para realizar peticiones usando el access token es necesario enviar una cabecera de Authorization como un Bearer token
curl -H "Authorization: Bearer access_token" https://api.tusitio.com/resource-private
Para más información