๐Ÿ“” Passport (2/2): Docs ๋ฒˆ์—ญ

Jake_Youngยท2020๋…„ 10์›” 10์ผ
0
post-thumbnail

๐Ÿ’ก ์ •๋ณด ์ถœ์ฒ˜

  1. passport-official website : Click Here
  2. github-passport : Click Here
  3. npm-passport : Click Here

๐Ÿ”‘ ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€ Documentation ๋ฒˆ์—ญ

http://www.passportjs.org/docs/

๐Ÿ˜€ Overview

  • $ npm install passport
app.post(
  '/login',
  passport.authenticate(
    'local',
    {
      successRedirect: '/',
      failureRedirect: '/login'
    }
  )
);

๐Ÿ˜ Authenticate

  • ๊ทธ๋ƒฅ ๋ผ์šฐํ„ฐ์— ๋ฏธ๋“ค์›จ์–ด์ฒ˜๋Ÿผ ์“ฐ๋ฉด ๋œ๋‹ค.
  • strategy๋งŒ ์ •ํ•ด์„œ ์•Œ๋ ค์ฃผ๋ฉด ๋œ๋‹ค.
  • ๋งŒ์•ฝ ์ธ์ฆ์ด ์‹คํŒจํ•˜๋ฉด passport๋Š” 401 Unauthorized๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ์ธ์ฆ์ด ์„ฑ๊ณตํ•˜๋ฉฐ req.user์— user ์ •๋ณด๊ฐ€ ๋‹ด๊ธฐ๊ณ  next()๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
  • flash message ๊ธฐ๋Šฅ์€ req.flash()๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ด๋Š” Express 2 ๋ฒ„์ „์—๋Š” ๋‚ด์žฅ๋˜์–ด ์žˆ์ง€๋งŒ, Express 3๋ฒ„์ „์—๋Š” ์—†๊ธฐ ๋•Œ๋ฌธ์— connect-flash๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • var flash = require('connect-flash'); app.use(flash());
// default
app.post('/login',
  passport.authenticate('local'),
  function(req, res) {
    // If this function gets called, authentication was successful.
    // `req.user` contains the authenticated user.
    res.redirect('/users/' + req.user.username);
  });

// redirect
app.post('/login',
  passport.authenticate(
  	'local',
    {
      successRedirect: '/',
      failureRedirect: '/login',
      failureFlash: true // ์ด๋Ÿฌ๋ฉด error ๋ฉ”์„ธ์ง€๋ฅผ ์œ ์ €์—๊ฒŒ Flash๋กœ ๋ณด์—ฌ์ค€๋‹ค.
    }
));

// flash ์˜ˆ์ œ
passport.authenticate('local', { failureFlash: 'Invalid username.' });
passport.authenticate('local', { successFlash: 'Welcome!' });

// Disable Sessions
app.get('/api/users/me',
  passport.authenticate('basic', { session: false }),
  function(req, res) {
    res.json({ id: req.user.id, username: req.user.username });
  });

// Custom Callback
app.get('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) { return next(err); }
    if (!user) { return res.redirect('/login'); }
    req.logIn(user, function(err) {
      if (err) { return next(err); }
      return res.redirect('/users/' + user.username);
    });
  })(req, res, next);
});

๐Ÿ˜‚ Configure

  • strategy, app middleware, sessions(optional) ์„ธ ๊ฐ€์ง€๋ฅผ ์„ค์ •ํ•ด์•ผํ•œ๋‹ค.
// strategy ์‚ฌ์šฉ๋ฒ•
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    });
  }
));

// middleware ์‚ฌ์šฉ๋ฒ• (Express 4)
var session = require("express-session");
var bodyParser = require("body-parser");

app.use(express.static("public"));
app.use(session({ secret: "cats" }));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session());

// sessions
passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

๐Ÿ˜ƒ Username & Password

  • the most widely used way for websites is the "passport-local"
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
  // you can skip right below object
  // if you use input named username and password
  {
    usernameField: 'email',
    passwordField: 'passwd'
  },
  function(username, password, done) {
    User.findOne({ username: username }, function(err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    });
  }
));

app.post('/login',
  passport.authenticate(
    'local',
    { 
      successRedirect: '/',
      failureRedirect: '/login',
      failureFlash: true
    }
  )
);

๐Ÿ˜† OpenID

  • openID๋Š” ์‚ฌ์šฉ์ž์˜ openID Provider๋ฅผ ํ†ตํ•ด ์ธ์ฆ์„ ์ง„ํ–‰ํ•˜๋Š” ๋กœ๊ทธ์ธ ๋ฐฉ์‹์ด๋‹ค.
  • npm install passport-openID๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
var passport = require('passport');
var OpenIDStrategy = require('passport-openid').Strategy;

passport.use(new OpenIDStrategy({
    // ๋กœ๊ทธ์ธ ํ›„ ๋Œ์•„๊ฐ€์•ผํ•  ๊ณณ(๋‚ด ์‚ฌ์ดํŠธ ์ฃผ์†Œ)
    returnURL: 'http://www.example.com/auth/openid/return',
    // ๋‚ด ์‚ฌ์ดํŠธ์˜ root url
    realm: 'http://www.example.com/'
  },
  function(identifier, done) {
    User.findOrCreate({ openId: identifier }, function(err, user) {
      done(err, user);
    });
  }
));

app.post('/auth/openid', passport.authenticate('openid'));

app.get('/auth/openid/return',
  passport.authenticate('openid', { successRedirect: '/',
                                    failureRedirect: '/login' }));

// if you need profile info, you can get those like below
passport.use(new OpenIDStrategy({
    returnURL: 'http://www.example.com/auth/openid/return',
    realm: 'http://www.example.com/',
    profile: true
  },
  function(identifier, profile, done) {
    // ...
  }
));
  • HTML Form ์–‘์‹
<form action="/auth/openid" method="post">
    <div>
        <label>OpenID:</label>
        <input type="text" name="openid_identifier"/><br/>
    </div>
    <div>
        <input type="submit" value="Sign In"/>
    </div>
</form>

๐Ÿ˜‡ OAuth 1.0

  • OAuth๋ž€ API ์ ‘๊ทผ์„ ํ—ˆ๊ฐ€ํ•ด์ฃผ๋Š” ํ‘œ์ค€ํ™”๋œ ํ”„๋กœํ† ์ฝœ์ด๋‹ค.
  • npm install passport-oauth
var passport = require('passport');
var OAuthStrategy = require('passport-oauth').OAuthStrategy;

passport.use('provider', new OAuthStrategy({
    requestTokenURL: 'https://www.provider.com/oauth/request_token',
    accessTokenURL: 'https://www.provider.com/oauth/access_token',
    userAuthorizationURL: 'https://www.provider.com/oauth/authorize',
    consumerKey: '123-456-789',
    consumerSecret: 'shhh-its-a-secret'
    callbackURL: 'https://www.example.com/auth/provider/callback'
  },
  function(token, tokenSecret, profile, done) {
    User.findOrCreate(..., function(err, user) {
      done(err, user);
    });
  }
));

app.get('/auth/provider', passport.authenticate('provider'));

app.get('/auth/provider/callback',
  passport.authenticate('provider', { successRedirect: '/',
                                      failureRedirect: '/login' }));

// It can be used like this
<a href="/auth/provider">Log In with OAuth Provider</a>

๐Ÿ˜ OAuth 2.0

  • OAuth 1.0 ๊ณผ ์‹ค์ œ ์›Œํฌํ”Œ๋กœ์šฐ๋Š” ๊ฐ™์ง€๋งŒ, ์ดˆ๊ธฐ ๋ฒ„์ „์˜ ๋‹จ์ ๋“ค์„ ๊ฐœ์„ ํ•ด๋‘์—ˆ๋‹ค.
var passport = require('passport')
  , OAuth2Strategy = require('passport-oauth').OAuth2Strategy;

passport.use('provider', new OAuth2Strategy({
    authorizationURL: 'https://www.provider.com/oauth2/authorize',
    tokenURL: 'https://www.provider.com/oauth2/token',
    clientID: '123-456-789',
    clientSecret: 'shhh-its-a-secret'
    callbackURL: 'https://www.example.com/auth/provider/callback'
  },
  function(accessToken, refreshToken, profile, done) {
    User.findOrCreate(..., function(err, user) {
      done(err, user);
    });
  }
));

app.get('/auth/provider', passport.authenticate('provider'));

app.get('/auth/provider/callback',
  passport.authenticate('provider', { successRedirect: '/',
                                      failureRedirect: '/login' }));

// you can limit the scope of access
// it is provider-specific. so check the docs of each provider
app.get('/auth/provider',
  passport.authenticate('provider', { scope: ['email', 'sms'] })
);

๐Ÿ˜œ User Profile

  • provider๋ฅผ ํ†ตํ•ด ์œ ์ €๊ด€๋ฆฌ๋ฅผ ํ•˜๋Š” ๊ฒฝ์šฐ, ์œ ์ € ์ •๋ณด๊ฐ€ ์ œ๊ฐ๊ฐ์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
  • passport๋Š” ์ด๋ฅผ ์–ด๋Š์ •๋„ ํ‘œ์ค€ํ™”ํ•˜์—ฌ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค.

๐Ÿง Google

  • npm install passport-google-oauth
  • google develope console์—์„œ ID์™€ Key๋ฅผ ๋ฐœ๊ธ‰ ๋ฐ›์•„์•ผํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  Google+ API ๋ฅผ ํ™œ์„ฑํ™”ํ•ด๋ผ.
  • ์•ˆ ๊ทธ๋Ÿฌ๋ฉด ์œ ์ €์˜ ์‹ ์ƒ ์ •๋ณด๋ฅผ ๋ชป ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

OAuth 1.0 - Google

var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuthStrategy;

passport.use(new GoogleStrategy({
    consumerKey: GOOGLE_CONSUMER_KEY,
    consumerSecret: GOOGLE_CONSUMER_SECRET,
    callbackURL: "http://www.example.com/auth/google/callback"
  },
  function(token, tokenSecret, profile, done) {
      User.findOrCreate({ googleId: profile.id }, function (err, user) {
        return done(err, user);
      });
  }
));

app.get('/auth/google',
  passport.authenticate('google', { scope: 'https://www.google.com/m8/feeds' });

app.get('/auth/google/callback', 
  passport.authenticate('google', { failureRedirect: '/login' }),
  function(req, res) {
    res.redirect('/');
  });

OAuth 2.0 - Google

var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

passport.use(new GoogleStrategy({
    clientID: GOOGLE_CLIENT_ID,
    clientSecret: GOOGLE_CLIENT_SECRET,
    callbackURL: "http://www.example.com/auth/google/callback"
  },
  function(accessToken, refreshToken, profile, done) {
       User.findOrCreate({ googleId: profile.id }, function (err, user) {
         return done(err, user);
       });
  }
));

app.get('/auth/google',
  passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/plus.login'] }));

app.get('/auth/google/callback', 
  passport.authenticate('google', { failureRedirect: '/login' }),
  function(req, res) {
    res.redirect('/');
  });

๐Ÿ˜Ž Basic & Digest

  • ์ด ๋‘ ์Šคํ‚ค๋งˆ๋Š” ์•„์ด๋””์™€ ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์œ ์ €๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ API endpoint๋ฅผ ๋ณดํ˜ธํ•œ๋‹ค.
  • ํ•ดํ‚น์— ์ทจ์•ฝํ•œ ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ OAuth 2.0์„ ์‚ฌ์šฉํ•  ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.
  • Basic๊ณผ Digest ์Šคํ‚ค๋งˆ๋Š” passport-http ๋ชจ๋“ˆ์„ ํ†ตํ•ด ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.
passport.use(new BasicStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      if (!user.validPassword(password)) { return done(null, false); }
      return done(null, user);
    });
  }
));
app.get('/api/me',
  passport.authenticate('basic', { session: false }),
  function(req, res) {
    res.json(req.user);
  });
passport.use(new DigestStrategy({ qop: 'auth' },
  function(username, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      return done(null, user, user.password);
    });
  },
  function(params, done) {
    // validate nonces as necessary
    done(null, true)
  }
));
app.get('/api/me',
  passport.authenticate('digest', { session: false }),
  function(req, res) {
    res.json(req.user);
  });

๐Ÿคฉ OAuth 2.0

  • OAuth 2.0 ์€ ํ† ํฐ์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ด๋Š” ๋‘ ๊ฐ€์ง€ ํฐ ์žฅ์ ์ด ์žˆ๋‹ค.
  • ์ฒซ์งธ๋กœ, ํ† ํฐ์€ ์œ ์ €์˜ ์ด๋ฆ„๊ณผ ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • ๋‘˜์งธ๋กœ, ํ† ํฐ์—๋Š” ์ œ์•ฝ์„ ๋‘˜ ์ˆ˜ ์žˆ๋‹ค(์ฝ๊ธฐ-์ „์šฉ ์ฒ˜๋Ÿผ..)
  • npm install passport-http-bearer
passport.use(new BearerStrategy(
  function(token, done) {
    User.findOne({ token: token }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      return done(null, user, { scope: 'read' });
    });
  }
));
app.get('/api/me',
  passport.authenticate('bearer', { session: false }),
  function(req, res) {
    res.json(req.user);
  });

๐Ÿฅบ Login

  • login ์„ธ์…˜์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด login() ํ•จ์ˆ˜๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ๋ ‡๊ฒŒ ์ €์žฅ๋œ user ์ •๋ณด๋Š” req.user๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • passport.authenticate() ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์“ฐ๋ฉด req.login()์ด ์ž๋™์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.
  • ๊ทธ๋ž˜์„œ ์ด ํ•จ์ˆ˜๋Š” ๋ณดํ†ต ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ๋งŒ ์‚ฌ์šฉ๋œ๋‹ค.
req.login(user, function(err) {
  if (err) { return next(err); }
  return res.redirect('/users/' + req.user.username);
});

๐Ÿ˜ค Logout

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

๐Ÿค— Authorize

  • google, facebook ๋“ฑ์„ ํ†ตํ•ด ๊ฐ€์ž…ํ•œ ํšŒ์›์„ ํ•˜๋‚˜๋กœ ํ†ตํ•ฉํ•ด์•ผํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค.
  • passport.authorize()์œผ๋กœ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ ์ •๋ณด๋“ค์€ req.account์— ์ €์žฅ๋œ๋‹ค.
app.get('/connect/twitter',
  passport.authorize('twitter-authz', { failureRedirect: '/account' })
);

app.get('/connect/twitter/callback',
  passport.authorize('twitter-authz', { failureRedirect: '/account' }),
  function(req, res) {
    var user = req.user;
    var account = req.account;

    // Associate the Twitter account with the logged-in user.
    account.userId = user.id;
    account.save(function(err) {
      if (err) { return self.error(err); }
      self.redirect('/');
    });
  }
);

passport.use('twitter-authz', new TwitterStrategy({
    consumerKey: TWITTER_CONSUMER_KEY,
    consumerSecret: TWITTER_CONSUMER_SECRET,
    callbackURL: "http://www.example.com/connect/twitter/callback"
  },
  function(token, tokenSecret, profile, done) {
    Account.findOne({ domain: 'twitter.com', uid: profile.id }, function(err, account) {
      if (err) { return done(err); }
      if (account) { return done(null, account); }

      var account = new Account();
      account.domain = 'twitter.com';
      account.uid = profile.id;
      var t = { kind: 'oauth', token: token, attributes: { tokenSecret: tokenSecret } };
      account.tokens.push(t);
      return done(null, account);
    });
  }
));

passport.use(new TwitterStrategy({
    consumerKey: TWITTER_CONSUMER_KEY,
    consumerSecret: TWITTER_CONSUMER_SECRET,
    callbackURL: "http://www.example.com/auth/twitter/callback",
    passReqToCallback: true
  },
  function(req, token, tokenSecret, profile, done) {
    if (!req.user) {
      // Not logged-in. Authenticate based on Twitter account.
    } else {
      // Logged in. Associate Twitter account with user.  Preserve the login
      // state by supplying the existing user after association.
      // return done(null, req.user);
    }
  }
));
profile
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ํŒŒ์ด์ฌ ๊ทธ๋ฆฌ๊ณ  ์ปดํ“จํ„ฐ์™€ ๋„คํŠธ์›Œํฌ

0๊ฐœ์˜ ๋Œ“๊ธ€