Vue.js 2, Laravel 5.4, Passport, axios - API 'GET' Problems - laravel

I'm tying to get an API working with Vue.JS 2 and Laravel 5.4.
In my App.vue I have the following code:
export default {
name: 'app',
components: {
Hello
},
created () {
const postData = {
grant_type: 'password',
client_id: 2,
client_secret: 'somesecret',
username: 'my#mail.com',
password: 'password',
scope: ''
}
this.$http.post('http://localhost:8000/oauth/token', postData)
.then(response => {
console.log(response)
const header = {
'Accept': 'application/json',
'Authorization': 'Bearer ' + response.body.access_token
}
this.$http.get('http://localhost:8000/api/test', {headers: header})
.then(response => {
console.log(response)
})
})
}
}
While 'POST' is working without any problems, I just can't get 'GET' working.
My console.log shows the following:
Uncaught (in promise) TypeError: Cannot read property 'access_token'
of undefined at eval (eval at <anonymous> (app.js:810), <anonymous>:33:51)
(anonymous) # App.vue?86c0:29
Unfortunately I couldn't find any explanation why this might be happening.
Does anybody know if they changed 'access_token' to something else or why this is happening?
Hope someone knows :|
Thanks in advance!
Paul

This is off the top of my head.
But i'm sure if you are using axios as your XHR client your response body is under the 'data' parameter and not 'body'.
In summary your reference to access_token would be
'Authorization': 'Bearer ' + response.data.access_token

You can refer to the following link or you can compare with the below git repository
https://github.com/safiahmed4cs/Larave-Passport-with-Vue-Vue-Router-and-axios
which has Laravel App, Laravel Passport,
Axios, Vue Js, Vue Resource and Vue Router are imported as well,
Let me know if you required more info or if you face any issues.

Related

The first API call after login is unauthorized until page reload (Laravel Sanctum & VueJS)

I am trying to resolve this 401 issue for some time. After logging in and obtaining the token I am setting it as a header, but keep getting 401 exception on first load of the page. The error goes away after refresh. It seems that the token is not written to store or localStorage the first time around. Here's my code for login (I set the token to state.token in the mutation):
retrieveToken(context, credentials) {
return new Promise((resolve, reject) => {
axios.post('api/login', {
email: credentials.email,
password: credentials.password,
})
.then(response => {
const token = response.data.access_token
localStorage.setItem('access_token', token)
context.commit('RETRIEVE_TOKEN', token)
resolve(response)
console.log(response.data)
})
.catch(error => {
console.log(error)
reject(error)
})
})
},
And that's how I set it to header (setting it from localStorage doesn't solve the issue):
const authorizedApiClient = axios.create({
baseURL: process.env.VUE_APP_PRODUCTION_URL,
headers: {
Accept: 'application/json',
'Authorization': `Bearer ${store.getters.token}`
}
})
This behavior baffles me. Is there any theory or suggestions on how to debug?
I guess when the axios client is created the token is not yet retrieved from api. Try setting the header before each request using an interceptor:
const authorizedApiClient = axios.create({
baseURL: process.env.VUE_APP_PRODUCTION_URL,
headers: {
Accept: 'application/json'
}
})
authorizedApiClient.interceptors.request.use((config) => {
if (store.getters.token){ // or get it from localStorage
config.headers["Authorization"] = "Bearer " + store.getters.token
}
return config
})

How to resolve 401 unauthorized response in laravel using vue axios

I am creating a simple CRUD operations in laravel by using Vue js along with vue axios and vue router.
Routing is working fine as expected but the issue is when i have save the data by using axios the response i get is 401 unauthorized.
I have protected my route just like this in web.php
Route::get('/{any}', function () {
return view('posts');
})->where('any', '.*')->middleware(['auth']);
And in api.php i have routes like this.
Route::post('/quiz/create-quiz-category', 'QuizCategoryController#store')->middleware(['auth']);
I am currently using laravel default auth system.
Below is my vue component script code.
this.axios.post(uri, {post: this.post}, {
headers: {
'x-csrf-token': document.querySelectorAll('meta[name=csrf-token]')[0].getAttributeNode('content').value,
'Accept' : 'application/json'
}
}).then((response) => {
// this.$router.push({name: 'home'});
});
I appreciate if someone tell me that what wrong with code, and i am not currently using any passport or jwt.
Thanks.
api_token: this.user.api_token
axios.post("http://foo",
{headers: { 'Authorization' : 'Bearer '+ api_token}})
.then(response => {
//action
})
link: https://forum.vuejs.org/t/401-unauthorized-vuejs-laravel-and-passport/59770
or
postComment() {
axios.post('/api/posts/'+this.post.id+'/comment', {
api_token: this.user.api_token,
body: this.commentBox
})
but make sure that you have "user.api_token"

Vuejs Laravel Passport - what should I do if access token is expired?

I am using Vuejs SPA with Laravel API as backend. I successfully got the personal access token and store in localStorage and Vuex state like below.
token: localStorage.getItem('token') || '',
expiresAt: localStorage.getItem('expiresAt') || '',
I use the access token every time I send axios request to laravel api. Every thing works well. However, initially the token was set to 1 year expiration so when I develop I didn't care about token being expired and today suddenly I thought what is going to happen if token expired. So I set token expiry to 10 seconds in laravel AuthServiceProvier.php.
Passport::personalAccessTokensExpireIn(Carbon::now()->addSecond(10));
and then I logged in and after 10 seconds, every requests stopped working because the token was expired and got 401 unauthorised error.
In this case, how can I know if the token is expired? I would like to redirect the user to login page if token is expired when the user is using the website.
Be as user friendly as possible. Rather than waiting until the token expires, receiving a 401 error response, and then redirecting, set up a token verification check on the mounted hook of your main SPA instance and have it make a ajax call to e.g. /validatePersonalToken on the server, then do something like this in your routes or controller.
Route::get('/validatePersonalToken', function () {
return ['message' => 'is valid'];
})->middleware('auth:api');
This should return "error": "Unauthenticated" if the token is not valid. This way the user will be directed to authenticate before continuing to use the app and submitting data and then potentially losing work (like submitting a form) which is not very user friendly.
You could potentially do this on a component by component basis rather than the main instance by using a Vue Mixin. This would work better for very short lived tokens that might expire while the app is being used. Put the check in the mounted() hook of the mixin and then use that mixin in any component that makes api calls so that the check is run when that component is mounted. https://v2.vuejs.org/v2/guide/mixins.html
This is what I do. Axios will throw error if the response code is 4xx or 5xx, and then I add an if to check if response status is 401, then redirect to login page.
export default {
methods: {
loadData () {
axios
.request({
method: 'get',
url: 'https://mysite/api/route',
})
.then(response => {
// assign response.data to a variable
})
.catch(error => {
if (error.response.status === 401) {
this.$router.replace({name: 'login'})
}
})
}
}
}
But if you do it like this, you have to copy paste the catch on all axios call inside your programs.
The way I did it is to put the code above to a javascript files api.js, import the class to main.js, and assign it to Vue.prototype.$api
import api from './api'
Object.defineProperty(Vue.prototype, '$api', { value: api })
So that in my component, I just call the axios like this.
this.$api.GET(url, params)
.then(response => {
// do something
})
The error is handled on api.js.
This is my full api.js
import Vue from 'vue'
import axios from 'axios'
import router from '#/router'
let config = {
baseURL : process.env.VUE_APP_BASE_API,
timeout : 30000,
headers : {
Accept : 'application/json',
'Content-Type' : 'application/json',
},
}
const GET = (url, params) => REQUEST({ method: 'get', url, params })
const POST = (url, data) => REQUEST({ method: 'post', url, data })
const PUT = (url, data) => REQUEST({ method: 'put', url, data })
const PATCH = (url, data) => REQUEST({ method: 'patch', url, data })
const DELETE = url => REQUEST({ method: 'delete', url })
const REQUEST = conf => {
conf = { ...conf, ...config }
conf = setAccessTokenHeader(conf)
return new Promise((resolve, reject) => {
axios
.request(conf)
.then(response => {
resolve(response.data)
})
.catch(error => {
outputError(error)
reject(error)
})
})
}
function setAccessTokenHeader (config) {
const access_token = Vue.cookie.get('access_token')
if (access_token) {
config.headers.Authorization = 'Bearer ' + access_token
}
return config
}
/* https://github.com/axios/axios#handling-errors */
function outputError (error) {
if (error.response) {
/**
* The request was made and the server responded with a
* status code that falls out of the range of 2xx
*/
if (error.response.status === 401) {
router.replace({ name: 'login' })
return
}
else {
/* other response status such as 403, 404, 422, etc */
}
}
else if (error.request) {
/**
* The request was made but no response was received
* `error.request` is an instance of XMLHttpRequest in the browser
* and an instance of http.ClientRequest in node.js
*/
}
else {
/* Something happened in setting up the request that triggered an Error */
}
}
export default {
GET,
POST,
DELETE,
PUT,
PATCH,
REQUEST,
}
You could use an interceptor with axios. Catch the 401s and clear the local storage when you do then redirect user to appropriate page.

No response data from Laravel API using Axios

I am setting up authentication using Laravel (Laravel Framework version 5.8.4) as a REST API, but when I make a post request using Axios, I get back an empty string.
Here is my code in Laravel: "login" endpoint in my main controller:
class MainController extends Controller
{
public function login(Request $request){
$data = [
'message' => 'yo'
];
return Response::json($data, 200);
}
}
Here is my Axios code (from Vue.js method):
methods: {
submitRegistration: function() {
axios.post('http://envelope-api.test/api/auth/login', {
name: this.form.name,
email: this.form.email,
password: this.form.password,
password_confirmation: this.form.password_confirmation
})
.then(function (response) {
console.log("here's the response")
console.log(response);
})
.catch(function (error) {
console.log(error);
});
},
}
Here is the response from Postman (it works!)
{
"message": "yo"
}
Here is the response from my axios request in console (empty string, where's the data?) :
{data: "", status: 200, statusText: "OK", headers: {…}, config: {…}, …}
To get data from axios you should use response.data, not just response.
Edit: Try to respond with the helper.
response()->json($data);
I've got this figured out. Thanks for your help.
I had this chrome extension installed to allow CORS (Cross Origin Resource Sharing) so I could do API requests from localhost (apparently, not needed for Postman?).
I turned it off and installed it locally on Laravel using this post (answer from naabster)
After I installed this way, it worked regularly.

How to use rxjs ajax operator instead of axios in my react project?

I am new to rxjs and want to know how to handle this use case.
This is axios promise, how to convert it so that it uses rxjs's ajax operator
export const onLogin = ({ username, password }) =>
axios({
method: "post",
url: O_TOKEN_URL,
data: querystring.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
username,
password
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
This is my action,
export const onSubmit = payload => ({
type: FETCH_USER,
payload // payload contains username & password
});
This is my epic for now,
export const loginEpic = action$ =>
action$
.ofType(FETCH_USER)
// somehow import onLogin from above and resolve it
// then, dispatch FETCH_USER_FULFILLED
.do(
payload => console.log(payload.username, payload.password)
// i am able to console these username and password
)
.mapTo(() => null);
I want to resolve onLogin function somehow, when FETCH_USER is dispatched, using rxjs's ajax operator.
And, I want onLogin function, which returns promise/observable, to be set up in different file so that I can keep track of all the ajax requests
These are the packages,
"redux-observable": "^0.18.0",
"rxjs": "^5.5.10",
Could you also point me to a documentation that covers this and various use case for post, put ... requests? I couldn't find any.
The ajax config object is fairly similar to what you already have. I'm assuming the data property for the axios request is the request body.
import {ajax} from 'rxjs/observable/dom/ajax';
export const onLogin = ({ username, password }) =>
ajax({
method: "POST",
url: O_TOKEN_URL,
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "password",
username,
password
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
Your epic would look something like this:
export const loginEpic = action$ =>
action$
.ofType(FETCH_USER)
.mergeMap(action =>
onLogin(action.payload)
// map will be called when the request succeeded
// so we dispatch the success action here.
.map((ajaxResponse) => fetchUserFulfilled())
// catch will be called if the request failed
// so we dispatch the error action here.
// Note that you must return an observable from this function.
// For more advanced cases, you can also apply retry logic here.
.catch((ajaxError, source$) => Observable.of(fetchUserFailed()))
);
Where fetchUserFulfilled and fetchUserFailed are action creator functions.
There does not seem to be much documentation of the RxJS 5 ajax method yet. Here are the links to the official v5 docs for the AjaxRequest, AjaxResponse and AjaxError. The AjaxError object in particular has 0 information so far (at the time of this answer) so you will need to rely on the source code if you need to use this object for more than a trigger to tell the user that something went wrong. The ajax source code is here.

Resources