웹 서비스를 이용하다보면 가장 짜증나는 것이 아이디랑 비번 찾기이다... 왜 항상 내가 쓴 비밀번호는 틀리는지, 항상 내가 생각한 아이디가 없다고 하는지 모르겠다.
비밀번호의 빡침을 그나마 해소시켜주는 것이 바로 소셜 로그인이다. 자주 들어가는 소셜 서비스의 아이디로 간단하게 서비스를 이용할 수 있는 편리한 시스템인 것 같다.
이번에는 Express X Passport.js 로 간단한 소셜 로그인을 구현해 보았다.
Passport.js는 node.js에서 사용하는 인증 미들웨어이다. 다양한 소셜 서비스의 인증을 지원하고 편하게 사용할 수 있다.
자세한 내용은 직접 코드를 작성하면서 소개하겠다.
서버를 만들기 전에 다음의 규칙?순서?생각?에 따라서 진행하도록 하겠다.
GET '/'
GET '/login'
GET '/login/google'
GET '/login/google/callback'
Express Generater 로 간단히 서버를 만들어보았다.
그리고 우리가 사용할 passport.js를 설치한다.
$ npm install passport
불필요한 부분을 모두 지우고 다음과 같이 필요한 endpoint만 작성해보았다.
app.get('/', function (req, res, next) {
res.render('index', { title: 'Express' });
});
app.get('/login', function (req, res, next) {
res.render('login', { title: 'Login' })
});
app.get('/login/google', function (req, res, next) {
});
app.get('/login/google/callback', function (req, res, next) {
});
먼저 Google 플랫폼에 나의 앱을 등록하고 인증 정보를 만들어야 한다. 구글 로그인의 인증 정보를 이용하기 위해서이다.
구글 클라우드 플랫폼 > api및 서비스 > 사용자 인증 정보
로 접속하여 인증정보를 만든다.
Oauth ID를 생성하면 클라이언트 ID와 Secret key를 받을 수 있다. Passport 설정 시 입력해야 한다.
다음의 3단계로 passport 설정이 필요하다.
우리 서버에서 google로 인증 요청을 보내는 것을 passport가 대신하기 때문에 해당 인증 정보를 사전에 세팅하는 과정이라고 보면 된다. 세팅은 다음과 같은 코드 진행으로 이루어진다.
그 전에 google 인증을 위한 추가 모듈 설치가 필요하다.
$ npm install passport-google-oauth20
그리고 passport를 세팅하는 코드는 다음과 같다.
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/login/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
return cb(null, profile);
}
));
GoogleStarategy
의 인자는 다음과 같다.
clientID
: 구글 인증 생성시 받았던 클라이언트 IDclientSecret
: 구글 인증 생성시 받았던 클라이언트 secret keycallbackURL
: 구글 인증 생성시 설정했던 redirection URI 그러므로 endpoint로 만든 /login/google/callback
을 적는다.인증에 대한 확인 콜백함수로는 4개의 인자를 받는다.
여기서 마지막 인자인 callback함수를 실행하면 두 번째 인자로 넣은 정보를 serializeUser
미들웨어로 전달한다.serializeUser
미들웨어는 전달받은 객체(정보)를 세션에 저장하는 역할을 한다. 그리고 저장한 객체를 deserializeUser
미들웨어로 전달한다.
**deserializeUser
미들웨어는 서버로 들어오는 요청마다 세션 정보가 유효한 지를 검사하는 역할을 한다.
제대로 된 정보가 들어왔으면 req.user
에 그 정보를 저장하고 요청마다 유저 정보를 넘겨준다. 다음의 코드처럼 작성한다.
passport.serializeUser((user, done) => {
done(null, user); // user객체가 deserializeUser로 전달됨.
});
passport.deserializeUser((user, done) => {
done(null, user); // 여기의 user가 req.user가 됨
});
설정을 완료한 다음 라우트 설정을 해준다.
GET /login/google
Google 로그인 페이지로 이동시켜야 한다. passport의 authenticate
메소드를 이용하면 된다. 사용 방법은 다음과 같다.
app.get('/login/google',
passport.authenticate('google', { scope: ['profile'] })
);
scope
는 받아오는 정보 중에 profile 정보를 받아온다는 것을 뜻한다. google에 로그인을 하면 다양한 정보가 넘어오는데 그 중에 우리가 필요한 정보를 선택하여 받아올 수 있다.
GET /login/google/callback
Google 인증이 완료된 후의 작업이 필요하다. 앞서 계획했던 것처럼, 인증이 완료되면 루트페이지('/')로 이동하고 실패하면 다시 로그인 페이지로 이동하도록 설정한다.
app.get('/login/google/callback',
passport.authenticate('github', {
failureRedirect: '/login',
successRedirect: '/'
}));
authenticate
옵션으로 failureRedirect와 successRedirect를 설정할 수 있다.
이렇게 세팅을 하면 로그인 버튼을 누르는 순간 Google 로그인 창이 뜨고 로그인이 완료되면 설정했던 Callback URI로 돌아온다. 하지만,
passport.initialize() middleware not in use
라는 에러 창이 뜬다.
이 에러창이 뜨는 이유는 기본적으로 로그인 세션을 유지하여 서버를 이용해야 하기 때문에 passport의 세션을 초기화 하는 과정이 필요하기 때문이다.
덧붙여서 express의 세션 설정도 해주어야 한다. express 4 이상의 버전에서는 session설정을 따로 해주어야 한다.
express session 설정
$ npm install express-session
const session = require('express-session');
app.use(session({
secret: SECRET_CODE,
cookie: { maxAge: 60 * 60 * 1000 }
resave: true,
saveUninitialized: false
}));
session > maxAge
속성으로 세션 시간을 제한할 수 있다. (위의 설정된 시간은 1시간)
그런다음 passport의 세션을 설정하고 초기화하는 코드를 작성한다.
app.use(passport.initialize()); // passport 구동
app.use(passport.session()); // 세션 연결
여기까지 했다면 기본적인 설정이 완료 된 것이다. 이제 로그인 인증을 활용하여 인증된 사용자만 접근이 가능한 페이지를 만들 수 있다.
"루트 페이지는 인증된 사용자만 접근이 가능하다"
인증 정보를 활용하여 루트페이지에 인증된 사용자만 접근하도록 만들 수 있다.req.isAuthenticated()
메소드를 사용할 수 있는데 이 메소드는 서버에 요청을 보낸 사용자가 인증이 되어있는 상태인지를 확인하여 'Boolean' 으로 알려준다. 아래와 같이 미들웨어를 만들어서 페이지 요청 전 미들웨어로 꽂아 넣으면 인증된 사용자만 컨텐츠를 볼 수 있게 할 수 있다.
const authenticateUser = (req, res, next) => {
if (req.isAuthenticated()) {
next();
} else {
res.status(301).redirect('/login');
}
};
app.get('/', authenticateUser, function (req, res, next) {
res.render('index', { title: 'Express' });
});
지금까지 구글 소셜 로그인을 구현해보았다. passport.js를 이용하면 소셜 뿐만 아니라 로컬 로그인의 인증 과정도 쉽게 구현할 수 있다. 마지막으로 지금까지 구현 과정을 전체 코드로 아래에 적어놓겠다.
const app = express();
const session = require('express-session');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
app.use(session({ secret: 'SECRET_CODE', resave: true, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new GoogleStrategy({
clientID: CLIENT_ID,
clientSecret: CLIENT_SCRET,
callbackURL: "http://localhost:3000/login/google/callback"
},
(accessToken, refreshToken, profile, cb) => {
return cb(null, profile);
}
));
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
const authenticateUser = (req, res, next) => {
if (req.isAuthenticated()) {
next();
} else {
res.status(301).redirect('/login');
}
};
app.get('/', authenticateUser, (req, res, next) => {
res.render('index', { title: 'Express' });
});
app.get('/login', (req, res, next) => {
res.render('login', { title: 'Login' })
});
app.get('/login/google',
passport.authenticate('google', { scope: ['profile'] })
);
app.get('/login/google/callback',
passport.authenticate('google', {
failureRedirect: '/login',
successRedirect: '/'
}));
By Cyrano on SEP 22, 2019.
Thanks to Vanillacoding
웹 서비스를 이용하다보면 가장 짜증나는 것이 아이디랑 비번 찾기이다... 왜 항상 내가 쓴 비밀번호는 틀리는지, 항상 내가 생각한 아이디가 없다고 하는지 모르겠다. 비밀번호의 빡침을 그나마 해소시켜주는 것이 바로 소셜 로그인이다. 자주 들어가는 소셜 서비스의 아이디로 간단하게 서비스를 이용할 수 있는 편리한 시스템인 것 같다. 이번에는 Expres...
React의 State는 읽기 전용이어야 한다. react에서 state는 항상 읽기 전용이어야 하며, array 등의 reference value들은 새로운 reference를 만들어서 state를 변경해 주어야 한다. 이와 같은 방식으로 redux에서도 state는 읽기 전용으로 관리 되어야 한다. 그래서 spread operator (...) 를 ...
Update(업데이트) Component들은 state 나 props 가 변경이 되면 update가 진행이 되며 다시 rendering 된다. Input이 달라지니 output이 달라져야 하기 때문이다. 그리고 상위 component가 update되면 그에 속한 하위 component들도 다시 mount가 된다. component가 update될 때 ...
React Life Cycle React는 Component는 상위 component에서 받은 props 를 input으로 하고 React를 구성하는 가장 작은 단위인 Element 를 output으로 하는 함수!!이다. React를 사용하면 각 component 단위로 UI를 화면에 보이게 하고, 다른 UI로 바꾸고, 현재 보이는 UI를 화면에서 없...
배경지식 JavaScript는 엔진은 Single Thread이다. 그래서 동시에 두 가지 작업을 할 수 없다. 그렇다면 여러 작업이 동시에 요청이 될 때 이 전 작업이 마무리 될 때까지 기다려야 하는가? 그렇다. 그래서 JavaScript 엔진은 비동기 처리가 가능하도록 설계되었다. 비동기(Asynchronous)란? 동기(Synchron...
파일명이 잘 명시되어 있지 않아 따라가기가 힘드네요 ㅠㅠ
좋은 글이었습니다만...