Challenge 3: Issue and Use a Sanctum Token — Possible Solution ==================================================================== # composer require laravel/sanctum # php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" # php artisan migrate // app/Models/User.php — add Sanctum's trait namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens; protected $fillable = ['name', 'email', 'password']; } // app/Http/Controllers/Api/LoginController.php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Validation\ValidationException; class LoginController extends Controller { public function store(Request $request) { $credentials = $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); if (! Auth::attempt($credentials)) { throw ValidationException::withMessages([ 'email' => 'Invalid credentials.', ]); } $user = Auth::user(); $token = $user->createToken('mobile-app')->plainTextToken; return response()->json(['token' => $token]); } } // routes/api.php use App\Http\Controllers\Api\LoginController; use Illuminate\Http\Request; Route::post('/login', [LoginController::class, 'store']); Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); }); # --- Usage (from a client, e.g. curl) --- # 1. Log in and get a token: # curl -X POST http://example.test/api/login \ # -d "email=jane@example.com&password=secret" # -> {"token": "1|xK7f3jP9q2mZ8vN1wR5tY6uI0oL4aS7d..."} # # 2. Use the token on a protected route: # curl http://example.test/api/user \ # -H "Authorization: Bearer 1|xK7f3jP9q2mZ8vN1wR5tY6uI0oL4aS7d..." # -> {"id": 1, "name": "Jane", "email": "jane@example.com", ...} WHY THIS WORKS -------------- - HasApiTokens is the trait that gives a model createToken() and the ability to be resolved by the sanctum auth guard — without it, a User model has no concept of API tokens at all. - $user->createToken('mobile-app')->plainTextToken generates a new, randomly-generated token, stores its HASHED form in the database (via the personal_access_tokens table Sanctum's migration creates), and returns the PLAIN text token exactly once — this is the only moment the raw token value is ever visible; it can't be retrieved again later, the same one-time-visibility principle bearer tokens generally follow. - Route::middleware('auth:sanctum') explicitly selects the sanctum guard for this route, rather than the default session guard — a request without a valid "Authorization: Bearer " header is rejected the same way an unauthenticated session-based request would be, just using a completely different mechanism to establish who's making the request. - This mirrors the same session-vs-token duality covered generally throughout this project's FastAPI/Express comparisons — one application, two different ways to authenticate a request depending on whether the client is a browser (session) or an API consumer (token).