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
Related
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
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 am trying to make a post request via axios but getting this error: app.js:285 POST http://127.0.0.1:8000/concerts/1/orders 500 (Internal Server Error)
The order is being processed though (I see it coming is Stripe and database). Another problem is that redirect is not happening as window.location =/orders/${response.data.confirmation_number}; I just stay on the same page.
Any ideas what could go wrong here?
<template>
<div>
<div class="row middle-xs">
<div class="col col-xs-6">
{{ csrf_token}}
<div class="form-group m-xs-b-4">
<label class="form-label">
Price
</label>
<span class='form-control-static '>
${{ priceInDollars }}
</span>
</div>
</div>
<div class="col col-xs-6">
<div class="form-group m-xs-b-4">
<label class="form-label">
Qty
</label>
<input type="number" v-model="quantity" class="form-control">
</div>
</div>
</div>
<div class="text-right">
<button class="btn btn-primary btn-block"
#click="openStripe"
:class="{ 'btn-loading': processing }"
:disabled="processing"
>
Buy Tickets
</button>
</div>
</div>
This is script part:
<script>
export default {
props: [
'price',
'concertTitle',
'concertId',
],
data() {
return {
quantity: 1,
stripeHandler: null,
processing: false,
}
},
computed: {
description() {
if (this.quantity > 1) {
return `${this.quantity} tickets to ${this.concertTitle}`
}
return `One ticket to ${this.concertTitle}`
},
totalPrice() {
return this.quantity * this.price
},
priceInDollars() {
return (this.price / 100).toFixed(2)
},
totalPriceInDollars() {
return (this.totalPrice / 100).toFixed(2)
},
},
methods: {
initStripe() {
const handler = StripeCheckout.configure({
key: App.stripePublicKey
})
window.addEventListener('popstate', () => {
handler.close()
})
return handler
},
openStripe(callback) {
this.stripeHandler.open({
name: 'TicketBeast',
description: this.description,
currency: "usd",
allowRememberMe: false,
panelLabel: 'Pay {{amount}}',
amount: this.totalPrice,
// image: '/img/checkout-icon.png',
token: this.purchaseTickets,
})
},
purchaseTickets(token) {
this.processing = true
axios.post(`/concerts/${this.concertId}/orders`, {
email: token.email,
ticket_quantity: this.quantity,
payment_token: token.id,
}).then(response => {
window.location =`/orders/${response.data.confirmation_number}`;
console.log('Charge succeeded.')
}).catch(response => {
this.processing = false
})
}
},
created() {
this.stripeHandler = this.initStripe()
}
}
You have to go and look under the Network tab if you are using Chrome browser, you can see the failed request response
The issue turns out to be Mailer. In .env file, along with Mailtrap credentials you must provide sender email and they don't tell you that :( This also somehow prevented the redirect. In case that helps someone.
My goal is that laravel has an implemented Authorization Provider for us
https://laravel.com/docs/5.3/authentication
so I want to authenticate my users by using that by an API and set it back to my Vue Router
and authorize the users
How can i implement it?
Im always getting an error on authentication
im using axios as my HTTP provider
Here is the app.js
require('./bootstrap');
import VueRouter from 'vue-router';
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
Vue.use(VueRouter);
axios.defaults.baseURL = '/api';
axios.defaults.headers.common['X-CSRF-TOKEN'] = window.Laravel.csrfToken;
import LoginForm from './components/LoginForm.vue';
import RegisterForm from './components/RegisterForm.vue';
Vue.component('login-form',LoginForm)
Vue.component('register-form',RegisterForm)
// Directives
const routes = [
{ path: '/', component: require('./pages/Index.vue') },
{ path: '/admin/users', component: require('./pages/admin/Users.vue') },
{ path: '/user/:id', component: require('./pages/user/Dashboard.vue'),
children: [
// UserHome will be rendered inside User's <router-view>
// when /user/:id is matched
{ path: '', component: require('./pages/user/Index.vue')},
// UserPosts will be rendered inside User's <router-view>
// when /user/:id/posts is matched
{ path: 'settings', component: { template: '<div>Settings</div>' } },
]
},
{ path: '/manager/:id', component: require('./pages/user/Dashboard.vue'),
children: [
// UserHome will be rendered inside User's <router-view>
// when /user/:id is matched
{ path: '', component: require('./pages/user/Index.vue')},
// UserPosts will be rendered inside User's <router-view>
// when /user/:id/posts is matched
{ path: 'settings', component: require('./pages/user/Settings.vue') },
]
},
{ path: '/store/:id', component: require('./pages/user/Dashboard.vue'),
children: [
// UserHome will be rendered inside User's <router-view>
// when /user/:id is matched
{ path: '', component: require('./pages/user/Index.vue')},
// UserPosts will be rendered inside User's <router-view>
// when /user/:id/posts is matched
{ path: 'settings', component: { template: '<div>Settings</div>' } },
]
},
{ path: '/*', component: require('./pages/404.vue') },
];
const router = new VueRouter({
routes,
});
const app = new Vue({
el: '#app',
router,
template: `<div id="#app">
<router-view></router-view>
</div>`,
})
Here is the a Login form component
<template>
<form class="form" #submit.prevent='submitForm'>
<div class="form-group">
<input type="email" class="form-control" name="email" v-model="login.email" placeholder="Email">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" v-model="login.password" placeholder="Password">
</div>
<div class="form-group">
<button type="submit" class="btn btn-info btn-block"> Login </button>
</div>
</form>
</template>
<script>
export default {
data() {
return {
errors: [],
login: {
email: '',
password: '',
_token: window.Laravel.csrfToken
}
}
},
methods: {
submitForm() {
this.axios.post('/login',this.login)
.then(response => {
})
.catch(response => {
})
}
}
}
</script>
this is my Laravel API
in api.php
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group(['middleware' => 'auth'], function () {
Route::get('/auth',function() {
return Auth::user();
});
Route::resource('/users','UserController');
Route::resource('/stores','StoreController');
Route::resource('/items','ItemController');
Route::resource('/transactions','StoreController');
Route::resource('/managers','ManagerController');
Route::resource('/employees','EmployeeController');
Route::resource('/customers','CustomerController');
Route::resource('/tags','TagController');
});
Route::group(['middleware' => 'web'], function() {
Auth::routes();
});
So my BIG PROBLEM here is the authentication using vue i'm used to authentication in blade templates and laravel routes but not on vue