Closed dschreij closed 6 years ago
CreateFreshApiToken don't create the laravel_token cookie #400 CreateFreshApiToken not always setting 'laravel_token' #564
laravel_token is literally like a implicit grant oauth route.
Laravel makes this easier for 1st party app use:
if you're wondering how to make step 5 to your auth apps... Socialite et al
after creating user and login them in use:
Auth::user()->createToken('')->accessToken;
give me a smiley if you find this useful :)
Laravel tokens last for a year... Every Login will make a token that expires in a year... If you're not a native or progressive app schedule a job that will clear tokens OR in your AuthServiceProvider add:
Passport::tokensExpireIn(Carbon::now()->addDays(15));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
Read more about this here Don't forget about that smiley :)
Thanks @awoyele, but you can't pass the laraven_token to the client explicitly by just adding
Auth::user()->createToken('')->accessToken;
to the JSON response. laravel_token
is a httpOnly cookie, so it's shielded completely from JS and is handled/passed by the browser. I could of course se the laravel_token explicitly in my function, but I thought that was the function of the CreateFreshApiToken middleware...
Hi @dschreij, did you manage to resolve this? I am facing the exact same issue and it is driving me nuts!
I did, partially. When I'm near my dev machine again I'll post my code here.
On Tue, Nov 21, 2017, 18:33 norahw notifications@github.com wrote:
Hi @dschreij https://github.com/dschreij, did you manage to resolve this? I am facing the exact same issue and it is driving me nuts!
— You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/laravel/passport/issues/564#issuecomment-346102254, or mute the thread https://github.com/notifications/unsubscribe-auth/AAqDlEIaa76JDw0ZQIViuRuvCcUw4xh7ks5s4wligaJpZM4QWKwY .
@dschreij I saw your post on Stackoverflow, is this your solution? https://stackoverflow.com/questions/47032871/refreshing-authentication-tokens-for-a-vue-js-spa-using-laravel-for-the-backend
It's still not perfect, but I ended up with a login function like:
login(){
// Copy the user object
const data = {...this.user};
// If remember is false, don't send the parameter to the server
if(data.remember === false){
delete data.remember;
}
this.authenticating = true;
this.authenticate(data)
.then( csrf_token => {
window.Laravel.csrfToken = csrf_token;
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = csrf_token;
// Perform a dummy GET request to the site root to obtain the larevel_token cookie
// which is used for authentication. Strangely enough this cookie is not set with the
// POST request to the login function.
axios.get('/')
.then( () => {
this.authenticating = false;
this.$router.replace(this.$route.query.redirect || '/');
})
.catch(e => this.showErrorMessage(e.message));
})
.catch( error => {
// Sometimes the backend is still logged in while the front-end registers as being logged out
// and weird behavior ensues. In this case, Laravel's guest middleware redirects to the main page
// and a HTML response is sent instead of JSON. If this happens, send a log out request to the backend
// logout and refresh if a type error was thrown
if( error instanceof TypeError){
console.error('Incorrect response type; possibly redirected');
this.logout()
.then(window.location.reload());
}
this.authenticating = false;
if(error.response && [422, 423].includes(error.response.status) ){
this.validationErrors = error.response.data.errors;
this.showErrorMessage(error.response.data.message);
}else{
this.showErrorMessage(error.message);
}
});
}
The authenticate
function (which is named login
in my vuex module) is as follows:
login({ dispatch }, data){
return new Promise( (resolve, reject) => {
axios.post(LOGIN, data)
.then( response => {
if(response.headers['content-type'] != 'application/json'){
return reject(new TypeError('Failed to login; Please try again.'));
}
const {csrf_token, ...user} = response.data;
// Set Vuex state
dispatch('setUser', user );
// Store the user data in local storage
Vue.ls.set('user', user );
return resolve(csrf_token);
})
.catch( error => reject(error) );
});
}
It still isn't as clean as I'd like it to be, but it seems to largely do the trick. Sometimes Laravel returns a 419 error after not having used the app for a long time (i.e. a day) and behind the screen tries to redirect back to HTML page. I try to catch this throwing a type error and handling it by:
if( error instanceof TypeError){
console.error('Incorrect response type; possibly redirected');
this.logout()
.then(window.location.reload());
}
I perform a page refresh to get everything fresh (e.g. the blade template page and other stuff that is sent with a synchronous request) when this happens.
I also tried directly logging in again after the 419, but that ended in a infinite loop of login attempts:
if( error instanceof TypeError){
console.error('Incorrect response type; possibly redirected');
this.logout()
.then(this.login());
}
So this sort of works for now, but definitely needs some polishing. For now I have lost too much time tackling this problem already, but let me know if you find some improvements.
Hey @dschreij. I saw you found a solution for your problem on Laracasts? Let me know if this is still a problem for you. It might be best to pick up the convo in #400 as this is sort of a duplicate of that issue. Thanks!
My solution worked, but showed some weird behavior on the rare occasion, and also felt a bit 'hacky'. Once I started to convert my app to a PWA, it stopped working though, so it's not an ideal solution in any sense. I've also been reading up on this topic a bit since, and I think using a JWT approach is a far better fit to this problem. I started working on other non-laravel projects though, so I haven't had time to tinker around with this myself yet.
@dschreij I think the problem is Resources specifically because they haven't been converted to responses yet when they pass the CreateFreshApiToken
middleware. I've asked for other examples in the other issue so hopefully those will provide more insight.
Alright, that clarifies it a bit. But does this also explain why it doesn't work with service workers/PWA?
And is the approach that causes this problem a good one in general? The consensus appears to be using JWT for authentication if you are creating a single page app. If that is the case, it may be not worth your time to work on this any longer (not my call, but just my two cents)
Yeah I believe so as well. I still want to check if we might be able to make the CreateFreshApiToken
work better with resource responses though. Just gonna wait what others have to say in the other issue.
Laravel 5.5 Passport 4.0.2
I'm struggling with the peculiar situation that the CreateFreshApiToken middleware does not always set a
laravel_token
cookie after a successful login, resulting in401 - Unauthorized
failures afterwards. On my local server everything works fine, but on the production server, having the exact same code, it doesn't. I'm building an SPA using Vue.js, so all the login requests are performed asynchronously using axios.My problem is similar to https://github.com/laravel/passport/issues/400, but I'm just using the normal Auth scaffolding functions for logging in, instead of oauth functions.
Just to give a complete picture, here are my relevant functions and settings:
The CreateFreshApiToken is at the end of my Kernel's web-middleware array:
The relevant methods of my login controller are as follows:
So the authenticated() method returns a Response instance with the user information attached in Json format. The routes are set up correctly:
My Vue code for authenticating the user is as follows:
I'm already aware that the
laravel_token
cookie is not set by the POST request to /login, so I perform another dummy GET request to /refreshTokens afterwards. I also do this after a timeout of 500ms to give the server some time to get its things in order (I thought that would matter, but it didn't).On both servers, the login function returns with a 200 response when entering the correct credentials
POST to /login
Local response headers
Remote response headers
After a succesful POST request to /login, both servers perform an extra GET request to /refreshTokens to get the
laravel_token
cookie. However, this cookie is not included in the response to the remote server:GET to /refreshTokens
Local response:
Remote response:
I'm baffled by this. On both servers (and even a third one exhibiting the same behavior as the remote server) the code base is exactly the same. What could cause this discrepant behavior? Some hunches:
laravel_token
with the response?