-
Notifications
You must be signed in to change notification settings - Fork 4
[Oauth2] 42api 로그인 구현
인증요청에 사용되는 node module.
인증 방식은 strategy라고 칭하며 개별 모듈로 패키지 돼있다. 어플리케이션은 어떤 strategy를 적용할지 선택할 수 있다.
- 우리는
passport-42
를 strategy로 사용한다.
성공적으로 인증하고 나면 passport는 지속되는 로그인 세션을 설정한다.
인증 요청은 passport.authenticate()
를 호출하는 것으로 간단하며, 이때 어떤 strategy를 적용할지 명시해야 한다. (strategy는 라우트에서 사용하기 이전에 설정돼야 한다.)
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);
});
기본적으로 인증이 실패하면 passport는 401 unauthorized
응답을 반환하며, 추가적인 라우트 핸들러를 불러오지 않는다. 인증이 성공하면 다음 핸들러가 불러와져 req.user
속성이 인증된 유저로 설정될 것이다.
요청을 인증한 뒤, 리다이렉션이 흔히 일어난다.
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
이 경우 리다이렉트 옵션이 기본 행위를 덮어버린다. 인증이 성공하면 유저는 홈페이지로 리다이렉션 된다. 인증이 실패하면 유저는 다시 한번 로그인 시도를 하기 위해 로그인 사이트로 리다이렉션 된다.
인증을 위해 Passport를 사용하기 위해선 다음과 같은 것들이 필요하다.
- authentication strategies
- application middleware
- sessions
strategies와 그 설정은 use()
함수를 통해 공급된다. 다음은 LocalStrategy
를 사용해 username/password 인증하는 예시다.
var passport = require('passport')
, 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);
});
}
));
strategy는 verify callback이라는 걸 필요로 한다. 이것의 목적은 인증서를 가지고 있는 유저를 찾는 것이다. passport는 요청을 인증하고 난 뒤 요청에 담겨져 있는 인증을 파싱한다. 그리고는 인증서를 argument로 verify callback을 부른다. 인증서가 유효하면, veryfy callback은 done
을 user와 함께 부른다(return done(null, user);
). 그렇지 않으면 done
은 실패를 나타내기 위해 false와 함께 부른다(return done(null, false);
). 인증서를 증명하는 과정에서 exception이 발생하면 에러와 함께 done
을 부른다(return done(err);
). 아래처럼 메세지를 같이 전달해 flash message를 띄워줄 수도 있다.
return done(null, false, { message: 'Incorrect password.' });
Express 기반 어플리케이션에서 passport를 시작하려면 passport.initialize()
middleware가 필요하다. 당신의 어플리케이션이 지속적인 로그인 세션을 사용한다면, passport.session()
middleware도 필히 사용해야 한다.
app.configure(function() {
app.use(express.static('public'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
});
세션 지원이 선택적이긴 하지만 대부분의 어플리케이션에 권장된다. 세션 서포트를 허용하려면, 로그인 세션을 올바른 순서로 반환하기 위해서는session()
을 passport.session()
이전에 사용해야 한다.
일반적인 웹 어플리케이션에서 유저를 인증하는 인증서는 로그인 리퀘스트에서만 전달된다. 인증이 성공하면 세션은 성립되고 유저 브라우저의 쿠키셋으로 유지된다. 이어지는 요청은 인증서를 포함하지 않지만 쿠키를 사용해 세션을 확인한다. 로그인 세션을 지원하기 위해, passport는 user
인스턴스를 세션으로부터 serialize, deserialize한다.
passport.serializeUser(function(user, done) {
done(null, user.id); // user ID가 session에 serialized
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
🔗 패스포트의 동작 원리와 인증 구현(ko/blog)
🔗 Passport(en/official
passport-42
가 포괄하는 내용이므로, 아래부터는 passport-42
의 문서를 기반으로 한 설명이다.
42에 어플리케이션을 등록해야 한다. 42 Applications에 어플리케이션을 등록하고, UID
와 SECRET
을 받아온다. 인증 후 리다이렉션 페이지도 설정한다.
42 인증 strategy는 42 계정과 Oauth2.0 토큰을 사용해 유저를 인증한다. 앞서 발행한 어플리케이션의 UID
와 SECRET
이 strategy를 생성할 때 사용된다. 이 strategy를 생성할 때 verify callback 또한 필요하다. verify callback은 access token, optional refresh token, 42 프로필의 profile
도 받는다. verify callback이 인증을 완료하려면 cb
를 불러야 한다.
var FortyTwoStrategy = require('passport-42').Strategy;
passport.use(new FortyTwoStrategy({
clientID: FORTYTWO_APP_ID,
clientSecret: FORTYTWO_APP_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/42/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ fortytwoId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
요청을 인증하기 위해 passport.authenticate()
를 42
strategy와 함께 사용한다. 다음은 Express 미들웨어에서의 예시다.
app.get('/auth/42',
passport.authenticate('42'));
app.get('/auth/42/callback',
passport.authenticate('42', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
위의 예시들은 express 기준이다. nest.js에서 어떻게 사용할지 알아보자. 우선 passport를 Nest 어플리케이션과 통합하려면 @nestjs/passport
모듈을 사용해야 한다. passport 는 다음과 같은 순서로 실행된다.
- 인증서(username/password, JSON Web Token (JWT), or identity token from an Identity Provider 등)으로 유저를 인증한다
- 인증 상태를 관리한다(JWT 같은 portable token 또는 express session을 통해)
- 추후 라우트 핸들러에서의 사용을 위해
Request
오브젝트에 인증된 유저의 정보를 덧붙인다
🔗 Nest.js Authentication(en/docs) 🔗 위 문서를 일부 번역한 블로그(ko/blog) 🔗 Nest.js 공식 샘플 코드(en/github) 🔗 Nest.js에 Passport 설치 튜토리얼(en/youtube)