I have a laravel backend and a react native frontend, I want to protect the api routes that are hit from my react native app with axios, for this i installed laravel sanctum.
My current workflow is : I log or register user with email and password, get a sanctum token that I store using AsyncStorage in my app, then I send this token on the headers of all my axios calls uisng interceptors.
THE ISSUE:
Routes protected by auth:sanctum middleware get a 302 Found, then redirected to homepage / 200 OK.
How I create a token in backend:
$token = $user->createToken($request['device_name'])->plainTextToken;
How I add my Bearer toke to headers (I verify they are attached via console log):
if (token)
{
console.log('SANCTUM: Adding bearer token to axios: ' + token);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
}
How I protect my routes:
Route::get('/auth/sanctum/user', 'AppHttpControllersApiAuthController@sanctumUser')->middleware('auth:sanctum');
In my RedirectIfAuthenticated middleware I tried changing it after reading some other posts but any i change I made it did not made any difference…
<?php
namespace AppHttpMiddleware;
use AppProvidersRouteServiceProvider;
use Closure;
use IlluminateSupportFacadesAuth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param IlluminateHttpRequest $request
* @param Closure $next
* @param string|null ...$guards
* @return mixed
*/
//Added && !$request->wantsJson() part
public function handle($request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard)
{
//Added !$request->wantsJson()
if (Auth::guard($guard)->check() /*&& !$request->expectsJson()*/ )
{
//Tried changing this too
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}
In my Kernel http:
protected $middlewareGroups = [
'web' => [
//AppHttpMiddlewareEncryptCookies::class,
IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class,
//IlluminateSessionMiddlewareStartSession::class,
//IlluminateSessionMiddlewareAuthenticateSession::class,
IlluminateViewMiddlewareShareErrorsFromSession::class,
AppHttpMiddlewareVerifyCsrfToken::class,
IlluminateRoutingMiddlewareSubstituteBindings::class,
],
'api' => [
//EnsureFrontendRequestsAreStateful::class,
IlluminateRoutingMiddlewareThrottleRequests::class.':api',
IlluminateRoutingMiddlewareSubstituteBindings::class,
],
];
sanctum.php file
<?php
return [
/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1,127.0.0.1:8000,::1')),
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. If this value is null, personal access tokens do
| not expire. This won't tweak the lifetime of first-party sessions.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
|--------------------------------------------------------------------------
|
| When authenticating your first-party SPA with Sanctum you may need to
| customize some of the middleware Sanctum uses while processing the
| request. You may change the middleware listed below as required.
|
*/
'middleware' => [
'verify_csrf_token' => AppHttpMiddlewareVerifyCsrfToken::class,
'encrypt_cookies' => AppHttpMiddlewareEncryptCookies::class,
],
];
auth.php file:
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => AppModelsUser::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];
I’m using console.log to log the headers of my request (like this:console.log(‘Request Headers: ‘, response.config.headers);).
Request Headers: {"Accept": "application/json", "Authorization": "Bearer rlRo0x0pz8ivKiveNEEAUqNL9K5hKxKdc2gdFi8lYhkMBMmCN7OzGgtv4Kex", "isPremiumActive": 0, "languageCode": "es", "lat": 36.7203234, "lng": -4.4062555}
2
Answers
make sure you have
Accept: application/json
in your axios headersin this line
if (.... !$request->wantsJson() )
you are saying "if it doens’t want json redirect it to homepage"
since its a Rest API project you dont need redirections just respond with HTML response codes if they arent authenticated
also i recommend postman for api testing during development
when working with api, its always preferable to get the api working first either on postman or qodex.ai (free alternatives for 100 teams members). once your api is properly working and tested, then maybe start the integration