I am building a react (ES6) application with Webpack in SailsJS.
The problem I came across is that session is not properly handled or stored in Local Memory Store IF the action is triggered from the frontend
Setup:
// TestAPIController:
module.exports = {
create: function(req, res){
// TODO: Add user authentication piece
var user = {name: 'test user'};
req.session.user = user.name;
req.session.authenticated = true;
console.log('session:', req.session);
// req.session.save();
res.json(req.session);
},
echo: function(req, res){
console.log('session:', req.session);
return res.json({message:'got it'});
}
};
Policy:
TestAPIController: {
echo : 'sessionAuth'
}
Route:
'GET /TestAPI/create' : {
controller: 'TestAPIController',
action:'create'
},
'GET /TestAPI/echo' : {
controller: 'TestAPIController',
action:'echo'
},
Front End
onClickCreate(e){
fetch('/TestAPI/create').then( res => res.json());
}
onClickEcho(e){
fetch('/TestAPI/echo').then(res => res.json());
}
MarkUp
<li><a onClick={_this.onClickCreate}>Create Test</a></li>
<li><a onClick={_this.onClickEcho}>Echo Test</a></li>
Senario A
Click on Create Console Log: [CORRECT!]
Requested :: GET /TestAPI/create
session: Session {
cookie:
{ path: '/',
_expires: 2017-01-18T20:21:01.069Z,
originalMaxAge: 86400000,
httpOnly: true },
user: 'test user',
authenticated: true }
And Then click on Echo, Console Log: [WRONG!]
Requested :: GET /TestAPI/echo
verbose: Sending 403 ("Forbidden") response:
You are not permitted to perform this action.
Senario B
Now go to http://localhost:3000/TestAPI/create in the chrome browser's URL
Browser prints the return from the call. and Log:
Requested :: GET /TestAPI/create
session: Session {
cookie:
{ path: '/',
_expires: 2017-01-18T20:21:13.645Z,
originalMaxAge: 86400000,
httpOnly: true },
user: 'test user',
authenticated: true }
And then enter http://localhost:3000/TestAPI/echo in url address
Browser prints the return from the call [CORRECT!]
Requested :: GET /TestAPI/echo
session: Session {
cookie:
{ path: '/',
_expires: 2017-01-18T20:25:24.019Z,
originalMaxAge: 86400000,
httpOnly: true },
user: 'test user',
authenticated: true }
Maybe there is some fundamental principle I'm not following?
Or maybe there is a better way to control sessions?
Any help would be appreciated.
You have the solution here guys: https://github.com/balderdashy/sails/issues/3965
Related
I have created a custom route in Strapi v4 called "user-screens". Locally I hit it with my FE code and it returns some data as expected. However when I deploy it to Heroku and attempt to access the endpoint with code also deployed to Heroku it returns a 404. I've tailed the Heroku logs and can see that the endpoint is hit on the server side, but the logs don't give anymore info other than it returned a 404.
I am doing other non custom route api calls and these all work fine on Heroku. I am able to auth, save the token, and hit the api with the JWT token and all other endpoints return data. This is only happening on my custom route when deployed to Heroku. I've set up cors with the appropriate origins, and I am wondering if I need to add something to my policies and middlewares in the custom route. I have verified the permissions and verified the route is accessible to authenticated users in the Strapi admin.
Here is my route:
module.exports = {
routes: [
{
method: "GET",
path: "/user-screens",
handler: "user-screens.getUserScreens",
config: {
policies: [],
middlewares: [],
},
},
],
};
And my controller:
"use strict";
/**
* A set of functions called "actions" for `user-screens`
*/
module.exports = {
getUserScreens: async (ctx) => {
const user = ctx.state.user;
if (!user) {
return ctx.badRequest(null, [
{ messages: [{ id: "No authorization header was found" }] },
]);
}
strapi.entityService
.findMany("api::screen.screen", {
owner: user.id,
populate: ["image"],
})
.then((result) => {
ctx.send(result);
});
},
};
For anyone facing this, the answer was to change how I returned the ctx response from a 'send' to a 'return' from the controller method. I am not sure why this works locally and not on Heroku, but this fixes it:
New controller code:
module.exports = {
getUserScreens: async (ctx) => {
const user = ctx.state.user;
if (!user) {
return ctx.badRequest(null, [
{ messages: [{ id: "No authorization header was found" }] },
]);
}
return strapi.entityService
.findMany("api::screen.screen", {
owner: user.id,
populate: ["image"],
})
.then((result) => {
return result;
})
.catch((error) => {
return error;
});
},
};
I am migrating a project from Webpack to Vite and have run into an issue with proxying requests to one of the endpoints in the MVC.Net backend.
Due to circumstances of the existing project, I need to handle certain calls manually - such as on initial page load of login page, check whether user is already authenticated and redirect to the main page.
I am trying to figure out how to use server.proxy.configure to handle these requests. I am managing fine with the GET requests, but I cannot seem to receive the POST request's body data.
Here is what I have at the moment:
server: {
proxy: {
"/api": {
target: "https://my.local.environment/",
changeOrigin: true,
configure: (proxy: HttpProxy.Server, options: ProxyOptions) => {
proxy.on("proxyReq", (proxyReq, req, res, options) => {
if (req.method === "GET") {
//handle simple get requests. no problems here
//...
} else {
const buffer = [];
console.log("received post request");
proxyReq.on("data", (chunk) => {
console.log("received chunk");
buffer.push(chunk);
});
proxyReq.on("end", () => {
console.log("post request completed");
const body = Buffer.concat(buffer).toString();
const forwardReq = http.request(
{
host: "https://my.local.environment",
port: 443,
method: "POST",
path: req.url,
headers: {
"Content-Type": "application/json",
"Content-Length": data.length,
},
},
(result) => {
result.on("data", (d) => {
res.write(d);
res.end();
});
}
);
forwardReq.on("error", (error) => {
console.log(error);
});
forwardReq.write(data);
forwardReq.end();
});
}
});
},
secure: false,
},
}
}
The problem is that neither proxyReq.on("data", (chunk) => { nor proxyReq.on("end", (chunk) => { ever actually trigger.
Additionally, req.body is undefined.
I have absolutely no idea where I am supposed to be getting the POST request's body.
I ended up finding a different question about the bypass option and this gave me the solution I was looking for. Ended up only handling the specific GET requests that I need to handle locally instead of forwarding to my deployed environment, and everything else gets handled automatically by vite.
"/api": {
target: "https://my.local.environment/",
changeOrigin: true,
agent: new https.Agent({
keepAlive: true,
}),
bypass(req, res, proxyOptions) {
if (req.method === "GET") {
//... here I get what I need and write to the res object
// and of course call res.end()
}
//all other calls are handled automatically
},
secure: false,
},
My goal is that i want to access $auth.user.roles from plugin and middleware to be able to not let this role reach the other role page.
what is expected is that when console.log($auth.user) it gives me the user data (id,...) and when a console.log($auth.loggedIn)it gives me true.
My problem is that i can't access $auth.user from plugin and middleware to chieve that which $auth.user = null and $auth.loggedIn = false while im logged in.
here is my nuxt.config.js:
axios: {
baseURL: env.parsed.API_URL || 'http://localhost:3000/api',
debug:true},
auth: {
strategies: {
local: {
endpoints: {
login: {
url: '/auth/signin',
method: 'post',
propertyName: 'data.token'
},
user: {
url: '/auth/me',
method: 'get',
propertyName: 'data'
},
logout: {
url: '/auth/signout',
method: 'post'
},
tokenRequired: true,
tokenType: 'bearer',
globalToken: true,
autoFetchUser: true
},
},
},
redirect:false,
plugins: [ '~/plugins/roles.js' ]
},
here is my plugins/roles.js :
export default function ({app}) {
const username = app.$auth.user
if (!app.$auth.loggedIn) {
return console.log(username ,'roles plugin ', app.$auth.loggedIn)
}}
here is the res: null roles plugin false
the same result using this code:
export default function ({$auth}) {
const username = $auth.user
if (!app.$auth.loggedIn) {
return console.log(username ,'roles plugin', $auth.loggedIn)
}}
Ps:when i use $auth.user in my vue pages it gives me the whole user data (thats wonderfull)
I searched about this problem so i found common answers like :
*Change the user propertyName to false.
*reinstall node_modules.
but same result
Thank you every one <3
I have problem regarding on my authentication after I login there is prompt localhost:8000 login or Sign in, this is the first time I encounter this, I don't know what this called, i don't find any reference for this. currently I am using laravel for the backend and vue js for front end. so this project created by old developer. so now I need to revise some module on there project, however i Login on the system in shows this prompt. note that this project is currently working and there is no error and prompt login when I login.
Here is prompt sign shown after I login.
Configuration:
Laravel Passport
Vuex For State Management
Vue Js
METHOD:
methods:{
authenticate(){
this.login_alert = false
this.$validator.validateAll().then((result)=>{
if(result){
const self = this;
const authUser = {}
try{
const data = {
username: this.email,
password: this.password,
remember: this.remember_me,
client_id: '2',
client_secret: 'just secret only',
grant_type : 'password',
scope : ''
}
this.$store.dispatch('AUTH_REQUEST',data)
.then(response=>{
authUser.access_token = response.access_token
authUser.refresh_token = response.refresh_token
authUser.expires_in = response.expires_in
window.localStorage.setItem('project_token',JSON.stringify(authUser))
/*LOGIN*/
this.login_alert = false
this.loading = false
window.location.reload()
})
.catch(error=>{
this.login_alert = true
window.localStorage.removeItem('project_token')
this.loading = false
})
}catch(err){
console.log(err);
}
}
})
}
},
AUTH REQUEST:
AUTH_REQUEST:({commit,dispatch},obj)=>{
return new Promise((resolve,reject) => {
axios({
url: '/oauth/token',
data: obj,
method:'post',
config:'JSON'
})
.then(response=>{
if(response.status == 200){
resolve(response.data);
}
})
.catch(error=>{
reject(error);
localStorage.removeItem('project_token');
commit('AUTH_ERROR',error);
})
})
},
Hope someone can help me to solve this problem.
Thank you.
I'm implementing graphql login mutation to authenticate user login credential. Mutation verifies the password with bcrypt then sends a cookie to the client, which will render user profile based on whether the cookie is a buyer or owner user).
GraphQL Login Mutation Code:
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
loginUser: {
type: UserType,
args: {
email: { type: GraphQLString },
password: { type: GraphQLString }
},
resolve: function (parent, args, { req, res }) {
User.findOne({ email: args.email }, (err, user) => {
if (user) {
bcrypt.compare(args.password, user.password).then(isMatch => {
if (isMatch) {
if (!user.owner) {
res.cookie('cookie', "buyer", { maxAge: 900000, httpOnly: false, path: '/' });
} else {
res.cookie('cookie', "owner", { maxAge: 900000, httpOnly: false, path: '/' });
}
return res.status(200).json('Successful login');
} else {
console.log('Incorrect password');
}
});
}
});
}
}
}
});
Server.js:
app.use("/graphql",
(req, res) => {
return graphqlHTTP({
schema,
graphiql: true,
context: { req, res },
})(req, res);
});
Error message:
(node:10630) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[0] at ServerResponse.setHeader (_http_outgoing.js:470:11)
[0] at ServerResponse.header (/Users/xxx/xxx/server/node_modules/express/lib/response.js:771:10)
[0] at ServerResponse.append (/Users/xxx/xxx/server/node_modules/express/lib/response.js:732:15)
[0] at ServerResponse.res.cookie (/Users/xxx/xxx/server/node_modules/express/lib/response.js:857:8)
[0] at bcrypt.compare.then.isMatch (/Users/xxx/xxx/server/schema/schema.js:89:41)
I've done some research on this error, but can't seem to find a relevant answer. The issue seems to lie within response body being executing more than once, thus "cannot set headers after they are sent to the client". Since I'm sending both res.cookie() and res.status(200), how could I fix this problem?
express-graphql already sets the status and sends a response for you -- there's no need to call either res.status or res.json inside your resolver.
GraphQL always returns a status of 200, unless the requested query was invalid, in which case it returns a status of 400. If errors occur while executing the request, they will be included the response (in an errors array separate from the returned data) but the status will still be 200. This is all by design -- see additional discussion here.
Instead of calling res.json, your resolver should return a value of the appropriate type (in this particular case UserType), or a Promise that will resolve to this value.
Additionally, you shouldn't utilize callbacks inside resolvers since they are not compatible with Promises. If the bcrypt library you're using supports using Promises, use the appropriate API. If it doesn't, switch to a library that does (like bcryptjs) or wrap your callback inside a Promise. Ditto for whatever ORM you're using.
In the end, your resolver should look something like this:
resolve: function (parent, args, { req, res }) {
const user = await User.findOne({ email: args.email })
if (user) {
const isMatch = await bcrypt.compare(args.password, user.password)
if (isMatch) {
const cookieValue = user.owner ? 'owner' : 'buyer'
res.cookie('cookie', cookieValue, { maxAge: 900000, httpOnly: false, path: '/' })
return user
}
}
// If you want an error returned in the response, just throw it
throw new Error('Invalid credentials')
}