Old title: Cant reset cookie on logout with NuxtJS/VueJS and Springboot/JWT
I have some backend (springboot REST api) that handles signing out and logging in to an account using a cookie but when I sign out it doesn't set the cookie to nothing. It works fine with postman but not with nuxtjs/vuejs. it might just be some header issue not too sure. Also I am using JWT
logout code
<template>
<div style="max-width: 1200px; margin-left: auto; margin-right: auto">
<div class="block" style="width: auto">
<div class="description">
<button v-on:click="signout">Signout</button>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
methods: {
signout: function () {
const config = {
headers: {
Accept: '*/*',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
}
axios.post('http://localhost:8080/api/auth/signout', config).then(function (response) {
console.log("signedout")
}).catch(function (error) {
console.log(error)
})
},
},
}
</script>
login code
<template>
<div style="max-width: 1200px; margin-left: auto; margin-right: auto">
<div class="block" style="width: auto">
<h1 class="title">Login</h1>
<div class="description">
Username: <input id="username" class="input-box" type="text" placeholder="Username"/><br>
Password: <input id="password" class="input-box" type="password" placeholder="Password"/><br>
<button v-on:click="login">Login</button>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'add',
methods: {
login: function () {
const config = {
headers: {
Accept: '*/*',
'Content-Type': 'application/json',
credentials: "include"
},
}
const username = document.getElementById('username').value
const password = document.getElementById('password').value
const data = {
username: username,
password: password,
}
axios.post('http://localhost:8080/api/auth/signin', data, config).then(function (response) {
console.log(response)
}).catch(function (error) {
console.log(error)
})
},
},
}
</script>
logout cookie is the same as the login but with an empty value and the expire time to 0
Backend cookies
// Login cookie
ResponseCookie.from(jwtCookie, jwt).path("/api").maxAge(24 * 60 * 60).httpOnly(true).build();
// Logout cookie
ResponseCookie.from(jwtCookie, "").path("/api").maxAge(0).httpOnly(true).build();
It sometimes seems to work. I tried something and it didn't work, then changed the code and checked the cookie before trying the new thing but the logout seemed to of worked. I tried the old code that seemed to of worked but it didn't work.
EDIT:
I have tried with normal cookies being sent as well without any JWT code and I still have the same issue. I have tried withCredentials and it didnt work either
Related
I am writing a SPA application (laravel + vue). There was a question how to hide routes in vue-router before authorization of a user with a certain role.
Now there is such a router.js fight with routes.
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../components/Calendar.vue'
import PermissionList from '../components/PermissionList.vue'
import BoardsList from '../components/BoardsList.vue'
import UsersList from '../components/UsersList.vue'
import Login from '../components/Login.vue'
const routes = [{
path: '/',
name: 'Home',
component: Home
},
{
path: '/permission-list',
name: 'PermissionList',
component: PermissionList
},
{
path: '/boards-list',
name: 'BoardsList',
component: BoardsList
},
{
path: '/users-list',
name: 'UsersList',
component: UsersList
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/dsad',
name: 'asd',
component: Login
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
linkActiveClass: "active",
})
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (!token) {
if (to.name == 'Login') {
next();
} else {
next({
name: 'Login'
});
}
} else {
if (to.name == 'Login') {
next({
name: 'Home'
});
} else {
next();
}
}
})
export default router
User data including his role and jwt token come after authorization and are stored in localstorage.
<template>
<main class="form-signin text-center">
<div>
<h1 class="h3 mb-3 fw-normal">Form</h1>
<div class="form-floating">
<input
type="text"
class="form-control"
placeholder="Login"
v-model="login"
/>
<label for="floatingInput">Login</label>
</div>
<div class="form-floating my-2">
<input
type="password"
class="form-control"
placeholder="Pass"
v-model="password"
/>
<label for="floatingPassword">Pass</label>
</div>
<a class="w-100 btn btn-lg btn-primary" #click="logIn()">
Login
</a>
</div>
</main>
</template>
<script>
export default {
name:'Login',
data() {
return {
login:"",
password:"",
};
},
methods: {
logIn() {
this.HTTP.get('/sanctum/csrf-cookie').then(response => {
this.HTTP.post("/login",{
email:this.login,
password:this.password,
})
.then((response) => {
localStorage.setItem('token',response.config.headers['X-XSRF-TOKEN']);
localStorage.setItem('user',JSON.stringify(response.data.user));
this.$emit('loginUpdate');
this.$router.push('/');
})
.catch((error) => {
console.log(error);
});
});
},
},
};
</script>
<style>
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
</style>
if you go to the vue developer panel, all routes will be visible even before the user is authorized, how can I hide them so that unauthorized users do not see the site structure.
did you solve this problem?
Just use separated js file using your mixin laravel config. One login.js, another app.js and then use each of them in separated view-laravel
Im trying to make a post in a vue component with Laravel Api.
I got CSRF token in my welcome.blade.php:
<meta name="csrf-token" content="{{ csrf_token() }}">
Page does not refresh or add anything when i click on the button.
If i click on the button i get this in my console:
POST http://localhost/api/post 419 (unknown status)
PostList.vue
<template>
<div class="container py-4">
<form enctype="multipart/form-data" method="post" action="" #submit.prevent="addPost">
<input type="hidden" name="_token" value=""/>
<div class="modal-header">
<h4 class="modal-title">Create Post</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" placeholder="Title" v-model="post.title">
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" placeholder="Body" v-model="post.body"></textarea>
</div>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-default" data-dismiss="modal" value="Cancel">
<input type="submit" class="btn btn-primary" value="Add">
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
post: {
id: '',
title: '',
body: ''
}
};
},
created() {
this.getPosts();
},
methods: {
addPost(){
fetch('api/post', {
method: 'post',
body: JSON.stringify(this.post),
headers: {
'content-type': 'apllication/json'
}
})
.then(response => response.json())
.then(data => {
this.getPosts();
})
.catch(err => console.log(err));
}
}
};
</script>
PostController.php
public function store_vue(Request $request){
$post = new Posts();
$post->title = $request->get('title');
$post->body = $request->get('body');
$post->slug = Str::slug($post->title);
$post->author_id = $request->user()->id;
if ($post->save()) {
return new PostResource($post);
}
}
You are getting a 419 error because the request is missing the CSRF token.
You can add it to your form and see if it works for you
<form enctype="multipart/form-data" method="post" action="" #submit.prevent="addPost">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
OR
Add the header with the CSRF to your call
<script>
export default {
data() {
return {
post: {
id: '',
title: '',
body: ''
}
};
},
created() {
this.getPosts();
},
methods: {
addPost(){
fetch('api/post', {
method: 'post',
body: JSON.stringify(this.post),
headers: {
'content-type': 'apllication/json',
'X-CSRF-TOKEN': document.querySelector("meta[property='csrf-token']").getAttribute("content");
}
})
.then(response => response.json())
.then(data => {
this.getPosts();
})
.catch(err => console.log(err));
}
}
};
</script>
Laravel has a middleware called VerifyCsrfToken which is enabled by default. It makes sure all POST requests have a csrf token. This tokens make sure the request is sent from our app only and not from any 3rd party scraper or form submiting tool.
When controller does not get _token in request, it throws error.
Add this 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
on header section in like belwo
You can try this
<script>
export default {
data() {
return {
post: {
id: '',
title: '',
body: ''
}
};
},
created() {
this.getPosts();
},
methods: {
addPost(){
fetch('api/post', {
method: 'post',
body: JSON.stringify(this.post),
headers: {
'content-type': 'apllication/json',
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
})
.then(response => response.text())
.then(data => {
this.getPosts();
})
.catch(err => console.log(err));
}
}
};
</script>
I have installed and configured Laravel 7.3 Passport, then I made a fresh install of Nuxt.js and configure it as explained here (works perfect with Laravel 5.8.34). But when logging in, I get a CORS error message in the javascript console:
Access to XMLHttpRequest at 'http://my-laravel.test/oauth/token' from
origin 'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
Below is how I configured Nuxt.js:
pages/index.vue
<template>
<section class="container">
<div>
<strong>Home Page</strong>
<pre>Both guests and logged in users can access!</pre>
<nuxt-link to="/login">Login</nuxt-link>
</div>
</section>
</template>
pages/login.vue
<template>
<div class="container">
<div class="row justify-content-center mt-5">
<div class="col-md-5">
<form>
<div class="form-group">
<input
v-model="user.username"
class="form-control"
placeholder="Username"
/>
</div>
<div class="form-group">
<input
v-model="user.password"
type="password"
class="form-control"
placeholder="Password"
/>
</div>
<button
#click.prevent="passwordGrantLogin"
type="submit"
class="btn btn-primary btn-block"
>
Login with Password Grant
</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
middleware: 'guest',
data() {
return {
user: {
username: '',
password: ''
}
}
},
mounted() {},
methods: {
async passwordGrantLogin() {
await this.$auth.loginWith('password_grant', {
data: {
grant_type: 'password',
client_id: process.env.PASSPORT_PASSWORD_GRANT_ID,
client_secret: process.env.PASSPORT_PASSWORD_GRANT_SECRET,
scope: '',
username: this.user.username,
password: this.user.password
}
})
}
}
}
</script>
pages/profile.vue
<template>
<section class="container">
<div>
<strong>Strategy</strong>
<pre>{{ strategy }}</pre>
</div>
<div>
<strong>User</strong>
<pre>{{ $auth.user }}</pre>
</div>
<button #click="logout" class="btn btn-primary">
Logout
</button>
</section>
</template>
<script>
export default {
middleware: 'auth',
data() {
return {
strategy: this.$auth.$storage.getUniversal('strategy')
}
},
mounted() {},
methods: {
async logout() {
await this.$auth.logout()
}
}
}
</script>
nuxt.config.js (partly)
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://axios.nuxtjs.org/usage
'#nuxtjs/axios',
'#nuxtjs/proxy',
'#nuxtjs/pwa',
'#nuxtjs/auth',
'#nuxtjs/dotenv',
'bootstrap-vue/nuxt'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {
baseURL: process.env.LARAVEL_ENDPOINT,
// proxy: true
},
// Proxy module configuration
proxy: {
'/api': {
target: process.env.LARAVEL_ENDPOINT,
pathRewrite: {
'^/api': '/'
}
}
},
// Auth module configuration
auth: {
// redirect: {
// login: '/login',
// logout: '/',
// callback: '/login',
// home: '/profile'
// },
// strategies: {
// 'laravel.passport': {
// url: '/',
// client_id: process.env.PASSPORT_PASSWORD_GRANT_ID,
// client_secret: process.env.PASSPORT_PASSWORD_GRANT_SECRET
// }
// }
strategies: {
local: false,
password_grant: {
_scheme: 'local',
endpoints: {
login: {
url: '/oauth/token',
method: 'post',
propertyName: 'access_token'
},
logout: false,
user: {
url: 'api/auth/me',
method: 'get',
propertyName: 'user'
}
}
}
}
},
middleware/guest.js
export default function({ store, redirect }) {
if (store.state.auth.loggedIn) {
return redirect('/')
}
}
.env
LARAVEL_ENDPOINT='http://my-laravel.test/'
PASSPORT_PASSWORD_GRANT_ID=6
PASSPORT_PASSWORD_GRANT_SECRET=p9PMlcO***********GFeNY0v7xvemkP
As you can see in the commented code source, I also tried unsuccessfully with proxy as suggested here and with auth strategy laravel.passport as suggested here.
Go to cors.php and make sure you have oauth endpoint like api/* or laravel sanctum.
You have to clear config and cache before test again
I am trying to upload image using ajax in laravel 5.2.but still i am getting error 500 Internal Server Error in route.
when i am trying to upload image using ajax request the browser shown correct route path but still i am not getting reason why it still showing error to me.
HTML
<!-- CHANGE AVATAR TAB -->
<div class="tab-pane" id="tab_1_2">
<div class="uploadimagediv"></div>
{{ Form::open(array('url' => 'admin/avatar','method'=>'post','files'=>'true','id'=>'updateprofileimage')) }}
<div class="form-group">
<div class="fileinput fileinput-new" data-provides="fileinput">
<div class="fileinput-new thumbnail" style="width: 200px; height: 150px;">
<img src="http://www.placehold.it/200x150/EFEFEF/AAAAAA&text=no+image" alt=""/>
</div>
<div class="fileinput-preview fileinput-exists thumbnail" style="max-width: 200px; max-height: 150px;">
</div>
<div>
<span class="btn default btn-file">
<span class="fileinput-new">
Select image </span>
<span class="fileinput-exists">
Change </span>
<p class="text-danger" id="error_image"></p>
<input type="file" id="picture" name="picture"/>
{{--{{ Form::file('picture') }}--}}
</span>
<span class="error alert-danger">{{ $errors->first('picture') }}</span>
<a href="javascript:;" class="btn default fileinput-exists" data-dismiss="fileinput">
Remove </a>
</div>
</div>
<div class="clearfix margin-top-10">
</div>
</div>
<div class="margin-top-10">
{{Form::hidden('id', 2, ["id"=>"id"])}}
{{ Form::button('Upload',['id'=> 'updatepicture','class'=>'btn green-haze']) }}
{{--{{ Form::submit('Submit',['class' => 'btn green-haze','name'=>'changeImage']) }}--}}
<a href="javascript:;" class="btn default">
Cancel </a>
</div>
{{ Form::close() }}
</div>
<!-- END CHANGE AVATAR TAB -->
Route
Route::group(['prefix' => 'admin'], function ()
{
Route::controller('/','DashboardController');
});
Ajax
$(document).on('click', '#updatepicture', function($e)
{
$e.preventDefault();
// send an ajax request
$("#error_image").empty();
$.ajax(
{
url: 'avatar',
processData: false,
contentType: false,
type: "post",//use for update
data: new FormData($("#updateprofileimage")[0]),
success: function(data)
{
if(data.status)
{
$("#uploadimagediv").html('<div class="alert alert-success"><button type="button" class="close">×</button>'+data.message+'</div>');
window.setTimeout(function()
{
$(".alert").fadeTo(500, 0).slideUp(500, function()
{
$(this).remove();
});
}, 5000);
$('.alert .close').on("click", function(e)
{
$(this).parent().fadeTo(500, 0).slideUp(500);
});
//console.log(data);
//$("#updateprofileimage")[0].reset();
//window.location.href = "http://localhost/laravel/admin/profile";
}
else
{
errors = data.errors;
for(error in errors)
{
$("#error_"+error.title).html(error.message);
}
}
},
error: function(xhr)
{
if(xhr.status == 422)
{
errors = xhr.responseJSON;
for(error in errors)
{
$("#error_"+error).html(errors[error][0]);
}
}
}
});
});
Error is :"NetworkError: 500 Internal Server Error - http://localhost/laravel/admin/avatar"
please suggest me where i am getting wrong.
Controller is
public function postAvatar(ImageUploadRequest $request)
{
---
}
Add the below line inside <head>
<meta name="csrf-token" content="{{ csrf_token() }}">
And add the below lines before your ajax call in javascript function
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
And don't forget to give permission to your storage folder
sudo chmod -R 777 storage
In your ajax setup you have to provide x-csrf-token with the request. For your ajax request , also there is a problem with your url:
$(document).on('click', '#updatepicture', function($e)
{
$e.preventDefault();
// send an ajax request
$("#error_image").empty();
$.ajax(
{
url: "{{url('avatar')}}",
processData: false,
contentType: false,
type: "post",//use for update
data: new FormData($("#updateprofileimage")[0]),
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
},
success: function(data)
{
if(data.status)
{
$("#uploadimagediv").html('<div class="alert alert-success"><button type="button" class="close">×</button>'+data.message+'</div>');
window.setTimeout(function()
{
$(".alert").fadeTo(500, 0).slideUp(500, function()
{
$(this).remove();
});
}, 5000);
$('.alert .close').on("click", function(e)
{
$(this).parent().fadeTo(500, 0).slideUp(500);
});
//console.log(data);
//$("#updateprofileimage")[0].reset();
//window.location.href = "http://localhost/laravel/admin/profile";
}
else
{
errors = data.errors;
for(error in errors)
{
$("#error_"+error.title).html(error.message);
}
}
},
error: function(xhr)
{
if(xhr.status == 422)
{
errors = xhr.responseJSON;
for(error in errors)
{
$("#error_"+error).html(errors[error][0]);
}
}
}
});
});
Im trying to do the login in laravel via ajax so I want this function to return only json object in case of errors
the function returns json only when a input (email or password is empty) , but I insert wrong data in bought of theme the function returns a html page with errors include; but I want to return only these errors without html page (I assume that it it dose return response()->back()->withErrors('errors') )
my Js code :
$('#form-login').submit(function(event) {
event.stopPropagation(); // Stop stuff happening
event.preventDefault(); // Totally stop stuff happening
var data = {
email : $('#login_email').val(),
password : $('#login_password').val(),
}
$.ajax({
url: '/login',
type: 'post',
data: data,
success:function(data, textStatus, jqXHR) {
if (jqXHR.getResponseHeader('Content-Type').includes('json')) {
window.location.reload();
}
},
error:function(data) {
// console.log(data['email'])
// console.log(data.email)
if(data.responseJSON.email){
$('#Email-help-block').html(data.responseJSON.email[0])
}else{
$('#Email-help-block').html('')
}
if(data.responseJSON.password){
$('#Password-help-block').html(data.responseJSON.password[0])
}else{
$('#Password-help-block').html('')
}
},
})
});
the function login :
public function login(Request $request)
{
$this->validateLogin($request); // this is where it returns errors
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
return $this->handleUserWasAuthenticated($request, $throttles);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
if ($throttles && ! $lockedOut) {
$this->incrementLoginAttempts($request);
}
return $this->sendFailedLoginResponse($request);
}
My solution to this problem is:
<body>
<main class="container small">
<div class="middle">
<body>
<main class="container small">
<div class="middle">
<img
<br>
<div class="error valign-wrapper" style=" border: 2px solid red; border-radius: 7px;" hidden>
<h5 class="center-align"> Helaas zijn uw inloggegevens incorrect </h5>
</div>
<form method="POST" action="/api/v1/login" class="login">
{!! csrf_field() !!}
<input type="text" name="email" class="emailaddress" placeholder="Je e-mailadres..." style="background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII="); cursor: auto;" autocomplete="off">
<input type="password" name="password" class="password" placeholder="Je wachtwoord..." style="background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII="); cursor: auto;" autocomplete="off">
<div class="left">
Wachtwoord vergeten?
← Terug
</div>
<div class="right">
<input type="submit" class="button" value="Log in">
</div>
</form>
</div>
</main>
</body>
#include('footer')
#push('scripts')
<script>
$('form.login').submit(function(e) {
$form = $(this);
e.preventDefault();
$.post(window.location.origin + $form.attr('action'), $form.serialize())
.done(function(data) {
window.location.href = 'account';
})
.fail(function() {
$('.error').show();
});
});
</script>
#endpush
This is html with the error hidden (or you could show it with just ajax) on error. My javascript is pushed to a javascript stack at the bottom.