[NodeJS] passport 정리

nakkim·2022년 4월 15일
0

Passport?

npm i passport

passport는 Node.js의 인증 미들웨어이다.

사용자이름/암호, 페이스북, 트위터 등을 이용한 인증을 지원한다.

요청 인증이라는 목적을 위해 만들어졌따.

캡슐화가 장점이므로 passport는 다른 모든 기능을 애플리케이션에 위임한다.

→ 코드를 깔끔하고 유지 보수가 쉽게 함 + passport를 애플리케이션에 쉽게 통합 가능

원래는 로그인을 아이디와 패스워드로 했다면?

요즘엔 다양한 형식으로 이루어진다.

소셜 네트워킹의 부상으로... 페이스북이나 트위터 등 OAuth 제공자를 이용한 싱글 사인온을 많이 사용하게 되었다.

API를 노출하는 서비스는 액세스를 보호하기 위해 토큰 기반의 자격 증명이 필요한 경우가 많다.

passport에서 'strategies'로 알려진 인증 메커니즘은 각각의 모듈로 묶인다.

애플리케이션은 불필요한 의존성을 만들지 않고, 사용할 인증 메커니즘을 선택할 수 있다.

인증은 복잡하지만 코드는 깔끔함!

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

Passport

Authenticate

인증 요청은 passport.authenticate() 을 부르고 전략을 고르면 된다.

authenticate() 의 함수 시그니처는 표준 Connect 미들웨어로, Express 프로그램에서 라우트 미들웨어로 편리하게 사용할 수 있다.

app.post('/login',
  passport.authenticate('local'),
	(req, res) => {
    // If this function gets called, authentication was successful.
    // 'req.user' contains the authenticated user.
    res.redirect('/users/' + req.user.username);
  });

인증 실패 시?

  • passport는 401 Unauthorized 상태로 응답
  • 뒤의 미들웨어들은 호출되지 않음

인증 성공 시?

  • 다음 미들웨어가 호출되고 req.user 속성이 인증된 사용자로 설정됨

Note: Strategies must be configured prior to using them in a route. Continue reading the chapter on configuration for details.

Redirects

요청 인증 후 사용

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

Flash Messages

상태 정보를 보여주기 위해 redirects와 함께 사용된다.

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

failureFlash: true 로 설정하면, strategy의 verify callback에서 주어진 에러 메세지를 사용한다.

이 방법이 짱인 이유: verify callback이 인증 실패 이유를 가장 정확하게 알 수 있기 때문

또는, 플래시 메세지를 설정할 수도 있다.

passport.authenticate('local', { failureFlash: 'Invalid username or password.'});

successFlash 옵션을 사용하면 인증에 성공했을 때의 메세지를 설정할 수 있다.

Note: Using flash messages requires a req.flash() function. Express 2.x provided this functionality, however it was removed from Express 3.x. Use of connect-flash middleware is recommended to provide this functionality when using Express 3.x.

Disable Sessions

인증 성공 후, passport는 로그인 세션을 생성한다.

대부분의 웹 애플리케이션에서 유용하지만, 필요없는 경우도 있다.

예를 들어, API 서버는 일반적으로 각 요청마다 자격 증명을 요구한다.

이 경우 세션 옵션을 false로 설정하여 비활성화할 수 있다.

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);
});

인증 실패 → user false로 설정

예외 발생 → err 설정

Configure

passport 사용에 필요한 설정

  1. Authentication strategies
  2. Application middleware
  3. Sessions (optional)

Strategies

strategies는 아이디/비밀번호 확인부터 OAuth 사용 인증, OpenID 사용 인증까지 다양하다.

Passport에게 인증을 요청하기 전에, strategy를 설정해야 한다.

use() 함수를 이용해서 설정한다.

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
	(username, password, done) => {
		User.findOne({ username: username }, (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);
		});
	}
));

Verify Callback

인증 정보들을 소유한 사용자를 찾는다.

passport가 요청을 인증

  1. 요청에 포함된 자격 증명 분석
  2. 자격 증명을 인수로 verify callback을 호출
  3. 자격 증명이 유효한 경우 done 호출
return done(null, user);
// 인증 실패하면 done(null, false);
// 예외 발생하면 done(err);

플래시 메세지를 전달할 수도 있다.

return done(null, false, { message: 'Incorrect password.' });

Note: 인증 실패와 예외를 잘 구분하는 것이 중요하다. 예외는 서버 문제를 말한다. (err 이 non-null 값으로 설정됨) 인증 실패는 서버가 올바르게 동작하는 정상 상태이다.

Middleware

Connect 또는 Express 기반의 애플리케이션에서, passport를 초기화하기 위해 passport.initialize() 미들웨어가 필요하다.

만약 지속되는 로그인 세션을 사용하면, passport.session() 또한 필요하다.

app.configure(() => {
	app.use(express.static('public'));
	app.use(express.cookieParser());
	app.use(express.session({ secret: 'keyboard cat' }));
	app.use(passport.initialize());
	app.use(passport.session());
	app.use(app.router);
});

Note: 세션을 활성화하는 건 권장되지만 선택 사항이다. 세션을 사용하려면 passport.session() 사용 전에 session() 을 사용해서 로그인 세션이 올바른 순서로 저장되도록 해야 한다.

Express 4.x에서는 Connect 미들웨어가 포함되지 않고, app.configure() 또한 삭제되었다.

따라서 npm 모듈에서 따로 설치해야 한다.

const session = require('express-session');

app.use(express.static('public'));
app.use(session({ secret: 'cats' }));
app.use(express.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session());
  • express-session 세션 데이터는 서버 쪽에 저장되고, 세션ID만 쿠키에 저장되어서 요청마다 쿠키와 함께 전송된다. npm i express-session express-session버전 1.5.0부터 cookie-parser 미들웨어는 필요하지 않다. 모듈이 직접 req/res에 쿠키를 읽고 쓰기 때문이다. (cookie-parser 사용 시, secret 이 해당 모듈과 cookie-parser 둘에서 동일하지 않으면 문제가 생길 수 있음)

Sessions(미완)

로그인 성공 시, 세션이 생성되고 사용자 브라우저의 쿠키를 통해 유지된다.

그 후의 요청에는 자격 증명이 아니라 세션을 식별하는 쿠키가 포함된다.

passport는 로그인 세션을 지원하기 위해 세션에 대한 사용자 인스턴스를 직렬화/역직렬화한다.

passport.serializeUser((user, done) => {
	done(null, user.id);
});

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

위 예시에서는 사용자 ID만 세션에 직렬화됨

→ 세션 내에 저장된 데이터 양을 적게 유지

이후 요청이 수신되면 해당 ID로 사용자를 찾고, req.user로 복원된다.


passport-42

npm i passport-42

42 계정과 OAuth 2.0 토큰을 사용하여 사용자를 인증한다.

verify 콜백은 사용자가 인증을 완료할 수 있도록 제공하는 cb를 호출해야 한다.

OAuth 2.0

유저의 타사 애플리케이션에 대한 액세스를 허가하는 인증 프레임워크를 제공한다.

인증에 성공하면 응용 프로그램은 자격 증명으로 사용할 토큰을 발급한다.

여기엔 두 가지 보안 이점이 있다.

  1. 응용 프로그램은 사용자의 이름과 암호를 저장할 필요가 없다.
  2. 토큰은 제한된 범위(예: read-only access)를 가질 수 있다.

OAuth 2.0을 사용하여 API 엔드포인트를 보호할 때 수행해야 하는 단계

  1. 응용 프로그램은 보호된 리소스에 대한 액세스 권한을 사용자에게 요청
  2. 사용자가 권한을 부여하면 토큰이 응용 프로그램에 발급
  3. 응용 프로그램은 토큰을 사용하여 보호된 리소스에 액세스하여 인증

passport-42 인증 구현

passport 등록

import passport from 'passport';
import session from 'express-session';
...
app.use(session({ secrete: 'cats' })); // session 방식 사용 시 추가
app.use(passport.initialize());
app.use(passport.session()); // session 방식 사용 시 추가
...
// passport 매뉴얼이랑 다른점? 
passport.serializeUser((user, done) => {
    done(null, user);
});
passport.deserializeUser((obj, done) => {
    done(null, obj);
});

strategy 정의

FortyTwoStrategy는 로그인, JWTStrategy는 API 접근 인증 수행

const FortyTwoStrategy = require('passport-42').Strategy;

export const Strategy42 = () => new FortyTwoStrategy({
    clientID: process.env.FORTYTWO_ID,
    clientSecret: process.env.FORTYTWO_SECRETE,
    callbackURL: 'http://localhost:4000/user/login/callback',
    profileFields: {
        '_id': 'id',
        'name': 'login',
    }
}, async (accessToken: any, refreshToken: any, profile: any, cb: any) => {
    return cb(null, profile);
});

passport.use() 를 이용해서 설정한다.

profile
nakkim.hashnode.dev로 이사합니다

0개의 댓글