Node.js에서 google oauth 인증하는 방법이다. 

이전에는 passport를 사용하지 않는 방법을 이용하여 JWT와 함께 쿠키로 로그인 인증처리를 했다면,

이번엔 세션을 이용하여 passport를 이용하는 방법이다.


기본적으로 https://console.cloud.google.com 에서 프로젝트를 생성했다는 가정하에 작성한다.


1. passport를 이용하지 않은 google oauth 인증 후 jwt 사용

2019/01/07 - [Develop/Node.js] - [Node.js] google oauth 인증 (구글 로그인)




1. 적당한 프로젝트를 생성 후 아래와 같은 명령어를 입력한다.


npm init
npm install express --save
npm install express-session --save
npm install session-file-store --save
npm install passport --save
npm install passport-google-oauth


express 를 사용할 것이기 때문에 express를 설치한다. 그리고 passport는 기본적으로 session을 이용하기 때문에 express-session도 설치하고

session 저장소를 파일로 처리할 것이기 때문에 session-file-store 를 설치한다.

그리고 사용할 passport와 passport의 전략중 하나인 passport-google-oauth를 설치한다.


※아래엔 express-session에서 파일로 세션을 저장하는 것 말고 데이터베이스 등을 사용하는 방법을 확인 할 수 있다.

https://www.npmjs.com/package/express-session

2. express를 설정하고 다음 session을 설정한다.


먼저 server.js 파일을 생성하고 아래와 같이 작성한다.


https://www.npmjs.com/package/express-session 에 접속해서 기본적인express-session을 사용하기 위해

아래 주석 //1의 미들웨어를 설정한다.

주석 //2번은 localhost:3000으로 접속할 때 나오는 기본 화면이다.

주석 //3은 express를 통해  3000포트를 이용하여 서버를오픈한다.

var express = require('express');
var session = require('express-session')
var FileStore = require('session-file-store')(session)
var app = express();

app.use(session({                // 1
    secret: 'keyboardcat',
    resave: false, 
    saveUninitialized: true,
    store: new FileStore()
}))

app.get('/', function (req, res, next) {    //2
    console.log('홈')
    
})


app.listen(3000, function () {            //3
    console.log('Example app listening on port 3000!');
});


3.  Google Cloud Platform 에서 Json 파일을 다운받고, config 폴더를 만든 뒤 다운받은 json 파일을 config폴더에 넣는다.


사진 맨 오른쪽 하단에 빨간줄로 표시한 아이콘을 클릭하여 json파일을 다운받는다.


다운받은 json 파일엔 기본적으로 아래와같이 나와있다.



이 파일을 아래 처럼 넣는다. 이유는 그냥 가져다 쓰는 것보다 저렇게 가져오는 것이 조금이나마 안전하기 때문.



4. passport를 설정한다.


4-1. passport 전략 중 하나인 우리가 사용할 passport-google-oauth를 사용한다.


server.js에 추가한다.

//1 아래와 같이 passport를 가져오고

//2 passport를 초기화시킨다.

//3 session을 이용하기 때문에 추가한다.

//4 실질적으로 로그인할 수 있는 경로와 scope이다. scope에서는 email이나 기타 정보를 가져 올 수도 있다.

//5 메인페이지에 4번에서 설명한 실질적으로 로그인하는 경로를 설정한다.

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

app.use(passport.initialize());    //2
app.use(passport.session());        //3

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

app.get('/', function (req, res, next)   {    //5
  
    var html = `
    login with google`
    res.send(html)
})


위와 같이 설정하고 'node server.js' 명령어를 통해 실행해서 접속하면 아래와 같은 화면이 뜬다.



4-2. 구글 로그인을 해서 정보를 얻어올 로직과 세션에 담을 로직을 구현한다.


//1 은 로그인했을 시 얻어오는 정보가아니라 그냥 임의로 넣었다 사실 이렇게 하는 것은 아니고 바로 아래에서 다시 설명할 예정

//2 로그인 성공하고 로그인 성공했을 때 가져오는 user.id를 세션에 설정한다.

//3 로그인이 성공했으면 페이지를 항상 새로 불러올 때마다 불러오는 것이다. 

그리고 세션에서 serialize에서 설정한 user id를 불러온 후 user 정보를 보여주는 것이다.

//4 아까 json파일에서 client_id와 secret, redirect uri를 불러온다.

//5 로그인이 성공했을 경우 scope를 설정한대로 구글측에서 profile 정보를 가져온다.


//5를 통해 로그인이 성공하면 scope에서 설정한 대로 profile에서 정보를 가져오는데 

성공적으로 가져왔으면 이를 done을 통해 정보를 설정하면

serializeUser로 가서 설정하고

설정된 값이 deserializeUser에서 불러온다.


다시말하자면 원래 var loginData를 설정하는 것이아니라

//5에서 일단 로그인 인증 처리를 한다 -> 데이터베이스에 넣거나, 데이터베이스에 있는 값을 가져와 회원이 맞는지 비교를 한다든지

이런 기본적인 로그인 처리를 설정하고. 이것이 통과된다면 done을 통해 정보를 return하면

//2의 serializeUser가 호출되는데 여기서 user.id ( 꼭 id가아니라 email이면 user.email도 가능) 을 session에 설정한다.

//3 그럼 페이지가 항상 호출될 때마다 deserializeUser가 호출 되는데 deserializeUser의 id를 통해

데이터베이스에 접속해서 이 아이디가 로그인이 된 상태라든지 이런 것들을 확인할 수 있다.

(너무 이상하게 설명한 것 같은데 이해가 되지않는다면 댓글에 적어주시면 감사하겠습니다.)


var config = require('./config/google.json')


var loginData = {        //1 id : 'test', pw : '1' } passport.serializeUser(function(user, done) {    //2 console.log('serialize', user); done(null, user.id); }); passport.deserializeUser(function(id, done) {    //3 console.log('deserialize', id); done(null, loginData) }); passport.use(new GoogleStrategy({                //4 clientID: config.web.client_id, clientSecret: config.web.client_secret, callbackURL: config.web.redirect_uris[0] }, function(accessToken, refreshToken, profile, done) {    //5 return done(null, loginData) } ));


4-3. 로그인이 정상 처리 되어 callback될 주소를 설정한다.


//1 Json파일의 redircet_uri를 설정한다 기본적으로 /auth/google/callback 이다.

//2 로그인이 정상 처리 된다면 //3의 redirect('/') 홈으로 가고 

실패하면 /bye로 보냈다. 이것은 다시 로그인 하는 곳으로 보낼 수 있고 처리하는 것은 개발자 마음이다.

//4 이것을 설정하지 않으면 세션이 스토어에 저장하는 데 오래걸릴 경우 리다이렉트를 시킬 수 있기 때문에 이를 방지하여

세션이 스토어에 저장이 완료되면 리다이렉트를 시키는 코드이다.

//5 로그인 실패시 보내는 주소이다.

//6 위에서 설정한 app.use(passport.session()) 미들웨어를 통해 req에서 user를 호출 할 수 있다. 

이것을 통해 deserilize가 호출 되는 것을 확인할 수 있다.


app.get('/auth/google/callback',     //1
passport.authenticate('google', {     //2
    failureRedirect: '/bye' 
    }),
    function(req, res) {                //3
        req.session.save(function(){     //4                              
          res.redirect('/');
        })
    });


app.get('/bye', function (req, res, next) {    //5
    res.send('login failed')
})

app.get('/', function (req, res, next) {
    console.log(req.user)        //6
    var html = `
    login with google`
    res.send(html)
})


5. 결과



node server.js 명령어를 통해 실행 후

login with google을 클릭하여 로그인을 하게되면

로그인이 성공하면 serialize를 호출하고 

페이지를 새로고침할 때마다 deserialize가 호출 되는 것을 볼 수 있다.



6. 전체 코드



var express = require('express');
var session = require('express-session')
var FileStore = require('session-file-store')(session)
var config = require('./config/google.json');
var app = express();

app.use(session({
    secret: 'keyboardcat',
    resave: false, 
    saveUninitialized: true,
    store: new FileStore()
}))

var loginData = {
    id : 'test',
    pw : '1'
}
var passport = require('passport'),
      GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser(function(user, done) {
    console.log('serialize', user);
    done(null, user.id);
  });
  
passport.deserializeUser(function(id, done) {
    console.log('deserialize', id);
    done(null, loginData)
  });


passport.use(new GoogleStrategy({
    clientID: config.web.client_id,
    clientSecret: config.web.client_secret,
    callbackURL: config.web.redirect_uris[0]
  },
  function(accessToken, refreshToken, profile, done) {
        return done(null, loginData)
    }
));

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


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

app.get('/', function (req, res, next) {
    console.log(req.user)
    var html = `
    login with google`
    res.send(html)
})

app.get('/bye', function (req, res, next) {
    res.send('login failed')
})

app.listen(3000, function () {
    console.log('Example app listening on port 3000!');
});



참고 : 

https://www.npmjs.com/package/express-session 



Node.js에서 google oauth 인증하는 방법이다. 

passport를 사용하는 방법이 있지만 passport는 사용해보니 무조건 session을 이용해야 했다.(아닐 수도 있음.)

이 글은 passport를 사용하지 않는 방법이다.


1.passport를 사용한 google oauth 인증

2019/03/11 - [Develop/Node.js] - [Node.js] google oatuh passport




프로젝트는 아래의 글을 사용했다. (순수 express 설치하는 글임)

2019/01/06 - [Develop/Node.js] - [Node.js] Express 설치(Windows)


1. googleapis 를 설치한다

npm install googleapis --save

2. google cloud console에서 .json파일을 받는다.


받은 파일을 아래와 같이 config 폴더에 넣어 google.json으로 변경한다.




3. server.js 파일에 아래와 같이 추가한다.


install 한 googleapis를 불러오고 .json 파일에서 client_id와 secret, redirect_uri를 불러온다.

const { google } = require('googleapis');
var googleClient = require('./config/google.json');

const googleConfig = {
  clientId: googleClient.web.client_id,
  clientSecret: googleClient.web.client_secret,
  redirect: googleClient.web.redirect_uris[0]
};

사용할 scopes를 입력하고, OAuth2.0에 실질적으로 연결할 정보들을 다시 연결한다. 

scope는 아래 API라이브러리에서 본인이 사용하고 싶은 api를 보고 가져오면된다.

여기서는 google api + 를 이용할 예정이다.



const scopes = [
  'https://www.googleapis.com/auth/plus.me'
];


const oauth2Client = new google.auth.OAuth2(
  googleConfig.clientId,
  googleConfig.clientSecret,
  googleConfig.redirect
);

로그인 url을 받아오기 위한 정보를 입력한다. 

access_type에는 online과 offline이 있는데 offline으로 해야 제일 처음 로그인 했을 때 refresh_token을 받아온다.

(refresh_token은 항상 제일 처음 로그인할 때 가져온다 그러므로 다시 가져오고 싶으면 https://myaccount.google.com/permissions 에서 액세스된 어플리케이션을 해제해야 한다.)


scope는 위에 입력한 scope들을 가져온다. 지금처럼 scope하나만 가져올경우 

scope : 'https://www.googleapis.com/auth/plus.me' 바로 적어도 된다. 하지만 여러 scope를 가져올 경우 위에 처럼 배열을 하나 만들어

여러 scope들을 사용할 수 있다.

그리고 google+ api를 사용하기 위해 google+ api에 대한 정보를 입력한다.

const url = oauth2Client.generateAuthUrl({
  
  access_type: 'offline',

  
  scope: scopes
});

function getGooglePlusApi(auth) {
  return google.plus({ version: 'v1', auth });
}

실질적으로 로그인해서 정보를 불러올 코드를 작성한다. 

간단하게 리프레시토큰, 액세스토큰, displayName과 id를 얻어와본다.

async function googleLogin(code) {
  const { tokens } = await oauth2Client.getToken(code);
  oauth2Client.setCredentials(tokens);
  oauth2Client.on('tokens', (token) => {
    if(tokens.refresh_token){
      console.log("리프레시 토큰 :", tokens.refresh_token);
    }
    console.log("액세스 토큰:", tokens.access_token);
  });
  const plus = getGooglePlusApi(oauth2Client);
  const res = await plus.people.get({ userId: 'me' });
  console.log(`Hello ${res.data.displayName}! ${res.data.id}`);
  return res.data.displayName;
}

4. login할 주소와 로그인이 되었을 경우 실행될 callback 주소를 작성한다.

로그인할 주소는 아까 로그인 할 url 받아올 url변수의 정보를 redirect 시키고

callback 주소는 google.json의 redirect 주소를 적으면 된다.

그리고 로그인이 성공했을 경우 돌아올 redircet주소를 적는다. 


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

app.get("/auth/google/callback", async function (req, res) {

  const displayName = await googleLogin(req.query.code);
  console.log(displayName);

  res.redirect("http://localhost:3000");
});

5. 실행한다.



localhost:3000/login 으로 들어간다.




localhost:3000/login 으로 들어가면 아까 url 을 통해 아래의 사진으로 연결된다.


이제 아이디를 쳐서 로그인하면 아까 입력한 scope에 따라 권한을 받게되고 이후 성공하면 redirect 시킨 home으로 돌아온다.



그리고 console을 확인해보면 현재 액세스토큰과 displayName, id가 나왔다.

리프레시토큰은 전에 로그인을 한번해서(시험 삼아 한번 해봄). 안나왔지만, 제일 처음 로그인을 했다면 리프레시토큰 또한 나올 것이다.

(리프레시토큰은 제일 처음 한번만 발급된다.)



6. 전체코드



var express = require('express');
var app = express();

const { google } = require('googleapis');
var googleClient = require('./config/google.json');

const googleConfig = {
  clientId: googleClient.web.client_id,
  clientSecret: googleClient.web.client_secret,
  redirect: googleClient.web.redirect_uris[0]
};

const scopes = [
  'https://www.googleapis.com/auth/plus.me'
];

const oauth2Client = new google.auth.OAuth2(
  googleConfig.clientId,
  googleConfig.clientSecret,
  googleConfig.redirect
);

const url = oauth2Client.generateAuthUrl({

  access_type: 'offline',

  scope: scopes
});

function getGooglePlusApi(auth) {
  return google.plus({ version: 'v1', auth });
}

async function googleLogin(code) {
  const { tokens } = await oauth2Client.getToken(code);
  oauth2Client.setCredentials(tokens);
  oauth2Client.on('tokens', (tokens) => {
    if(tokens.refresh_token){
      console.log("리프레시 토큰 :", tokens.refresh_token);
    }
    console.log("액세스 토큰:", tokens.access_token);
  });
  const plus = getGooglePlusApi(oauth2Client);
  const res = await plus.people.get({ userId: 'me' });
  console.log(`Hello ${res.data.displayName}! ${res.data.id}`);
  return res.data.displayName;
}

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

app.get("/auth/google/callback", async function (req, res) {

  const displayName = await googleLogin(req.query.code);
  console.log(displayName);

  res.redirect("http://localhost:3000");
});



app.get('/', function (req, res) {
  res.send('Hello World!');
  console.log("로그인 해서 홈으로 돌아옴");

});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});




참고 : 


https://www.npmjs.com/package/googleapis


+ Recent posts