Laravel Spark 6.0 Ajax request unauthenticated - laravel-5.6

I am building an app from a vanilla Spark 6.0 installation. I can login, access the Kiosk and click around.
I have created a new Card section with a form and am using the SparkForm object as directed by the documentation; however every single request returns unauthenticated and thus I have to re-login.
I cannot get the ajax request to authenticate. I have created a seperate adminApi to handle admin ajax request which is protected by auth/dev/web middleware.
Any ideas/pointer much appreitated.
Relevent Code:
RouteServiceProvider:
public function map(Router $router)
{
$this->mapWebRoutes($router);
$this->mapApiRoutes($router);
$this->mapAdminApiRoutes($router);
//
}
...
protected function mapAdminApiRoutes(Router $router)
{
$router->group([
'namespace' => $this->namespace,
'middleware' => ['dev', 'auth', 'web'],
'prefix' => 'admin/api',
], function ($router) {
require base_path('routes/adminApi.php');
});
}
routes/adminApi.php
Route::resource('/insurers', 'Admin\InsurersController');
vue component - insurers.js
var base = require('kiosk/users');
Vue.component('spark-kiosk-insurers', {
mixins: [base],
data: function() {
return {
showingInsurerProfile: false,
form: new SparkForm({
name: '',
email:'',
logo:''
})
}
},
props: {
insurer: {}
},
methods: {
search: function() {
},
create: function() {
Spark.post('/admin/api/insurers', this.form)
.then(response => {
console.log(response);
});
},
}
});

Add the CSRF Token to the form like
<meta name="csrf-token" content="{{ csrf_token() }}">
Then add the following to your request:
headers: {
'X-CSRF-TOKEN': 'Token Here' // from meta
}

Related

Why put request is not working using axios to make a laravel api request?

Here is a request from my Vue component:
submit() {
axios
.put(`/api/posts/${this.slug}`, this.fields, {
headers: { "content-type": "multipart/form-data" },
})
.then((res) => {
// some logic
})
.catch((error) => {
// some logic
});
}
api.php
Route::group(['prefix' => 'posts', 'middleware' => 'auth:sanctum'], function () {
Route::put('/{post:slug}', [PostController::class, 'update']);
});
put method doesn't work. I get the following error xhr.js:220 PUT http://127.0.0.1:8000/api/posts/test-title-updated-33 422 (Unprocessable Content) but when I replace put with post everything works as expected. I don't understand why put is not working.
Because HTTP PUT is not recognized by HTML standard.
You need to add POST type of method only but for update you can add a small flag with POST request for a PUT/PATCH type of operation.
axios.post(`/api/posts/${this.slug}`, { // <== use axios.post
data: this.fields,
_method: 'patch' // <== add this field
})

login from nuxtjs to laravel/passport server

I made api's using laravel, I am using laravel passport for authentication I want to send login request from nuxtjs to laravel backend but I'm getting 401 (Unauthorized) with any credential from the database.
nuxt.config.js
auth: {
strategies: {
local: {
endpoints: {
login: { url: '/login', method: 'post', propertyName: 'token' },
user: { url: '/user', method: 'get' }
},
clientId: '1',
clientSecret: 'clientsecret'
}
}
},
modules: [
'#nuxtjs/axios',
// https://go.nuxtjs.dev/bootstrap
'bootstrap-vue/nuxt',
'#nuxtjs/auth',
],
axios: {
baseURL: "http://prostudent.test/api"
},
login.vue
<template>
Login
</template>
<script>
export default {
auth: false,
email: "vuejs#gmail.com",
password:"123456",
methods: {
async login() {
try {
const data = { email: this.email, password: this.password }
await this.$auth.loginWith('local', { data: data })
} catch (e) {
}
}
}
}
</script>
login function in AuthController.php
public function login(Request $request){
try {
if (Auth::attempt($request->only('email', 'password'))) {
/** #var User $user */
$user = Auth::user();
$token = $user->createToken('app')->accessToken;
return response([
'message' => 'success',
'token' => $token,
'user' => $user
]);
}
}catch (\Exception $exception){
return response([
'message' =>$exception->getMessage()
], 400);
}
return response([
'message'=> 'invalid username/password'
], 401);
ps: I tried to do it like its mentioned here which says in nuxt.js set up "You will need to copy the .env.example file to .env and populate it with values from laravel." but I dont have .env file in my nuxtjs project should I create it manually? otherwise I noticed that he didn't do anything extra on the serverside to connect it with nuxtjs, all the work is done on nuxtjs side.
please debug to see if it is the last return line that is being returned or something else.
if it's not that line being returned, it might be the issue from Auth/Nuxt property and it's failing to fetch user data without token property.
as far as I know, in newer version of Nuxt/Auth, you should define properties seperately and not in endpoint. try codes below and see if it helps.
strategies: {
local: {
token: {
property: 'token',
global: true,
},
user: {
property: 'user',
},
endpoints: {
login: { url: '/login', method: 'post'},
user: { url: '/user', method: 'get' }
}
}
}

419 Error when attempting to post to my controller

I've been trying to submit a post request with axios to my projects controller and I keep getting an error 419(unknown status). Even though I'm passing the CSRF through headers to the controller. When I go into my network tab after posting it says:
X-CSRF-TOKEN: undefined
X-Requested-With: XMLHttpRequest
However, when I console.log(window.csrf_token) it returns a token.
This is included in my layout.blade.php
<script type="text/javascript">
window.csrf_token = "{{ csrf_token() }}"
</script>
I define the headers in my app.js for vue:
const axios = require('axios');
axios.defaults.headers.common = {
'X-CSRF-TOKEN': window.csrf_token,
'X-Requested-With': 'XMLHttpRequest',
};
and in my projects.vue here is my axios post request:
Swal.queue([{
title: 'Add a New Project?',
input: 'text',
inputAttributes: {
autocapitalize: 'on'
},
showCancelButton: true,
confirmButtonText: 'Create Project',
showLoaderOnConfirm: true,
preConfirm: (result) => {
return new Promise(function(resolve, reject) {
if (result) {
console.log(result)
axios.post('/api/projects', {title:result})
.then(function(response){
Swal.insertQueueStep({
type: 'success',
title: 'Your project has been created!'
})
resolve();
})
.catch(function(error){
Swal.insertQueueStep({
type: 'error',
title: 'Something went wrong.'
})
console.log(error);
reject();
})
}
});
}
}])
aswell as the store method in ProjectsController.php
public function store()
{
$validated = request()->validate([
'title' => 'required',
]);
Project::create($validated);
return response()->json($validated);
}
Most probably you are setting the CSRF token in your layout file after the usage hence the reason of getting undefined.
Try using the default way, which is by putting a meta tag in your head of the main template like this:
<meta name="csrf-token" content="{{ csrf_token() }}">
Then to use it you may open the given bootstrap.js file where this code is already set:
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
Or if this does not exist, put it in your app.js script or whichever you use on every page.

Laravel passport & Vue login

I've made login function in laravel with passport api and I'm getting status 200, the issue is that i don't know how to login the user and redirect to homepage after this successful request (I'm using vuejs component).
Code
controller
public function login(Request $request)
{
$credentials = [
'email' => $request->email,
'password' => $request->password
];
if (Auth::attempt($credentials)) {
// $token = auth()->user()->createToken('AppName')->accessToken;
// $success['token'] = $token;
// $success['name'] = auth()->user()->name;
// $success['id'] = auth()->user()->id;
$user = Auth::user();
$success['token'] = $user->createToken('AppName')->accessToken;
$success['user'] = $user;
return response()->json(['success'=>$success], 200);
} else {
return response()->json(['error' => 'UnAuthorised'], 401);
}
}
component script
<script>
import {login} from '../../helpers/Auth';
export default {
name: "login",
data() {
return {
form: {
email: '',
password: ''
},
error: null
};
},
methods: {
authenticate() {
this.$store.dispatch('login');
axios.post('/api/auth/login', this.form)
.then((response) => {
setAuthorization(response.data.access_token);
res(response.data);
})
.catch((err) =>{
rej("Wrong email or password");
})
}
},
computed: {
authError() {
return this.$store.getters.authError;
}
}
}
</script>
Auth.js (imported in script above)
import { setAuthorization } from "./general";
export function login(credentials) {
return new Promise((res, rej) => {
axios.post('/api/auth/login', credentials)
.then((response) => {
setAuthorization(response.data.access_token);
res(response.data);
})
.catch((err) =>{
rej("Wrong email or password");
})
})
}
general.js (imported in script above)
export function setAuthorization(token) {
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`
}
Question
How can I login my user after successful request?
...........................................................................................................................................................
Say that you have defined a vuex auth module with a login action
that accepts a credentials object.
If success it receives a response that contains the access_token our API granted to the user.
We store/commit the token and also update axios settings to use that token on each of the following requests we make.
import axios from 'axios';
const state = {
accessToken: null,
};
const mutations = {
setAccessToken: (state, value) => {
state.accessToken = value;
},
};
const getters = {
isAuthenticated: (state) => {
return state.accessToken !== null;
},
};
const actions = {
/**
* Login a user
*
* #param context {Object}
* #param credentials {Object} User credentials
* #param credentials.email {string} User email
* #param credentials.password {string} User password
*/
login(context, credentials) {
return axios.post('/api/login', credentials)
.then((response) => {
// retrieve access token
const { access_token: accessToken } = response.data;
// commit it
context.commit('setAccessToken', accessToken);
return Promise.resolve();
})
.catch((error) => Promise.reject(error.response));
},
};
Before every request to our API we need to send the token we received and store on our auth module therefore we define a global axios request interceptor on our main.js
import store from '#/store';
...
axios.interceptors.request.use(
(requestConfig) => {
if (store.getters['auth/isAuthenticated']) {
requestConfig.headers.Authorization = `Bearer ${store.state.auth.accessToken}`;
}
return requestConfig;
},
(requestError) => Promise.reject(requestError),
);
...
We then define our login component which on a success login redirects us to the dashboard page
<template>
<div>
...
<form #submit.prevent="submit">
...
<button>Submit</button>
</form>
...
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
data() {
return {
credentials: {
email: '',
password: '',
},
};
},
methods: {
...mapActions('auth', [
'login',
]),
submit() {
this.login({ ...this.credentials })
.then(() => {
this.$router.replace('/dashboard');
})
.catch((errors) => {
// Handle Errors
});
},
},
}
Finally we define our routes and their guards
import store from '#/store'
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'landing',
component: Landing,
// User MUST NOT BE authenticated
beforeEnter: (to, from, next) => {
const isAuthenticated = store.getters['auth/isAuthenticated'];
if (isAuthenticated) {
return next({
name: 'dashboard',
});
}
return next();
},
},
{
path: '/login',
name: 'login',
component: Login,
// User MUST NOT BE authenticated
beforeEnter: (to, from, next) => {
const isAuthenticated = store.getters['auth/isAuthenticated'];
if (isAuthenticated) {
return next({
name: 'dashboard',
});
}
return next();
},
},
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard,
// User MUST BE authenticated
beforeEnter: (to, from, next) => {
const isAuthenticated = store.getters['auth/isAuthenticated'];
if (!isAuthenticated) {
return next({
name: 'login',
});
}
return next();
},
},
{ path: '*', redirect: '/' },
],
});
Now only users with an access token can have access to dashboard route and any child routes you may define in the future. (No further check is necessary as any child of this route will execute that guard).
If someone attempts to access dashboard route without an access token will be redirected to login page
If someone attempts to access landing or login page with an access token will be redirected back to dashboard.
Now what happens if on any of our future API requests our token is invalid?
we add a global axios response interceptor on our main.js and whenever we receive a 401 unathorized response we clear our current token and redirect to login page
import store from '#/store';
...
axios.interceptors.response.use(
response => response,
(error) => {
if (error.response.status === 401) {
// Clear token and redirect
store.commit('auth/setAccessToken', null);
window.location.replace(`${window.location.origin}/login`);
}
return Promise.reject(error);
},
);
...
Final Words
I believe that all of the above steps are enough to help you have a better understanding on how to use the access token. Of course you should also store the token on browsers localStorage so that the user doesnot have to login whenever experiences a page refresh and token gets clear from memory. And at least refactor router beforeEnter functions by moving them to a separate file to avoid repetition.

Laravel Validation with vue js

i want to post ajax request using vue-resource this.$http.post request. it worked perfectly fine if i passed all validation rules but i want to get some validations if it fails. so far i keep getting 500 error if i don't fill out some input fields. it's hard for me to debug the error because it didn't appeared on the network tab.
here's what i've done so far
//my modal component
<script>
export default {
props: ['show'],
data() {
return {
input: {
id: '',
name: '',
address: '',
email: ''
},
errorInputs: {}
}
},
methods: {
createStudent() {
this.$http.post('/students', this.$data.input)
.then((response) => {
alert('added new row!)
}, (response) => {
console.log(response.data);
});
}
}
}
</script>
// my controller
public function store(Request $request) {
$validator = $this->validate($request,[
'id' => 'required',
'name' => 'required|unique:students',
'email' => 'required|unique:students|email',
'address' => 'required',
]);
if($validator->passes()){
Student::create($request->all());
return response()->json([], 201);
}
$errors = json_decode($validator->errors());
return response()->json([
'success' => false,
'message' => $errors
],422);
}
any helps and references would be appreciated. i am using laravel 5.3 and vue js 2
$this->validate() returns 422 error response alongside your validation errors, so you should get those errors in then() second callback (like you do now). Your vue component body should be like this:
{
data() {
// ...
},
createStudent() {
this.$http
.post('/students', this.input)
.then(this.handleSuccess, this.handleError)
},
handleSuccess(res) {
alert('student created')
},
handleError(res) {
if (res.status === 422) {
this.errorInputs = res.body
} else {
alert('Unkown error!')
}
}
}
Remember to add v-model="input.fieldName" properties to your inputs.
Remember to include your session token along with your post, unless of course you are disabling csrf tokens for that route.
Since Laravel 5.1 you can disable this in your verifytoken middleware
<?php namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as ...
class VerifyCsrfToken extends ... {
protected $except = [
'payment/*',
];
}

Resources