问题
I'm newbie and trying to start writing authorization API with graphql and passport, mostly my google search results are used the trendy JWT approach but I'm not sure if it safe, this article https://dev-blog.apollodata.com/a-guide-to-authentication-in-graphql-e002a4039d1 is good for me to start but I have some struggles in my mind:
I need to write API only, so the method
app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login', failureFlash: true }) );
It's not make sense for me.
- How about the session expiry time (ttl). Should I need to use a cache layer such as Redis (hard for me to image how to combine mongodb and redis with graphql)
- How to link user to post and test it in Graphiql
Here is my incomplete code: server.js
import express from 'express'
import logger from 'morgan'
import path from 'path'
import bodyParser from 'body-parser'
import { graphqlExpress, graphiqlExpress } from 'apollo-server-express'
import session from 'express-session'
import uuid from 'node-uuid'
import passport from 'passport'
import schema from './schema'
import connectMongo from './mongo-connector'
import { authenticate } from './auth'
import routes from './routes'
const start = async () => {
const mongo = await connectMongo()
const app = express()
const { PORT = 8080 } = process.env
app.disable('x-powered-by')
app.set('views', path.join(__dirname, '../views'))
app.set('view engine', 'pug')
app.use(logger('dev', {
skip: () => app.get('env') === 'test'
}))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(express.static(path.join(__dirname, '../public')))
app.use(session({
genid: req => uuid.v4(),
secret: "mySecret33",
saveUninitialized: true,
resave: true,
cookie: {
maxAge : 3600000,
//secure: true
},
}))
app.use(passport.initialize())
app.use(passport.session())
app.use('/', routes)
const buildOptions = async (req, res) => {
const user = await passport.authenticate('local',req, mongo.Users)
return {
context: {mongo, user},
schema
}
}
app.use('/graphql', graphqlExpress(buildOptions), (req, res) => {
let data
return data => res.send(JSON.stringify(data))
})
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
}))
app.use((req, res, next) => {
const err = new Error('Not Found')
err.status = 404
next(err)
})
app.use((err, req, res, next) => {
res
.status(err.status || 500)
.render('error', {
message: err.message
})
})
app.listen(PORT, () => console.log(`🚀 \x1b[34m Server start on port ${PORT} \x1b[0m`))
}
start()
mongo-connector.js
import { MongoClient } from 'mongodb'
const MONGO_URL = 'mongodb://localhost:27017/wemak'
const connectMongo = async (err, db) => {
db = await MongoClient.connect(MONGO_URL)
if (err) {
console.error('Unable to connect db')
process.exit()
} else {
return {
Users: db.collection('users'),
UserPosts: db.collection('user_posts'),
}
}
}
export default connectMongo
authenticate.js
import passport from 'passport'
import bcrypt from 'bcrypt'
const LocalStrategy = require('passport-local').Strategy
export const authenticate = async ({ headers: { authorization } }, Users) => {
passport.use('local', new LocalStrategy(
(name, password, done) => {
Users.find({ where: {username: name} })
.then(user => {
bcrypt.compare(password, user.password, (err, result) => {
if (err) { return done(err)}
if (!result) {
return done(null, false, { message: 'Invalid email or password' })
}
return done(null, user)
})
.catch(err => done(err))
})
}
))
passport.serializeUser((user, done) => done(null, user.id))
passport.deserializeUser((id, done) => {
Users.get(id).then((user, err) => done(err, user))
})
}
resolver.js
export default {
Query: {
allPosts: async (root, data, { mongo: { UserPosts } }) => {
return await UserPosts.find({}).toArray()
},
},
Mutation: {
createUser: async (root, data, { mongo: {Users} }) => {
const newUser = {
name: data.name,
email: data.email,
password: data.password,
}
const response = await Users.insert(newUser)
return Object.assign({id: response.insertedIds[0]}, newUser)
},
signinUser: async (root, data, { mongo: {Users} }) => {
const user = await Users.findOne({ email: data.email })
if (data.password === user.password) {
return { user }
}
},
createUserPost: async (root, data, { mongo: {UserPosts}, user }) => {
const newPost = Object.assign({ postedById: user && user._id }, data)
const response = await UserPosts.insert(newPost)
return Object.assign({ id: response.insertedIds[0] }, newPost)
},
},
User: {
id: root => root._id || root.id,
},
UserPost: {
id: root => root._id || root.id,
postedBy: async ({ postedById }, data, { mongo: {Users} }) => {
return await Users.findOne({ _id: postedById })
},
},
}
schema.js
import { makeExecutableSchema } from 'graphql-tools'
import resolvers from './resolvers'
// define types
const typeDefs = `
type Query {
allPosts: [UserPost!]!
}
type Mutation {
createUser(name: String!, email: String!, password: String!): User
signinUser(email: String!, password: String!): SigninPayload!
createUserPost(title: String!, description: String!): UserPost
}
type User {
id: ID!
name: String!
email: String!
password: String!
}
type SigninPayload {
user: User
}
type UserPost {
id: ID!
title: String!
description: String!
postedBy: User
}
`
export default makeExecutableSchema({ typeDefs, resolvers })
来源:https://stackoverflow.com/questions/45294962/how-to-email-authentication-api-with-graphql-and-passport