KeihakuOh / Auth_GraphQL_React_App

Auth_GraphQL_React_Appp
0 stars 0 forks source link

passportについて #2

Open KeihakuOh opened 5 days ago

KeihakuOh commented 4 days ago
const mongoose = require('mongoose');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

const User = mongoose.model('user');

// SerializeUser is used to provide some identifying token that can be saved
// in the users session.  We traditionally use the 'ID' for this.
passport.serializeUser((user, done) => {
  done(null, user.id);
});

// The counterpart of 'serializeUser'.  Given only a user's ID, we must return
// the user object.  This object is placed on 'req.user'.
passport.deserializeUser((id, done) => {
  User.findById(id, (err, user) => {
    done(err, user);
  });
});

// Instructs Passport how to authenticate a user using a locally saved email
// and password combination.  This strategy is called whenever a user attempts to
// log in.  We first find the user model in MongoDB that matches the submitted email,
// then check to see if the provided password matches the saved password. There
// are two obvious failure points here: the email might not exist in our DB or
// the password might not match the saved one.  In either case, we call the 'done'
// callback, including a string that messages why the authentication process failed.
// This string is provided back to the GraphQL client.
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
  User.findOne({ email: email.toLowerCase() }, (err, user) => {
    if (err) { return done(err); }
    if (!user) { return done(null, false, 'Invalid Credentials'); }
    user.comparePassword(password, (err, isMatch) => {
      if (err) { return done(err); }
      if (isMatch) {
        return done(null, user);
      }
      return done(null, false, 'Invalid credentials.');
    });
  });
}));

// Creates a new user account.  We first check to see if a user already exists
// with this email address to avoid making multiple accounts with identical addresses
// If it does not, we save the existing user.  After the user is created, it is
// provided to the 'req.logIn' function.  This is apart of Passport JS.
// Notice the Promise created in the second 'then' statement.  This is done
// because Passport only supports callbacks, while GraphQL only supports promises
// for async code!  Awkward!
function signup({ email, password, req }) {
  const user = new User({ email, password });

  return User.findOne({ email })
    .then(existingUser => {
      if (existingUser) { throw new Error('Email in use'); }
      return user.save();
    })
    .then(user => {
      return new Promise((resolve, reject) => {
        req.logIn(user, (err) => {
          if (err) { reject(err); }
          resolve(user);
        });
      });
    });
}

// Logs in a user.  This will invoke the 'local-strategy' defined above in this
// file. Notice the strange method signature here: the 'passport.authenticate'
// function returns a function, as its indended to be used as a middleware with
// Express.  We have another compatibility layer here to make it work nicely with
// GraphQL, as GraphQL always expects to see a promise for handling async code.
function login({ email, password, req }) {
  return new Promise((resolve, reject) => {
    passport.authenticate('local', (err, user) => {
      if (!user) { reject('Invalid credentials.') }

      req.login(user, () => resolve(user));
    })({ body: { email, password } });
  });
}

module.exports = { signup, login };

req.logIn: Passportの機能で、ログイン状態にするためのメソッドです。

passport.serializeUser((user, done) => { done(null, user.id); }); ユーザーオブジェクトを受け取り、ユーザーのIDを使ってセッションに情報を保存します。これにより、セッションに保存するデータ量を削減し、IDだけでユーザーを識別可能にします。

passport.deserializeUser((id, done) => { User.findById(id, (err, user) => { done(err, user); }); }); ユーザーIDを受け取り、データベースからユーザー情報を取得してreq.userにセットします。これにより、リクエストごとにユーザー情報が利用可能になります。

ーーーーー

ログインフロー: 1.login() → passport.authenticate('local') → req.login(user) → serializeUser() → セッション生成 2.serializeUser(): セッションにユーザーIDを保存するために、req.login(user)時に呼ばれます。 3.deserializeUser(): 認証済みのユーザーが新しいリクエストを送信したときに呼ばれ、セッションのIDを元にユーザー情報をreq.userに設定します。 4.クッキー: connect.sidがセッションIDを保存するクッキー名で、ブラウザに送信されます。