2013년 8월 13일 화요일

Meteor 에서 외부 Application으로 DDP 인증.

http://stackoverflow.com/questions/16729992/authenticating-with-meteor-via-ddp-and-srp

Basic Auth로 websocket frame을 떠봤더니

o
1
오전 1:47:09
a["{\"server_id\":\"X43fG44wD7iJNCdoq\"}"]
42
오전 1:47:09
["{\"msg\":\"connect\",\"version\":\"pre1\",\"support\":[\"pre1\"]}"]
69
오전 1:47:09
["{\"msg\":\"sub\",\"id\":\"8jW8AaNcWf2iRhXWC\",\"name\":\"meteor.loginServiceConfiguration\",\"params\":[]}"]
110
오전 1:47:09
a["{\"msg\":\"connected\",\"session\":\"ZddaS7aatWT4i8nAR\"}"]
62
오전 1:47:09
a["{\"msg\":\"added\",\"collection\":\"users\",\"id\":\"aHC7AHZXbnhs7Efs9\",\"fields\":{\"username\":\"mone\",\"profile\":{\"photoSmall\":\"/image/tempImage/member_photo_placeholder.png\"}}}"]
192
오전 1:47:09
a["{\"msg\":\"added\",\"collection\":\"users\",\"id\":\"HuL3adLrtPKMoFWMK\",\"fields\":{\"profile\":{\"photoSmall\":\"/image/tempImage/member_photo_placeholder.png\"},\"username\":\"admin\"}}"]
193
오전 1:47:09
a["{\"msg\":\"added\",\"collection\":\"users\",\"id\":\"MZqYAk6C9kYao3ATz\",\"fields\":{\"profile\":{\"photoSmall\":\"/image/tempImage/member_photo_placeholder.png\"},\"username\":\"spectrum\"}}"]
196
오전 1:47:09
a["{\"msg\":\"ready\",\"subs\":[\"8jW8AaNcWf2iRhXWC\"]}"]
----------------------------------------------------------------------

여기까지 로그인 전

----------------------------------------------------------------------
"{\"msg\":\"method\",\"method\":\"login\",\"params\":[{\"srp\":{\"M\":\" \"}}],\"id\":\"2\"}"]
158
오전 1:48:22
a["{\"msg\":\"changed\",\"collection\":\"users\",\"id\":\"MZqYAk6C9kYao3ATz\",\"fields\":{\"emails\":[{\"address\":\"spectrick@gmail.com\",\"verified\":false}]}}"]
163
오전 1:48:22
a["{\"msg\":\"ready\",\"subs\":[\"8jW8AaNcWf2iRhXWC\"]}"]
57
오전 1:48:22
a["{\"msg\":\"updated\",\"methods\":[\"2\"]}"]
46
오전 1:48:22
a["{\"msg\":\"result\",\"id\":\"2\",\"result\":{\"token\":\"oGyFPfZM3c95yXw7M\",\"id\":\"MZqYAk6C9kYao3ATz\",\"HAMK\":\"b372e317d359b294cd9c5a4ca39c8f5523c310288661eba731191e2b4614ef17\"}}"]
190
오전 1:48:22
h

뭐 대략 이렇게 나오는데
a는 응답일테고

순서대로 보면
1. Connect 요구
{ msg: 'connect',
  version: 'pre1',
  support: [ 'pre1' ] }
이건 간단한데
{msg: "connected", session: "4vXhahuK5GTNzcC4S"}
이런 식으로 응답이 오면 오케이

2. 인증설정요구
{ msg: 'sub',
  id: 'xaiCo33DPqaEJhkbc',   /* id는 UUID. 아무거나 넣어도 된다. */
  name: 'meteor.loginServiceConfiguration',
  params: [] }

잠시 삼천포
UUID 생성은

Meteor.uuid = function () {
  var HEX_DIGITS = "0123456789abcdef";
  var s = [];
  for (var i = 0; i < 36; i++) {
    s[i] = Random.choice(HEX_DIGITS);
  }
  s[14] = "4";
  s[19] = HEX_DIGITS.substr((parseInt(s[19],16) & 0x3) | 0x8, 1);
  s[8] = s[13] = s[18] = s[23] = "-";

  var uuid = s.join("");
  return uuid;
}

Random.choice = function (arrayOrString) {
  var index = Math.floor(Random.fraction() * arrayOrString.length);
  if (typeof arrayOrString === "string")
    return arrayOrString.substr(index, 1);
  else
    return arrayOrString[index];
}

이런 식이다.
아무튼 sub 즉 subscribe 메시지로 meteor.loginServiceConfiguration 이란 이름의 collection을 구독 요청하면

{ server_id: 'X43fG44wD7iJNCdoq' }
{ msg: 'connected', session: 'EzRf563xHiK7J2FDo' }
{ msg: 'added',
  collection: 'users',
  id: 'aHC7AHZXbnhs7Efs9',
  fields: 
   { username: 'mone',
     profile: { photoSmall: '/image/tempImage/member_photo_placeholder.png' } } }
{ msg: 'added',
  collection: 'users',
  id: 'HuL3adLrtPKMoFWMK',
  fields: 
   { profile: { photoSmall: '/image/tempImage/member_photo_placeholder.png' },
     username: 'admin' } }
{ msg: 'added',
  collection: 'users',
  id: 'MZqYAk6C9kYao3ATz',
  fields: 
   { profile: { photoSmall: '/image/tempImage/member_photo_placeholder.png' },
     username: 'spectrum' } }
{ msg: 'ready', subs: [ '1' ] }

유저명이 나오고, 'ready'를 받으면 끝. atmosphere.meteor.com 은 user 명을 보내지 않더라. 뭔가 설정이 있을 듯.


보내는게 무엇인지 살펴보니
{ msg: 'method',
  method: 'login',
  params: [ { srp: 
    "M":"HASHHASHHASHHASHHASHHASHHASHHASHHASHHASH"
  } ],
  id: '2' }
이렇다.
참고로 atmosphere는

{ msg: 'method',
  method: 'login',
  params: 
   [ 'atm',
     '0.1.0',
     { srp: 
      "M":"HASHHASHHASHHASHHASHHASHHASHHASHHASHHASH"
     } ],
  id: '2' }

뭔가 조금 더 있다.

요걸 보내면 된다는 말인데
M에 사용자계정과 암호를 잘 어떻게 해서 응답이 돌아오면 되겠다.

M에 해당하는 부분은 SRP라는 것으로 암호화되어있는데
이걸 사용하면 된다.
로직은 그냥 소스 읽자.

nextid = 0
connected = false;
ws = require 'ws'
ws = new ws 'ws://localhost:3000/websocket'
ws.on 'open', ->
  console.log "open socket"
  ws.send JSON.stringify
    msg: 'connect'
    version: 'pre1'
    support: ['pre1']
ws.on 'message', (data, flags)->
  dataJSON = JSON.parse data
  console.log dataJSON
  if dataJSON.msg is 'connected'
    connected = true
    ws.send JSON.stringify
      msg: 'sub'
      id: (++nextid).toString()
      name: 'meteor.loginServiceConfiguration'
  if dataJSON.msg is 'ready'
    ws.send JSON.stringify
      msg: 'method'
      method: 'login',
      params: [
        srp:
          M: "*이 부분에 SRP로 인코딩"
      ]
      id: '2'

해봤는데 잘 안된다.
사실 이 부분이 핵심인데 Basic auth / OAuth 도 다 다를것이다.
OAuth를 쓰면 어짜피 웹으로 왔다갔다 해야해서 모바일 등에서 쓰기 곤란할 듯.
그럼 로그인은 웹에서 하고 인증된 후에 재로그인이 가능한지 확인해보자.

{ msg: 'method',
  method: 'login',
  params: [ { resume: 'MvzqjyYD9bCs3rrcb' } ],
  id: '1' }

resume 값을 기억하고 있으면 되는가보다 리프레쉬 했는데 같은 값을 반환한다.

open socket
{ server_id: 'X43fG44wD7iJNCdoq' }
{ msg: 'connected', session: 'ggEMcek5WmGD9vJSG' }
{ msg: 'added',
  collection: 'users',
  id: 'aHC7AHZXbnhs7Efs9',
  fields: 
   { username: 'mone',
     profile: { photoSmall: '/image/tempImage/member_photo_placeholder.png' } } }
{ msg: 'added',
  collection: 'users',
  id: 'HuL3adLrtPKMoFWMK',
  fields: 
   { profile: { photoSmall: '/image/tempImage/member_photo_placeholder.png' },
     username: 'admin' } }
{ msg: 'added',
  collection: 'users',
  id: 'MZqYAk6C9kYao3ATz',
  fields: 
   { profile: { photoSmall: '/image/tempImage/member_photo_placeholder.png' },
     username: 'spectrum' } }
{ msg: 'ready', subs: [ '1' ] }
{ msg: 'changed',
  collection: 'users',
  id: 'MZqYAk6C9kYao3ATz',
  fields: { emails: [ [Object] ] } }
{ msg: 'ready', subs: [ '1' ] }
{ msg: 'updated', methods: [ '11' ] }
{ msg: 'result',
  id: '11',
  result: { token: 'MvzqjyYD9bCs3rrcb', id: 'MZqYAk6C9kYao3ATz' } }
{ msg: 'result',
  id: '11',
  result: { token: 'MvzqjyYD9bCs3rrcb', id: 'MZqYAk6C9kYao3ATz' } }
{ msg: 'updated', methods: [ '11' ] }


음 token 이 어짜피 있어서 의미없나?
암튼 전체 소스
nextid = 0
connected = false;
ws = require 'ws'
ws = new ws 'ws://localhost:3000/websocket'
ws.on 'open', ->
  console.log "open socket"
  ws.send JSON.stringify
    msg: 'connect'
    version: 'pre1'
    support: ['pre1']
ws.on 'message', (data, flags)->
  dataJSON = JSON.parse data
  console.log dataJSON
  if dataJSON.msg is 'connected'
    connected = true
    ws.send JSON.stringify
      msg: 'sub'
      id: (++nextid).toString()
      name: 'meteor.loginServiceConfiguration'
  if dataJSON.msg is 'ready'
    ws.send JSON.stringify
      msg: 'method'
      method: 'login',
      params: [
        resume: 'MvzqjyYD9bCs3rrcb'
      ]
      id: '1'