I'm trying to get nuxt with axios working.
I do the following post request (store/index.js):
login({ commit }, { username, password }) {
const data = JSON.stringify({
password
, username
})
console.log('store login ', username, password)
this.$axios.post('http://localhost:3000/api/login', data)
.then(res => {
commit('SET_USER', res.data)
})
.catch(error => {
console.log(error.response)
})
}
My backend looks like this (api/auth.js):
const express = require('express')
const session = require('express-session')
const body_parser = require('body-parser')
const app = express()
const router = express.Router()
app.use(body_parser.json())
app.use(body_parser.urlencoded({ extended: true }))
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}))
router.post('/login', (req, res) => {
console.log('try to login ', req.body)
if (!req.body)
return res.status(401).json({ message: 'No body' })
if (req.body.username === 'demo' && req.body.password === 'demo') {
req.session.auth_user = { username: 'demo' }
console.log('logged in')
return res.json({ username: 'demo' })
}
res.status(401).json({ message: 'Bad credentials' })
})
My Problem is, that the req.body is always undefined. But in the error message I can see, that the params are sent. (config.data)
Does anybody has an idea what I'm missing? I use body-parser and checked everything 100 times. I guess it's something total obvious, but I can't see it.
My nuxt.config.js:
module.exports = {
...
modules: [
'#nuxtjs/axios'
, '#nuxtjs/auth'
],
router: {
middleware: ['auth']
},
serverMiddleware: ['#api/auth']
...
}
Probably you take this error also. This is caused by cors policy.
Access to XMLHttpRequest at 'http://localhost:3000/.......' from
origin 'http://127.0.0.1:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
same problem occured on my project.
in server index.js change line
const host = process.env.HOST || 'localhost'
and nuxt.config.js
axios: {
baseURL: 'http://localhost:YOUR_PORT'
},
You must define bodyParser in router.
router.use(express.json())
Related
I am using facebook and google oauth2 login using passport js, with this flow
User clicked the login button
Redirects to facebook/google auth page (depending on what login the user chooses)
The auth page redirects back to a callback page (/auth/callback/[provider])
A passport express middleware will catch it to parse some data and then send it to a remote api of myown to sign the user in
The auth remote api will send a response back consisting the user token
A custom express middleware will catch the response to set cookie on the server
the express chain ends by route it to /profile (cookie with token is set on the browser)
/profile will then checks if there is a token, if there is not: it will redirect to /
Doing this flow on facebook login is fine, the user is successfully redirected to /profile, with all of its data and token, the google oauth2 login however seems to be doing the redirect to /profile then setting the token (step #7 then #6), so everytime the user is using google oauth2 login, its always gonna be redirected back to / since by the time it arrives at /profile, it doesnt have the token
here's the code on the above's flow
#./server.js
const express = require('express')
const next = require('next')
const Passport = require('./server/middleware/passport')
const Api = require('./server/api')
const port = parseInt(process.env.PORT, 10)
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app
.prepare()
.then(() => {
const server = express()
// ... other unrelated things
server.use(Passport.initialize())
Api.passport.facebook(server)
Api.passport.facebookCallback(server)
Api.passport.google(server)
Api.passport.googleCallback(server)
// ... other unrelated things
server.all('*', (req, res) => handle(req, res))
server.listen(port, (error) => {
if (error) throw error
// ... other unrelated things
})
})
#./server/api.js
const Passport = require('middleware/passport')
function setCookie(req, res, next) {
res.cookie('token', req.user.auth.token, {
httpOnly: true,
sameSite: 'strict',
path: '/',
secure: process.env.NODE_ENV !== 'development',
})
next()
}
function facebook(app) {
return app.get('/auth/facebook', (req, res, next) => {
Passport.authenticate('facebook', {
scope: ['email', 'public_profile']
})(req, res, next)
})
}
function facebookCallback(app) {
return app.get(
'/auth/callback/facebook',
Passport.authenticate('facebook', { session: false, failureRedirect: '/' }),
setCookie,
(req, res) => {
res.redirect('/profile')
},
)
}
function google(app) {
return app.get('/auth/google', (req, res, next) => {
Passport.authenticate('google', {
scope: [
'https://www.googleapis.com/auth/userinfo.email ',
'https://www.googleapis.com/auth/userinfo.profile ',
],
prompt: 'consent',
authType: 'rerequest',
accessType: 'offline',
})(req, res, next)
})
}
function googleCallback(app) {
return app.get(
'/auth/callback/google',
Passport.authenticate('google', { failureRedirect: '/', session: false }),
setCookie,
(req, res) => {
res.redirect('/profile')
},
)
}
module.exports = {
passport: {
facebook,
facebookCallback,
google,
googleCallback,
}
}
#./server/middleware/passport.js
const axios = require('axios')
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy
const FacebookStrategy = require('passport-facebook').Strategy
passport.serializeUser((user, done) => {
done(null, user)
})
passport.deserializeUser((obj, done) => {
done(null, obj)
})
function verifyCallback(req, ... , done) {
process.nextTick(async () => {
try {
const options = {
baseURL: baseUrl, // My remote api url
method: 'POST',
url: '/auth/signin',
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify({
// email, fullname, etc
}),
}
const response = await axios(options)
return done(null, response.data)
} catch (error) {
const { response } = error
return done(JSON.stringify(response.data, null, 2), null)
}
})
}
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: callbackURLGoogle,
passReqToCallback: true,
}, verifyCallback))
passport.use(new FacebookStrategy({
clientID: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
callbackURL: callbackURLFacebook,
enableProof: true,
profileFields: ['id', 'name', 'email', 'picture.type(large)'],
passReqToCallback: true,
}, verifyCallback))
module.exports = passport
I console.log() things, just to figure out if it falls to the correct sequence of flow, the console doesn't seem to log anything suspicious, is there's something i am missing here?
PS: i am also using next js with custom server
I was facing the same problem and was able to send cookies by using custom callback.
router.get('/google/callback', (req, res) => {
passport.authenticate('google', {session: false, failureRedirect:'/auth/google/failure'},
async(err, user) => {
// You can send cookies and data in response here.
})(req, res)
})
Please refer custom callback section in documentation for explanation.
I have recently integrated Expressjs with my Nextjs project and was trying to run as POST request to an API.
But I'm getting an error message
Cannot POST /api/auth/signin/`
I do not receive the same when I make a GET request. Based on the way I integrated ExpressJS from a tutorial, I'm curious if the error message for POST requests stems from not attaching the .post() method to the express server in my server side server file. Does this sound like the source of the issue?
ssr-server.js:
const express = require('express')()
const next = require('next')
const bodyParser = require('body-parser')
// Session Management
const session = require('express-session')
const redis = require('redis');
const redisClient = redis.createClient({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || '6379',
url: process.env.REDIS_URL || ''
});
const redisStore = require('connect-redis')(session);
redisClient.on_connect("error", function(error){
console.error(error);
})
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
console.log("Test")
app.prepare()
.then(() => {
const server = express()
// Session Management
server.use(session({
store: new redisStore({client: redis.RedisClient}),
secret: process.env.SESSION_SECRET,
saveUninitialized: false,
resave: false,
cookie: {
secure: process.env.SESSION_SECURE || false,
httpOnly: true,
}
}))
// Form body parsing
server.use(bodyParser.json())
server.use(bodyParser.urlencoded({extended: true}))
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
/api/auth/signin.js:
export default (req, res) => {
if (req.method === 'POST') {
if (res.statusCode = 200){
return res.json({ body: req.body, message: 'Success' })
} else {
return res.json({ message: 'Error on POST'})
}
} else {
return res.json({ message: 'Error' })
}
}
After adding authentication to our backend Graphql server the "Schema" and "Docs" are no longer visible in the Graphql Playground. Executing queries when adding a token to the "HTTP HEADERS" in the Playground does work correctly when authenticated and not when a user isn't authenticated, so that's ok.
We disabled the built-in Playground from Apollo-server and used the middleware graphql-playground-middleware-express to be able to use a different URL and bypass authentication. We can now browse to the Playground and use it but we can't read the "Schema" or "Docs" there.
Trying to enable introspection didn't fix this. Would it be better to call passport.authenticate() in the Context of apollo-server? There's also a tool called passport-graphql but it works with local strategy and might not solve the problem. I've also tried setting the token in the header before calling the Playground route, but that didn't work.
We're a bit lost at this. Thank you for any insights you could give us.
The relevant code:
// index/ts
import passport from 'passport'
import expressPlayground from 'graphql-playground-middleware-express'
const app = express()
app.use(cors({ origin: true }))
app.get('/playground', expressPlayground({ endpoint: '/graphql' }))
app.use(passport.initialize())
passport.use(bearerStrategy)
app.use(
passport.authenticate('oauth-bearer', { session: false }),
(req, _res, next) => { next() }
)
;(async () => {
await createConnections()
const server = await new ApolloServer({
schema: await getSchema(),
context: ({ req }) => ({ getUser: () => req.user, }),
introspection: false,
playground: false,
})
server.applyMiddleware({ app, cors: false })
app.listen({ port: ENVIRONMENT.port }, () => { console.log(`Server ready`) })
})()
// passport.ts
import { IBearerStrategyOptionWithRequest, BearerStrategy, ITokenPayload } from passport-azure-ad'
import { Account } from '#it-portal/entity/Account'
export const bearerStrategy = new BearerStrategy( config,
async (token: ITokenPayload, done: CallableFunction) => {
try {
if (!token.oid) throw 'token oid missing'
const knownAccount = await Account.findOne({ accountIdentifier: token.oid })
if (knownAccount) return done(null, knownAccount, token)
const account = new Account()
account.accountIdentifier = token.oid
account.name = token.name
account.userName = (token as any).preferred_username
const newAccount = await account.save()
return done(null, newAccount, token)
} catch (error) {
console.error(`Failed adding the user to the request object: ${error}`)
}
}
)
I figured it out thanks to this SO answer. The key was not to use passport as middleware on Express but rather use it in the Graphql Context.
In the example code below you can see the Promise getUser, which does the passport authentication, being used in the Context of ApolloServer. This way the Playground can still be reached and the "Schema" end "Docs" are still accessible when run in dev mode.
This is also the preferred way according to the Apollo docs section "Putting user info on the context".
// apollo.ts
passport.use(bearerStrategy)
const getUser = (req: Express.Request, res: Express.Response) =>
new Promise((resolve, reject) => {
passport.authenticate('oauth-bearer', { session: false }, (err, user) => {
if (err) reject(err)
resolve(user)
})(req, res)
})
const playgroundEnabled = ENVIRONMENT.mode !== 'production'
export const getApolloServer = async () => {
return new ApolloServer({
schema,
context: async ({ req, res }) => {
const user = await getUser(req, res)
if (!user) throw new AuthenticationError('No user logged in')
console.log('User found', user)
return { user }
},
introspection: playgroundEnabled,
playground: playgroundEnabled,
})
}
The best thing is that you only need two functions for this to work: passport.use(BearerStrategy) and passport.authenticate(). This is because sessions are not used so we don't need to add it as Express middleware.
// index/ts
const app = express()
app.use(cors({ origin: true }))
;(async () => {
await createConnections()
const server = await getApolloServer()
server.applyMiddleware({ app, cors: false })
app.listen({ port: ENVIRONMENT.port }, () => { console.log(`Server ready`) })
})()
I hope this helps others with the same issues.
I have a simple MEAN app and I want to implement a simple "home-made" user authentication. My idea is to save the userId in the session when he logs in, and to check if userId exists in the session on each page request (for example, when getting the list of all users).
Backend - server.js:
const express = require("express");
const session = require("express-session");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
var MemoryStore = session.MemoryStore;
app.use(
session({
name: "app.sid",
secret: "my_s3cr3t",
resave: true,
store: new MemoryStore(),
saveUninitialized: true
})
);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
const dbConfig = require("./config/database.config.js");
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
mongoose
.connect(dbConfig.url)
.then(() => {
// ...
})
.catch(err => {
// ...
process.exit();
});
require("./app/routes/user.routes.js")(app);
require("./app/routes/task.routes.js")(app);
require("./app/routes/login.routes.js")(app);
app.listen(3333, () => {
console.log("Server is listening on port 3333");
});
When a user clicks the Login button, a method from the frontend controller is called:
Frontend - login.controller.js:
vm.login = function() {
userService.getUserByUsername(vm.username).then(user => {
if (user.password === vm.password) {
console.log("Login ok");
loginService.login(user).then(($window.location.href = "/#!main"));
} else {
console.log("Login not ok");
}
});
};
Backend - login.controller.js:
exports.login = (req, res) => {
req.session.userId = req.body._id;
req.session.save(function(err) {
console.log(err); // prints out "undefined", so there's no error
});
console.log(req.session);
res.status(200).send({
message: "Login ok"
});
};
The frontend LoginController prints out "Login ok" (assuming that I entered correct credentials) and redirects me to the "main" page which uses main.controller.js:
In the meantime, the backend login controller prints out the following:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
userId: '5b4746cafe30b423181ad359' }
So there is definitely a userId in the session content. However, when I get redirected to the main.html and the main.controller.js gets invoked, it calls:
loginService.getSession().then(data => console.log(data));
(I just want to check if the userId is still in the session, and later I will perform some useful actions)
The getSession() method in the frontend LoginService only does the $http call:
function getSession() {
return $http.get("http://localhost:3333/session").then(
function(response) {
return response.data;
},
function(error) {
console.log(error.status);
}
);
}
This one calls the method which is defined in the backend LoginController:
exports.getSession = (req, res) => {
console.log(req.session);
if (req.session.userId) {
res
.status(200)
.send({ message: "Session existing with userId " + req.session.userId });
} else {
res.status(404).send({ message: "Session not existing" });
}
};
The frontend call prints the status code 404 in the console, while in the backend I get the following output:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
(no userId is present...)
One more thing... In a few tutorials I saw that they are using cookie-parser. However, when I try to use it, I don't get any data from my database, only the static text is displayed. So I removed it temporarily from server.js.
EDIT:
I tried adding MongoStore to my app:
const MongoStore = require("connect-mongo")(session);
...
app.use(
session({
name: "app.sid",
secret: "G4m1F1c4T10n_#ppL1c4t10N",
resave: true,
saveUninitialized: false,
cookie: { maxAge: 600000 },
store: new MongoStore({ url: "mongodb://localhost:27017/myAppDb" })
})
);
...but nothing changed.
How can I get my sessions to work?
As I found out after talking to several people, sessions are more or less deprecated and the new way of handling these things are the tokens. So I switched to JWT and it's working great.
I'm new to NUXT and SSR and I've been researching this for a few hours now and I can't seem to figure it out. I'm using JWT to authenticate users in my Nuxt app with a Bearer Token, which is working great until I hit refresh and lose my session.
Now I'm looking to persist sessions using express-session and connect-mongo. I can't get the cookie to set on the client to be included on future requests.
When a user is authenticated:
router.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user) {
res.status(401).send({success: false, msg: 'Authentication failed. User not found.'});
} else {
// check if password matches
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// if user is found and password is right create a token
var token = jwt.sign(user.toJSON(), config.secret, { expiresIn: 604800 });
req.session.authUser = { 'user': 'Test User' }
return res.json({success: true, token: token, user: user});
} else {
res.status(401).send({success: false, msg: 'Authentication failed. Wrong password.'});
}
});
}
The console.log above shows the authUser in the session.
Session {
cookie:
{ path: '/',
_expires: 2018-04-03T18:13:53.209Z,
originalMaxAge: 60000,
httpOnly: true },
authUser: { user: 'Test User' } }
When I look at my chrome devtools application cookies a connect.ssid hasn't been set and when I console.log(req.session) on future requests the authUser is missing.
My server code is:
// Passport
var passport = require('passport');
var passportJWT = require("passport-jwt");
var ExtractJwt = passportJWT.ExtractJwt;
var JwtStrategy = passportJWT.Strategy;
// Config File
let config = require('./config/settings.js')
// Initialize Express
var app = express();
// CORS-ENABLE
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://127.0.0.1:1337");
res.header("Access-Control-Allow-Credentials", 'true');
next();
});
app.use(cors())
const dbPath = 'mongodb://blogUser:blogUserPassword#localhost:27017/blog'
// Express Session
app.use(session({
secret: 'super-secret-key',
resave: false,
saveUninitialized: false,
store: new MongoStore({ url: dbPath }),
cookie: { maxAge: 60000 }
}))
// File Upload
app.use(fileUpload());
// view engine setup
// app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Routes
var index = require('./routes/index');
var users = require('./routes/users');
app.use('/api', index);
app.use('/users', users);
// Passport Config
app.use(passport.initialize());
app.use(passport.session())
// mongoose
const options = {
autoIndex: true, // Don't build indexes
reconnectTries: Number.MAX_VALUE, // Never stop trying to reconnect
reconnectInterval: 500, // Reconnect every 500ms
poolSize: 10, // Maintain up to 10 socket connections
// If not connected, return errors immediately rather than waiting for reconnect
bufferMaxEntries: 0
};
console.log(options);
// Localhost Connect
mongoose.connect(dbPath, options).then(
() => { console.log("connected !!!"); },
err => { console.log(err); }
);
Any and all help is appreciated.
If you want to use the server you create the problem with the session is the express router, because change res and req vars so like recommend in nuxt use this.
const express = require('express');
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
const app = express()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
You are missing this step
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
// So we can use res.status() & res.json()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
The req, res parameters need to be interchanged on the client side
Once you do router.post('/login') and logout
app.use('/api', router)
And that will work perfectly