cookie NOT setting in browser with hapi-auth-cookie plugin - reactjs

I've used hapi js for my backend and react for my frontend. The front end server is running on localhost port 3000, and the backend on 8000. So i have 2 routes like this -
let userDetails = {
method: "GET",
path: "/api/user/userdata",
config: {
description: 'Get userdata',
notes: 'Returns a todo item by the id passed in the path',
tags: ['api', 'User Data'],
cors: corsHeaders,
auth: {
strategy: 'restricted',
}
},
handler: (request, h) => {
return h.response(userData)
}
}
and
let createUser = {
method: "POST",
path: "/api/user/userdata",
config: {
tags: ['api', 'User Data'],
description: 'Upload User data to the db',
cors: corsHeaders,
},
handler: async (request, h) => {
const { username, emailId, password } = request.payload
request.cookieAuth.set({ username })
return h.response('cookie created!')
}
}
now the 'post' route sets the cookie by request.cookieAuth.set({ username }),
so when i posted through postman application, it's setting the cookie in postman and the get route sends the data without any problem. But in browser, the cookie is'nt being set.
i'm using hapi-auth-cookie plugin and the registration is done like this -
await server.register(HapiAuthCookie)
server.auth.strategy('restricted', 'cookie',{
password: 'AudhASp342SID3acdh83CDASHciAS93rashdiva34a',
cookie: 'session',
isSecure: false,
ttl: 7 * 24 * 60 * 60 * 1000,
})
someone please help

Related

CORS issue: Next.js application

I am trying to deploy a Next.js application with Dokku (Heroku). The application was previously deployed to Vercel without error, but on the Dokku deploy CORS is failing. I've made some progress in fixing it.
The Next.js server communicates with another eternal Python Django API through an API gateway.
Initially the POST request errored with the "No 'Access-Control-Allow-Origin' header is present" error. I added the headers to moduleExports:
next.config.js
const moduleExports = {
async headers() {
return [
{
source: "/api/(.*)",
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
{ key: "Access-Control-Allow-Methods", value: "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
{ key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
]
}
]
},
async redirects() {
return [
{
source: '/account',
destination: '/account/profile',
permanent: true,
},
]
},
};
Thereafter I began receiving a new error that the preflight options request did not return 200. I added a check for options requests to my handler:
pages/api/sign-up.js
export default async function handler(req, res) {
if (req.method === 'OPTIONS') {
res.status(200).end();
}
const { email, password1, password2, first_name, last_name } = await req.body
const response = await fetch(REGISTRATION_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify( { email, password1, password2, first_name, last_name } ),
});
const data = await response.json()
res.status(200).json(data)
}
At this point, interestingly, the request does make it to the gateway and is accepted and indeed the new user is successfully created at the Django API. However the communication between the next.js server and client still shows a CORS error and the page does not update to show success. The CORS error is back to the first one "No 'Access-Control-Allow-Origin' header is present on the requested resource". The difference of course is that earlier the user was not created on the Django end.
My question of course is how can I fix this since I'm out of ideas or things to try now.
There is your problem:
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
// ...
]
You cannot use the wildcard (*) in conjunction with credentialed requests. As explained in the section entitled Credentialed requests and wildcards of the MDN Web Docs about CORS:
When responding to a credentialed request, the server must not specify the "*" wildcard for the Access-Control-Allow-Origin response-header value, but must instead specify an explicit origin; for example: Access-Control-Allow-Origin: https://example.com.
Accordingly, instead of using the wildcard, you should specify the allowed origin(s) explicitly:
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "https://yourfrontendorigin.com" },
// ...
]

Cookies are being sent but chrome doesn't set them

I saw many similar questions and played with many combinations but nothing works.
I specify that it's all on localhost.
regUser = () => {
var username = getE("username-input").value;
var email = getE("email-input").value;
var password = getE("password-input").value;
axios({
url: process.env.REACT_APP_API_URL + "register",
method: "post",
data: {
username, email, password
},
withCredentials: true
}).then(res => {
if(res.data.regSuccess) {
// Registration successful
this.setState({
regSuccess: true,
regTextHidden: false,
regText: "Registration Successful! An e-mail was sent to the specified e-mail address with confirmation information! You will be redirected to the Login page..."
}, ()=>{
setTimeout(
()=>{window.location.href=("/login")}, 5000
)
})
} else {
this.setState({
regSuccess: false,
regTextHidden: false,
regText: "An error occured. Please try again later!"
})
}
})
}
Backend code:
f.checkPassword(userData, function(result) {
if(!result.correct) {
// Wrong password
res.send({found: true, correct: false})
} else {
// Proceed with authentication
var token = f.genToken(userData.user);
res.header("OPTIONS", 'true')
res.cookie("access-token", token.token, {httpOnly: true, sameSite: "none", maxAge: "100000", secure: false});
res.send({found: true, correct: true})
}
})
No matter what cookie settings I use, they are being sent, the "Set-Cookie" header is present but no cookie is set.
I've played with every option for like 2 days but it just doesn't work. Any advice?
I was stuck on this for a while too. A couple things fixed it for me:
in the frontend in the axios call (which I see you've done), use:
withCredentials: true
Then in the backend in express, use npm package cors, with the following:
const app = express();
app.use(cors({ credentials: true, origin: 'http://localhost:3000' }));
Or use whatever your url origin is. Hope this works for you.

Authorization Denied trying to access Bookings Api in Microsoft Graph

I am trying to create an app that runs from an AWS lambda that acts as a middle man for customers wanting to sign up for a booking using Microsoft Bookings. Following the documentation I am able to generate an access token for Graph, but I get an authorization denial when I try to request information from bookings through the api.
My Code:
import request from "request";
import { Callback } from "./callback";
const APP_ID = process.env.BOOKINGS_APP_ID;
const APP_SECRET = process.env.BOOKINGS_API_SECRET;
const TOKEN_ENDPOINT = `https://login.microsoftonline.com/${process.env.BOOKINGS_TENANT_NAME}.onmicrosoft.com/oauth2/token`;
const requestParams = {
client_id: APP_ID,
client_secret: APP_SECRET,
grant_type: "client_credentials",
resource: "https://graph.microsoft.com",
scope: "https://graph.microsoft.com/.default",
};
export default class Bookings {
public static async listBusinesses(callback: Callback) {
Bookings.generateAPIToken((err: string | null, data: any) => {
const options = {
// body: {},
headers: {
"Authorization": `Bearer ${data}`,
"Content-Type": "application/json",
},
method: "GET",
url: "https://graph.microsoft.com/beta/bookingBusinesses",
};
console.log(data);
return request(options, (error: string, res: any) => {
if (error) {
return callback(error, {});
}
return callback(null, JSON.parse(res.body));
});
});
}
public static async generateAPIToken(callback: Callback) {
request.post({ url: TOKEN_ENDPOINT, form: requestParams }, (err, res, body) => {
if (err) {
return callback(err, {});
}
return callback(null, JSON.parse(body).access_token);
});
}
}
The error that I get:
{
error: {
code: '',
message: 'Authorization has been denied for this request.',
innerError: {
'request-id': '695d3f90-357d-490c-b980-4a2018dd39a5',
date: '2020-06-08T03:21:59'
}
}
}
I have also tried using the microsoft-graph-client library to access bookings but that doesn't work either. What I am doing wrong? Thank you for any help.
We can see the document shows the graph api(bookingBusinesses) which you want to request requires delegated type permissions and not support application type permission.
So we can not use "client_credentials" grant flow, your code shows you use "client_credentials" as the grant type. You can use "username/password" grant flow to get the access token instead. So the param you request for the access token should be like below:
const requestParams = {
client_id: APP_ID,
client_secret: APP_SECRET,
grant_type: "password",
scope: "https://graph.microsoft.com/.default",
username: "your user name/email(like xxxxx#xxx.onmicrosoft.com)",
password: "your password"
};
By the way, I noticed the "TOKEN_ENDPOINT" in your code is https://login.microsoftonline.com/${process.env.BOOKINGS_TENANT_NAME}.onmicrosoft.com/oauth2/token and you use both params resource and scope in requestParams. If we use v1 endpoint as your code, we just need to use the param resource. If we use v2 endpoint(https://login.microsoftonline.com/${process.env.BOOKINGS_TENANT_NAME}.onmicrosoft.com/oauth2/v2.0/token), we need to use use the param scope instead of the param resource. The code I provided above use v2, so I use scope param and you also need to change the "TOKEN_ENDPOINT" to v2(just add a v2.0 between the oauth2/ and /token).
If you don't want to change the "TOKEN_ENDPOINT" to v2, just use the params like below:
const requestParams = {
client_id: APP_ID,
client_secret: APP_SECRET,
grant_type: "password",
resource: "https://graph.microsoft.com",
username: "your user name/email(like xxxxx#xxx.onmicrosoft.com)",
password: "your password"
};
Hope it helps~

CORS: proxying localhost via webpack-dev-server to external API

I'm building simple react-powered contact page and it would be swell if I could test email sending from my local development machine. I'm using Zoho Email API, which I tested with curl and Insomnia - there everything is fine, I'm able to send emails.
Problems start when I try to do the same from React app via axios. The app itself is served locally via webpack-dev-server. Without proxying request through wds, the browser complains about CORS. But when I'm trying to proxy request via wds with the appropriate headers set I'm being greeted with 400 Bad Request and not much more to aid with the debugging.
The relevant wds setup:
const ReactRefreshWebpackPlugin = require('#pmmmwh/react-refresh-webpack-plugin')
exports.devServer = ({ host, port } = {}) => {
const plugin = new ReactRefreshWebpackPlugin()
return {
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers':
'X-Requested-With, content-type, Authorization',
},
proxy: {
'/api/accounts/ZOHO_ID_NOT_SHOWN/messages': {
target: 'https://mail.zoho.eu',
secure: false,
},
},
https: true,
contentBase: path.join(__dirname, 'assets'),
stats: 'errors-only',
historyApiFallback: true,
overlay: true,
hot: true,
host,
port,
},
plugins: [plugin],
}
}
And function in which I'm using to axios to send the email:
const sendEmail = async () => {
const { name, email, message } = formState
try {
const response = await axios({
method: 'post',
url: EMAIL_SEND,
headers: { Authorization: EMAIL_AUTH },
data: {
toAddress: EMAIL_TO,
subject: `Message from ${name} # ${email}`,
content: message,
},
})
console.log(response) // debugging
} catch (e) {
console.log(e.response)
}
}
In the above function, EMAIL_SEND is imported from elsewhere and its value is /api/accounts/ZOHO_ID_NOT_SHOWN/messages - mirroring the wds proxy value.
With the above setup, when I try to send a test email from localhost I'm getting 400 Bad Request... and not much more. I'm sure that the issue is with how I'm proxying the request via wds but I kind of run out of ideas how I can fix it. Any suggestions will be greatly appreciated!

Handling file upload via multipart form post in Loopback custom remote method

I've tried everything recommended in How can I use body-parser with LoopBack? and yet still no luck.
My client app is in AngularJS and uses the ng-file-upload module, specifically like this:
Upload.upload({
url: apiUrl + '/Recipes/uploadImage',
file: $scope.picFile,
params: {
recipeId: newRecipe.id
}
})
.then(function(res) {
console.log('succes:', res);
}, function(err) {
console.log('error:', err);
}, function(evt) {
console.log('progress:', evt);
});
On the server (Loopback) side, I have made sure that server/middleware.json has the middleware registered:
...
"parse": {
"body-parser#json": {},
"body-parser#urlencoded": {"params": { "extended": true }}
},
...
For good measure, although I'm not sure if body-parser is even needed in addition to multer (and body-parser is being required anyway because of being registered in middleware.json), I've included these lines in server/server.js:
var bodyParser = require('body-parser');
var multer = require('multer');
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(multer().any()); // for parsing multipart/form-data
And I've also installed both:
npm install --save multer
npm install --save body-parser
Finally, my custom remote method looks like this:
Recipe.remoteMethod(
'uploadImage',
{
accepts: [
{ arg: 'req', type: 'object', http: { source: 'req' } },
{ arg: 'res', type: 'object', http: { source: 'res' } }
],
returns: {
arg: 'status', type: 'object', root: true
},
http: {verb: 'post'}
}
);
The actual function so far is just trying to get something to work with:
Recipe.uploadImage = function (req, query, cb) {
console.log('params:', req.file, req.recipeId);
console.log('body:', req.body);
... // And once I can get this I'm going to get the stream and pipe it to a remote storage container, but I need the file first!
The output from posted to the above is
params: undefined undefined
body: {}
Which sort of suggests that multer isn't really doing its thing, and I'm not really getting the parts of the multi-part form post?
Help!
Tell me if that help you. With upload and check information in loopback3.
/server/server.js
var loopback = require('loopback');
var bodyParser = require('body-parser');
var multer = require('multer');
var boot = require('loopback-boot');
var app = module.exports = loopback();
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(multer().any()); // for parsing multipart/form-data
app.start = function() {
/server/midleware.json
"parse": {
"body-parser#json": {},
"body-parser#urlencoded": {"params": { "extended": true }}
},
final model:
module.exports = function(Offer) {
Offer.add = function (ctx,options,cb) {
console.log(ctx.req.body);
console.log(ctx.req.files[0]);
if(!options) options = {};
ctx.req.params.container = 'x';
Container.upload(ctx.req,ctx.result,{container: 'x'},function (err,fileObj) {
fileObj.files[''] ...
and remote my is different then your
Offer.remoteMethod(
'add',
{
description: 'Uploads a file',
accepts: [
{ arg: 'ctx', type: 'object', http: { source:'context' } },
{ arg: 'options', type: 'object', http:{ source: 'query'} }
],
returns: {
type: 'object', root: true
},
http: {verb: 'post'}
}
);
I use postman to send data explorer do not allow for files.
Something explorer do some error.
with this options:
address: http://0.0.0.0:3000/api/Offers/add?access_token=c6qlcG19lhEYp6C6CztcPHYKpXj2uA3qARMJU7nxBL3trjveHbxNTo0BsvV3vleO
type: POST
authentication: no authentication
headers: empty (default content type is included)
body: form-data with data you want to send
How to configure storage connector to filter size and type of file loopback:
"storage": {
"name": "storage",
"nameConflict": "makeUnique",
"connector": "loopback-component-storage",
"provider": "filesystem",
"root": "./server/storage",
"maxFileSize": "5242880",
"allowedContentTypes":["image/gif", "image/png", "image/tiff", "image/webp", "image/x-icon", "image/jpeg", "image/svg+xml", "image/x-icon"]
}
How to create relation for model and file in loopback?
How to save file info in loopback?
You need to have model in data base what save name of file.
Container.upload(ctx.req,ctx.result,{container: 'x'},function (err,fileObj) {
cFile = fileObj.files[''][0];
Image.create({
name: cFile.name,
type: cFile.type,
originalFilename: cFile.originalFilename,
offerId: 3,
container: "x"},
function (err, obj) { ...

Resources