├── TODO.org ├── package.json ├── main.js ├── README.md ├── lib ├── data.js ├── store │ └── memory.js ├── util.js └── server.js └── examples ├── server.js └── consumer.js /TODO.org: -------------------------------------------------------------------------------- 1 | * TODO Interface for user storage. 2 | * TODO Verify all steps from specification. 3 | * DONE Sample for OAuth consumer. 4 | * TODO Ability to connect middleware. 5 | * TODO Don't specify port separately (in server constructor) 6 | * TODO Tests 7 | * TODO Store keys in redis. 8 | * TODO Move vars inside modules in settings 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name": "oauth-server", 2 | "description": "Server, supporting OAuth version 1.0A", 3 | "version": "0.2.0", 4 | "main": "main", 5 | "engines": ["node >= 0.3.1"], 6 | "author": "Temnov Kirill ", 7 | "dependencies": { "connect": ">= 1.0", "oauth": "*" }, 8 | "repository": {"type": "git", "url": "http://github.com/selead/oauth-server.git"} 9 | } -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 2 | exports.version = '0.1.5'; 3 | 4 | exports.server = require('./lib/server'); 5 | exports.util = require('./lib/util'); 6 | exports.Consumer = require('./lib/data').Consumer; 7 | 8 | var fs = require('fs'); 9 | 10 | // keys store middleware 11 | exports.store = {}; 12 | 13 | fs.readdirSync(__dirname + '/lib/store').forEach(function(filename){ 14 | if (/\.js$/.test(filename)) { 15 | var name = filename.substr(0, filename.lastIndexOf('.')); 16 | Object.defineProperty(exports.store, name, { get: function(){ 17 | return require('./lib/store/' + name); 18 | }}); 19 | } 20 | }); 21 | 22 | 23 | // Expose getters as first-class exports. 24 | exports.__proto__ = exports.store; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Server-side implementation of OAuth 1.0A protocol. 2 | ================================================== 3 | 4 | Installation: 5 | npm install oauth-server 6 | 7 | 8 | 9 | Usage example: 10 | -------------- 11 | 12 | Add example.com to your hosts file: 13 | 14 | echo "127.0.0.1 example.com" >> /etc/hosts 15 | 16 | 17 | Launch in console from repo root: 18 | 19 | node examples/server.js& 20 | node examples/consumer.js& 21 | 22 | Browse to http://example.com:3000 and follow instructions 23 | 24 | *Note* 25 | ------ 26 | This server handles requests for one (anonimous) user. Add store with users and connect them with 27 | access keys. If you will test both server and consumer, set various domain names, because consumer 28 | and oauth server *must have different cookie files* 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/data.js: -------------------------------------------------------------------------------- 1 | /** 2 | OAuth server data storages. 3 | Copyright(c) 2011 selead 4 | MIT Licensed 5 | */ 6 | 7 | /** 8 | Module dependencies. 9 | */ 10 | 11 | Consumer = function (params) { 12 | //name, descr, key, secret, callbackUrl, accessType, url) { 13 | this.name = params.name || ''; 14 | this.descr = params.descr || ''; 15 | this.key = params.key; 16 | this.secret = params.secret; 17 | this.accessType = params.accessType || 'r'; 18 | this.callbackUrl = params.callbackUrl || ''; 19 | this.url = params.url || ''; 20 | }; 21 | 22 | 23 | // todo update docs 24 | /** 25 | Token object. 26 | 27 | @param {String} key Token public key. 28 | @param {String} secret Token private key. 29 | @api public 30 | */ 31 | RequestToken = function (key, secret, consumerKey, userID) { 32 | this.key = key; 33 | this.secret = secret; 34 | this.consumerKey = consumerKey; 35 | this.userID = userID; 36 | }; 37 | 38 | AccessToken = function (key, secret, consumerKey, userID, accessType) { 39 | this.key = key; 40 | this.secret = secret; 41 | this.consumerKey = consumerKey; 42 | this.userID = userID; 43 | this.accessType = accessType || 'r'; 44 | this.createdAt = new Date(); 45 | this.freeze = false; 46 | }; 47 | 48 | 49 | 50 | exports.RequestToken = RequestToken; 51 | exports.AccessToken = AccessToken; 52 | exports.Consumer = Consumer; 53 | 54 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | Note: Before use example.com domain, make sure, that you add alias 127.0.0.1 to hosts file. 3 | */ 4 | 5 | var oasrv = require('oauth-server'), 6 | Server = oasrv.server.OAuthServer, 7 | memStore = oasrv.store.memory.OAuthMemoryStore, 8 | store = new memStore(); 9 | Consumer = oasrv.Consumer; 10 | 11 | var URL = require('url'), 12 | qs = require('querystring'); 13 | 14 | store.addConsumer( 15 | new Consumer({name:'oauth-test-consumer', key:'key', secret:'secret', 16 | callbackUrl: 'http://example.com:3000/oauth/example.com/verify'})); 17 | 18 | server = new Server('example.com:4000', {}, {}, 4000, store); 19 | 20 | server.addAction('/oauth/protected-resource', 21 | function (req, res, store) { 22 | var query = qs.parse((URL.parse(req.url)).query); 23 | var params = {}; 24 | console.log('ACCESS to protected resource\n'); 25 | if (req.method.toUpperCase() === 'GET') { 26 | params = query; 27 | store.lookupAccessToken(params.oauth_token, function (token) { 28 | if (token && token.secret === params.oauth_token_secret) { 29 | res.writeHead(200, {'Content-Type': 'text/html'}); 30 | res.write('

Access granted!

'); 31 | } else { 32 | res.writeHead(403, {'Content-Type': 'text/plain'}); 33 | res.write('Invalid token'); 34 | } 35 | res.end(); 36 | }); 37 | } else if (req.method.toUpperCase() === 'POST') { 38 | // todo 39 | } else { 40 | invalidUrl(); 41 | } 42 | }, true); 43 | 44 | server.listen(); 45 | -------------------------------------------------------------------------------- /examples/consumer.js: -------------------------------------------------------------------------------- 1 | /** 2 | Note: Before use example.com domain, make sure, that you add alias 127.0.0.1 to hosts file. 3 | 4 | This module depends on oauth and connenct, that may be installed via 5 | npm install oauth connect 6 | */ 7 | 8 | 9 | var sys = require('sys'), 10 | connect = require('connect'), 11 | url = require('url'), 12 | OAuth = require('oauth').OAuth; 13 | consumer = new OAuth('http://example.com:4000/oauth/request-token', 14 | 'http://example.com:4000/oauth/access-token', 15 | 'key', 'secret', '1.0A', 16 | 'http://example.com:3000//oauth/example.com/verify', 'HMAC-SHA1'); 17 | 18 | function app(app) { 19 | app.get('/', function(req, res){ 20 | res.writeHead(200, { 'Content-Type': 'text/html' }); 21 | res.write('Test consumer for oauth-server.
'+ 22 | ' Click link to start test'); 23 | res.end(); 24 | }); 25 | app.get('/test-oauth-server', function (req, res) { 26 | consumer.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results ){ 27 | if (error) { 28 | res.writeHead(200, { 'Content-Type': 'text/html' }); 29 | res.write('Error in request: ' + sys.inspect(error)); 30 | res.end(); 31 | } else { 32 | res.writeHead(302, { Location: 33 | 'http://example.com:4000/oauth/authorize?oauth_token=' + 34 | oauth_token}); 35 | var tok = {oauth_token: oauth_token, oauth_token_secret: oauth_token_secret}; 36 | req.session['oauth-token'] = tok; 37 | res.end(); 38 | } 39 | }); 40 | }); 41 | app.get('/oauth/example.com/verify', function (req, res) { 42 | consumer.getOAuthAccessToken(req.session['oauth-token'].oauth_token, 43 | req.session['oauth-token'].oauth_token_secret, 44 | url.parse(req.url, true).query.oauth_verifier, 45 | function(error, oauth_access_token, 46 | oauth_access_token_secret) { 47 | if (error === null) { 48 | req.session.oauth_access_token = oauth_access_token; 49 | req.session.oauth_access_token_secret = 50 | oauth_access_token_secret; 51 | res.writeHead(200, { 'Content-Type': 'text/html'}); 52 | res.write(' Access protected resource! '); 55 | } else { 56 | res.writeHead(500, { 'Content-Type': 'text/html' }); 57 | res.write('Error: ' + sys.inspect(error)); 58 | } 59 | res.end(); 60 | }); 61 | 62 | }); 63 | } 64 | 65 | var server = connect.createServer( 66 | connect.bodyParser(), 67 | connect.cookieParser(), 68 | connect.session({secret: 'change-me'}), 69 | connect.router(app), 70 | connect.errorHandler({ dumpExceptions: true, showStack: true }) 71 | ).listen(3000); 72 | -------------------------------------------------------------------------------- /lib/store/memory.js: -------------------------------------------------------------------------------- 1 | /** 2 | Initialize OAuthMemoryStore with optons. 3 | 4 | @param {Object} opts Options. 5 | @api public 6 | */ 7 | OAuthMemoryStore = function (opts) { 8 | this.consumers = {}; 9 | this.accessTokens = {}; 10 | this.requestTokens = {}; 11 | }; 12 | 13 | /** 14 | Add new consumer to OAuthMemoryStore. 15 | 16 | @param {Object} consumer New consumer. 17 | @param {String} consumer.key Consumer public key. 18 | @param {String} consumer.secret Consumer private key. 19 | @param {String} consumer.name Consumer display name. 20 | @api public 21 | */ 22 | OAuthMemoryStore.prototype.addConsumer = function (consumer) { 23 | this.consumers[consumer.key] = consumer; 24 | }; 25 | 26 | /** 27 | Remove consumer in OAuthMemoryStore by a public key. 28 | 29 | @param {String} key Consumer public key. 30 | @api public 31 | */ 32 | OAuthMemoryStore.prototype.removeConsumer = function (key) { 33 | delete this.consumers[key]; // splice ? 34 | }; 35 | 36 | /** 37 | Search for consumer in OAuthMemoryStore by a public key. 38 | 39 | @param {String} key Consumer public key. 40 | @return {Object} fn Callback function. 41 | @api public 42 | */ 43 | OAuthMemoryStore.prototype.lookupConsumer = function (key, fn) { 44 | fn(typeof this.consumers[key] === 'undefined' ? null : this.consumers[key]); 45 | }; 46 | 47 | /** 48 | Lookup token in tokens dictionary. 49 | 50 | @param {Object} consumer Consumer object. 51 | @param {String} tokenKey Token key. 52 | @param {Object} store Dictionary with tokens. 53 | @return {Token|null} token Token object. 54 | @api private 55 | */ 56 | OAuthMemoryStore.prototype._lookupToken = function (tokenKey, store) { 57 | var tok = store[tokenKey]; 58 | return (tok) ? tok : null; 59 | // if (tok && tok.consumerKey === consumer.key) { // wtf ??? 60 | // return tok; 61 | // for (var i in tok.consumerApps) { 62 | // if (tok.consumerApps[i].key === consumer.key) { 63 | // return tok; 64 | // } 65 | // } 66 | // } 67 | // return null; 68 | } 69 | 70 | /** 71 | Lookup request token in OAuthMemoryStore. 72 | 73 | @param {String} tokenKey Request token key. 74 | @return {Object} fn Callback function. 75 | @api public 76 | */ 77 | OAuthMemoryStore.prototype.lookupRequestToken = function (tokenKey, fn) { 78 | fn(this._lookupToken(tokenKey, this.requestTokens)); 79 | }; 80 | 81 | /** 82 | Lookup access token in OAuthMemoryStore. 83 | 84 | @param {String} tokenKey Access token key. 85 | @return {Object} fn Callback function. 86 | @api public 87 | */ 88 | OAuthMemoryStore.prototype.lookupAccessToken = function (tokenKey, fn) { 89 | fn(this._lookupToken(tokenKey, this.accessTokens)); 90 | }; 91 | 92 | /** 93 | Add token to store. 94 | 95 | @param {Token} token Token to add. 96 | @param {Object} store Dict to save token. 97 | @api private 98 | */ 99 | OAuthMemoryStore.prototype._addToken = function (token, store) { 100 | store[token.key] = token; 101 | }; 102 | 103 | /** 104 | Add request token to OAuthMemoryStore. 105 | 106 | @param {Token} token Request token. 107 | @api public 108 | */ 109 | OAuthMemoryStore.prototype.addRequestToken = function (token) { 110 | return this._addToken(token, this.requestTokens); 111 | }; 112 | 113 | /** 114 | Add access token to OAuthMemoryStore. 115 | 116 | @param {Token} token Access token. 117 | @api public 118 | */ 119 | OAuthMemoryStore.prototype.addAccessToken = function (token) { 120 | return this._addToken(token, this.accessTokens); 121 | }; 122 | 123 | /** 124 | Remove token from store. 125 | 126 | @param {Token} token Token to remove. 127 | @param {Object} store Store for tokens. 128 | @api private 129 | */ 130 | OAuthMemoryStore.prototype._removeToken = function (token, store) { 131 | delete store[token.key]; 132 | }; 133 | 134 | /** 135 | Remove request token. 136 | 137 | @param {Token} token Token to remove. 138 | @api public 139 | */ 140 | OAuthMemoryStore.prototype.removeRequestToken = function (token) { 141 | this._removeToken(token, this.requestTokens); 142 | }; 143 | 144 | OAuthMemoryStore.prototype.authorizeRequestToken = function (token, user) { 145 | 146 | }; 147 | 148 | exports.OAuthMemoryStore = OAuthMemoryStore; -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | Utils for oauth server. 3 | 4 | 5 | */ 6 | 7 | var max_timestamp_delta = 600; // todo !! 8 | 9 | 10 | var encodeData = function (data) { 11 | return data === null || data === '' ? "" : 12 | // replace symbols ! ' ( ) * 13 | encodeURIComponent(data).replace(/\!/g, "%21").replace(/\'/g, "%27") 14 | .replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/\*/g, "%2A"); 15 | } 16 | 17 | var decodeData = function (data) { 18 | return decodeURIComponent( data !== null ? data.replace(/\+/g, " ") : data); 19 | } 20 | 21 | 22 | var URL = require('url'), 23 | sys = require('sys'), 24 | crypto = require('crypto'); 25 | 26 | 27 | function sortParams (params) { 28 | params.sort(function (a, b) { 29 | if ( a[0] === b[0] ) { 30 | return a[1] < b[1] ? -1 : 1; 31 | } 32 | else { 33 | return a[0] < b[0] ? -1: 1; 34 | }}); 35 | return params; 36 | } 37 | 38 | function signParams (method, url, signType, secretKey, params) { 39 | for (var i in params) { 40 | if (params[i][0] === 'oauth_signature') { 41 | params.splice(i,1); 42 | break; 43 | } 44 | } 45 | url = encodeData(url) // todo normalize url 46 | var baseString = method.toUpperCase() + '&' + url + '&' + 47 | encodeData(params.map(function (p) { return p.join('='); }).join('&')); 48 | return crypto.createHmac('sha1', secretKey).update(baseString).digest('base64'); 49 | } 50 | 51 | // check for correct request token 52 | exports.checkRequest = function (req, store, fn) { 53 | var auth = req.headers.authorization.split(' '); 54 | if (auth[0] === 'OAuth') { 55 | // parsedParams 56 | var pp = sortParams(auth[1].replace(/\"/g, '').split(',').map(function (p) { 57 | return p.split('=')})); 58 | params = {}; 59 | for (var i in pp) { 60 | params[pp[i][0]] = pp[i][1]; 61 | } 62 | store.lookupConsumer(params.oauth_consumer_key, function (consumer) { 63 | if (consumer) { 64 | var signature = params.oauth_signature, 65 | url = 'http://' + req.headers.host + URL.parse(req.url).pathname; 66 | var sig2 = signParams(req.method, url, 67 | 'sha1', consumer.secret + '&', pp); 68 | sig2 = encodeData(sig2); 69 | // console.log('MATCHING SIGNATIRES : \n' + 70 | // signature + ' == ' + sig2 + ' is '+ (signature === sig2)); 71 | 72 | // if (consumer.callbackUrl !== params.oauth_callback) { 73 | // console.log('callback problem:\n' + 74 | // decodeURIComponent(params.oauth_callback)); 75 | // } 76 | fn && fn(signature !== sig2 ? signature : null, params, consumer); 77 | } 78 | else { 79 | // error 80 | // return false; 81 | fn && fn('error: consumer not found!'); 82 | }}); 83 | } 84 | else { 85 | fn && fn('Error: missing authorization header'); 86 | } 87 | } 88 | 89 | exports.checkAccess = function(req, store, fn) { 90 | var auth = req.headers.authorization.split(' '); 91 | if (auth[0] === 'OAuth') { 92 | console.log('DATA: ' + auth[1] + '\n'); 93 | // parsedParams 94 | var pp = sortParams(auth[1].replace(/\"/g, '').split(',').map( 95 | function (p) { 96 | return p.split('=')})); 97 | params = {}; 98 | for (var i in pp) { 99 | params[pp[i][0]] = pp[i][1]; 100 | } 101 | store.lookupRequestToken(params.oauth_token, function (token) { 102 | console.log('check access token:\n' + sys.inspect(params) + '\n\n'); 103 | // console.log('store:\n' + sys.inspect(store)); 104 | console.log('\ntoken = ' + sys.inspect(token)); 105 | if (token) { 106 | store.lookupConsumer(params.oauth_consumer_key, function (consumer) { 107 | if (consumer) { 108 | var signature = params.oauth_signature; 109 | url = 'http://' + req.headers.host + URL.parse(req.url).pathname; 110 | var sig2 = signParams(req.method, url, 111 | 'sha1', consumer.secret + '&' + token.secret, pp); 112 | sig2 = encodeData(sig2); 113 | var correct = signature === sig2 && 114 | params.oauth_consumer_key === token.consumer.key && 115 | params.oauth_verifier === token.verifier && 116 | // todo safe parsing 117 | ((new Date()).getTime() / 1000) - parseInt(params.oauth_timestamp) < 118 | max_timestamp_delta; 119 | 120 | fn(correct ? null : signature, token); 121 | //fn && fn(signature !== sig2 ? signature : null, params, consumer); 122 | } 123 | else { 124 | // error 125 | // return false; 126 | fn('error: consumer not found!'); 127 | }}); 128 | } 129 | }); 130 | } 131 | else { 132 | fn('Error: missing authorization header'); 133 | } 134 | } 135 | 136 | 137 | var generateKeypair = function(generatorKey, string, algorithm) { 138 | algorithm = algorithm || 'sha256'; 139 | var key = crypto.createHmac('sha1', generatorKey).update(Date.now().toString()).digest('base64') 140 | .replace(/\=/g, '.').replace(/\//g, '-').replace(/\+/g, '_'); 141 | var secret = crypto.createHmac(algorithm, key).update(string).digest('base64') 142 | .replace(/\=/g, '.').replace(/\//g, '-').replace(/\+/g, '_'); 143 | var pair = {key: key, secret: secret}; 144 | // console.log(sys.inspect(pair)); 145 | return pair; 146 | } 147 | 148 | var generateRequestToken = function (oauthParams, consumer, algorithm) { 149 | var tok = generateKeypair(oauthParams.oauth_nonce, consumer.secret + 150 | params.oauth_signature, algorithm); 151 | tok.nonce = oauthParams.oauth_nonce; 152 | tok.timestamp = oauthParams.oauth_timestamp; 153 | tok.consumer = consumer; 154 | tok.verifier = generateVerifier(); 155 | return tok; 156 | } 157 | exports.generateRequestToken = generateRequestToken; 158 | 159 | 160 | // add userid to access token 161 | var generateAccessToken = function(requestToken, algorithm) { 162 | var tok = generateKeypair(requestToken.consumer.secret, 163 | requestToken.consumer.name + requestToken.secret + 164 | requestToken.consumer.callbackUrl, algorithm); 165 | return tok; 166 | } 167 | exports.generateAccessToken = generateAccessToken; 168 | 169 | // range of verification chars 170 | const verifierSet = 'abcdefghijklmnopqrstuvwxyz0123456789'; 171 | 172 | /** 173 | Generate verification code. 174 | 175 | @params {Number} size Size of verification code. Optional, default - 20. 176 | @return {String} code Generated code. 177 | @api public 178 | */ 179 | var generateVerifier = function (size) { 180 | var verifier = ''; 181 | size = size || 20; 182 | for (var i = 0; i < size; i++) { 183 | verifier += verifierSet[Math.floor(Math.random()*verifierSet.length)]; 184 | } 185 | return verifier; 186 | } 187 | exports.generateVerifier = generateVerifier; 188 | 189 | -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'), 2 | qs = require('querystring'); 3 | 4 | //var Mstore = require('./oauth-store').OAuthMemoryStore, 5 | //Consumer = require('./oauth-server').Consumer, 6 | var util = require('./util'), 7 | checkRequest = util.checkRequest, 8 | generateRequestToken = util.generateRequestToken, 9 | generateAccessToken = util.generateAccessToken, 10 | URL = require('url'), 11 | http = require('http'); 12 | //store = new Mstore(); 13 | 14 | 15 | 16 | function getRequestToken(query, store, fn) { 17 | if (typeof query.oauth_token !== 'string') { 18 | return null; 19 | } 20 | var key = query.oauth_token.replace(/\"/g, ''); 21 | store.lookupRequestToken(key, function (rt) { 22 | if (rt && query.oauth_verifier) { 23 | fn(query.oauth_verifier.replace(/\"/g, '') === rt.verifier ? rt : null); 24 | } 25 | fn(rt); 26 | }); 27 | } 28 | 29 | 30 | function invalidUrl (res) { 31 | res.setHeader('Content-Type', 'text/plain'); 32 | res.statusCode = 404 33 | res.end("url invalid"); 34 | } 35 | 36 | /** 37 | Create oauth server object. 38 | 39 | @param {String} domain Domain name. 40 | 41 | 42 | 43 | 44 | */ 45 | var OAuthServer = function (domain, actions, protectedActions, port, store) { 46 | this._domain = domain; 47 | 48 | this.port = port; 49 | function authorize (req, res, store, allow) { 50 | if (req.method.toUpperCase() === 'POST') { 51 | var data = ''; 52 | req.on('data', function (chunk) { 53 | data += chunk.toString(); 54 | }); 55 | 56 | req.on('end', function () { 57 | getRequestToken(qs.parse(data), store, function (rt) { 58 | if (rt) { 59 | if (allow) { 60 | // todo 61 | // check timestamp and nonce 62 | // create access token and sent it to consumer 63 | res.setHeader("Location",rt.consumer.callbackUrl + 64 | '?oauth_token=' + rt.key + 65 | '&oauth_verifier=' + rt.verifier); 66 | res.statusCode = 302; 67 | res.end(); 68 | } 69 | else { 70 | // todo 71 | // deny access 72 | // remove request token 73 | res.setHeader('Content-Type', 'text/plain'); 74 | res.statusCode = 404; 75 | res.end('Access from ' + rt.consumer.name + ' declined'); 76 | } 77 | } 78 | else { 79 | invalidUrl(res); 80 | } 81 | }); 82 | }); 83 | } else { 84 | invalidUrl(res); 85 | } 86 | 87 | } 88 | 89 | this.store = store; // || new require('./oauth-store').OAuthMemoryStore(), 90 | // todo update actions 91 | this.actions = { 92 | '/oauth/request-token': function (req, res, store) { 93 | if(req.headers.authorization) { 94 | console.log('STORE = ' + sys.inspect(store)); 95 | checkRequest(req, store, function (error, params, consumer) { 96 | // console.log('check request. error = ' + sys.inspect(error)); 97 | if (! error) { 98 | var token = generateRequestToken(params, consumer); 99 | store.addRequestToken(token); 100 | res.setHeader('Content-Type', 'text/plain'); 101 | res.end('oauth_token='+ token.key + '&oauth_token_secret=' + 102 | token.secret+ '&oauth_callback_confirmed=true'); 103 | // additional params 104 | } 105 | else { 106 | res.setHeader('Content-Type', 'text/plain') 107 | console.log(error); 108 | res.statusCode = 404; 109 | res.end(); 110 | } 111 | }); 112 | } 113 | // else { missing authorization 114 | }, 115 | '/oauth/access-token': function (req, res, store) { 116 | if (req.method.toUpperCase() === 'POST') { 117 | var data = ''; 118 | req.on('data', function (chunk) { 119 | data += chunk.toString(); 120 | }); 121 | 122 | req.on('end', function () { 123 | util.checkAccess(req, store, function (error, requestToken) { 124 | if (!error) { 125 | // save user id! 126 | var accessToken = generateAccessToken(requestToken); 127 | store.addAccessToken(accessToken); 128 | store.removeRequestToken(requestToken); 129 | res.setHeader('Content-Type', 'text/plain'); 130 | res.end('oauth_token=' + accessToken.key + 131 | '&oauth_token_secret=' + accessToken.secret); 132 | } 133 | else { 134 | res.setHeader('Content-Type', 'text/plain'); 135 | res.statusCode = 404; 136 | res.end(error); 137 | } 138 | }); 139 | }); 140 | } 141 | else { 142 | invalidUrl(res); 143 | } 144 | 145 | }, 146 | '/oauth/authorize': function (req, res, store) { 147 | console.log('Inside /oauth/authorize'); 148 | var query = qs.parse((URL.parse(req.url)).query); 149 | getRequestToken(query, store, function (rt) { 150 | if (rt) { 151 | res.setHeader('Content-Type','text/html') 152 | var data = 153 | ' OAuth server test page ' + 154 | '

Allow ' + rt.consumer.name + ' application ?

'+ 155 | '
' + 156 | '' + 158 | '
' + 159 | '
' + 160 | '' + 162 | '' + 163 | 164 | 165 | ''; 166 | res.end(data); 167 | } 168 | else { 169 | res.setHeader('Content-Type', 'text/plain') 170 | res.statusCode = 403; 171 | res.end('goes wrong'); 172 | } 173 | }); 174 | 175 | }, 176 | 177 | '/oauth/authorize/allow': function (req, res) { 178 | authorize(req, res, store, true); 179 | }, 180 | 181 | '/oauth/authorize/cancel': function (req, res) { 182 | authorize(req, res, store, false); 183 | }}; 184 | 185 | this.protectedActions = protectedActions; 186 | 187 | var self = this; 188 | this.server = http.createServer(function (req, res) { 189 | var url = URL.parse(req.url).pathname; // todo regexp search 190 | if (typeof self.actions[url] === 'function') { 191 | self.actions[url](req, res, self.store); 192 | } 193 | else if (typeof self.protectedActions[url] === 'function' ){ 194 | self.protectedActions[url](req, res, self.store); 195 | } 196 | else { 197 | invalidUrl(res); 198 | // error, page not found 199 | } 200 | }); 201 | } 202 | 203 | OAuthServer.prototype.listen = function () { 204 | this.server.listen(4000); //this.port); 205 | } 206 | 207 | OAuthServer.prototype.addAction = function (url, action, isProtected) { 208 | if (isProtected) { 209 | this.protectedActions[url] = action; 210 | } 211 | else { 212 | this.actions[url] = action; 213 | } 214 | } 215 | 216 | OAuthServer.prototype.removeAction = function (url, isProtected) { 217 | isProtected ? delete protectedActions[url] : actions[url]; 218 | } 219 | 220 | exports.OAuthServer = OAuthServer; 221 | --------------------------------------------------------------------------------