I have a Laravel project where I use the same controllers for web- and api- routes by checking the request->wantsJson() to determine what to return. I'm using the built in Auth for web routes.
In the front end, I use jQuery/ajax to fetch and post data, but I'm not sure how to forward the current logged in user in the ajax-calls so the requests can both be authenticated and the current user be identified.
What I would like to do is to attach some kind of temporary session token in the request header when doing the ajax calls and authenticate this as a user through middleware.
What is the recommended way of doing this?
Thanks for the tip Tim, but as I'm planning on accessing the api for external clients as well, I figured I needed some token middleware.
I solved it using tymon/jwt-auth on all api routes and adding to my user class
public function getToken(){
return JWTAuth::fromUser($this);
}
Then i simply add this to the header in ajax calls
headers: {
'Authorization': Bearer {{auth()->user()->getToken()}},
'Content-Type': 'application/json',
'Accept': 'application/json'
},
Related
Within my Laravel 9 (Jetstream) installation, I've createad an account (admin) and also created an API token for it directly from the user interface available by default. So the API has an account and an API token for it.
I am able to login to my (Laravel) API, using that token as a Bearer token method 'Authorization: Bearer '.
Everything fine for now, I can access protected routes of the API that are only available for logged-in users.
I've implemented this approach within my frontend app (SPA), which is on a different domain.
At a certain point in my frontend app, I need to communicate with my API, so I am doing a request to it using the same approach like I did in Postman. Basically, within my (axios) request, I am configuring the route to the api, and also the required header (Authorization: Bearer xxxx) to authorize the request using the Bearer token.
axios.post(
'url',
{
"body": data
},
{
headers: {
'Authorization': 'Bearer <my token>'
}
}
)
Now, my main problem is that this request (including the token) can be seen in Chrome Developer tools for example (or other request trackings tools)
How can I secure this in a easy and strong way?
For those who know more about Laravel, this way of authorization that comes from Jetrstream is Sanctum, and comes out of the box. I know there's another method of creating tokens, by making an initial request to the API using basic auth, and in response, I'll get a token that I should use for each request. But does that mean that it's more secure? In the end, that "fresh" token will be also visible to next requests, right? Even if it will be deleted on logout. If someone sees that token, he can easily make a request to my API using it and pretend to be a logged in user.
Is JWT a solution?
There must be something that I'm missing here. Any solutions are welcome
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 have a single page create-react-app running on localhost:3000 and I want to log in to a laravel 7.x instance running on myapp.loc (vhost).
Eventually I would like a single page running on app.mysite.com with laravel running on api.mysite.com.
I'm able to log in to my laravel instance directly from myapp.loc. I've installed Laravel passport and the scaffolding, etc and can create Client IDs and Secrets, but I'm unsure if they are needed and if so, how to use them.
What I am unsure of and cannot find any documentation for, is how to log in to laraval from my SPA (running on localhost:3000). I have set my CORS headers and can connect requests that don't require auth, but I don't know how to log in or structure auth requests once logged in.
I can't find any documentation on axios.post / get requests with a focus on logging in from another domain and maintain user-based access.
Since I don't know enough to ask a concise question, there are three layers that I feel like I should be searching for an answer.
Is it possible for laravel to act as the backend for my single page app from another domain?
If so, are there documented, best practices / well worn paths for accomplishing this?
If so, what would a sample axios login and subsequent session call look like? (e.g. payload and header shape)
Yes you can, I suggest to use https://laravel.com/docs/7.x/sanctum instead of passport because is easier to setup and it was created especially for this scenario.
You need to configure CORS using the official Laravel Package https://github.com/fruitcake/laravel-cors this way you will open Laravel's CORS to be able to reach it from anywhere localhost, or any domain that you can set up into allowed_origins. inside the cors.php config file according to the documentation of the package.
After configuring Sanctum/Passport and ensuring you are generating the required token for your SPA using the createToken method described in Sanctum or Passport docs, you have to save the token to connect to your protected endpoints however they recommend to use cookie SPA based authentication but that's not strictly necessary.
Create an API Service
In this example I will create an API Service to encapsulate API calls
import axios from 'axios';
const URI = 'https://yourlaravel.api/api/';
axios.defaults.headers.common = { Accept: 'application/json', 'Content-Type': 'application/json' };
const ApiInstance = axios.create();
const API = {
login: (user) => {
return ApiInstance.post(`${URI}login`, user);
},
getUser: () => {
return ApiInstance.get(`${URI}user`);
},
setUser: (user) => {
return ApiInstance.post(`${URI}user`, user);
},
};
Send A Login Request to your login endpoint and save the token
import API;
API.login({email:'mail#domain.com',password:'32323'})
.then(response=>{
//save the token
//response.data.accessToken
})
Fetch data from your protected endpoints using the token
//Set the default authorization header when you have an access token
axios.defaults.headers.common = {'Authorization': `Bearer ${token}`}
//Get your data
API.getUser().then(response=>{
//response.data
})
//Post something
API.setUser({user:'Os'}).then(response=>{
//response.data
})
All those things are possible, you just need to set up cors and you are good to go. For auth you can use passport or your own custom app key setup, it all depends on what you are trying to achieve. I suggest reading up about RESTfull apis, that would be a good start.
In order to perform a handshake between FE and BE on FE you would have a login form submission of which will send e request to BE (backend api) and if login is success you send back a key which then FE should store. Any future requests from FE should append that key in the header to gain access to authorised areas.
There should be plenty of information on this subject (RESTfull Api & Token authentication) on google.
I want to upload images via Vue2Dropzone package in my Laravel project but it keep throwing 401 Unauthorized error. The auth:api middleware works fine when the request is sent from axios. The package itself uses plain javascript to make a request.
I think I just need to pass the token via Vue2Dropzone's sending method but when I checked the database, the passport table was empty. I need to login via API route to generate its token but my project is a Multi Page Application and it doesn't do a user authentication via API at all. Instead, user is logged in via it's built in authentication on web routes.
Is there any way to generate passport token when a user logged in via web middleware without modifying its built in login controller? I need it to be like that so I can pass it to my Vue props like this in my blade file:
<my-component user-token="{{ Auth::user()->passportToken }}"></my-component>
Or maybe there is another solution for this problem? Please let me know.
Solved by adding X-CSRF-TOKEN on the header.
data() {
return {
dropzoneOptions: {
url: '/api/v1/productImageUploads',
thumbnailWidth: 150,
maxFilesize: 0.5,
thumbnailWidth: 200,
addRemoveLinks: true,
headers: {
"X-CSRF-TOKEN": document.head.querySelector("[name=csrf-token]").content
}
}
}
},
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');