How to share google gmail api authentication from client to node server - node.js

I have a nodeserver serving two routes
/ = https://developers.google.com/gmail/api/quickstart/js
/json/ = {mydata: 'abc'};
node-server.js
import express from 'express';
import path from 'path';
const app = express();
app.set('view engine', 'ejs');
app.set('views', ['src/views']);
app.use('/', express.static(path.join(__dirname, '/')));
app.get('/', (req, res) => {
res.render('index');
});
app.get('/json', (req, res) => {
res.setHeader('Content-Type', 'application/json');
// if authenticated
res.end(JSON.stringify({mydata: 'abc'}));
// else
res.end(JSON.stringify({auth: false}));
});
src/views/index.ejs === (browser example) https://developers.google.com/gmail/api/quickstart/js
Question
After a user has authenticated via browser method, how can I share authenticated state with my nodeserver so that I only show json data if authenticated, otherwise an error?

ok I think it is not possible to access the token if created via the browser. Unless somone else can chip in with a solution.
Meanwhile I have created an alternative to the browser example provided by google quickstart but in node. That way I have control of the access_token from node server and can determine if user is signed in or not.
My solution:
Pre-requisites
Must have created a google workspace with a - first project
Must have created a client id and api key - https://developers.google.com/gmail/api/quickstart/js
Must downloaded the credentials.json file and keep local to nodeserver for use.
Must have supplied: [link OAuthClient Client] > Credentials] Authorised redirect URIs
http://localhost:8000/oauth2callback
This is a typescript implementation so will need cross-env or equivalent to transpile with babel wile running from package.json script.
nodeServer.ts
import yargs from 'yargs';
import express, { Response } from 'express';
import path from 'path';
import fs from 'fs';
import { google, Auth } from 'googleapis';
import url from 'url';
import { gmail_v1 } from 'googleapis/build/src/apis/gmail/v1';
type OAuth2Client = Auth.OAuth2Client;
interface Icredentials {
web: {
client_id: string;
project_id: string;
auth_uri: string;
token_uri: string;
auth_provider_x509_cert_url: string;
client_secret: string;
redirect_uris: string;
};
}
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
const pathCredentials = path.resolve(__dirname, '../credentials/credentials-webserver.json');
const contentCredentials: string = fs.readFileSync(pathCredentials, 'utf8');
const credentials: Icredentials = JSON.parse(contentCredentials);
const { client_secret, client_id, redirect_uris } = credentials.web;
const oAuth2Client: OAuth2Client = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[0]
);
const app = express();
app.set('view engine', 'ejs');
app.set('views', ['src/views']);
const { argv } = yargs;
const port = argv.port || 8000;
const host = argv.host || 'localhost';
const pathRoot = path.join(__dirname, '/');
type TgetSignedIn = (oAuth2Client: OAuth2Client) => Promise<boolean>;
const getSignedIn: TgetSignedIn = oAuth2Client =>
new Promise((resolve, reject) => {
if (!oAuth2Client.credentials.access_token) {
reject(Error('access_token does not exist'))
} else {
resolve(true);
}
});
type TgetLabels = (
auth: OAuth2Client
) => Promise<gmail_v1.Schema$ListLabelsResponse | gmail_v1.Schema$Label[]>;
const getLabels: TgetLabels = auth => {
const gmail = google.gmail({ version: 'v1', auth });
return gmail.users.labels.list({ userId: 'me' }).then(
res =>
// res.config.Authorization: 'Bearer Accesstoken....'
res.data.labels
);
};
type TrenderLabels = (res: Response, isSignedIn: boolean) => void;
const renderLabels: TrenderLabels = (res, isSignedIn) => {
getLabels(oAuth2Client)
.then(labels => {
res.render('index', { labels, isSignedIn });
})
.catch(err => {
res.render('index', { isSignedIn });
});
};
app.use('/', express.static(pathRoot));
app.get('/', (req, res) =>
getSignedIn(oAuth2Client)
.then(() => {
renderLabels(res, true);
})
.catch(() => {
res.render('index', { isSignedIn: false });
})
);
app.get('/getAuthCode', (req, res) => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
res.render('index', { authUrl, isSignedIn: false });
});
app.get('/oauth2callback', (req, res) => {
const qs = url.parse(req.url, true).query;
const { code } = qs;
if (!code) {
return console.error('Error - unselected email');
}
const decodedCode = decodeURIComponent(code as string);
oAuth2Client.getToken(decodedCode, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
renderLabels(res, true);
});
});
app.get('/signout', (req, res) => {
delete oAuth2Client.credentials.access_token;
res.render('index', { isSignedIn: false });
});
app.get('/services', (req, res) => {
getSignedIn(oAuth2Client)
.then(() => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ a: 1 }));
})
.catch(err => {
res.end(JSON.stringify({ error: err, custom: 'You are signed out!' }));
});
});
app.listen(port, host, () => {
// console.log('listening on ', host, ':', port);
});
index.ejs
<!DOCTYPE html>
<html>
<head>
<title>Gmail API Quickstart</title>
<meta charset="utf-8" />
<% if (typeof authUrl !== 'undefined') { %>
<meta http-equiv="refresh" content="0 url=<%= authUrl %>" />
<% } %>
</head>
<body>
<p>Gmail API Quickstart</p>
<button id="authorize_button" style="display: none;">Authorize</button>
<button id="signout_button" style="display: none;">Sign Out</button>
<% if (typeof labels !== 'undefined') { %>
<ul>
<% labels.forEach(item => { %>
<li><%= item.name %></li>
<% }) %>
</ul>
<% } %>
<script>
const handleAuthClick = () => {
location.href = '/getAuthCode';
};
const handleSignout = () => {
location.href = '/signout';
};
const init = (isSignedIn) => {
var authorizeButton = document.getElementById('authorize_button');
authorizeButton.style.display = isSignedIn ? 'none' : 'block';
authorizeButton.onclick = handleAuthClick;
var signoutButton = document.getElementById('signout_button');
signoutButton.style.display = isSignedIn ? 'block' : 'none';
signoutButton.onclick = handleSignout;
}
init(<%= isSignedIn %>);
</script>
</body>
</html>
This provides me the following paths:
It creates a similar experience as this: https://developers.google.com/gmail/api/quickstart/js
Now when user goes to /services/ path I display json if logged in or error if not.
Also if anyone has a better suggestion that just removing the access_token manually to detect if user is signed in, please advise.

Related

Node-Oauth authentication problems; access token possibly not being passed on redirect?

I'm building a React-Node app to consume QuickBooks APIs using OAuth 2 authentication. The app is structured so that the react app runs off a dev server at localhost:3000, and proxies http requests to the express server at localhost:3001.
So, I'm having some trouble making API calls: the react component responsible for rendering API data is crashing, and I'm getting the following error
"Missing required parameter: access_token"
I have the following code in my express server, which converts the authorization code into an access token, and then (I think) passes that token to http://localhost:3000/companyInfo. However I suspect this is where the problem is - is the token actually being sent to this address, or have I misunderstood how OAuth works? Here's the server-side code in question:
app.get("/callback", function (req, res) {
oauthClient
.createToken(req.url)
.then(function (authResponse) {
oauth2_token_json = JSON.stringify(authResponse.getJson(), null, 2);
})
.catch(function (e) {
console.error(e);
});
res.redirect("http://localhost:3000/companyInfo" );
});
...here's my entire server:
const express = require("express");
const OAuthClient = require("intuit-oauth");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const port = process.env.PORT || 3001;
let urlencodedParser = bodyParser.urlencoded({ extended: true });
let oauth2_token_json = null;
let oauthClient = null;
app.get("/authUri", urlencodedParser, (req, res) => {
oauthClient = new OAuthClient({
clientId: "****",
clientSecret: "****",
environment: "sandbox",
redirectUri: "http://localhost:3001/callback",
});
let authUri = oauthClient.authorizeUri({
scope: [OAuthClient.scopes.Accounting],
state: "testState",
});
res.send(authUri);
});
app.get("/callback", function (req, res) {
oauthClient
.createToken(req.url)
.then(function (authResponse) {
oauth2_token_json = JSON.stringify(authResponse.getJson(), null, 2);
})
.catch(function (e) {
console.error(e);
});
res.redirect("http://localhost:3000/companyInfo" );
});
app.get("/getCompanyInfo", (req, res) => {
let companyID = oauthClient.getToken().realmId;
let url =
oauthClient.environment == "sandbox"
? OAuthClient.environment.sandbox
: OAuthClient.environment.production;
oauthClient
.makeApiCall({
url: url + "v3/company/" + companyID + "/companyinfo/" + companyID,
})
.then(function (authResponse) {
console.log(
"The response for API call is :" + JSON.stringify(authResponse)
);
res.send(JSON.parse(authResponse.text()));
})
.catch(function (e) {
console.error(e);
});
});
app.listen(port, () => {
console.log(`Server is listening on port: ${port}`);
});
...and here's the react component where I want to render the returned API call data:
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const CompanyInfo = () => {
const [ info, setInfo ] = useState('')
useEffect(() => {
axios.get('/getCompanyInfo')
.then(res => setInfo(res.data))
},[])
return(
<div>
<p> {info.CompanyInfo.CompanyName} </p>
</div>
)
}
export default CompanyInfo
The strange this is that sometimes I have been able to render the API call data in this component, but I can only do it once. If I refresh my page, it crashes, and I have to start the login process again in order to make the API call work.

I can't get express-fileupload or multer to work.. keeps showing up as undefined - middlware issue?

So I have spent hours trying to figure out why express-fileupload is not working for me.. I have also tried using multer, but I keep getting req.files as either undefined or null. From looking around, it seems like it may have to do with my middleware bc the form is multipart data. I still can't figure out how to get this to work though. Forgive me if it's a stupid mistake.
express app (index.js)
const path = require('path')
const express = require('express')
const morgan = require('morgan')
const compression = require('compression')
const session = require('express-session')
const passport = require('passport')
const SequelizeStore = require('connect-session-sequelize')(session.Store)
const db = require('./db')
const sessionStore = new SequelizeStore({db})
const PORT = process.env.PORT || 8080
const app = express()
const socketio = require('socket.io')
const fileUpload = require('express-fileupload')
var methodOverride = require('method-override');
var multipart = require('multipart');
module.exports = app
// This is a global Mocha hook, used for resource cleanup.
// Otherwise, Mocha v4+ never quits after tests.
if (process.env.NODE_ENV === 'test') {
after('close the session store', () => sessionStore.stopExpiringSessions())
}
/**
* In your development environment, you can keep all of your
* app's secret API keys in a file called `secrets.js`, in your project
* root. This file is included in the .gitignore - it will NOT be tracked
* or show up on Github. On your production server, you can add these
* keys as environment variables, so that they can still be read by the
* Node process on process.env
*/
if (process.env.NODE_ENV !== 'production') require('../secrets')
// passport registration
passport.serializeUser((user, done) => done(null, user.id))
passport.deserializeUser(async (id, done) => {
try {
const user = await db.models.user.findByPk(id)
done(null, user)
} catch (err) {
done(err)
}
})
const createApp = () => {
// logging middleware
app.use(morgan('dev'))
// body parsing middleware
app.use(express.json())
app.use(express.urlencoded({extended: true}))
//file uploads
app.use(fileUpload()); //express-fileupload
// app.use(multer({dest:'./uploads/'})); //multer
// compression middleware
app.use(compression())
// session middleware with passport
app.use(
session({
secret: process.env.SESSION_SECRET || 'my best friend is Cody',
store: sessionStore,
resave: false,
saveUninitialized: false
})
)
app.use(passport.initialize())
app.use(passport.session())
// auth and api routes
app.use('/auth', require('./auth'))
app.use('/api', require('./api'))
// static file-serving middleware
app.use(express.static(path.join(__dirname, '..', 'public')))
// any remaining requests with an extension (.js, .css, etc.) send 404
app.use((req, res, next) => {
if (path.extname(req.path).length) {
const err = new Error('Not found')
err.status = 404
next(err)
} else {
next()
}
})
// sends index.html
app.use('*', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'public/index.html'))
})
// error handling endware
app.use((err, req, res, next) => {
console.error(err)
console.error(err.stack)
res.status(err.status || 500).send(err.message || 'Internal server error.')
})
}
const startListening = () => {
// start listening (and create a 'server' object representing our server)
const server = app.listen(PORT, () =>
console.log(`Mixing it up on port ${PORT}`)
)
// set up our socket control center
const io = socketio(server)
require('./socket')(io)
}
const syncDb = () => db.sync()
async function bootApp() {
await sessionStore.sync()
await syncDb()
await createApp()
await startListening()
}
// This evaluates as true when this file is run directly from the command line,
// i.e. when we say 'node server/index.js' (or 'nodemon server/index.js', or 'nodemon server', etc)
// It will evaluate false when this module is required by another module - for example,
// if we wanted to require our app in a test spec
if (require.main === module) {
bootApp()
} else {
createApp()
}
app.post('/photos/upload', async (req, res, next) => {
try {
console.log(req.files, 'req.files ------')
if (req.files === null) {
res.status(400).send("no file uploaded");
}
console.log(req.files, 'req.files!!!----')
const file = req.files.file;
file.mv(`${__dirname}/client/public/uploads/${file.name}`, err => {
if(err) {
console.error(err);
return res.status(500).send(err);
}
res.json('hello')
// res.json({ fileName: file.name, filePath: `uploads/${file.name}`});
})
// const {name, data} = req.files.picture;
// await knex.insert({name: name, img: data}).into('picture');
} catch (err) {
next(err)
}
}
)
File upload form (CreateListingTest.js)
import React, {useEffect, useState} from 'react'
import {connect} from 'react-redux'
import {useForm} from 'react-hook-form'
import {addNewListing} from '../store/listings'
import axios from 'axios'
/**
* COMPONENT
*/
export const CreateListing = props => {
// const {register, handleSubmit, errors} = useForm()
const [file, setFile] = useState('');
const [filename, setFilename] = useState('Choose File')
const [uploadedFile, setUploadedFile] = useState({});
const onChange = (e) => {
setFile(e.target.files[0]);
setFilename(e.target.files[0].name);
console.log('onChange' , file, filename)
}
const onSubmit = async e => {
e.preventDefault();
const formData = new FormData();
formData.append('files', file);
try {
const res = axios.post('/photos/upload', formData, {
headers: {
'Content-Type' : 'multipart/form-data'
}
});
console.log(res.data, 'res.data in test')
const { fileName, filePath } = res.data;
setUploadedFile({ fileName, filePath });
} catch(err) {
console.log(err, 'error')
}
}
return (
<div className="create-listing">
<h2>Create New Listing</h2>
<div className="all-listings">
<form className="create-listing-form" onSubmit={onSubmit} action="/upload" method="POST">
<input
type="file"
id="img"
name="file"
accept="image/*"
onChange={onChange}
/>
<label>{filename}</label>
<div className="create-listing-form-section">
<input type="submit" value="Upload"/>
</div>
</form>
</div>
</div>
)
}
/**
* CONTAINER
*/
const mapState = state => {
return {
// myListings: state.listings.myListings,
user: state.user
}
}
const mapDispatch = (dispatch, state) => {
return {
addNewListing: (userId, listing) => dispatch(addNewListing(userId, listing))
}
}
export default connect(mapState, mapDispatch)(CreateListing)

React Hooks/Next.js and Multer: 401 Unauthorized, Error while uploading image to MongoDB Atlas

I've tried numerous configurations and been pulling my hair out for a couple days now. However no matter what I do when I try to hit /users/uploadmulter I get an authorized error.
On mongoDB atlas I've tried creating a new collection.
Moving the route declaration in my users file (that way I know it's not an express config issue as other operations work i.e. make a user, get new password etc.)
Anyway, here is my React component:
import { useState, useEffect } from 'react';
import { Card, Icon, Image, Segment, Form } from 'semantic-ui-react';
import axios from 'axios';
function ImageUploader() {
var [defaultImage, setDefaultImage] = useState(
require('../../assets/images/placeholder.jpg')
);
var [userAvatar, setUserAvatar] = useState(defaultImage);
useEffect(() => {
setUserAvatar(userAvatar);
}, [userAvatar]);
function fileUploader(e) {
console.log('event fileUploader ', e);
var imageFormObj = new FormData();
console.log('e.target.files[0] ', e.target.files[0]);
imageFormObj.append('avatar', 'multer-image-' + Date.now());
imageFormObj.append('imageData', e.target.files[0]);
setUserAvatar(URL.createObjectURL(e.target.files[0]));
console.log('userAvatar ', userAvatar);
console.log('imageFormObj ', imageFormObj);
axios
.post('http://localhost:8016/users/uploadmulter', imageFormObj)
.then(data => {
if (data.data.success) {
alert('Image has been successfully uploaded using multer');
}
})
.catch(err => {
alert('Error while uploading image using multer');
});
}
return (
<>
<Segment>
<Card fluid>
<Image src={userAvatar} alt="upload-image" />
<Segment>
<Form encType="multipart/form-data">
<Form.Field>
<input
placeholder="Name of image"
className="process__upload-btn"
type="file"
name="avatar"
content="Edit your Avatar!"
onChange={e => fileUploader(e)}
/>
{/* <Button
content="Edit your Avatar!"
labelPosition="left"
icon="file"
onClick={e => fileUploader(e)}
/> */}
</Form.Field>
</Form>
</Segment>
<Card.Content>
<Card.Header>Charly</Card.Header>
<Card.Meta>
<span className="date">Joined in 2015</span>
</Card.Meta>
<Card.Description>Charly</Card.Description>
</Card.Content>
<Card.Content extra>
<a>
<Icon name="user" />
22 Friends
</a>
</Card.Content>
</Card>
</Segment>
</>
);
}
export default ImageUploader;
And this is my route which handles sending the data to MongoDB Atlas:
var router = require('express').Router();
var Image = require('../models/UserImagesSchema');
var multer = require('multer');
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/');
},
filename: function(req, file, cb) {
cb(null, Date.now() + file.originalname);
}
});
var upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
}
});
/*
stores image in uploads folder
using mulkter and creates a reference to the file
*/
router.post(upload.single('imageData'), (req, res, next) => {
console.log(req.body);
var newImage = new Image({
avatar: {
imageName: req.body.avatar,
imageData: req.file.path
}
});
newImage
.save()
.then(result => {
console.log(result);
res.status(200).json({
success: true,
document: result
});
})
.catch(err => next(err));
});
module.exports = router;
This is my Schema:
var mongoose = require('mongoose');
/* Image Schema for storing images in the mongodb database */
var UserImagesSchema = new mongoose.Schema({
avatar: {
_userId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
imageName: { type: String, default: 'none', required: true },
imageData: {
data: Buffer,
contentType: String
}
}
});
module.exports = mongoose.model('UserImages', UserImagesSchema);
This is the app/server file for express:
var express = require('express');
require('dotenv').config();
var path = require('path');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var cors = require('cors');
var nextJS = require('next');
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
var bodyParser = require('body-parser');
var auth = require('./lib/auth');
var HttpStatus = require('http-status-codes');
var compression = require('compression');
var helmet = require('helmet');
var PORT = process.env.PORT || 8016;
var { isBlockedPage, isInternalUrl } = require('next-server/dist/server/utils');
function NODE_ENVSetter(ENV) {
var environment,
environments = {
production: () => {
environment = process.env.MONGODB_URI;
console.log(`We are currently in the production environment: ${environment}`);
return environment;
},
test: () => {
environment = process.env.TEST_DB_DSN;
console.log(`We are currently in the test environment: ${environment}`);
return environment;
},
default: () => {
environment = process.env.DEVELOPMENT_DB_DSN;
console.log(`We are currently in the development environment: ${environment}`);
return environment;
}
};
(environments[ENV] || environments['default'])();
return environment;
}
var db = NODE_ENVSetter('development');
var mongoose = require('mongoose');
function errorHandler(err, req, res, next) {
// Set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// Log error
console.error(err.stack);
// Render the error page
res.status(err.status || 500);
// Default error message by HTTP code
res.render('error', {
title: HttpStatus.getStatusText(err.status),
message: HttpStatus.getStatusText(err.status)
});
}
function start() {
const dev = process.env.NODE_ENV !== 'production';
const app = nextJS({ dev });
const server = express();
// const proxy = createProxyMiddleware(options);
app
.prepare()
.then(() => {
mongoose.connect(db, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${db}`);
})
.on('error', err => {
console.log(`Connection error: ${err.message}`);
});
})
.catch(err => {
console.error(err);
});
server.use(
session({
secret: 'very secret 12345',
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
})
);
server.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', '*'); // enables all the methods to take place
return next();
});
server.set('view engine', 'html');
server.use(cors());
server.use(morgan('dev'));
server.use('/uploads', express.static('uploads'));
server.use(bodyParser.json({ limit: '50mb' }));
server.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
server.use(cookieParser());
server.use(express.static(path.join(__dirname + 'uploads')));
server.use(compression());
server.use(helmet());
server.use(auth.initialize);
server.use(auth.session);
server.use(auth.setUser);
// console.log('auth.setUser ', auth.setUser);
server.use('/users', require('./users'));
// Redirect all requests to main entrypoint pages/index.js
server.get('/*', async (req, res, next) => {
try {
// #NOTE code duplication from here
// https://github.com/zeit/next.js/blob/cc6fe5fdf92c9c618a739128fbd5192a6d397afa/packages/next-server/server/next-server.ts#L405
const pathName = req.originalUrl;
if (isInternalUrl(req.url)) {
return app.handleRequest(req, res, req.originalUrl);
}
if (isBlockedPage(pathName)) {
return app.render404(req, res, req.originalUrl);
}
// Provide react-router static router with a context object
// https://reacttraining.com/react-router/web/guides/server-rendering
req.locals = {};
req.locals.context = {};
const html = await app.renderToHTML(req, res, '/', {});
// Handle client redirects
const context = req.locals.context;
if (context.url) {
return res.redirect(context.url);
}
// Handle client response statuses
if (context.status) {
return res.status(context.status).send();
}
// Request was ended by the user
if (html === null) {
return;
}
app.sendHTML(req, res, html);
} catch (e) {
next(e);
}
});
// catch 404 and forward to error handler
server.use(function(req, res, next) {
next(createError(404));
});
// error handler
server.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.errorStatus = err.status;
res.locals.errorMessage = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
console.log('err.status ', err.status);
res.status(401).send(err.message);
});
if (process.env.NODE_ENV === 'production') {
server.use(express.static('.next/static'));
server.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '.next/static', 'index.html'));
});
server.listen(PORT, err => {
if (err) throw err;
console.log(
`> Ready and listening on PORT:${PORT} in the ${process.env.NODE_ENV} environment`
);
});
} else {
server.listen(PORT, err => {
if (err) throw err;
console.log(`> Ready and listening on http://localhost:${PORT}`);
});
}
}
start();
If your files are actually less than 16 mb, please try using this Converter that changes the image of format jpeg / png to a format of saving to mongodb, and you can see this as an easy alternative for gridfs ,
please follow this github repo for more details, please try this method,
https://github.com/saran-surya/Mongo-Image-Converter

How to authenticate user with company account on Actions on Google?

I'm building a 3rd server to authenticate users with company accounts on google action. I use account linking with OAuth, Linking type: Implicit. I used ngrok.io to build.
auth.js:
const jwtHelper = require("../helpers/jwt.helper");
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET || "xxx";
let auth = async (req, res) => {
try {
const userFakeData = {
email: req.body.email,
};
const accessToken = await jwtHelper.generateToken(userFakeData, accessTokenSecret);
return res.status(200).json({"token_type": "Bearer", accessToke);
} catch (error) {
return res.status(500).json(error);
}
}
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Login Form Tutorial</title>
</head>
<body>
<div class="login-form">
<h1>Login Form</h1>
<form action="login" method="POST">
<input type="text" name="email" placeholder="email" required>
<input type="submit">
</form>
</div>
</body>
</html>
jwt.helper.js
const jwt = require("jsonwebtoken");
let generateToken = (user, secretSignature, tokenLife) => {
return new Promise((resolve, reject) => {
const userData = {
email: user.email,
}
jwt.sign(
{data: userData},
secretSignature,
{
algorithm: "HS256",
expiresIn: tokenLife,
},
(error, token) => {
if (error) {
return reject(error);
}
resolve(token);
});
});
}
let verifyToken = (token, secretKey) => {
return new Promise((resolve, reject) => {
jwt.verify(token, secretKey, (error, decoded) => {
if (error) {
return reject(error);
}
resolve(decoded);
});
});
}
module.exports = {
generateToken: generateToken,
verifyToken: verifyToken,
};
middelware auth.js:
const jwtHelper = require('../helpers/jwt.helper');
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET || "xxx";
let isAuth = async(req, res, next) =>{
const tokenFromCLient = req.body.token || req.query.token || req.headers["access-token"];
if ( tokenFromCLient) {
//thuc hien giai ma xem co hop len khong
try{
const decode = await jwtHelper.verifyToken(tokenFromCLient, accessTokenSecret);
req.jwtDecoded = decode;
next();
}
catch (error) {
return res.status(401).json({ message: Unauthorized})
}
}
else {
return res.status(403).send({message: 'No token provided'})
}
}
module.exports = {
isAuth: isAuth
}
router.js
const express = require("express");
const router = express.Router();
const AuthMiddleWare = require("../middleware/auth");
const AuthController = require("../controllers/AuthController");
let initAPIs = (app) => {
router.post("/auth", AuthController.login);
router.use(AuthMiddleWare.isAuth);
return app.use("/", router);
}
module.exports = initAPIs;
server.js
const express = require("express");
const app = express();
const initAPIs = require("./routes/router");
var bodyParser = require('body-parser');
var path = require('path');
app.use(bodyParser.urlencoded({extended : true}));
app.use(bodyParser.json());
app.get('/', function(request, response) {
response.sendFile(path.join(__dirname + '/public/login.html'));
});
initAPIs(app);
app.listen(2310, () => {
console.log("server is working in localhost:2310")
})
My account linking setup
My Credentials Google Cloud :
This is what happened when I "talk to fun app"
After submit form :
Click link :
Click link:
Nothing happened in Google Console

Cannot fetch between Express and React apps due to CORS

My app was working fine and I was able to fetch data between my Express and React servers. I reorganised my code and now i cannot get rid of the CORS errors and cannot fetch any data at all. I cannot move on with my project and simply can't figure it out for myself, I have really tried.
The front end works ok until i try to login, and then the authentication fails
I have tried adding headers and have installed CORS to my express app. I have a proxy specified in my react package.json to the express URL.
This is my Express server.js
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const path = require('path');
const cors = require('cors');
const methodOverride = require('method-override');
const db = require('./db/index.js')
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
require('dotenv').config();
const app = express();
app.disable('x-powered-by');
app.use(function(req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
app.use(methodOverride('_method'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(cors({
origin: 'http://localhost:8080'
}));
app.use(express.static(path.join(__dirname, '../build')));
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, '../build', 'index.html'));
});
const userRouter = require('./routes/user-routes')
app.use('/api', userRouter)
const fileRouter = require('./routes/file-routes')
app.use('/file', fileRouter)
// mongoose.connection.once('open', run);
app.listen(process.env.PORT || 8080);
console.log('Server is listening on port ' + process.env.PORT);
This is my user controller
const User = require('../models/Users');
const secretShh = 'mysecretsshhh';
const jwt = require('jsonwebtoken');
const home = (req, res) => {
res.send('Welcome!');
};
const secret = (req, res) => {
res.send('The password is potato');
};
const register = (req, res) => {
const { email, password } = req.body;
const user = new User({ email, password });
user.save(function(err) {
if (err) {
console.log(err);
res.status(500).send("Error registering new user please try again.");
} else {
res.status(200).send("Welcome to the club!");
}
});
};
const authenticate = (req, res) => {
const { email, password } = req.body;
User.findOne({ email }, function(err, user) {
if (err) {
console.error(err);
res.status(500)
.json({
error: 'Internal error please try again'
});
} else if (!user) {
res.status(401)
.json({
error: 'Incorrect email or password'
});
} else {
user.isCorrectPassword(password, function(err, same) {
if (err) {
res.status(500)
.json({
error: 'Internal error please try again'
});
} else if (!same) {
res.status(401)
.json({
error: 'Incorrect email or password'
});
} else {
// Issue token
const payload = { email };
const token = jwt.sign(payload, secretShh, {
expiresIn: '1h'
});
res.cookie('token', token, { httpOnly: true }).sendStatus(200);
}
});
}
});
};
const token = (req, res) => {
res.sendStatus(200);
};
module.exports = {
home,
secret,
register,
authenticate,
token
}
this is my user routes
const express = require('express')
const UserCtrl = require('../controllers/user-ctrl')
const withAuth = require('../middleware');
const router = express.Router()
router.get('/home', UserCtrl.home)
router.get('/secret', withAuth, UserCtrl.secret)
router.post('/register', UserCtrl.register)
router.post('/authenticate', UserCtrl.authenticate)
router.get('/checktoken', withAuth, UserCtrl.token)
module.exports = router
this is a middleware function to check tokens, this is where the error seems to point towards, but I'm sure its actually something to do with the proxy and fetch being blocked by CORS.
const jwt = require('jsonwebtoken');
const secret = 'mysecretsshhh';
const withAuth = (req, res, next) => {
const token =
req.body.token ||
req.query.token ||
req.headers['x-access-token'] ||
req.cookies.token;
if (!token) {
res.status(401).send('Unauthorized: No token provided');
} else {
jwt.verify(token, secret, function(err, decoded) {
if (err) {
res.status(401).send('Unauthorized: Invalid token');
} else {
req.email = decoded.email;
next();
}
});
}
}
module.exports = withAuth;
this is my auth components where the error is also pointing towards
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
//withAuth is a high-order component which takes in a component to protect
export default function withAuth(ComponentToProtect) {
return class extends Component {
constructor() {
super();
this.state = {
loading: true,
redirect: false,
};
}
async componentDidMount() {
fetch('http://localhost:8080/api/checktoken', {
credentials: 'include',
mode: 'cors'
})
.then(res => {
if (res.status === 200) {
this.setState({ loading: false });
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
this.setState({ loading: false, redirect: true });
});
}
render() {
const { loading, redirect } = this.state;
if (loading) {
return null;
}
if (redirect) {
return <Redirect to="/login" />;
}
return (
<React.Fragment>
<ComponentToProtect {...this.props} />
</React.Fragment>
);
}
}
}
login component
import React, { Component } from 'react';
export default class Login extends Component { //impplicit vs explicit returns
constructor(props) {
super(props)
this.state = {
email : '',
password: ''
};
}
handleInputChange = (event) => {
const { value, name } = event.target;
this.setState({
[name]: value
});
}
onSubmit = async (event) => {
event.preventDefault();
fetch('/api/authenticate', {
method: 'POST',
body: JSON.stringify(this.state),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
if (res.status === 200) {
this.props.history.push('/');
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
alert('Error logging in please try again');
});
}
render() {
return (
<form onSubmit={this.onSubmit}>
<h1>Login Below!</h1>
<input
type="email"
name="email"
placeholder="Enter email"
value={this.state.username}
onChange={this.handleInputChange}
required
/>
<input
type="password"
name="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handleInputChange}
required
/>
<input type="submit" value="Submit"/>
</form>
);
}
}
This is the main error:
Access to fetch at 'http://localhost:8080/api/checktoken' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:8080' that is not equal to the supplied origin. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
It also says:
withAuth.jsx:17 GET http://localhost:8080/api/checktoken net::ERR_ABORTED 401 (Unauthorized)
In my express app the terminal says it cant read the token in the middleware, I presume its also due to cors :
TypeError: Cannot read property 'token' of undefined
at withAuth (/Users/nancycollins/virtuload-beta/backend/middleware.js:6:16)
Apologies if this is too much information I've just been stuck on this for too long and really dont know what else to do.
Update:
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
With:
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
I encourage you to do app.use(cors()); before those lines
app.disable('x-powered-by');
app.use(function(req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
...
Express stacks those functions lets say as a "array" and because of that the order matters
Hope it resolve your problem.
Bit late to the party but just going to leave my answer here incase someone else has the same problem.
The idea is to allow CORS request to your Express server. Go to the directory with your server.js file and run:
npm install cors
Then inside server.js add the following lines:
const cors = require('cors');
app.use(cors());

Resources