트위터 API 사용하기에 대해서 잘 설명해준 블로그
http://www.taeyo.pe.kr/Forum/Content.aspx?SEQ=157484&TBL=ASP&PGN=1
http://arisu.mireene.com/xe/textyle/2654
http://flexagon.egloos.com/category/Twitter%20API
http://smok95.tistory.com/214
liboauth
트위터API 사용을 위해 OAuth관련 정보를 찾던 중 liboauth라는 라이브러리를 찾았습니다.
liboauth 홈페이지 : http://liboauth.sourceforge.net/
liboauth는 OAuth인증을 쉽게 사용할 수 있도록 C로 구현된 라이브러리로
암호화부분은 OpenSSL 또는 NSS(Mozilla's Network Security Services) 라이브러리를 사용했습니다. 그리고 네트웍(http)부분은 cURL 또는 libcurl 을 사용합니다.
liboauth 컴파일시 설정에 따라 각각의 라이브러리와 연동됩니다.
뭐 아무튼 oauth공식홈페이지에는 소개가 되어있지 않습니다.
oauth쪽에서 liboauth를 모를것 같지는 않은데 왜 소개하지 않았는지 모르겠습니다... 그리고 참고적으로 oauth 스펙과 약간 맞지 않은 부분이 있지만 그리 큰 문제는 아닙니다.
▶ 트위터 어플리케이션 등록
먼저 트위터API 사용을 위한 첫번째 단계는 트위터에 연동하려는 어플리케이션을 등록해야합니다.
등록 방법은 아래 페이지를 참고하시면 됩니다.
[Programming/twitterAPI] - 트위터 twitter API 어플리케이션 등록하기
▶ OAuth 인증과정
그리고 OAuth인증과정에 대해서 대략적으로는 알고 있어야 합니다.
OAuth인증에 대한 정보는 아래 사이트를 참고하시면 됩니다.
https://apis.daum.net/oauth/main/welcome
http://dev.springnote.com/pages/1083108
http://www.ibm.com/developerworks/kr/library/wa-oauth1/
http://blueb.net/blog/1445
http://oauth.net/
OAuth홈페이지에서 제공하는 OAuth인증 흐름도를 보면서 대략적인 과정을 보겠습니다.
일단 용어에 대해 간략히 설명하면
- Service Provider : API를 제공하는 쪽입니다. 여기서는 트위터가 서비스프로바이더가 됩니다.
- Consumer : API를 사용하는 어플리케이션, 여기서는 트위터에 등록한 어플리케이션이 컨슈머입니다.
- User : 서비스프로바이더 또는 컨슈머를 사용하는 사용자, 트위터 또는 트위터관련 어플을 사용하는 트위터 사용자입니다.
OAuth를 사용하는 이유는 Consumer쪽에서 사용자의 정보(리소스)에 접근하기 위해서 사용자의 비밀번호가 필요하게 됩니다.
그런데 Consumer에서 악의적으로 사용할 수 있는 위험과 API를 http 요청하는 과정에서 사용자의 비밀번호가 그대로 노출되는 위험 등의 이유로 인해 OAuth라는 방식을 통해 이러한 문제점을 해결하면서 사용자의 정보(리소스)를 Consumer쪽에서 접근할 수 있도록 해주는 방법이 OAuth인증입니다. OpenAPI쪽에서 OAuth가 인증방식의 표준이라고 하네요..
뭐 암튼 기존에 제공하던 basic인증방식은 모르는 사람한테 집열쇠 맡긴거나 다름 없었나봅니다..
아무튼 그럼 그림의 순서를 보면
- Consumer Requests - Request Token
: 컨슈머(어플리케이션)가 서비스프로바이더(트위터)에게 리퀘스트 토큰을 요청합니다. 리퀘스트 토큰은 최종적으로 받게될 억세스 토큰을 받기 위해 임시적으로 사용되는 토큰입니다.
- Service Provider Grants - Request Token
: 컨슈머의 요청에 따라 서비스프로바이더는 리퀘스트 토큰을 발급해줍니다.
- Consumer Directs User to Service Provider
: 서비스프로바이더로부터 발급받은 리퀘스트 토큰과 인증URL을 사용자에게 알려주고 사용자가 직접 인증을 하도록 유도(?)합니다.
사용자는 컨슈머에게 받은 토큰을 가지고 서비스프로바이더에게 직접 접근을 하여 인증을 받습니다.
사용자가 인증에 성공하면 서비스프로바이더는 사용자에게 verifier(트위터 pin번호)값을 전달합니다.
- Service Provider Directs User to Consumer
: 사용자는 서비스프로바이더로 부터 받은 verifier(pin번호)을 컨슈머에게 알려줍니다.
- Comsumer Requests - Access Token
: 컨슈머는 사용자로 부터 받은 verifier(pin번호)값과 리퀘스트 토큰 등의 정보로 서비스프로바이더에게 억세스 토큰을 요청합니다.
- Service Provider Grants - Access Token
: 서비스 프로바이더는 컨슈머로 받은 요청을 검증하여 최종적으로 사용자의 리소스에 접근할 수 있는 억세스 토큰을 발급합니다.
- Comsumer Accesses Protected Resources
: 컨슈머는 이제 억세스 토큰을 이용하여 사용자의 보호된 리소스에 접근할 수 있습니다.
요약하자면 컨슈머(트위터 어플)은 사용자의 보호된 데이터에 접근하려면 Access Token이 필요하고 ,
이 토큰을 받으려면 서비스프로바이더(트위터)에게 먼저 Request Token을 발급받아야 하고,
컨슈머는 다시 사용자에게 Request Token을 주면서 서비스프로바이더에게 직접 인증을 받아줄 것을 부탁하고,
사용자는 자신의 보호된 데이터를 접근해도 된다는 것을 서비스프로바이더에게 알려 주고,
서비스프로바이더는 사용자가 허락했다는 증명서(pin번호)를 사용자에게 주고,
사용자는 다시 컨슈머에게 증명서를 줘서 컨슈머는 증명서와 Request Token을 가지고 Access Token을 발급해달라고 하면,
서비스프로바이더는 컨슈머가 제출한 자료를 검사하여 이상이 없으면 Access Token을 발급한다.
Access Token을 발급받은 컨슈머는 해당 토큰을 가지고 자유롭게 사용자의 보호된 데이터에 접근할 수 있게 된다.
음... 요약이 된긴 된건지;;;
아무튼 지금까지의 설명은 데스크탑 어플리케이션의 인증과정입니다.
D.Service Provider Directs User to Consumer 의 과정은 웹 어플리케이션에서는 약간 다릅니다.
▶ liboauth 트위터 인증 예제
그럼 이제 본격적으로 liboauth를 사용하여 트위터 OAuth인증을 해보도록 하겠습니다.
* 예제에 나오는 customer key나 token값들은 실제값이 아닙니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <oauth.h> /* 응답 메시지 파싱 */ int parse_response( const char * reply, char ** token, char ** secret, char ** username); int main( int argc, char * argv[]) { /* * http://dev.twitter.com/apps/new 를 통해 등록한 어플리케이션의 * customer key와 customer secret를 설정하시면 됩니다. */ const char *customerKey = "jWQZH0WTEEyHM0tqDgZd" , *customerSecret= "mbgrslyqyJz4V51hT1CJZqL8FUl1wPrIks" ; /* twitter api url 목록 */ const char const char *req_uri = NULL, *response = NULL; /* request token * 트위터에 요청하여 받게될 request token */ char *request_token = NULL, *request_token_secret = NULL, pin[100]={0,}; /* access token * 트위터에 요청하여 받게된 access token */ char *access_token = NULL, *access_token_secret = NULL, *screen_name = NULL; /*** * 1. request token 요청 */ { /* request url 생성 */ req_uri = oauth_sign_url2( request_token_url, NULL, OA_HMAC, NULL, customerKey, customerSecret, NULL, NULL); printf ( "request token request url:\n\t%s\n" , req_uri); /* 전송 */ response = oauth_http_get(req_uri,NULL); printf ( "response:\n\t%s\n" , response); /* 응답메시지에서 request token, request token secret 추출 */ parse_response(response, &request_token, &request_token_secret, NULL); printf ( "request token :\n\t%s\n" , request_token); printf ( "request token secret :\n\t%s\n" , request_token_secret); } /*** * 2. 사용자 인증 */ { /* 사용자 인증 url 생성 * 브라우저를 통해 해당 url로 이동한후 사용자 인증을 한다. */ char userAuthUrl[1024]={0,}; sprintf (userAuthUrl, "%s?oauth_token=%s" , authorize_url, request_token); printf ( "user authorization url:\n%s\n\n" , userAuthUrl); printf ( "Enter PIN number:" ); gets (pin); printf ( "PIN number:%s\n" , pin); } /*** * 3. access token 요청 */ { /* oauth_verifier(pin번호) 항목 수동 설정 현재 liboauth는 oauth_verifier항목을 지원하지 않아서 요청url에 해당 항목을 먼저 설정해준후 oauth_sign_url2를 호출합니다. */ char accUrl[1024]; sprintf (accUrl, "%s?oauth_verifier=%s" , access_token_url, pin); req_uri = oauth_sign_url2(accUrl, NULL, OA_HMAC, NULL, customerKey, customerSecret, request_token, request_token_secret); printf ( "access token request url:\n\t%s\n" , req_uri); response = oauth_http_get(req_uri,NULL); printf ( "response:\n\t%s\n" , response); /* 응답메시지에서 access token, access token secret, screen_name 추출 */ parse_response(response, &access_token, &access_token_secret, &screen_name); printf ( "access token :\n\t%s\n" , access_token); printf ( "access token secret :\n\t%s\n" , access_token_secret); printf ( "screen name :\n\t%s\n" , screen_name); } /*** * 4. api 사용하기 * user timeline 가져오기 예 */ { req_uri = oauth_sign_url2(user_timeline_url,NULL,OA_HMAC,NULL,customerKey,customerSecret, access_token,access_token_secret); response = oauth_http_get(req_uri,NULL); printf ( "user_timeline xml data:\n%s\n" , response); } return 0; } int parse_response( const char * reply, char ** token, char ** secret, char ** username) { int rc,idx; char **rv = NULL; const char *oauth_token = "oauth_token=" , *oauth_token_secret = "oauth_token_secret=" , *screen_name = "screen_name=" ; int tokenLen = ( int ) strlen (oauth_token), secretLen = ( int ) strlen (oauth_token_secret), nameLen = ( int ) strlen (screen_name); rc = oauth_split_url_parameters(reply, &rv); if (rc<=0) return -1; for (idx=0; idx<rc; idx++) { if (token && strncmp (oauth_token, rv[idx], tokenLen)==0 ) { *token = strdup(rv[idx]+tokenLen); } else if ( secret && strncmp (oauth_token_secret, rv[idx], secretLen)==0 ) { *secret = strdup(rv[idx]+secretLen); } else if ( username && strncmp (screen_name, rv[idx], nameLen)==0 ) { *username = strdup(rv[idx]+nameLen); } } if (rv) free (rv); return 0; } |
위의 예제에서 사용된 liboauth api는 oauth_sign_url2, oauth_http_get, oauth_split_url_parameters 이렇게 딱 3개입니다.
1
2
3
4
5
6
7
8 |
char *oauth_sign_url2 ( const char *url, char **postargs, OAuthMethod method, const char *http_method, const char *c_key, const char *c_secret, const char *t_key, const char *t_secret ); |
- url
토큰을 요청한다거나 api사용하여 특정 데이터를 가져온다거나 할때 요청 대상이 되는 url입니다. - postargs
POST방식 사용시 사용되는 인자입니다. GET방식사용시는 NULL로 설정합니다. - method
요청 파라미터를 어떤 방식으로 암호화하는지를 설정합니다.
OA_HMAC(hmac-sha1), OA_RSA(rsa), OA_PLAINTEXT(평문) 이렇게 3가지 형식을 지원합니다.
OA_PLAINTEXT는 https사용시만 가능하고 주로 OA_HMAC를 사용한다고 합니다. - http_method
POST, GET 등의 방식으로 NULL로 설정하는 경우 "GET"으로 처리되고 postargs인자가 NULL이 아닌경우 "POST"로 처리됩니다. - c_key
customer key값 - c_secret
customer secret값 - t_key
token key값 - t_secret
token secret값 - 리턴값
OAuth형식에 맞게 생성된 요청uri입니다. 리턴값은 동적할당된 값이기 때문에 사용후 반드시 해제해 주어야 합니다.
1 |
char *oauth_http_get ( const char *u, const char *q); |
- u
request url로 oauth_sign_url2로 생성된 url를 사용합니다. - q
추가적인 쿼리스트링을 붙여야하는 경우 해당 인자로 넘겨줍니다. - 리턴값
서비스프로바이더에서 요청후 응답받은 리턴값입니다. 리턴값은 동적할당된 값이기 때문에 사용후 반드시 해제해 주어야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
/* request url 생성 */ req_uri = oauth_sign_url2( request_token_url, NULL, OA_HMAC, NULL, customerKey, customerSecret, NULL, NULL); printf ( "request token request url:\n\t%s\n" , req_uri); /* 전송 */ response = oauth_http_get(req_uri,NULL); printf ( "response:\n\t%s\n" , response); /* 응답메시지에서 request token, request token secret 추출 */ parse_response(response, &request_token, &request_token_secret, NULL); printf ( "request token :\n\t%s\n" , request_token); printf ( "request token secret :\n\t%s\n" , request_token_secret); |
Access token을 얻기 위한 첫번째 단계입니다.
oauth_sign_url2에 트위터 request token url인 http://api.twitter.com/oauth/request_token 과 어플리케이션 등록후 받은 customer key와 customer secret값을 설정하여 만들어진 uri를 oauth_http_get함수로 트위터로 보내면 request token을 발급하여줍니다.
리턴값은 키=값&키=값.. 의 형태로 되어 있습니다. parse_response함수는 해당 형태의 문자열을 파싱하여 토큰값을 추출하는 역할을 합니다.
http://api.twitter.com/oauth/request_token?oauth_consumer_key=jWQZH0WTEEyHM0tqDgZd&oauth_nonce=mWt3pyyxUBWkjnp0caZg7hCa1ZO5&
oauth_signature_method=HMAC-SHA1&oauth_timestamp=1283904684&oauth_version=1.0&
oauth_signature=7YYF5L3WXiFL%2BAmeJ5fATzNlkc8%3D
response:
oauth_token=JrjZFiIUQORxHjJqDLyJhfSzngrqrDjuJS2Bisjbk&
oauth_token_secret=Y00zw2Cenqtemw8XpbFq1NkhKIanbWmEgxn3ROHFVU&oauth_callback_confirmed=true
request token :
wtQRiiCWSfUnRTqK01hyfXj0qbjL1WY6grhfn6cu8
request token secret :
Y00zw2Cenqtemw8XpbFq1NkhKIanbWmEgxn3ROHFVU
여기까지의 과정이 그림상의 A,B에 해당합니다.
2. 사용자 인증
1
2
3
4
5
6
7
8
9 |
/* 사용자 인증 url 생성 * 브라우저를 통해 해당 url로 이동한후 사용자 인증을 한다. */ char userAuthUrl[1024]={0,}; sprintf (userAuthUrl, "%s?oauth_token=%s" , authorize_url, request_token); printf ( "user authorization url:\n%s\n\n" , userAuthUrl); printf ( "Enter PIN number:" ); gets (pin); printf ( "PIN number:%s\n" , pin); |
인증 url에 request token만 붙이고 해당 url로 사용자가 직접 인증을 한후 PIN번호를 사용자가 입력하는 과정입니다.
인증을 트위터쪽에서 바로 하기때문에 어플리케이션쪽에서는 사용자의 아이디/비밀번호를 알 수 없는 구조이긴 하지만 사실 어플쪽에서 알려고 하면 방법이 없는것도 아니고 아무튼 인증과정에서 사용자가 끼어들어간다는게 여러모로 불편합니다. 이런 불편함은 데스크탑 어플리케이션쪽에만 해당하는 사항이고 그리고 이런 문제때문에 xAuth란 방식을 제공하고 있습니다. 다만 xAuth를 사용하려면 트위터에 메일을 보내 허락(?)을 받아야 합니다.
이 과정의 그림에서의 C,D에 해당합니다.
http://api.twitter.com/oauth/authorize?oauth_token=wtQRiiCWSfUnRTqK01hyfXj0qbjL1WY6grhfn6cu8
Enter PIN number:5996657
3. Access Token 요청
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
/* oauth_verifier(pin번호) 항목 수동 설정 현재 liboauth는 oauth_verifier항목을 지원하지 않아서 요청url에 해당 항목을 먼저 설정해준후 oauth_sign_url2를 호출합니다. */ char accUrl[1024]; sprintf (accUrl, "%s?oauth_verifier=%s" , access_token_url, pin); req_uri = oauth_sign_url2(accUrl, NULL, OA_HMAC, NULL, customerKey, customerSecret, request_token, request_token_secret); printf ( "access token request url:\n\t%s\n" , req_uri); response = oauth_http_get(req_uri,NULL); printf ( "response:\n\t%s\n" , response); /* 응답메시지에서 access token, access token secret, screen_name 추출 */ parse_response(response, &access_token, &access_token_secret, &screen_name); printf ( "access token :\n\t%s\n" , access_token); printf ( "access token secret :\n\t%s\n" , access_token_secret); printf ( "screen name :\n\t%s\n" , screen_name); |
사용자 인증을 마치고 사용자에게 PIN번호도 받고 access token발급을 위한 모든 준비가 끝났습니다.
access token 요청 url인 http://api.twitter.com/oauth/access_token 과 customer key, secret, request token, secret, pin(oauth_verifier값)을 oauth_sign_url2에 넘겨주면 최종적으로 요청uri를 만들어 주는데 문제는 oauth_sign_url2에는 pin값을 전달받는 인자가 없습니다.
그래서 첫번째 인자인 url을 넘길때 http://api.twitter.com/oauth/access_token?oauth_verifier=pin번호 와 같이 만들어 넘겨주면 됩니다.
이 방법이 불편하신 경우에는 liboauth소스를 수정하시면 됩니다.
아무튼 이제 트위터에 만들어진 uri를 보내면 access token을 발급받게 됩니다.
리턴값에는 access token과 함께 screen_name(사용자id)가 함께 포함되어 있습니다. 그럼 이제 발급받은 이 access_token을 파일이나 DB 등에 잘 저장해 두고 사용하시면 됩니다.
현재 트위터에서는 access token에 따로 만료기한을 설정하고 있지 않습니다. 특수한 경우가 아닌 이상 한번 받은 토큰으로 계속 사용이 가능합니다.
이 과정의 그림에서 E,F 에 해당합니다.
http://api.twitter.com/oauth/access_token?oauth_consumer_key=jWQZH0WTEEyHM0tqDgZd&oauth_nonce=NF8azpiDkOMST1uWaMv3PGUgkH7RI8&
oauth_signature_method=HMAC-SHA1&oauth_timestamp=1283906365&
oauth_token=wtQRiiCWSfUnRTqK01hyfXj0qbjL1WY6grhfn6cu8&oauth_verifier=5996657&
oauth_version=1.0&oauth_signature=NxjE4AuzJeqGCH%2FT1YaLbGQyqD4%3D
response:
oauth_token=7uqq1sDBwchFK114765419-XkCvUwW7XxOH50EU1ZFh1aV8spw&
oauth_token_secret=PpjW785u2KL4Zf3TYnMw9e6oEEInwRKggavDtCdCctd&user_id=114765419&screen_name=smok95
access token :
7uqq1sDBwchFK114765419-XkCvUwW7XxOH50EU1ZFh1aV8spw
access token secret :
PpjW785u2KL4Zf3TYnMw9e6oEEInwRKggavDtCdCctd
screen name :
smok95
4. API 사용하기 (user timeline 가져오기 예)
1
2
3
4
5
6 |
req_uri = oauth_sign_url2(user_timeline_url,NULL,OA_HMAC,NULL,customerKey,customerSecret, access_token,access_token_secret); printf ( "user timeline request url:\n%s\n" , req_uri); response = oauth_http_get(req_uri,NULL); printf ( "user_timeline xml data:\n%s\n" , response); |
http://api.twitter.com/1/statuses/user_timeline.xml?oauth_consumer_key=jWQZH0WTEEyHM0tqDgZd&oauth_nonce=qCZqJ2W8ZgQpvqTKVFeMWY2kgU&
oauth_signature_method=HMAC-SHA1&oauth_timestamp=1283908341&
oauth_token=7uqq1sDBwchFK114765419-XkCvUwW7XxOH50EU1ZFh1aV8spw&
oauth_version=1.0&oauth_signature=34NBYlwJuJU8JzNALuxAPQt8jdE%3D
<created_at>Tue Aug 17 13:18:56 +0000 2010</created_at>
<id>21402398564</id>
<text>어느새..</text>
<source><a href="http://twitter.com/" rel="nofollow">Twitter for iPhone</a></source>
<truncated>false</truncated>
<in_reply_to_status_id></in_reply_to_status_id>
<in_reply_to_user_id></in_reply_to_user_id>
<favorited>false</favorited>
<in_reply_to_screen_name></in_reply_to_screen_name>
<retweet_count></retweet_count>
<retweeted>false</retweeted>
<user>
<id>114765419</id>
<name>JK</name>
<screen_name>smok95</screen_name>
<location>Incheon</location>
<description>programmer</description>
<profile_image_url>http://a3.twimg.com/profile_images/1118451467/never_normal.png</profile_image_url>
<url>http://smok95.tistory.com</url>
<protected>false</protected>
<followers_count>5</followers_count>
<profile_background_color>C0DEED</profile_background_color>
<profile_text_color>333333</profile_text_color>
<profile_link_color>0084B4</profile_link_color>
<profile_sidebar_fill_color>DDEEF6</profile_sidebar_fill_color>
<profile_sidebar_border_color>C0DEED</profile_sidebar_border_color>
<friends_count>20</friends_count>
<created_at>Tue Feb 16 14:54:14 +0000 2010</created_at>
<favourites_count>2</favourites_count>
<utc_offset>32400</utc_offset>
<time_zone>Seoul</time_zone>
<profile_background_image_url>http://s.twimg.com/a/1283564528/images/themes/theme1/bg.png</profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<profile_use_background_image>true</profile_use_background_image>
<notifications>false</notifications>
<geo_enabled>false</geo_enabled>
<verified>false</verified>
<following>false</following>
<statuses_count>33</statuses_count>
<lang>en</lang>
<contributors_enabled>false</contributors_enabled>
<follow_request_sent>false</follow_request_sent>
<listed_count>0</listed_count>
<show_all_inline_media>false</show_all_inline_media>
</user>
<geo/>
<coordinates/>
<place/>
<contributors/>
</status>
실제로 사용자의 timeline을 가져온 모습입니다.
'개발 > App Developer' 카테고리의 다른 글
Bing Map iOS SDK (0) | 2011.05.10 |
---|---|
Grand Central Dispatch (1) | 2011.04.15 |
앱 개발자 등록 관련 블로그/사이트 (0) | 2010.11.17 |
코어 데이터 Core Data (0) | 2010.11.12 |
* 놈에 대한 참고 하나더 (0) | 2010.11.12 |