When using koa-passport I am always receiving a 404 or 401 error.
passport.authenticate( 'local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
} );
(This gave me many hours of struggle so wanted to post it here for others.)
When using koa-passport you must be sure to both return the result of the authentication, and call authenticate with the context.
return passport.authenticate( 'local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
} )( ctx );
Related
I'm trying to create an SPA for a laravel REST API protected by Sanctum
Right now, on my SPA the user can log in without a problem and the API sends back the token; but then the SPA doesn't fetch the user. I mean, it doesn't even try to fetch it; no error, no request, no nothing. Login and logout work flawlessly, but I'm unable to fetch the user.
Here's my config for auth module ( v5 ):
auth: {
strategies: {
laravelSanctum: {
provider: 'laravel/sanctum',
url: 'https://XXXXXXXXXXXXX/api',
token: {
property: 'access_token',
required: true,
type: 'Bearer'
},
endpoints: {
login: { url: '/login', method: 'post' },
logout: { url: '/logout', method: 'post' },
user: { url: '/user', method: 'get' }
},
user: {
autoFetch: true
}
}
},
My login function. If I understand correctly, just after the login the laravel/sanctum provider should fetch the user data:
async login() {
try {
let response = await this.$auth.loginWith('laravelSanctum', { data: this.form })
console.log('response -> ', response)
this.$router.push('/')
} catch (error) {
console.log('Error Response login -> ', error.response)
}
},
My logout function, just for completion ( it shouldn't have anything to do with the problem ):
async logout() {
try {
let response = await this.$auth.logout()
console.log('responselogout -> ', response)
this.$router.push('/login')
} catch (error) {
console.log('Error Response -> ', error.response)
}
},
Out of despair, I even created a function to try to fetch the user manually 😅 :
async fetch() {
try {
let responseuser = await this.$auth.fetchUser()
console.log('responseuser -> ', responseuser)
let loggedin = await this.$auth.loggedIn
console.log('loggedin -> ', loggedin)
} catch (erroruser) {
console.log('Error Response user -> ', erroruser.response)
}
},
On login, everything's fine but there is no request to the user endpoint:
Login request
When I try to fetch it manually, there is no request either:
Undefined response
And then on logout, everything works as it should:
Logout request
If it made the request to the /user endpoint ( either automatically after login, or manually when I use the fetch function ) and the API rejected it, or if there was an empty answer ... I would have something to work with ( I'm in control of the API too ), but with no request I just don't know where to start debugging the problem.
Any tip would be useful. Thanks in advance!
Just passing by to say I could finally solve the problem! Yay! 🥳
There was no request to /user because there was no XSRF-TOKEN cookie. And there was no XSRF-TOKEN because of browser security.
Long story short, this solution worked for me -> https://github.com/nuxt-community/auth-module/issues/1164#issuecomment-839199946
I hope this is helpful for anyone on the same situation :)
In my universal nuxt app, I have setted proxy at true and rewritte my url to avoid CORS issue.
But when I'm setting proxy to true, all my post requests are changed to get request. Don't understand why and how to configure it no to have this transformation.
Here is my nuxt.config.js :
/*
** Axios module configuration
*/
axios: {
proxy: true
},
proxy: {
'/apicore/': { target: 'http://blablabla.fr', pathRewrite: { '^/apicore/': '' }, changeOrigin: true }
}
My call:
async createJoueur({ state, dispatch, commit }, data) {
const URL = '/apicore/joueur'
await this.$axios
.post(
URL,
data, {
headers: {
'Content-Type': 'application/json'
}
}
)
.then((response) => {
console.log('JOUEUR LOGGED : ')
if (response.status === 200) {
} else {
console.log('Login failed / Not found')
}
}
)
.catch((error) => {
console.log('ERROR')
})
With this proxy set to true, my post-call becomes a get one.
Do I have forgotten something in my configuration?
Thanks for your help.
I was with the same problem! i solved it using changeOrigin: false.
I know that it must be the default value (Look at changeOrigin session ),
but it seems like in nuxtjs proxy implementation this value default is true (Look at Options session) .
I had the same issue and after some logging using the onProxyReq option I found out that the issue was the Cloudflare proxy, not the nuxt proxy. Cloudflare was forwarding HTTPS requests to HTTP and this forces POST requests to become GET requests as is common with 301/302 redirects.
As far as I know, it's not possible to configure Cloudflare to do 308 redirects, which would not alter the HTTP method/body.
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.
I am wondering if it is possible to do in fetch all the things you can do in traditional ajax?
Because I'm having a problem with a simple login authentication using express. I want to send a response like Login error if the username/password is incorrect, or to redirect the user to the homepage if both is correct, to the client without refreshing the page.
I understand that you can do this in AJAX, but is it possible to do it in fetch also?
I tried using express js and sending a response through a json, but I can't figure out how to handle the response without refreshing the page.
I tried doing it like this in the express server
//if valid
res.json({
isValid: true
})
//if invalid
res.json({
isValid: false
})
And in the client side, specifically in the login page, I have this javascript that handles the submitting of the information
fetch('https://localhost:3000/auth', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username,
password
})
})
.then(response => response.json())
.then(data => {
//I understand that in this part, you can handle the response, but the problem is, I don't know how.
}
})
.catch(console.log)
You are SO close! You've got the fetch, then you've parsed it with response.json, so the next thing is the .then(). In that, you have the JSON object being passed into a param you've named data. All you need to do is check if that has the isValid property!
fetch('https://localhost:3000/auth', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username,
password
})
})
.then(response => response.json())
.then(data => {
if(data.isValid){
// Do something with a valid user. Redirect or whatever.
} else {
// Here, isValid is not set, or is false.
// Send them packing!
}
}
})
.catch(err => console.error("I died: ", err) );
ALSO, take a look at the .catch() block -- in the event of an error, that catches an Error thrown by either the fetch(), or a then(). So you need to add a parameter for the error, and a function body to handle that. I've edited my code sample to demonstrate.
Won't actually run here, but it's formatted all pretty.
I send a request to a remote API. It takes a little time for API to proceed on its side.
After this little waiting time, i can see in network tab a HTTP 200. In the response, I got the proper intended information. Everything on the API side works fine.
BIT on the console, I can see I encountered a XMLHttpRequest Error.
Why, especially if I have a XMLHttpRequest Error, the POST is completed with 200? Shouldn't it be "blocked" by Angular2?
The unintended result is: my file is correctly uploaded and handled by the API, but in Angular2, it triggers the ERROR part of my call.
If I use https://resttesttest.com/ for example, it seems to encounter the same error but it doesn't finalize the POST:
Oh no! Javascript returned an
HTTP 0 error. One common reason this might happen is that you
requested a cross-domain resource from a server that did not include
the appropriate CORS headers in the response.
Angular 2 Code for this call
this.http
.post(this.documentUploadAPIUrl, formData, options)
.subscribe(
res => {
this.responseData = res.json();
console.log(this.responseData);
console.log('Uploaded a blob or file!');
},
error => {
console.log('Upload failed! Error:', error);
}
);
try to set withCredential attribute of xmlHttpRequest to true, this will send credentials managed by the browser, in angular 2 you can do like this
import { RequestOptions } from '#angular/http';
this.http
.post(this.documentUploadAPIUrl, formData, this.post_options)
.subscribe(
res => {
this.responseData = res.json();
console.log(this.responseData);
console.log('Uploaded a blob or file!');
},
error => {
console.log('Upload failed! Error:', error);
}
);
post_options() {
return new RequestOptions({ method: 'post', withCredentials : true });
}