OAuth 2.0(Open Authorization 2.0, OAuth2)은 인증을 위한 개방형 표준 프로토콜
이 프로토콜에서는 Third-Party 프로그램에게 리소스 소유자를 대신하여 리소스 서버에서 제공하는 자원에 대한 접근 권한을 위임하는 방식을 제공한다.
구글, 페이스북, 카카오, 네이버 등에서 제공하는 간편 로그인 기능도 OAuth2 프로토콜 기반의 사용자 인증 기능을 제공하는 것이다.
용어 | 설명 |
---|---|
Authentication | 인증, 접근 자격이 있는지 검증하는 단계 |
Authorization | 리소스 접근 권한을 부여하는 것. 인가가 완료되면 리소스 접근 권한이 담긴 Access Token이 클라이언트에게 부여된다. |
Access Token | 리소스 서버에게서 리소스 소유자의 보호된 자원을 획득할 때 사용되는 만료 기간이 있는 Token. |
Refresh Token | Access Token 만료시 이를 갱신하기 위한 용도로 사용하는 Token |
$ npm install passport-google-oauth20
passport-google-oauth20
사용 전에 구글 클라우드 플랫폼에 어플리케이션을 등록해야한다. 클라이언트 ID와 secret이 필요하며 URI 설정도 필요함
Google authentication strategy는 사용자의 구글 계정과 OAuth 2.0 tokens을 사용하여 사용자를 인증한다.
strategy 생성 시 client ID와 secret이 필요하다.
또한 액세스 토큰을 받는 verify callback이 필요함
Passport.js 공식 문서를 확인하면, passport-google-oauth20
패키지를 사용하는 방법은 아래와 같다
// 전략 불러오기
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
// 구글이 현재 프로젝트를 인식하기 위해서 clientID와 Secret을 식별한다
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
// 특정 Url로 요청을 보낸다
callbackURL: "http://www.example.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
// 컬렉션에서 find를 하거나 create를 하거나 함수 작성은 자유롭게
// profile.id는 구글 개발자 센터에서 프로젝트 생성시 설정한 profile을 말하는 것임
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
User.findOrCreate
은 실제 mongoose의 메서드가 아니다. 근데 저 부분을 지원해주는 패키지가 있어서 그걸 사용할거다 ㅎ
npm install mongoose-findorcreate
화면에 google을 사용하여 로그인하기 위한 버튼을 추가해주고 url을 설정해준다
(<a href="/auth/google" class="btn btn-block" role="button">
)
해당 버튼을 클릭하면 HTTP GET 메서드를 보낸다.
app.js에서 GET /auth/google에 대한 로직을 작성해준다
app.js
app.get('/auth/google',
// 구글 전략을 사용하여 인증을 요청한다.
// scope는 범위 설정해주는 것인데, 구글 프로젝트 생성시 설정했던 profile 내용을 요청한다.
passport.authenticate('google', { scope: ['profile'] })
);
이래 화면 바뀜
연동할 계정을 선택하면 callbackurl로 요청을 보내게 된다
callbackurl에 대한 요청 처리 코드작성
app.get(
'/auth/google/secrets',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
res.redirect('/secrets');
}
);
인증이 완료되면 시크릿 페이지로 이동
app.get('/secrets', (req, res) => {
// secret이 있는지 확인 $ne
User.find({ secret: { $ne: null } }, (err, foundUsers) => {
if (err) {
console.log(err);
} else {
if (foundUsers) {
console.log(foundUsers);
res.render('secrets', { usersWithSecrets: foundUsers });
}
}
});
});
// 새 시크릿 게시글 작성
app.post('/submit', function (req, res) {
const submittedSecret = new Secret({ content: req.body.secret });
// user가 authenticated되고 세션이 저장되면, 유저의 상세정보는 req.user에 저장된다
User.findById(req.user.id, function (err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
foundUser['secrets'].push(submittedSecret);
foundUser.save(function () {
res.redirect('/secrets');
});
}
}
});
});