2014년 12월 22일 월요일

[Sails.js] sailsjs에서 passport를 이용한 사용자 인증

개요
sailsjs에서 passport를 이용한 사용자 인증이 가능케 하고자 한다.
passport의 strategy라는 각 인증 구현을 이용하면, 대개의 인증은 간단하게 이용할 수 있을거 같아서다.

참고자료
http://passportjs.org/guide/authorize/
http://www.bearfruit.org/2014/07/21/tutorial-easy-authentication-for-sails-js-apps/
https://github.com/kasperisager/sails-generate-auth

전제
passport binder에 해당하는 기능 구현은 직접 하지 않고, 상기 sails-generate-auth라는 구현체를 이용하고자 한다. 문서화가 아직 미약한 면이 있지만 소스가 그리 방대한것도 아니고 살짝 훑어보면 어떤 구조를 하고있는지는 간단하게 알 수 있다.

기본적으로 template을 통해 sails의 router, services, policy, controller, model등에 인증에 필요한 기능을 부분적으로 구현해 이용하는 형태를 취하고있다.

passport의 strategy는 config/passport.js에서 연결할 수 있으며, 기본적으로 local, google, twitter등의 인증은 기본으로 들어가 있는 상태이다.

기본적으로 auth에 필요한 파일들 및 디렉토리 구조를 작성해주는 형태이니 가급적이면 빈 프로젝트에서 진행하자.

git을 통해 확인해본 추가되는 파일들의 목록은 아래와 같다

        api/controllers/AuthController.js
        api/models/Passport.js
        api/models/User.js
        api/policies/passport.js
        api/services/passport.js
        api/services/protocols/
        config/passport.js
        node_modules/sails-generate-auth/
        views/auth/

적어도 상기 파일들과 충돌이 일어나지 않게끔 준비가 되어있을 필요가 있다.

수순

sudo npm install sails-generate-auth passport bcryptjs validator

# on the sails project root...
sails generate auth

이후 각 파일별의 설정

필수사항

1. config/routes.json
'get /login': 'AuthController.login',
'get /logout': 'AuthController.logout',
'get /register': 'AuthController.register',

'post /auth/local': 'AuthController.callback',
'post /auth/local/:action': 'AuthController.callback',

'get /auth/:provider': 'AuthController.provider',
'get /auth/:provider/callback': 'AuthController.callback',
'get /auth/:provider/:action': 'AuthController.callback',

2. config/bootstrap.json
sails.services.passport.loadStrategies();

3. config/policies.json
'*': [ 'passport' ]

더불어 사용하고 싶은 인증형태의 관련 passport-strategy를 인스톨 할 필요가 있다.

sudo npm install passport-local
sudo npm install passport-twitter
sudo npm install passport-github
sudo npm install passport-facebook
sudo npm install passport-google-oauth

상기는 기본적으로 설정되어있는 strategy를 도입하는 수순인데, 필요 없는 부분에 관해선 config에서 제거후, 도입하지 않으면 될거라 생각된다.

선택사항

1. 에러메세지를 지정하고 싶다면 아래와 같은 내용을 설정
{
  "Error.Passport.Password.Invalid": "The provided password is invalid!",
  "Error.Passport.Password.Wrong": "Whoa, that password wasn't quite right!",
  "Error.Passport.Password.NotSet": "Oh no, you haven't set a password yet!",
  "Error.Passport.Username.NotFound": "Uhm, what's your name again?",
  "Error.Passport.User.Exists": "This username is already taken.",
  "Error.Passport.Email.NotFound": "That email doesn't seem right",
  "Error.Passport.Email.Missing": "You need to supply an email-address for verification",
  "Error.Passport.Email.Exists": "This email already exists. So try logging in.",
  "Error.Passport.Username.Missing": "You need to supply a username",
  "Error.Passport.Password.Missing": "Oh no, you haven't set a password yet!",
  "Error.Passport.Generic": "Snap. Something went wrong with authorization."
}

2. 기본은 비인증도 접근할 수 있는 상태로 설정되어 있으나, 인증한정으로 하고싶다면 아래와 같은 내용을 config/policies에 설정
'*': ['passport', 'sessionAuth'],

 'auth': {
    '*': ['passport']
  }


특정 WaterlineORM(Database)을 통한 database-migration

sails lift를 실시할때 매번 database migration에 관해 물어오는데, scheme가 존재하는 database의 경우, 이 시점에서 alter를 선택하면 데이터베이스에 필요한 테이블들이 자동적으로 생성된다. (여기서 한 며칠 mysql에서 association이 아직 미지원인줄 알고 잘못된 테이블을 가지고 삽질을 했다(...))

sails-generate-auth는 내부적으로 2개의 모델간에 model-association기능을 이용하고 있다. 수동으로 이를 정의하게 되면 이 부분에서 문제가 발생할 수 있으니, 가급적 기동 초기에 alter선택지를 활용해서 데이터베이스에 테이블을 생성하도록 하자.

http://stackoverflow.com/questions/25255567/sails-js-production-env-sails-mysql-database-tables-not-created-upon-lift


Google oAuth2.0연계하기

1. ClientID & ClientSecret 발급

https://console.developers.google.com
상기 url에 접근한후, 인증용 프로젝트를 작성, 프로젝트 하위의 인증정보 페이지에서 ClientID를 작성한다.

2. ClientID & ClientSecret & Scope 설정

config/passport.js
상기 파일 내의 google에 대한 설정을 아래와 같이 설정한다. options하위에 scope란 항목을 추가한다. 기본적으로 google plus로 맞추어두었으나, 다른 서비스에 스코프를 추가한다면 해당 서비스의 스코프를 입력할 필요가 있다.
google: {
  name: 'Google',  
  protocol: 'oauth2',  
  strategy: require('passport-google-oauth').OAuth2Strategy,  
  options: {
    clientID: '*****************', 
    clientSecret: '******************',
    scope: 'https://www.googleapis.com/auth/plus.login',
   scope: 'http://yourhost-url/auth/google/callback',
  }}

https://github.com/kasperisager/sails-generate-auth/issues/136
개발자측에 문의해보니, scope에 ['email']이라 설정할 필요가 있다고 한다.

google: {
  name: 'Google',  
  protocol: 'oauth2',  
  strategy: require('passport-google-oauth').OAuth2Strategy,  
  options: {
    clientID: '*****************', 
    clientSecret: '******************',
    scope: ['email'],
   scope: 'http://yourhost-url/auth/google/callback',
  }}

위와 같이 설정을 한 후에, 구글측에 접근하면 email만 요구하게 되고, 이를 통해 사용자 인증을 하게된다.

인증정보의 확인

sails-generate-auth는 조금 불친절하게도 로그인 상태를 확인할 수 있는 장소를 따로 설명해두지 않았다. 일단 인증상태정보를 확인하기 위해, 아래와 같은 route와 AuthController.js이하에 다음 코드들을 추가하고

http://your-host/whoami

로 접근해보자.

AuthController.js
whoami: function(req,res) {
  res.json(req.session);},

config/routes.js
'get /whoami': 'AuthController.whoami',

이후에 http://your-host/login 에서 로그인후 내용을 확인하면 다음과 같이 표시되는걸 알수가 있다.

logged-out
{
  • cookie:
    {
    • expiresnull,
    • httpOnlytrue,
    • path"/"
    },
  • passport: { },
  • flash: { }
}

local-strategy
{
  • cookie:
    {
    • expiresnull,
    • httpOnlytrue,
    • path"/"
    },
  • passport:
    {
    • user2
    },
  • flash: { }
}

google-oauth-strategy
{
  • cookie:
    {
    • originalMaxAgenull,
    • expiresnull,
    • httpOnlytrue,
    • path"/"
    },
  • passport:
    {
    • user1
    },
  • flash: { }
}

google의 경우, sails-generate-oauth에서 제공해주는 설정만으로는 로그인할수가 없다.
services/passport.js에서 다음과 같은 수정이 필요하다.


// If the profile object contains a username, add it to the user.if (profile.hasOwnProperty('username'))
{
  user.username = profile.username;}
else if(profile.hasOwnProperty('displayName'))
{
  user.username = profile.displayName;
}

profile
은 획득이 가능하나, property-name이 다르기때문에 이 부분에서 user-name을 수정해서 맞추어 주어야한다. google+를 기준으로 할때 이름은 displayName혹은 name(object)형태로 온다. 후자의 경우는 first, lastname으로 나뉘어져있다. 기본적으로 displayName을 사용하면 되지 않나 싶긴 하다.

이후의 플로우에서는 이름 혹은 메일이 만족되면 진행되기때문에, 상기 조정으로 충분하지 않나 하는 생각이 든다.

일단 passportjs자체의 스펙도 확인해봤으나, 아무래도 수정해서 쓴다는거 자체가 좀 의아한 느낌이 들었기때문에 본가에 문의를 해본상태

https://github.com/kasperisager/sails-generate-auth/issues/136

빠뜨린게 있으면 조정해서 사용을 하던지 해야할듯 싶다.

위 부분에 관해서는 상기 google oAuth2.0연계하기 부분의 하단을 참고해줬으면 한다.

댓글 없음:

댓글 쓰기