How to email authentication API with graphql and passport

爱⌒轻易说出口 提交于 2019-12-13 03:46:30

问题


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:

  1. 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.

  1. 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)
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!