Up and running with Laravel Sanctum and Fortify. Basics for API tokens and SPA authentication
⚠️ Note that i would recommend using Laravel Breeze that does exactly the same thing. But if you are interested how breeze is structured you can continue reading :)
In this guide we are going to see the steps needed for being able to get up and running with Sanctum and Fortify. So what are and what do Sanctum and Fortify do?
Just want to check out the code? It exist here laravel-sanctum-fortify 👀
Sanctum: Provides the authentication for your application. It works in two ways.
Authorization
header.How it works is that Laravel will first check for the authentication cookie and if none exist it will check for the API token to know if the user is authenticated.
Fortify
Sanctum only handles the managing of API tokens and authentication of session cookies or tokens. Fortify provides the routes and logic needed for login, user registration, password recovery e.t.c.
First, we need a basic laravel installation.
laravel new sanctum-fortify-tutorial
cd sanctum-fortify-tutorial
git init
git add .
git commit -m "init"
Here you can also take advantage of the Laravel built-in support for git.
laravel new sanctum-fortify-tutorial --git
. You also have support to directly create a GitHub repository together with the GitHub CLI!
If laravel
does not exist. You need to install the installer
Next, we need to connect to our Database.
Update the .env
to match your database.
After we have configured our database we can run migrate to initialize all required tables.
$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (32.55ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (17.98ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (22.27ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated: 2019_12_14_000001_create_personal_access_tokens_table (46.57ms)
To start the application we can run php artisan serve
. Now we are ready to begin the Sanctum installation.
More information can be found at the Sanctum documentation A new installation of Laravel comes with Sanctum out of the box!
Update env
SANCTUM_STATEFUL_DOMAINS=localhost:8080,localhost:8000
SESSION_DOMAIN=localhost
Update app/Http/Kernel.php
to include Sanctum middleware
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
We start by setting up two middleware that is used to authenticate the request token.
This is done in the app/Http/Kernel.php
'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,
abilities
can be used to verify that a request have all of the listed abilities.ability
can be used to verify that a request has at least one of the listed abilities.Now to test this we need a user. We do not have any endpoints or views to create a user but we can make use of Tinker.
We can create a user by using the factory method. After that, we can create a token for the user. The created token will then be shown in the console.
php artisan tinker
>>> User::factory(1)->create(['name'=>'Luke Skywalker', 'email'=>'luke@jedi.com', 'password'=>Hash::make('luke')])
>>> User::find(1)->createToken('server-admin', ['server:update'])->plainTextToken
"1|3Oovymkti8w9Ac61tdHhK2mytDNiBPw27DL4fONg"
That token is now stored hashed in our database. The server:update
is one of the abilities that can be verified against.
If we update our routes/api.php
file to validate against this ability.
Route::middleware(['auth:sanctum', 'ability:server:update'])->get('/user', function (Request $request) {
return $request->user();
});
If we try and call that endpoint now we will get a 401 Unathenticated
. I'm using curl here but I recommend you use e.g. Postman or Insomnia. You will notice later why i recommend that :)
curl -H "Accept: application/json" http://localhost:8000/api/users
{"message":"Unauthenticated."}
That's because we are not providing the token we created earlier. So we need to pass this in the Authorization
header.
curl -H "Accept: application/json" -H "Authorization: Bearer 1|3Oovymkti8w9Ac61tdHhK2mytDNiBPw27DL4fONg" http://localhost:8000/api/user
{"id":1,"name":"Luke Skywalker","email":"luke@jedi.com","email_verified_at":"2022-01-27T20:27:22.000000Z","created_at":"2022-01-27T20:27:22.000000Z","updated_at":"2022-01-27T20:27:22.000000Z"}
And it works! If we create another user and try to access the same endpoint it will not work because we are missing the correct token.
Some troubleshooting if it does not work
Now when we have working access to our API by using API tokens we can step over to set up our SPA token.
First, we need to make sure that our responses will return the Access-Control-Allow-Credentials. This is so we expose the response to the frontend JavaScript code.
This is done in the config/cors.php
file.
'supports_credentials' => true,
When we installed Sanctum we got one route installed sanctum/csrf-cookie
. This route will create a token that will be used to verify upcoming requests to prevent Cross-Site-Request Forgery.
If we request the server
curl -H "Accept: application/json" -H "Access-Control-Allow-Credentials: true" http://localhost:8000/sanctum/csrf-cookie
We will not get back any response... Hmm. That is because the endpoint uses the Set-Cookie response to populate the XSRF-TOKEN
that we need for upcoming requests. We do not have any login yet so to test this out we can make a basic authentication endpoint.
We can add a new endpoint api/sanctum/token
that will validate the user input and create a new user token that we can provide for our Authorization
header.
Route::post('/sanctum/token', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
return $user->createToken($request->device_name)->plainTextToken;
});
If we try and make a request now we will get an "message": "CSRF token mismatch."
because we are not providing our CSRF token.
"message": "CSRF token mismatch.",
Let's rerun our command but with the -v
flag so we can see the response headers.
curl -H "Accept: application/json" -H "Access-Control-Allow-Credentials: true" http://localhost:8000/sanctum/csrf-cookie -v
* Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8000 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /sanctum/csrf-cookie HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.64.1
> Accept: application/json
> Access-Control-Allow-Credentials: true
>
< HTTP/1.1 204 No Content
< Host: localhost:8000
< Date: Fri, 28 Jan 2022 19:36:39 GMT
< Connection: close
< X-Powered-By: PHP/7.3.24-(to be removed in future macOS)
< Cache-Control: no-cache, private
< Date: Fri, 28 Jan 2022 19:36:39 GMT
< Vary: Origin
< Set-Cookie: XSRF-TOKEN=eyJpdiI6ImxNcnBodkpXQXFLMmZCbWVwc2FvZGc9PSIsInZhbHVlIjoiYndpdzlmYW04elNoUWlwaENBSWYyeWEzMHhxQW1SR1phUUlNQjRqM294aThpbGhLbHlhcnBOeWhzVGhESW5BS3k3d3BlQnVqcUtjbEJiSlVIbm1hWFRqK0dVcnVzYkJmY2o4U1BSYkFoWm8vMVRseTZySWVnZWxGZFpaZlZQWDEiLCJtYWMiOiIzNzhmODkzNThlNzQ1ZDgxZjQwNWZhMzliYjA0NWRmMzBmZjEwYTM2MTM2YmRlMTI5ZmI0YTRlNzQ5MjJkYThiIiwidGFnIjoiIn0%3D; expires=Fri, 28-Jan-2022 21:36:39 GMT; Max-Age=7200; path=/; domain=localhost; samesite=lax
< Set-Cookie: laravel_session=eyJpdiI6IkhiLzhzUTB4d2liaU1JOGplZjNLRGc9PSIsInZhbHVlIjoiR1NEd1R1bFBHSVhLdUVDMkcvdkl2d0ptNkNCS2FBQzZFNk1OT1JiRzFlMHE0NG84ZEdhVlgzVEhnUGxqQWJLcjZ4dnc3RE9QRm9KQjVLLzNxcXdrazlCdGlNZVFzMCtmVTJzZHRQTWJLblJ0VW41TVl0UDI5aFJlUmxxZXZzK2MiLCJtYWMiOiI0ZDc5YjEyNDBjOGI4MGQxNTQwNmY1ZDY5MWI5NWIyMjI3YTcwNjg1NTE0NjM1ZTE5YTUyYmRkMjFhMzQ1MmJlIiwidGFnIjoiIn0%3D; expires=Fri, 28-Jan-2022 21:36:39 GMT; Max-Age=7200; path=/; domain=localhost; httponly; samesite=lax
<
* Closing connection 0
There we have the XSRF-TOKEN
that we can provide for our X-XSRF-TOKEN
header.
curl -X POST \
-H "Accept: application/json" \
-H "Access-Control-Allow-Credentials: true" \
-H "X-XSRF-TOKEN: eyJpdiI6ImxNcnBodkpXQXFLMmZCbWVwc2FvZGc9PSIsInZhbHVlIjoiYndpdzlmYW04elNoUWlwaENBSWYyeWEzMHhxQW1SR1phUUlNQjRqM294aThpbGhLbHlhcnBOeWhzVGhESW5BS3k3d3BlQnVqcUtjbEJiSlVIbm1hWFRqK0dVcnVzYkJmY2o4U1BSYkFoWm8vMVRseTZySWVnZWxGZFpaZlZQWDEiLCJtYWMiOiIzNzhmODkzNThlNzQ1ZDgxZjQwNWZhMzliYjA0NWRmMzBmZjEwYTM2MTM2YmRlMTI5ZmI0YTRlNzQ5MjJkYThiIiwidGFnIjoiIn0=" \
-F "email=luke@jedi.com" \
-F "password=luke" \
-F "device_name=curl" \
http://localhost:8000/api/sanctum/token
3|MMseBBcJmLYN78iETh1KAGzToBp3rlvtF8L0GJwI
(Note the change at the end of the cookie %3D
is the encoded =
char. Need to change that to work correctly.)
Nice looks like everything is working!
Now we have two approaches. We implement all authentication routes ourselves. That is /login
, /register
, /password-recovery
etc. Or we make use of Laravel Fortify!
With the authentication up and running we can introduce Fortify so we do not need to write all authentication logic ourselves.
First, we install the required packages.
composer require laravel/fortify
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
php artisan migrate
This will introduce some new fortify actions and configurations. Second, we configure our config/app.php
to register the provider.
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\FortifyServiceProvider::class,
Now with all controllers and routes up and running we can try and create a new user. First, remember to require a new CSRF token.
curl -H "Accept: application/json" -H "Access-Control-Allow-Credentials: true" http://localhost:8000/sanctum/csrf-cookie -v
* Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8000 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /sanctum/csrf-cookie HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.64.1
> Accept: application/json
> Access-Control-Allow-Credentials: true
>
< HTTP/1.1 204 No Content
< Host: localhost:8000
< Date: Sun, 30 Jan 2022 17:17:51 GMT
< Connection: close
< X-Powered-By: PHP/7.3.24-(to be removed in future macOS)
< Cache-Control: no-cache, private
< Date: Sun, 30 Jan 2022 17:17:51 GMT
< Vary: Origin
< Set-Cookie: XSRF-TOKEN=eyJpdiI6ImhXRk13UW5WakcxS1BQYXpZVDJUMEE9PSIsInZhbHVlIjoiZGFpbTlVczdiTTkwaGhRNjY4RnNyU3k5TmhzS3J2Q3dUWld0d3JWT25QT1B5K2hNenA0R25TVG1KM2xKVnNZZGpDT1FqaHZmcUoyM3NnYkZmalZPWkNCcW1hOEZ0TU5kZ1FKWjhqb1dCTEFidVBWellKNTY1cHhVc3lUdU83enQiLCJtYWMiOiJmOGE2ZTMzNmQ0NGU3NjI4N2VkMWE1MTIwNDZmZDg0YWEzNzg2OGE3YzBiZWNiODUzMTA3ZGUzNDcxZWIyMjlmIiwidGFnIjoiIn0%3D; expires=Sun, 30-Jan-2022 19:17:51 GMT; Max-Age=7200; path=/; domain=localhost; samesite=lax
< Set-Cookie: laravel_session=eyJpdiI6IkllZUlySzArOXNSZzVSdkYwTnk5NlE9PSIsInZhbHVlIjoiT3J3S3R1ZzFQdUxIaW5aZjFnNUNtd3lsRWM3TEpHdnh0SFNhTUgxei91dnJsMHRlcDRTckE3QjZhRXlvZ3hhZUlnUVBTRC9IdHYzdjJ3OGdJUW1RUzM4RWFLWGpZRlA2eW9icWIrNXRjTC9KVWROUjVQSU5iZGpGWU9nQW00Z2MiLCJtYWMiOiJkMjY3ZjM5NzVhZTJhYmM0MDFiNjRmZDRjYjVjZGVlY2U5NWViYWMxMzVlNTdhNjEwNGMwMDQ1ZDFmNDAwNjFhIiwidGFnIjoiIn0%3D; expires=Sun, 30-Jan-2022 19:17:51 GMT; Max-Age=7200; path=/; domain=localhost; httponly; samesite=lax
< Set-Cookie: XHwipE64JLQpFn4Fg98nIaAyIXPcd6i1jBYotrov=eyJpdiI6Ik85VEEvRGg3elJRdnRLY2pVSEk1RHc9PSIsInZhbHVlIjoiRHZ6VWxOcFBmQVlPRGNlVUhpcDIzd3RxQ3RRUGtXdENlaE9NS1VwQUw5UEYvSVRVOEFBemMrNWJtaFJsei9vanlybTcrQXNKc2V0REllVW5FMnVRekZQeHcxeXlXSnVYNTI3Q2NwSU1FMUxXZlN4RWdxckJmRk9uZjNzbEpmenRZUk5CTlN2SGRmejI1Y25mVi9VQ290MHlpdU9YandLRWFkeFFCdE9mOGdhVW80bndjQXRVbG1MU2hNRWx6NTNEdldqV0FSTnVoUkd5UHFhSm5WR0JJeXBBUTZwTUdUT3pGTmhCejNubE4xMkcrWDRTYmlhZnE2dFUza3E0M1h4Uk5LRjF1NCsvZk5NQ0NFRjZScVlMMDhYRHpnVWw5cTVxc0pZUktZMEhJdENmbi9YVElOckVYTytBai83NFNmYnplWFlKWmcxbDRRb0x0Ym1UbVJFZTRkcjZNKzZmcklyL3FoSkU2N2Zlc01aZ0Z4SytjUE1ueGpQODUyQjVjNlA1ZWtoeUVYYzZWSWdXSXhHejljcDlEQT09IiwibWFjIjoiZWIwZWUxNzQwYWEyMjcxNDVlNjk2ZTFlODAxZmQzY2E2YzI5ODI0N2QyYWE1MjU2MzliMmFhNmU1OTZjMDcyNSIsInRhZyI6IiJ9; expires=Sun, 30-Jan-2022 19:17:51 GMT; Max-Age=7200; path=/; domain=localhost; httponly; samesite=lax
<
* Closing connection 0
Now we register a new user by using the register
endpoint.
curl -X POST \
-H "Accept: application/json" \
-H "Access-Control-Allow-Credentials: true" \
-H "Referer: localhost:8000" \
-H "X-XSRF-TOKEN: eyJpdiI6ImhXRk13UW5WakcxS1BQYXpZVDJUMEE9PSIsInZhbHVlIjoiZGFpbTlVczdiTTkwaGhRNjY4RnNyU3k5TmhzS3J2Q3dUWld0d3JWT25QT1B5K2hNenA0R25TVG1KM2xKVnNZZGpDT1FqaHZmcUoyM3NnYkZmalZPWkNCcW1hOEZ0TU5kZ1FKWjhqb1dCTEFidVBWellKNTY1cHhVc3lUdU83enQiLCJtYWMiOiJmOGE2ZTMzNmQ0NGU3NjI4N2VkMWE1MTIwNDZmZDg0YWEzNzg2OGE3YzBiZWNiODUzMTA3ZGUzNDcxZWIyMjlmIiwidGFnIjoiIn0=" \
-F "name=Han Solo" \
-F "email=han@millennium-falcon.com" \
-F "password=myfalcon" \
-F "password_confirmation=myfalcon" \
http://localhost:8000/register
Hmm, wait a minute. This does not work, I get "message": "CSRF token mismatch."
. That's because we are now requesting the web
route and not the api
route that is protected with CSRF.
The difference with the previous request with api/sanctum/token
is that we need to also pass the Cookie
header with the combined values from the Set-Cookie
headers. If one is using e.g. Postman or Axios. This Cookie header is set automatically. But we are using Curl here so we need to pass it. Let's add that to our Cookie
header.
curl -X POST \
-H "Accept: application/json" \
-H "Access-Control-Allow-Credentials: true" \
-H "Referer: localhost:8000" \
-H "X-XSRF-TOKEN: eyJpdiI6ImhXRk13UW5WakcxS1BQYXpZVDJUMEE9PSIsInZhbHVlIjoiZGFpbTlVczdiTTkwaGhRNjY4RnNyU3k5TmhzS3J2Q3dUWld0d3JWT25QT1B5K2hNenA0R25TVG1KM2xKVnNZZGpDT1FqaHZmcUoyM3NnYkZmalZPWkNCcW1hOEZ0TU5kZ1FKWjhqb1dCTEFidVBWellKNTY1cHhVc3lUdU83enQiLCJtYWMiOiJmOGE2ZTMzNmQ0NGU3NjI4N2VkMWE1MTIwNDZmZDg0YWEzNzg2OGE3YzBiZWNiODUzMTA3ZGUzNDcxZWIyMjlmIiwidGFnIjoiIn0=" \
-H "Cookie: XSRF-TOKEN=eyJpdiI6ImhXRk13UW5WakcxS1BQYXpZVDJUMEE9PSIsInZhbHVlIjoiZGFpbTlVczdiTTkwaGhRNjY4RnNyU3k5TmhzS3J2Q3dUWld0d3JWT25QT1B5K2hNenA0R25TVG1KM2xKVnNZZGpDT1FqaHZmcUoyM3NnYkZmalZPWkNCcW1hOEZ0TU5kZ1FKWjhqb1dCTEFidVBWellKNTY1cHhVc3lUdU83enQiLCJtYWMiOiJmOGE2ZTMzNmQ0NGU3NjI4N2VkMWE1MTIwNDZmZDg0YWEzNzg2OGE3YzBiZWNiODUzMTA3ZGUzNDcxZWIyMjlmIiwidGFnIjoiIn0=; XHwipE64JLQpFn4Fg98nIaAyIXPcd6i1jBYotrov=eyJpdiI6Ik85VEEvRGg3elJRdnRLY2pVSEk1RHc9PSIsInZhbHVlIjoiRHZ6VWxOcFBmQVlPRGNlVUhpcDIzd3RxQ3RRUGtXdENlaE9NS1VwQUw5UEYvSVRVOEFBemMrNWJtaFJsei9vanlybTcrQXNKc2V0REllVW5FMnVRekZQeHcxeXlXSnVYNTI3Q2NwSU1FMUxXZlN4RWdxckJmRk9uZjNzbEpmenRZUk5CTlN2SGRmejI1Y25mVi9VQ290MHlpdU9YandLRWFkeFFCdE9mOGdhVW80bndjQXRVbG1MU2hNRWx6NTNEdldqV0FSTnVoUkd5UHFhSm5WR0JJeXBBUTZwTUdUT3pGTmhCejNubE4xMkcrWDRTYmlhZnE2dFUza3E0M1h4Uk5LRjF1NCsvZk5NQ0NFRjZScVlMMDhYRHpnVWw5cTVxc0pZUktZMEhJdENmbi9YVElOckVYTytBai83NFNmYnplWFlKWmcxbDRRb0x0Ym1UbVJFZTRkcjZNKzZmcklyL3FoSkU2N2Zlc01aZ0Z4SytjUE1ueGpQODUyQjVjNlA1ZWtoeUVYYzZWSWdXSXhHejljcDlEQT09IiwibWFjIjoiZWIwZWUxNzQwYWEyMjcxNDVlNjk2ZTFlODAxZmQzY2E2YzI5ODI0N2QyYWE1MjU2MzliMmFhNmU1OTZjMDcyNSIsInRhZyI6IiJ9; laravel_session=eyJpdiI6IkllZUlySzArOXNSZzVSdkYwTnk5NlE9PSIsInZhbHVlIjoiT3J3S3R1ZzFQdUxIaW5aZjFnNUNtd3lsRWM3TEpHdnh0SFNhTUgxei91dnJsMHRlcDRTckE3QjZhRXlvZ3hhZUlnUVBTRC9IdHYzdjJ3OGdJUW1RUzM4RWFLWGpZRlA2eW9icWIrNXRjTC9KVWROUjVQSU5iZGpGWU9nQW00Z2MiLCJtYWMiOiJkMjY3ZjM5NzVhZTJhYmM0MDFiNjRmZDRjYjVjZGVlY2U5NWViYWMxMzVlNTdhNjEwNGMwMDQ1ZDFmNDAwNjFhIiwidGFnIjoiIn0=;" \
-F "name=Han Solo" \
-F "email=han@millennium-falcon.com" \
-F "password=myfalcon" \
-F "password_confirmation=myfalcon" \
http://localhost:8000/register
If we check our database now we should have a new user! So with our new user let's try and login
curl -v -X POST \
-H "Accept: application/json" \
-H "Access-Control-Allow-Credentials: true" \
-H "Referer: localhost:8000" \
-H "X-XSRF-TOKEN: eyJpdiI6IlQxaVhQcUplbmN5M0Z2d1V0ZFV0SGc9PSIsInZhbHVlIjoiN2Z2QnozbGdDV2Jac0tzTk1FaFB6cndNVlVocDlNRjN4bkt5UHZUR3VmaG9YYmszSUlNNmowd0RPbTVPcHFKMHpoU0tyWGRkVWUxUlZkYi9aanlnemxsREJlK2ZreWtqb1pzQ2hVNExLcEJranFiZ1RiVW1VWUpJVVB5am9FVjAiLCJtYWMiOiJjY2RmNDQ1Y2MwZDFkNDQ0Y2JjNTkzZmNmY2I1NjQxOGJkMzU3YWI0MWUxNjJkMjYwY2FlYWM0YmJmYjRhMmIxIiwidGFnIjoiIn0=" \
-H "Cookie: XSRF-TOKEN=eyJpdiI6IlQxaVhQcUplbmN5M0Z2d1V0ZFV0SGc9PSIsInZhbHVlIjoiN2Z2QnozbGdDV2Jac0tzTk1FaFB6cndNVlVocDlNRjN4bkt5UHZUR3VmaG9YYmszSUlNNmowd0RPbTVPcHFKMHpoU0tyWGRkVWUxUlZkYi9aanlnemxsREJlK2ZreWtqb1pzQ2hVNExLcEJranFiZ1RiVW1VWUpJVVB5am9FVjAiLCJtYWMiOiJjY2RmNDQ1Y2MwZDFkNDQ0Y2JjNTkzZmNmY2I1NjQxOGJkMzU3YWI0MWUxNjJkMjYwY2FlYWM0YmJmYjRhMmIxIiwidGFnIjoiIn0=; K8ogV76bi5BblE3XE150Y93owKipa4ju9m3cRxgN=eyJpdiI6InpiWUY1WkkxY0hzQjBrZ3V5Vlo3M1E9PSIsInZhbHVlIjoiVWNCejRhSzBNTm1oS3FRd21yNEcxak1FWVJUMGZFdFRsL25EV0g1aFFQaE1TelVuN0J4S2oyTXVTemJaREdKVC9mV3NrYnRnOWU5Z1NYcC84eVFHUFk5THFnUlQ4QnNvdnBURmwwR2UxUy9tZ3V5YXlWZFBIUXVDY3FKVEIxbjlJM2I4OS84RldzWXg4Wm1YS3E1clpCbm5FM3l6Z2JyeEowVW9RNkpyNDgycC9aSm9IK1I3QnVtQm1tV3MrQWd6OS9DYlBYR2dpcDVkcVRQN0VMRWVUR2t1ZjdWS3Zobk9qVlpWQ1RHR3dwSWZ0Y0RHVFlkSDg2RWx3amJzdlo4dWE0NDFsajBuQm5yYWtvVDhkQXBjVlQ1WVdoYWQ3ZERkSVNBT3diV0xCL1YyMkhNb0JTUGg4b0hVL2pxK3lpanRha1VwS2s0MTdqSUNSQTdMZ2R5NE00aFlqUnQ5M2pUYlJzemdGRG1QSnVQcjQvRXhXM1lnaW9rS0NhQUdoZUtZK0J4RXRmcXhDQWsxTjYrL1BBSzIwdz09IiwibWFjIjoiNjRhMWUyZGJlMjgzYjJhNTA1YjVkOTQwZjRkZDNkNmQ2ZjE0Yjc3YWFjZmYxNjVkOWRhOWU3ZTUwZDM5NWZmOCIsInRhZyI6IiJ9; laravel_session=eyJpdiI6ImY3YXl2Q3hSNElNMFZ0K2szNlFxWXc9PSIsInZhbHVlIjoiem03OXNZbFdTMk10a0tkZk9zcUZhMjdUeWZqOHRJNjdPMnlIMXpXV214ZXlwZENlRWc2d0VibGYrUlpNTDRtVXVNQVhwUkhMeHhkaTFhUkJqb2JKOFlVODZkK1dvWXJIckEyRnhsanJLbHBvRXE5a1VJV1ZyMnlLVFJuRE5wWUMiLCJtYWMiOiI3N2VkZGY2NjdiMzAxMTJhZjQxOWViZjYxNGY5NjZkNmEyYzI5ZThlZjVhMDM2MWJlMWY0MzZlMWE3ZDY4NDlmIiwidGFnIjoiIn0=;" \
-F "email=han@millennium-falcon.com" \
-F "password=myfalcon" \
http://localhost:8000/login
Should get the response {"two_factor":false}
and we should now be logged in. Let's try and fetch the user information. Remember that the Cookies have changed so we need to use the new cookies we got back. Recommend using e.g. Postman for testing the API calls so you do not need to include the cookie header all the time.
curl -v \
-H "Accept: application/json" \
-H "Access-Control-Allow-Credentials: true" \
-H "Referer: localhost:8000" \
-H "X-XSRF-TOKEN: eyJpdiI6IlZudytBbDVUWTFCa0JCM3BCNjQxVGc9PSIsInZhbHVlIjoiUjJxZ2c2T3c4SnROVjJZY2hiOGZIZDJhWEkyT3Vzc0F5TWM5RUlHS3dROWpZZnhMTmI1YXA2enQ1dE4ycFRhTEt1SmljbDdiZU91UXNPSHJZNFR6SlBTaGpSekZqcVMwSHIyK2w3SXA1dUhVOHV1bEtGT0ZnMDcxbzA4bzRvdHYiLCJtYWMiOiJkNzkyNDgwYjk0YzFjMTA1NTc0ZWZiYWJiMWMxZDBlMmQzYmVmYmQzNWNjZTlkOTllNzgwYzVmMDJiMGFhY2M1IiwidGFnIjoiIn0=" \
-H "Cookie: XSRF-TOKEN=eyJpdiI6IlZudytBbDVUWTFCa0JCM3BCNjQxVGc9PSIsInZhbHVlIjoiUjJxZ2c2T3c4SnROVjJZY2hiOGZIZDJhWEkyT3Vzc0F5TWM5RUlHS3dROWpZZnhMTmI1YXA2enQ1dE4ycFRhTEt1SmljbDdiZU91UXNPSHJZNFR6SlBTaGpSekZqcVMwSHIyK2w3SXA1dUhVOHV1bEtGT0ZnMDcxbzA4bzRvdHYiLCJtYWMiOiJkNzkyNDgwYjk0YzFjMTA1NTc0ZWZiYWJiMWMxZDBlMmQzYmVmYmQzNWNjZTlkOTllNzgwYzVmMDJiMGFhY2M1IiwidGFnIjoiIn0=; zGvmvStSlrsqxdEOfTx7gcTRL4bIFpq1kcDB5W5b=eyJpdiI6ImRxazFzNkVWNW1yK1crblNoRjRONlE9PSIsInZhbHVlIjoiQzJoTHZWUVNCakpMaVRGOG1ibFFIQW10dWRjKzFBRmdrMUdLQU9QeUhTUkk3a0lzUTc5ZmF5TU00M3RGc0lQckxsMlY4QVEvRU91RzYvUGVQV2pFMUJ2bW55OFE1aUVrUVE3ZTZZVm5Ed2h3YTNIZUZzamJlRUh4QjUwZStBTXRmT3pFSHlETmNUMVdpZis1ckFGWGRyY0pFR1NLUXJrWVpJdVZNRjE2NEY3MVlacGt5bmU4cVVvczYxQVAxOUpKWWxuZiszTk53LzQ1V1lQdDlsSW5Ib2c3c3NkNGxJUU1lZUszU1dpSTd2MVdlcm13WFR5cE5QUjVwU2NHeHFrbUkydW9QWEN3VXo3SVNXemdaVzhSWTZrUHFOdzVDbGlMY01Zc2loZHgvbHBuVUZVUGJjUnJPc0VSTUU3VFk1Z1I1VjIvaTNIT1B2Rk9QNnpSVVJvdTlmVGZLaitlUFNxa0lMMS8xbUMyaFBLWXNsOTIwSGRqbUV4OGNMdGo4bGpoL3l5T2JwaDNwL3lteFpFTjV5YU9sZHhnU3RIU01mcmZmWHVlR1VDbnVoMWI2V0lVSUZIRjh6N0YxbUdub0R0YTRMWHNYWHhnVzNEL0pudGpnUkdnamFSTTExcG1KYS92VW5CSWZlSXhZUW89IiwibWFjIjoiYjM3MDNkYzMwZDI3ZTM2ZjY4OTk2Mjg1NDNlZmE1YTY0MWFlMWY1YWI0MTA3OWRiYTU1MDcxOTVlYTQ0NGIwYiIsInRhZyI6IiJ9; laravel_session=eyJpdiI6IjZ6aDBmS1VDdmxoWVU1aWdLZGRsSlE9PSIsInZhbHVlIjoiTHJsUUhGRWlhOHNiejNIckV2TFl4RWFXaDBUS0l2NGlaVXd2MUxPb2htTjhjM1l2NUJaNkg2NE1wYkc4a2k0Sk1ab2ZYSWZzdExZM1cvaWRpRHZQYlM1QjFEWEpXcGlpOVdMYzJSS1FoejFPdTNNeXU4eDdJb0N4R1M4cHNEMEwiLCJtYWMiOiI0YzlmOTRhYzIyMGQ5Y2VjZDhkNjI0M2NhMTE3OTdkYmUwOGEyYzhkN2UyNDhmYTQ2NWExYzYxMzM5NDNjYjdlIiwidGFnIjoiIn0=;" \
http://localhost:8000/user
We should now get the user data back!
{
"id":10,
"name":"Han Solo",
"email":"han@millennium-falcon.com",
"email_verified_at":null,
"two_factor_secret":null,
"two_factor_recovery_codes":null,
"created_at":"2022-01-30T17:20:44.000000Z",
"updated_at":"2022-01-30T17:20:44.000000Z"
}
Now we have everything up and running to be able to include a frontend application.