Sanctum docs outline 2 authentication mechanisms:
- SPA Authentication
- API Token Authentication
SPA states for a Single Page Applications, which should be a JavaScript based application running in a browser, where aspects such as handling cookies are a built-in feature.
Your use-case fits better in the API Token Authentication scenario, as you are using another platform to interact with your app.
Sanctum docs has a section on "Mobile Application Authentication", that despite the name, outlines how an external app can obtain a token with a email/password and use it afterwards:
https://laravel.com/docs/8.x/sanctum#mobile-application-authentication
Based on that docs' part you could try the following:
Create a login route that returns an API token
First, create a controller:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class ApiLoginController
{
use ValidatesRequests;
public function __invoke(Request $request)
{
/////////////////////////////////
// WILL ADD SOMETHING HERE LATER
/////////////////////////////////
$credentials = $this->validate($request, [
'email' => 'required|string|email',
'password' => 'required|string',
]);
$user = User::query()->firstWhere('email', $credentials['email']);
if (! $user) {
return response()->json(['message' => 'invalid credentials'], 401);
}
if (! Hash::check($credentials['password'], $user->password)) {
return response()->json(['message' => 'invalid credentials'], 401);
}
// User model should use the HasApiTokens trait,
// as per Sanctum docs
$token = $user->createToken('token-name');
return response()->json(['token' => $token->plainTextToken], 200);
}
}
Then register it in your ./routes/api.php file (IMPORTANT: not the web.php file)
use App\Http\Controllers\ApiLoginController;
Route::post('/login', ApiLoginController::class);
The catch here is that you now have an endpoint that accepts a pair of username and password and checks for a user login. Apparently no problem whatsoever, but as this endpoint has no additional security (note we are not using auth:api or auth:sanctum to protect it) and no CSRF protection, one could make a script that send requests to try brute forcing a user's password.
One way to solve this is to send a header with a token that identifies your C# application, so your server would know the request was issued by a first-party application.
Replace the comment block where there is this string WILL ADD SOMETHING HERE LATER with :
if ($request->header('X-APP-TOKEN') !== 'MY-SHARED-SECRET-TOKEN') {
return response()->make('Forbidden.', 403);
}
ideally you would be reading this "shared secret" token from a config file.
This is not the best security measure, but it is valid one. It works a "shared secret" between both apps. As long you don't expose this token to anyone else it should improve this endpoint's security.
Then in your C# code you could do:
// Now your login endpoint is /api/login
readonly string loginURL = "http://website.com/api/login";
WWWForm loginForm = new WWWForm();
loginForm.AddField( "email", email );
loginForm.AddField( "password", pw );
UnityWebRequest www = UnityWebRequest.Post( loginURL, loginForm );
// You can keep this header, as Laravel
// is happier with it when generating JSON error responses
www.SetRequestHeader( "X-Requested-With", "XMLHttpRequest" );
// Use the "shared secret" token used in ApiLoginController
www.SetRequestHeader( "X-APP-TOKEN", "MY-SHARED-SECRET-TOKEN" );
// process the response
// if successful save the returned token,
// otherwise handle any errors
One note: Compare this ApiLoginController to the implementation sample from Sanctum docs, to see which better fit your requirements.
Accessing guarded routes
With the token saved any subsequent calls to API routes guarded with auth:sanctum could be made a such:
readonly string loginURL = "http://website.com/api/another-endpoint";
WWWForm loginForm = new WWWForm();
UnityWebRequest www = UnityWebRequest.Get( loginURL );
// You can keep this header, as Laravel
// is happier with it when generating JSON error responses
www.SetRequestHeader( "X-Requested-With", "XMLHttpRequest" );
// Use the token returned in the login
www.SetRequestHeader( "Authorization", "Bearer " + userToken );
Logging out
Logging out is just a matter of deleting the token on your C# app.
Form time to time you might need to prune old tokens from your Laravel app.
Bottom line
As you can see we avoided the Cookies handling by leveraging Sanctum API token capabilities.
Bear in mind your API endpoints won't have a Session object. But that is intended as API endpoints are meant to be stateless.
Hope it makes things clearer and helps in some way.