88//
99var TWITTER_API_TIMEOUT = 10000 ,
1010 TWITTER_API_USERAGENT = 'Node/' + process . version . substr ( 1 ) ,
11- TWITTER_API_BASE = 'https://api.twitter.com/1.1' ;
12- TWITTER_STREAM_BASE = 'https://stream.twitter.com/1.1' ;
13- TWITTER_OAUTH_REQUEST_TOKEN_URL = 'https://twitter.com/oauth/request_token' ;
14- TWITTER_OAUTH_AUTHORIZE_URL = 'https://twitter.com/oauth/authorize' ;
15- TWITTER_OAUTH_AUTHENTICATE_URL = 'https://twitter.com/oauth/authenticate' ;
16- TWITTER_OAUTH_ACCESS_TOKEN_URL = 'https://twitter.com/oauth/access_token' ;
11+ TWITTER_API_BASE = 'https://api.twitter.com/1.1' ,
12+ TWITTER_STREAM_BASE = 'https://stream.twitter.com/1.1' ,
13+ TWITTER_OAUTH_REQUEST_TOKEN_URL = 'https://twitter.com/oauth/request_token' ,
14+ TWITTER_OAUTH_AUTHORIZE_URL = 'https://twitter.com/oauth/authorize' ,
15+ TWITTER_OAUTH_AUTHENTICATE_URL = 'https://twitter.com/oauth/authenticate' ,
16+ TWITTER_OAUTH_ACCESS_TOKEN_URL = 'https://twitter.com/oauth/access_token' ,
17+ TWITTER_OAUTH2_BEARER_TOKEN_URL = 'https://api.twitter.com/oauth2/token' ,
18+ TWITTER_OAUTH2_INVALIDATE_URL = 'https://api.twitter.com/oauth2/invalidate_token' ;
19+
1720
1821
1922
@@ -33,6 +36,10 @@ OAuthToken.prototype.getAuthorizationUrl = function(){
3336 return TWITTER_OAUTH_AUTHORIZE_URL + '?oauth_token=' + encodeURIComponent ( this . key ) ;
3437}
3538
39+ OAuthToken . prototype . getBasicAuthHeader = function ( ) {
40+ var creds = new Buffer ( encodeURIComponent ( this . key ) + ':' + encodeURIComponent ( this . secret ) ) ;
41+ return 'Basic ' + creds . toString ( 'base64' ) ;
42+ }
3643
3744
3845
@@ -41,16 +48,22 @@ OAuthToken.prototype.getAuthorizationUrl = function(){
4148 * Object for compiling, signing and serializing OAuth parameters
4249 */
4350function OAuthParams ( args ) {
51+ this . bearer_token = '' ;
4452 this . consumer_secret = '' ;
4553 this . token_secret = '' ;
4654 this . args = args || { } ;
47- this . args . oauth_version = this . args . oauth_version || '1.0' ;
48-
55+ }
56+
57+ OAuthParams . prototype . setBearer = function ( bearer ) {
58+ this . bearer_token = bearer ;
59+ delete this . args . oauth_version ;
60+ return this ;
4961}
5062
5163OAuthParams . prototype . setConsumer = function ( token ) {
5264 this . consumer_secret = token . secret || '' ;
5365 this . args . oauth_consumer_key = token . key || '' ;
66+ this . args . oauth_version = '1.0' ;
5467 return this ;
5568}
5669
@@ -94,6 +107,11 @@ OAuthParams.prototype.sign = function( requestMethod, requestUri ){
94107}
95108
96109OAuthParams . prototype . getHeader = function ( ) {
110+ // OAuth 2
111+ if ( this . bearer_token ) {
112+ return 'Bearer ' + encodeURIComponent ( this . bearer_token ) ;
113+ }
114+ // OAuth 1
97115 var a , args = { } , lines = [ ] ;
98116 for ( a in this . args ) {
99117 if ( 0 === a . indexOf ( 'oauth_' ) ) {
@@ -118,9 +136,6 @@ OAuthParams.prototype.getHeader = function(){
118136 */
119137function TwitterClient ( ) {
120138 this . deAuth ( ) ;
121- // register rate limits for all REST calls
122- this . lastCall = null ;
123- this . lastMeta = { } ;
124139}
125140
126141TwitterClient . prototype . getLastMeta = function ( method ) {
@@ -139,24 +154,32 @@ TwitterClient.prototype.getRateLimitReset = function( method ){
139154 return new Date ( this . getLastMeta ( method ) . reset * 1000 ) ;
140155}
141156
142- TwitterClient . prototype . setAuth = function ( consumerKey , consumerSecret , accessKey , accessSecret ) {
143- this . consumerToken = new OAuthToken ( consumerKey , consumerSecret ) ;
157+ TwitterClient . prototype . setAuth = function ( consumerOrBearer , consumerSecret , accessKey , accessSecret ) {
158+ this . deAuth ( ) ;
159+ // OAuth 2
160+ if ( 1 === arguments . length ) {
161+ this . bearerToken = consumerOrBearer ;
162+ return this ;
163+ }
164+ // OAuth 1
165+ this . consumerToken = new OAuthToken ( consumerOrBearer , consumerSecret ) ;
144166 if ( accessKey || accessSecret ) {
145167 this . accessToken = new OAuthToken ( accessKey , accessSecret ) ;
146168 }
147- else {
148- this . accessToken = null ;
149- }
150169 return this ;
151170}
152171
153172TwitterClient . prototype . hasAuth = function ( ) {
154- return ( this . accessToken instanceof OAuthToken ) && ( this . consumerToken instanceof OAuthToken ) ;
173+ return ( this . bearerToken && this . bearerToken . length ) || ( this . accessToken instanceof OAuthToken ) && ( this . consumerToken instanceof OAuthToken ) ;
155174}
156175
157176TwitterClient . prototype . deAuth = function ( ) {
177+ this . bearerToken = null ;
158178 this . consumerToken = null ;
159179 this . accessToken = null ;
180+ // register rate limits for all REST calls
181+ this . lastCall = null ;
182+ this . lastMeta = { } ;
160183 return this ;
161184}
162185
@@ -180,7 +203,7 @@ TwitterClient.prototype._rest = function( requestMethod, requestPath, requestArg
180203 }
181204 this . lastCall = requestPath ;
182205 var client = this ;
183- return this . call ( requestMethod , requestUri , requestArgs , TWITTER_API_TIMEOUT , function ( res , err ) {
206+ return this . _call ( requestMethod , requestUri , requestArgs , '' , TWITTER_API_TIMEOUT , function ( res , err ) {
184207 if ( ! res ) {
185208 callback ( null , err , 0 ) ;
186209 return ;
@@ -246,7 +269,7 @@ TwitterClient.prototype.stream = function( requestPath, requestArgs, callback ){
246269 } ;
247270 }
248271 var client = this ;
249- return this . call ( requestMethod , requestUri , requestArgs , 0 , function ( res , err ) {
272+ return this . _call ( requestMethod , requestUri , requestArgs , '' , 0 , function ( res , err ) {
250273 if ( ! res ) {
251274 callback ( null , err ) ;
252275 return ;
@@ -269,20 +292,27 @@ TwitterClient.prototype.stream = function( requestPath, requestArgs, callback ){
269292 } ) ;
270293}
271294
272- TwitterClient . prototype . call = function ( requestMethod , requestUri , requestArgs , timeout , callback ) {
295+ TwitterClient . prototype . _call = function ( requestMethod , requestUri , requestArgs , authHeader , timeout , callback ) {
273296 requestMethod = String ( requestMethod || 'GET' ) . toUpperCase ( ) ;
274297 // build and sign request parameters
275298 var params = new OAuthParams ( requestArgs ) ;
276- this . consumerToken && params . setConsumer ( this . consumerToken ) ;
277- this . accessToken && params . setAccess ( this . accessToken ) ;
278- params . sign ( requestMethod , requestUri ) ;
299+ if ( this . bearerToken ) {
300+ params . setBearer ( this . bearerToken ) ;
301+ }
302+ else {
303+ this . consumerToken && params . setConsumer ( this . consumerToken ) ;
304+ this . accessToken && params . setAccess ( this . accessToken ) ;
305+ }
279306 // grab authorization header and any remaining params
280- var oauth = params . getHeader ( ) ,
281- query = params . serialize ( ) ;
307+ if ( ! authHeader ) {
308+ params . sign ( requestMethod , requestUri ) ;
309+ authHeader = params . getHeader ( ) ;
310+ }
311+ var query = params . serialize ( ) ;
282312 // build http request starting with parsed endpoint
283313 var http = require ( 'url' ) . parse ( requestUri ) ;
284314 http . headers = {
285- Authorization : oauth ,
315+ Authorization : authHeader ,
286316 'User-Agent' : TWITTER_API_USERAGENT
287317 } ;
288318 if ( 'POST' === requestMethod ) {
@@ -330,22 +360,36 @@ TwitterClient.prototype.abort = function(){
330360TwitterClient . prototype . fetchRequestToken = function ( url , callback ) {
331361 var requestUri = TWITTER_OAUTH_REQUEST_TOKEN_URL ,
332362 requestArgs = { oauth_callback : url || 'oob' } ;
333- return this . _oauthExchange ( requestUri , requestArgs , callback ) ;
363+ return this . _oauthExchange ( requestUri , requestArgs , '' , callback ) ;
334364}
335365
336366TwitterClient . prototype . fetchAccessToken = function ( verifier , callback ) {
337367 var requestUri = TWITTER_OAUTH_ACCESS_TOKEN_URL ,
338368 requestArgs = { oauth_verifier : verifier } ;
339- return this . _oauthExchange ( requestUri , requestArgs , callback ) ;
369+ return this . _oauthExchange ( requestUri , requestArgs , '' , callback ) ;
340370}
341371
342- TwitterClient . prototype . _oauthExchange = function ( requestUri , requestArgs , callback ) {
372+ TwitterClient . prototype . fetchBearerToken = function ( callback ) {
373+ var requestUri = TWITTER_OAUTH2_BEARER_TOKEN_URL ,
374+ requestArgs = { grant_type : 'client_credentials' } ,
375+ authHeader = this . consumerToken . getBasicAuthHeader ( ) ;
376+ return this . _oauthExchange ( requestUri , requestArgs , authHeader , callback ) ;
377+ }
378+
379+ TwitterClient . prototype . invalidateBearerToken = function ( bearer , callback ) {
380+ var requestUri = TWITTER_OAUTH2_INVALIDATE_URL ,
381+ requestArgs = { access_token : bearer } ,
382+ authHeader = this . consumerToken . getBasicAuthHeader ( ) ;
383+ return this . _oauthExchange ( requestUri , requestArgs , authHeader , callback ) ;
384+ }
385+
386+ TwitterClient . prototype . _oauthExchange = function ( requestUri , requestArgs , authHeader , callback ) {
343387 if ( 'function' !== typeof callback ) {
344388 callback = function ( ) {
345389 console . error ( 'No callback for POST ' + requestUri ) ;
346390 }
347391 }
348- this . call ( 'POST' , requestUri , requestArgs , TWITTER_API_TIMEOUT , function ( res , err ) {
392+ this . _call ( 'POST' , requestUri , requestArgs , authHeader , TWITTER_API_TIMEOUT , function ( res , err ) {
349393 if ( ! res ) {
350394 callback ( null , err || { } , 0 ) ;
351395 return ;
@@ -357,8 +401,14 @@ TwitterClient.prototype._oauthExchange = function( requestUri, requestArgs, call
357401 body += chunk ;
358402 } ) ;
359403 res . on ( 'end' , function ( ) {
360- var params = require ( 'querystring' ) . parse ( body ) ;
361- var token = params . oauth_token && params . oauth_token_secret && new OAuthToken ( params . oauth_token , params . oauth_token_secret ) ;
404+ var token ,
405+ params = 0 === body . indexOf ( '{' ) ? JSON . parse ( body ) : body . require ( 'querystring' ) . parse ( body ) ;
406+ if ( 'bearer' === params . token_type ) {
407+ token = decodeURIComponent ( params . access_token ) ;
408+ }
409+ else if ( params . oauth_token && params . oauth_token_secret ) {
410+ token = new OAuthToken ( params . oauth_token , params . oauth_token_secret ) ;
411+ }
362412 callback ( token , params , res . statusCode ) ;
363413 } ) ;
364414 } ) ;
0 commit comments