I've done some googling and checked this answer but am still facing a 401 Unauthorized response when consuming my own api from my laravel app from a non logged in user.
I've followed the official docs and have installed passport.
I've also have this on every axios request:
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
Token is also defined on my boilerplate:
<meta name="csrf-token" content="{{ csrf_token() }}">
Why are the requests still unauthorised?
If your api route is wrapped in the auth:api middleware, the key word being auth, then what you're doing will be impossible unless your user is not first logged in.
You can remove the auth and just have the api middleware but this will not protect this endpoint from cross-site/unauthorised requests.
A solution could be to create a client credentials grant token and send it through to your front-end, although you'll have to store this somewhere.
Personally because it's your own api I'd just create a route or route group in its own file and require this file into both your web and api route files. Therefore you'll be able to consume it from your own web app with the csrf protection & if you try to access it elsewhere it will need the prepended api in the url https://website.com/api and an access token.
Okay then, do not use "auth:api" for routes that do not need authenticated users, Why then?, have you noticed the key word AUTH === authenticated on 'auth:api" middleware ? this means it requires an authenticated user or rather check for authentication first even before going to api check.
So what to do ?
If you happen to have made that every request should got under "auth:api" middleware check under Http/Kernel.php file like below
'api' => [
'throttle:60,1',
'bindings',
'auth:api'
],
Just remove it there and add this middleware on the routes/api.php file on the routes that needs authenticated user only like below
Route::post('/comments', CommentsController#post')->middleware('auth:api');
Then let routes which do not need authenticated users to be like so
Route::get('/comments', 'CommentsController#index);
Related
I just trying to makes my auth flow more secure using a cookie on Laravel 5.7
Here my code
/**
* auth logic
*/
return response()->json(["status" => "logged in"], 200)->cookie('token', $token, $lifetime);
Then the cookie will be saved on the browser and will be used on every request.
On header with Axios
"cookie":"token={token}"
And I validate the auth using default middleware
Route::group(['middleware' => ['auth:api']])
But the auth:api did not recognize it, I can make custom middleware by manually detect the cookie, but I can't use the auth()->user() function on it.
Is there any solution for this?
From your sample code I believe your app is built on a stateless architecture where you have your JavaScript client and laravel api.
Now I am a bit confused as to why you do not want the client storing the token, if you just want to escape cross site scripting vulnerability (XSS) then you have to prepare to deal with cross site request forgery (CSRF) if you store the token in the browsers cookie.
Regarding the middleware not being able to find the token, by default the middleware is configured to lookup tokens in the request header (specifically the Authorization header) so if you decide to store it in the cookie, you have to find a way to change the token lookup in the api middleware which unfortunately I have not done before in laravel.
APIs don't generally store and send cookies. Therefore the api token authentication guard will not look for the token in a cookie. There are multiple options you can send it as though the easiest one in axios:
{
headers: {
Authorization: `Bearer ${token}`
}
}
I am trying to protect API routes with a bearer token using Laravel Sanctum.
I have added the middleware correctly for the route as follows in api.php. The api/me route is set to return auth()->user();
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::post('/me', 'App\Http\Controllers\APIController#me');
});
To test this, I first login successfully and generate a bearer token, so that is working fine.
However, when I try to access the api/me route without the bearer token, it still displays the full user. It is not supposed to be allowing access to the route without a bearer token, why is it doing that?
I've searched for hours but no joy - does anyone have any insight?
I'm just guessing here.. Did you "EnsureFrontendRequestAreStateful" in kernel.php?
Because then Sanctum will use Session-Cookie based authentication. Sanctum will only use the bearer token if you authenticate third party apps, that don't run on your domain or subdomain.
If you don't want to use session-cookie based authentication for your SPA remove the "EnsureFrontendRequestsAreStateful" class from kernel.php.
Now Sanctum will always use the bearer token for authentication.
I am trying to use Laravel Sanctum for my SPA. There are some basic home pages from web.php routes but other axios API interactions with the SPA are in api.php routes guarded by auth:sanctum
From the official documentation (https://laravel.com/docs/7.x/sanctum#spa-authenticating), it says we have to send a request to /sanctum/csrf-cookie to initialize CSRF protection prior login. However, I noticed that even without login, Laravel by default already initialized XSRF-TOKEN and <app_name>_session cookies to my browser. I do not need to initialize it via /sanctum/csrf-cookie and my subsequent API request in the logged-in SPA still works. Later I checked https://laravel.com/docs/7.x/csrf#csrf-x-xsrf-token and it says it is the default behavior that Laravel will include the CSRF token in each response.
My question is, is it true that /sanctum/csrf-cookie initialization is optional and it is safe for axios to use the default CSRF token return by Laravel? Or am I doing something wrong which exposes my SPA to CSRF attack?
Your main SPA home page is probably provided by a route that is defined in your web.php route file as you mentionned. In App/Http/Kernel.php, check in your middleware groups if there is VerifyCsrfToken::class defined as a middleware for web :
protected $middlewareGroups = [
'web' => [
...
StartSession::class,
...
VerifyCsrfToken::class,
...
]
]
This middleware is responsible for creating header response like : set-cookie XSRF-TOKEN=kgXZBZ4AccC0H17KEMw.... when you request any route available in web.php (if the cookie yet doesn't exist obviously), that will indeed initialize a XSRF-TOKEN cookie.
Therefore, you don't need to request route /sanctum/csrf-cookie when you already use this VerifyCsrfToken middleware.
However, if you are doing full SPA totally separated from your Laravel backend and deliver a html page differently, you won't have this XSRF-TOKEN cookie generated by default. Thus, as mentionned in Sanctum documentation, you need to request /sanctum/csrf-cookie to generate cookie before going further.
Until now I have only used my Laravel app as a backend API to my mobile app. The user logs in and get a token that then is stored on the device, then the device use that basic http token on each request to my backend API.
But now I am building a web based version of my app. I want to send a POST update in my page using axios and vue, but I am not sure how I should do this since my route is protected by auth.
Should I do something like this?:
<your-component :auth_user="{{auth()->user()->api_token}}"></your-component>
Or simply create a meta:
<meta name="token" id="token" value="{{ auth()->check() ? auth()->user()->api_token : null }}">
This way my component gets my users api_token which can later be used in axsios when I send a post request using a basic http auth.
Or what is the common way to talk to a protected API using axios?
thanks
Laravel have a good package for API authentication called Passport
, so after configured, it create the routes for require and return the token. To request it's http://{domain}/oauth/token.
When the user try to log in, Vue must send a post request with axios passing the user data. If the user have access, the token it's returned.
To protect your routes you can use middleware('auth:api'). Passport uses this middleware to validate the token.
ie:
<script>
userData = {
...
},
axios.post('http://{domain}/oauth/token', userData) {
.then(function (response) {
console.log(response.access_token)
... redirect to dashboard or something else
}
}
...
</script>
As you may know, the token has to be passed in every client request, and a way to do this is passing the token in the HTTP request header. Fortunately Passport already do this for you.
Hope it helps.
Im using Laravels default auth to lock down some paths in the routes/api.php file.
Route::get('/projects', 'ProjectController#index')->middleware('auth:api');
I log the user in via a PHP form (not via a http request via Vue).
This creates the session and when I submit a HTTP request via Vue I can see that the header includes the Cookie and X-CSRF-Token however I keep getting a 401 {"error":"Unauthenticated."}
In my config/auth I have api driver set as 'token' (have tried changing this to 'session' but that did work :/)
From my understanding and what I have read online I should be able to use the default Laravel auth functionality to accomplish API calls from Vue HTTP requests.
this.$http.get('/api/projects')
.then(response => {
this.projects = response.body;
})
.catch (err => {
console.log(err);
});
I've read about methods of authenticating by generating an JWT token and storing that in local storage when the user logs in. Is this the method I should use or should I be able to accomplish it with the default Laravel Auth middleware?
Hope my questions make sense, any help/advice would be appreciated.
The auth:api middleware doesn't use cookies, it uses api_token param, which can be passed via get or Bearer <token> header. Just use web middleware.
I suppose you need to access the same route in two ways - for API users and for browser users. So why don't you create two routes for one action?
// api group with /api prefix
Route::get('/projects', 'ProjectController#index')->middleware('auth:api');
// web group
Route::get('/projects', 'ProjectController#index')->middleware('web');