├── README.md ├── oauth.js ├── test.js └── webtest.js /README.md: -------------------------------------------------------------------------------- 1 | 2 | # OAuth.js 3 | OAuth Client for Node.js(nodejs.org) 4 | 5 | ## Example(test.js) 6 | 7 | var OAuth = require('./oauth').OAuth; 8 | 9 | var config = { 10 | server: 'www.google.com', 11 | requestTokenURI: 'https://www.google.com/accounts/OAuthGetRequestToken', 12 | authorizeTokenURI: 'https://www.google.com/accounts/OAuthAuthorizeToken', 13 | accessTokenURI: 'https://www.google.com/accounts/OAuthGetAccessToken', 14 | signatureMethod: 'HMAC-SHA1', 15 | consumerKey: 'anonymous', 16 | consumerSecret: 'anonymous', 17 | callbackURI: 'http://itealabs.net/callback' 18 | }; 19 | 20 | var oa = new OAuth(config); 21 | oa.acquireRequestToken({scope: 'http://www.google.com/calendar/feeds http://picasaweb.google.com/data', xoauth_displayname: 'my test'}, function(oa){ 22 | console.log(oa.oauthToken); 23 | console.log(oa.getAuthorizeTokenURI()); 24 | console.log('------------PLEASE INPUT OAUTH_VERIFIER(NOT ENCODED):'); 25 | 26 | var v = ''; 27 | var stdin = process.openStdin(); 28 | stdin.on('data', function(d){ 29 | v = d.toString(); 30 | }); 31 | stdin.on('end', function() { 32 | console.log('stdinend'); 33 | oa.setOAuthVerifier(v); 34 | oa.acquireAccessToken(function(oa){ 35 | console.log(oa.generateAuthorizationString()); 36 | }); 37 | }); 38 | }); 39 | 40 | ## More Examples 41 | Please view webtest.js 42 | 43 | ## Notice 44 | RSA-SHA1 signature has not been implemented temporary. 45 | 46 | ## License 47 | MIT 48 | 49 | -------------------------------------------------------------------------------- /oauth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * oauth.js 3 | * OAuth Client for Node.js 4 | * @author itea 2010-10-29 5 | * @version 0.1 2010-11-06 6 | * @license MIT 7 | * @git git://github.com/itea/oauthjs.git 8 | */ 9 | 10 | var http = require('http'), querystring = require('querystring'); 11 | var https = require('https'); 12 | var enc = encodeURIComponent, dec = decodeURIComponent; 13 | /** 14 | * 15 | * @param config: Object 16 | */ 17 | function OAuth(configuration) { 18 | this.config = configuration || {}; 19 | } 20 | 21 | OAuth.prototype.acquireRequestToken = function(body, callback, ctx) { 22 | var client = OAuth.createClient(this.config.requestTokenURI); 23 | 24 | var oauthHeader = OAuth.buildRequestAuthorizationHeader(this); 25 | var signatureBaseString = OAuth.generateSignatureBaseString('POST', 26 | this.config.requestTokenURI, oauthHeader, body); 27 | //console.log('baseString: '+ signatureBaseString); 28 | oauthHeader['oauth_signature'] = OAuth.sign(this, signatureBaseString); 29 | 30 | var headers = { 31 | 'Host': this.config.server, 32 | 'Content-Type': 'application/x-www-form-urlencoded', 33 | 'Authorization': OAuth.toAuthorizationHeaderString(oauthHeader) 34 | }; 35 | //console.log(JSON.stringify(headers)); 36 | var request = client.http.request({ 37 | host: client.host, 38 | port: client.port, 39 | path: client.path, 40 | method: 'POST', 41 | headers: headers 42 | }); 43 | 44 | request.write(querystring.stringify(body)); 45 | request.end(); 46 | 47 | var oauth = this; 48 | request.on('response', function(response) { 49 | if(+response.statusCode === 200) { 50 | response.setEncoding('utf8'); 51 | response.on('data', function(data) { 52 | OAuth.parseOAuthBody(data.toString(), oauth); 53 | callback && callback.call(ctx, oauth); 54 | }); 55 | } else { 56 | //console.log('ERROR: '+ response.statusCode); 57 | response.on('data', function(data) { 58 | //console.log('ERROR-BODY: '+ data); 59 | var err = new Error(data.toString()); 60 | err.statusCode = response.statusCode; 61 | callback && callback.call(ctx, err); 62 | }); 63 | } 64 | }); 65 | return this; 66 | }; 67 | 68 | OAuth.prototype.getAuthorizeTokenURI = function(parameters){ 69 | parameters = parameters || {}; 70 | var s = []; 71 | s.push('oauth_token='+ enc(this.oauthToken)); 72 | for(var p in parameters) s.push([p, '=', enc(parameters[p])].join('')); 73 | return [this.config.authorizeTokenURI, '?', s.join('&')].join(''); 74 | }; 75 | 76 | OAuth.prototype.setOAuthVerifier = function(oauthVerifier){ 77 | this.oauthVerifier = oauthVerifier; 78 | return this; 79 | }; 80 | 81 | OAuth.prototype.acquireAccessToken = function(callback, ctx){ 82 | var client = OAuth.createClient(this.config.accessTokenURI); 83 | var oauthHeader = OAuth.buildAccessAuthorizationHeader(this); 84 | var signatureBaseString = OAuth.generateSignatureBaseString('POST', 85 | this.config.accessTokenURI, oauthHeader); 86 | oauthHeader['oauth_signature'] = OAuth.sign(this, signatureBaseString); 87 | 88 | var headers = { 89 | 'Host': this.config.server, 90 | 'Content-Type': 'application/x-www-form-urlencoded', 91 | 'Authorization': OAuth.toAuthorizationHeaderString(oauthHeader) 92 | }; 93 | var request = client.http.request({ 94 | host: client.host, 95 | port: client.port, 96 | path: client.path, 97 | method: 'POST', 98 | headers: headers 99 | }); 100 | request.end(); 101 | 102 | var oauth = this; 103 | request.on('response', function(response) { 104 | if(+response.statusCode === 200) { 105 | response.setEncoding('utf8'); 106 | response.on('data', function(data) { 107 | OAuth.parseOAuthBody(data.toString(), oauth); 108 | callback && callback.call(ctx, oauth); 109 | //console.log(data.toString()); 110 | }); 111 | } else { 112 | //console.log('ERROR: '+ response.statusCode); 113 | response.on('data', function(data) { 114 | //console.log('ERROR-BODY: '+ data); 115 | var err = new Error(data.toString()); 116 | err.statusCode = response.statusCode; 117 | callback && callback.call(ctx, err); 118 | }); 119 | } 120 | }); 121 | return this; 122 | }; 123 | 124 | /** 125 | * Generate Authorization header String for data access api request 126 | */ 127 | OAuth.prototype.generateAuthorizationString = function(method, uri, parameters) { 128 | var oauthHeader = OAuth.buildAuthorizationHeader(this); 129 | var signatureBaseString = OAuth.generateSignatureBaseString(method, uri, oauthHeader, parameters); 130 | oauthHeader['oauth_signature'] = OAuth.sign(this, signatureBaseString); 131 | return OAuth.toAuthorizationHeaderString(oauthHeader); 132 | }; 133 | 134 | /* -------------------------------- */ 135 | OAuth.createClient = function(uri) { 136 | var secure = /^https.+/.test(uri) ? true : false; 137 | var group = /^https?:\/\/([^\/:]+)(?:\:(\d+))?(\/.+)?$/.exec(uri) || []; 138 | var port = group[2], server = group[1]; 139 | if(!port) port = secure ? 443 : 80; 140 | port = +port; 141 | if (secure) { 142 | return { 143 | http: https, 144 | host: server, 145 | port: port, 146 | path: group[3] 147 | }; 148 | } 149 | else { 150 | return { 151 | http: http, 152 | host: server, 153 | port: port, 154 | path: group[3] 155 | }; 156 | } 157 | }; 158 | 159 | OAuth.parseOAuthBody = function(body, oauth) { 160 | oauth = oauth || {}; 161 | var b = querystring.parse(body); 162 | oauth.oauthToken = b.oauth_token; 163 | oauth.oauthTokenSecret = b.oauth_token_secret; 164 | return oauth; 165 | }; 166 | 167 | /** 168 | * Build Authorization header string for request token request 169 | */ 170 | OAuth.buildRequestAuthorizationHeader = function(oauth) { 171 | var config = oauth.config; 172 | return { 173 | 'oauth_consumer_key': config.consumerKey, 174 | 'oauth_version': '1.0', 175 | 'oauth_callback': config.callbackURI, 176 | 'oauth_timestamp': (new Date().valueOf()/1000).toFixed().toString(), 177 | 'oauth_nonce': new Date().valueOf().toString(), 178 | 'oauth_signature_method': config.signatureMethod 179 | }; 180 | }; 181 | 182 | /** 183 | * Build Authorization header string for access token request 184 | */ 185 | OAuth.buildAccessAuthorizationHeader = function(oauth) { 186 | var config = oauth.config; 187 | return { 188 | 'oauth_consumer_key': config.consumerKey, 189 | 'oauth_version': '1.0', 190 | 'oauth_timestamp': (new Date().valueOf()/1000).toFixed().toString(), 191 | 'oauth_nonce': new Date().valueOf().toString(), 192 | 'oauth_signature_method': config.signatureMethod, 193 | 'oauth_verifier': oauth.oauthVerifier, 194 | 'oauth_token': oauth.oauthToken 195 | }; 196 | }; 197 | 198 | /** 199 | * Build Authorization header string for data access api request 200 | */ 201 | OAuth.buildAuthorizationHeader = function(oauth) { 202 | var config = oauth.config; 203 | return { 204 | 'oauth_consumer_key': config.consumerKey, 205 | 'oauth_version': '1.0', 206 | 'oauth_timestamp': (new Date().valueOf()/1000).toFixed().toString(), 207 | 'oauth_signature_method': config.signatureMethod, 208 | 'oauth_token': oauth.oauthToken 209 | }; 210 | }; 211 | 212 | OAuth.toAuthorizationHeaderString = function(header) { 213 | var a = []; 214 | for(var v in header) { 215 | a.push([v, '="', enc(header[v]), '"'].join('')); 216 | } 217 | return 'OAuth ' + a.join(','); 218 | }; 219 | 220 | /** 221 | * @param method: string ('POST' or 'GET') 222 | * @param baseURL: string 223 | * @param headers: header parameters object 224 | * @param bodys: body parameter object 225 | */ 226 | OAuth.generateSignatureBaseString = function(method, uri, headers, bodys) { 227 | var params = [], uri = encodeURI(uri); 228 | headers = headers || {}; 229 | bodys = bodys || {}; 230 | 231 | for(var idx in headers) { 232 | params.push([idx, enc(headers[idx])].join('=')); 233 | } 234 | for(var idx in bodys) { 235 | params.push([enc(idx), enc(bodys[idx])].join('=')); 236 | } 237 | return [method.toUpperCase(), enc(uri.replace(/\?.+$/, '')), enc(params.sort().join('&'))].join('&'); 238 | }; 239 | 240 | OAuth.sign = function(oauth, baseString) { 241 | var method = oauth.config.signatureMethod; 242 | var key = [enc(oauth.config.consumerSecret ||''), '&', enc(oauth.oauthTokenSecret ||'')].join(''); 243 | if(method === 'HMAC-SHA1') return OAuth.signHmacSha1(baseString, key); 244 | else if(method === 'RSA-SHA1') return null; 245 | }; 246 | 247 | OAuth.signHmacSha1 = function(baseString, key) { 248 | var signer = require('crypto').createHmac('SHA1', key); 249 | signer.update(baseString); 250 | return signer.digest('base64'); 251 | }; 252 | 253 | exports.OAuth = OAuth; 254 | 255 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 2 | var OAuth = require('./oauth').OAuth; 3 | 4 | var config = { 5 | server: 'www.google.com', 6 | requestTokenURI: 'https://www.google.com/accounts/OAuthGetRequestToken', 7 | authorizeTokenURI: 'https://www.google.com/accounts/OAuthAuthorizeToken', 8 | accessTokenURI: 'https://www.google.com/accounts/OAuthGetAccessToken', 9 | signatureMethod: 'HMAC-SHA1', 10 | consumerKey: 'anonymous', 11 | consumerSecret: 'anonymous', 12 | callbackURI: 'http://itealabs.net/callback' 13 | }; 14 | 15 | var oa = new OAuth(config); 16 | oa.acquireRequestToken({scope: 'http://www.google.com/calendar/feeds http://picasaweb.google.com/data', xoauth_displayname: 'my test'}, function(oa){ 17 | if(oa instanceof Error) { 18 | console.log(oa.statusCode +' '+ oa); 19 | process.exit(1); 20 | } 21 | console.log(oa.oauthToken); 22 | console.log(oa.getAuthorizeTokenURI()); 23 | console.log('------------PLEASE INPUT OAUTH_VERIFIER(NOT ENCODED):'); 24 | 25 | var v = ''; 26 | var stdin = process.openStdin(); 27 | stdin.on('data', function(d){ 28 | v = d.toString(); 29 | }); 30 | stdin.on('end', function() { 31 | console.log('stdinend'); 32 | oa.setOAuthVerifier(v); 33 | oa.acquireAccessToken(function(oa){ 34 | if(oa instanceof OAuth) { 35 | console.log(oa.generateAuthorizationString()); 36 | } else console.log(oa.statusCode +' '+ oa); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /webtest.js: -------------------------------------------------------------------------------- 1 | 2 | var OAuth = require('./oauth').OAuth; 3 | var express = require('express'); 4 | 5 | var app = express.createServer(); 6 | app.use(express.bodyParser()); 7 | app.use(express.errorHandler({showStack:true, dumpExceptions:true})); 8 | 9 | var config = { 10 | server: 'www.google.com', 11 | requestTokenURI: 'https://www.google.com/accounts/OAuthGetRequestToken', 12 | authorizeTokenURI: 'https://www.google.com/accounts/OAuthAuthorizeToken', 13 | accessTokenURI: 'https://www.google.com/accounts/OAuthGetAccessToken', 14 | signatureMethod: 'HMAC-SHA1', 15 | consumerKey: 'anonymous', 16 | consumerSecret: 'anonymous', 17 | callbackURI: 'http://itea.sytes.net:8080/callback' 18 | }; 19 | 20 | var oauth = new OAuth(config); 21 | 22 | app.get('/', function(request, response) { 23 | response.send('requestToekn'); 24 | }); 25 | app.get('/requestToken', function(request, response) { 26 | oauth.acquireRequestToken({scope: 'http://www.google.com/calendar/feeds'}, 27 | function(oa) { 28 | if(oa instanceof Error) { 29 | response.send(oa.statusCode +' '+ oa.toString()); 30 | } else response.redirect(oa.getAuthorizeTokenURI()); 31 | }); 32 | }); 33 | app.get('/callback', function(request, response) { 34 | oauth.setOAuthVerifier(request.param('oauth_verifier')); 35 | oauth.acquireAccessToken(function(oa){ 36 | if(oa instanceof Error) { 37 | response.send(oa.statusCode +' '+ oa.toString()); 38 | } else response.send('success:' + oa.oauthToken); 39 | }); 40 | }); 41 | 42 | app.listen(8080); 43 | 44 | --------------------------------------------------------------------------------