I'm using admin-on-rest behind a login screen. I'm writing a custom form component. When I do a fetch call to the same server AOR is using, I get a 401. How can I use the same auth data that AOR is using with its requests?
In your fetch call, include this second argument:
fetch(url, { credentials: 'include' })
otherwise the auth cookie isn't sent with the request.
Related
I am having a problem using cy.request to authorise. Looking at what is coming back after the request, body is empty.
I think this may be due to the setup I have so hopefully someone can tell me.
I have a frontend svelte app with a login screen. This calls the login rest api for a backend server and receives a token if the username / password is successful.
Now when I login manually via the browser - success.
When I use cypress to complete the screen - success.
The backend is on a server such as mint20 then calling cy.request on https://mint20:3000/users/login rest api directly - success.
The login screen is (for instance) localhost:5000 which is svelte running locally for dev. So if use cy.request localhost:5000/ , then it doesn't work and I get back an empty body rather than a list of data for the user.
So with the configuration above, is request just not going to work?
You may already know this, so apologies in advance if so.
Generally, to authorise the app without having to fill in the login screen you would use cy.request() against the backend API and receive the token in the response.
The token needs to be stored somewhere, either cookies, localstorage, or sessionstorage (or multiple places).
When you use cy.request() to obtain the token, you would need to store it in the same place as happens when the user logs in through the login form.
One more step that's useful is to add a cy.session() to cache the token, because by default Cypress tries to clear down everything between tests (to avoid cross-test contamination).
This is the general pattern.
beforeEach(() => {
cy.session('login', () => {
cy.request(...) // call backend to get the token
.then(response => {
cy.setCookie('token', response.body.token)
})
})
})
What happens is each test runs the beforeEach() first, but only the first call does the actual request.
Subsequent calls, the cy.session() reinstates the token to wherever you stored during the first call.
From reading the Laravel docs, it appears that when using Sanctum Vue will just use the cookie-based authentication.
I was trying to move an existing app (with login done using Livewire) to Vue, but calls direct to my first api endpoint were redirecting to the login page.
So I created a clean installation of Larvel, installed Breeze (with Inertia), then Sanctum, published the config etc.
But when I login and then visit my test endpoint (which just returns 200 Ok), it redirects to the login page (which, because I am logged in, redirects to the Breeze dashboard).
Do I need to do anything manually for my endpoint, guarded by auth:sanctum, to pass authentication?
Update
I've set axios.defaults.withCredentials, and it's returning 401 Unauthorized. My app.js:
axios.defaults.withCredentials = true;
axios.get('/api/test')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
First ensure that your application's CORS configuration is returning the Access-Control-Allow-Credentials header with a value of True. This may be accomplished by setting the supports_credentials option within your application's config/cors.php configuration file to true.
Then, if you are using axios, you should enable the withCredentials option on your axios instance:
axios.get('some api url', {withCredentials: true});
or globally:
axios.defaults.withCredentials = true;
If you are not using Axios to make HTTP requests from your frontend, you should perform the equivalent configuration on your own HTTP client.
Laravel Docs Reference.
If you are using Postman to test your api, here is a smart implementation of sanctum authentication requests. Basically you have to get the sanctum cookie first, and then send the cookie on XSRF-TOKEN header.
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.
Following asp-net core docs I successfully call my actions using ajax and add the antiforgery token as a header.
Now I want to make this validation "per request". The ajax call is not refreshing the form and the validation token it's the same for all subsequent requests. Which works ok but does not satisfy my security requirements.
I would like to attach to my response a new token, update the form and be sure that once I used a token that is not valid anymore.
To generate a new AntiForgeryToken do the following:
//inject IAntiforgery antiforgery into your class
var antiforgeryTokenSet = antiforgery.GetAndStoreTokens(httpContext);
Then also from your backend code return antiforgeryTokenSet.RequestToken to the client code so that it now has access to the new Request Token. Note the call to GetAndStoreTokens will return the new cookie portion of the antiforgery TokenSet as part of the response. So when the client code sends back the RequestToken in it's next request both parts of the TokenSet will be present and the TokenSet will validate.
You have to call below code on every request:-
var token = antiforgery.GetAndStoreTokens(httpContext);
context.Response.Cookies.Append(requestTokenCookieName, tokens.RequestToken, new CookieOptions()
{
HttpOnly = false,
Secure = true
});
It will create new token and will add it to the response. But calling above code on every request is not all that beneficial.
Please see the below link for more detail on Antiforgery:-
How to implement X-XSRF-TOKEN with angular2 app and net core app?
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');