From 28f56a70a1db88d8be081965e98911ee5cb0fb1d Mon Sep 17 00:00:00 2001 From: Ahn YoungSeon Date: Fri, 26 Nov 2021 20:42:16 +0900 Subject: [PATCH 01/77] #5 passport kakao module install --- node_modules/.package-lock.json | 48 + node_modules/oauth/.npmignore | 1 + node_modules/oauth/LICENSE | 8 + node_modules/oauth/Makefile | 7 + node_modules/oauth/Readme.md | 190 +++ .../oauth/examples/express-gdata/server.js | 168 +++ .../express-gdata/views/google_calendars.ejs | 21 + .../express-gdata/views/google_contacts.ejs | 24 + .../examples/express-gdata/views/layout.ejs | 9 + node_modules/oauth/examples/github-example.js | 73 ++ .../oauth/examples/term.ie.oauth-HMAC-SHA1.js | 31 + .../oauth/examples/twitter-example.js | 75 ++ node_modules/oauth/index.js | 3 + node_modules/oauth/lib/_utils.js | 4 + node_modules/oauth/lib/oauth.js | 581 +++++++++ node_modules/oauth/lib/oauth2.js | 228 ++++ node_modules/oauth/lib/sha1.js | 334 ++++++ node_modules/oauth/package.json | 15 + node_modules/oauth/tests/oauth2tests.js | 304 +++++ node_modules/oauth/tests/oauthtests.js | 1064 +++++++++++++++++ node_modules/oauth/tests/sha1tests.js | 13 + node_modules/oauth/tests/shared.js | 26 + node_modules/passport-kakao/.eslintrc.js | 51 + node_modules/passport-kakao/.prettierrc | 12 + node_modules/passport-kakao/.travis.yml | 6 + node_modules/passport-kakao/README.md | 204 ++++ node_modules/passport-kakao/dist/Strategy.js | 81 ++ .../passport-kakao/dist/passport-kakao.js | 8 + .../passport-kakao/dist/types/models.js | 2 + node_modules/passport-kakao/package.json | 45 + node_modules/passport-kakao/sample/README.md | 5 + .../passport-kakao/sample/package-lock.json | 397 ++++++ .../passport-kakao/sample/package.json | 12 + node_modules/passport-kakao/sample/sample.js | 49 + node_modules/passport-kakao/src/Strategy.ts | 93 ++ .../passport-kakao/src/passport-kakao.ts | 5 + .../passport-kakao/src/types/models.ts | 19 + .../tests/passport-kakao.spec.ts | 24 + node_modules/passport-kakao/tsconfig.json | 64 + node_modules/passport-oauth2/LICENSE | 20 + node_modules/passport-oauth2/README.md | 93 ++ .../lib/errors/authorizationerror.js | 44 + .../lib/errors/internaloautherror.js | 49 + .../passport-oauth2/lib/errors/tokenerror.js | 36 + node_modules/passport-oauth2/lib/index.js | 25 + node_modules/passport-oauth2/lib/strategy.js | 356 ++++++ node_modules/passport-oauth2/lib/utils.js | 30 + node_modules/passport-oauth2/package.json | 50 + node_modules/passport-strategy/.jshintrc | 20 + node_modules/passport-strategy/.travis.yml | 15 + node_modules/passport-strategy/LICENSE | 20 + node_modules/passport-strategy/README.md | 61 + node_modules/passport-strategy/lib/index.js | 15 + .../passport-strategy/lib/strategy.js | 28 + node_modules/passport-strategy/package.json | 50 + node_modules/pkginfo/.npmignore | 2 + node_modules/pkginfo/LICENSE | 19 + node_modules/pkginfo/README.md | 86 ++ node_modules/pkginfo/docs/docco.css | 194 +++ node_modules/pkginfo/docs/pkginfo.html | 101 ++ .../pkginfo/examples/all-properties.js | 19 + .../pkginfo/examples/array-argument.js | 20 + .../pkginfo/examples/multiple-properties.js | 19 + .../pkginfo/examples/object-argument.js | 22 + node_modules/pkginfo/examples/package.json | 10 + .../pkginfo/examples/single-property.js | 19 + .../pkginfo/examples/subdir/package.json | 11 + node_modules/pkginfo/examples/target-dir.js | 20 + node_modules/pkginfo/lib/pkginfo.js | 136 +++ node_modules/pkginfo/package.json | 21 + node_modules/pkginfo/test/pkginfo-test.js | 83 ++ node_modules/uid2/HISTORY.md | 21 + node_modules/uid2/LICENSE | 21 + node_modules/uid2/README.md | 32 + node_modules/uid2/index.js | 55 + node_modules/uid2/package.json | 12 + package-lock.json | 90 +- package.json | 3 +- 78 files changed, 6230 insertions(+), 2 deletions(-) create mode 100644 node_modules/oauth/.npmignore create mode 100644 node_modules/oauth/LICENSE create mode 100644 node_modules/oauth/Makefile create mode 100644 node_modules/oauth/Readme.md create mode 100644 node_modules/oauth/examples/express-gdata/server.js create mode 100644 node_modules/oauth/examples/express-gdata/views/google_calendars.ejs create mode 100644 node_modules/oauth/examples/express-gdata/views/google_contacts.ejs create mode 100644 node_modules/oauth/examples/express-gdata/views/layout.ejs create mode 100644 node_modules/oauth/examples/github-example.js create mode 100644 node_modules/oauth/examples/term.ie.oauth-HMAC-SHA1.js create mode 100644 node_modules/oauth/examples/twitter-example.js create mode 100644 node_modules/oauth/index.js create mode 100644 node_modules/oauth/lib/_utils.js create mode 100644 node_modules/oauth/lib/oauth.js create mode 100644 node_modules/oauth/lib/oauth2.js create mode 100644 node_modules/oauth/lib/sha1.js create mode 100644 node_modules/oauth/package.json create mode 100644 node_modules/oauth/tests/oauth2tests.js create mode 100644 node_modules/oauth/tests/oauthtests.js create mode 100644 node_modules/oauth/tests/sha1tests.js create mode 100644 node_modules/oauth/tests/shared.js create mode 100644 node_modules/passport-kakao/.eslintrc.js create mode 100644 node_modules/passport-kakao/.prettierrc create mode 100644 node_modules/passport-kakao/.travis.yml create mode 100644 node_modules/passport-kakao/README.md create mode 100644 node_modules/passport-kakao/dist/Strategy.js create mode 100644 node_modules/passport-kakao/dist/passport-kakao.js create mode 100644 node_modules/passport-kakao/dist/types/models.js create mode 100644 node_modules/passport-kakao/package.json create mode 100644 node_modules/passport-kakao/sample/README.md create mode 100644 node_modules/passport-kakao/sample/package-lock.json create mode 100644 node_modules/passport-kakao/sample/package.json create mode 100644 node_modules/passport-kakao/sample/sample.js create mode 100644 node_modules/passport-kakao/src/Strategy.ts create mode 100644 node_modules/passport-kakao/src/passport-kakao.ts create mode 100644 node_modules/passport-kakao/src/types/models.ts create mode 100644 node_modules/passport-kakao/tests/passport-kakao.spec.ts create mode 100644 node_modules/passport-kakao/tsconfig.json create mode 100644 node_modules/passport-oauth2/LICENSE create mode 100644 node_modules/passport-oauth2/README.md create mode 100644 node_modules/passport-oauth2/lib/errors/authorizationerror.js create mode 100644 node_modules/passport-oauth2/lib/errors/internaloautherror.js create mode 100644 node_modules/passport-oauth2/lib/errors/tokenerror.js create mode 100644 node_modules/passport-oauth2/lib/index.js create mode 100644 node_modules/passport-oauth2/lib/strategy.js create mode 100644 node_modules/passport-oauth2/lib/utils.js create mode 100644 node_modules/passport-oauth2/package.json create mode 100644 node_modules/passport-strategy/.jshintrc create mode 100644 node_modules/passport-strategy/.travis.yml create mode 100644 node_modules/passport-strategy/LICENSE create mode 100644 node_modules/passport-strategy/README.md create mode 100644 node_modules/passport-strategy/lib/index.js create mode 100644 node_modules/passport-strategy/lib/strategy.js create mode 100644 node_modules/passport-strategy/package.json create mode 100644 node_modules/pkginfo/.npmignore create mode 100644 node_modules/pkginfo/LICENSE create mode 100644 node_modules/pkginfo/README.md create mode 100644 node_modules/pkginfo/docs/docco.css create mode 100644 node_modules/pkginfo/docs/pkginfo.html create mode 100644 node_modules/pkginfo/examples/all-properties.js create mode 100644 node_modules/pkginfo/examples/array-argument.js create mode 100644 node_modules/pkginfo/examples/multiple-properties.js create mode 100644 node_modules/pkginfo/examples/object-argument.js create mode 100644 node_modules/pkginfo/examples/package.json create mode 100644 node_modules/pkginfo/examples/single-property.js create mode 100644 node_modules/pkginfo/examples/subdir/package.json create mode 100644 node_modules/pkginfo/examples/target-dir.js create mode 100644 node_modules/pkginfo/lib/pkginfo.js create mode 100644 node_modules/pkginfo/package.json create mode 100644 node_modules/pkginfo/test/pkginfo-test.js create mode 100644 node_modules/uid2/HISTORY.md create mode 100644 node_modules/uid2/LICENSE create mode 100644 node_modules/uid2/README.md create mode 100644 node_modules/uid2/index.js create mode 100644 node_modules/uid2/package.json diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index c7d1e0d..933b825 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -336,6 +336,11 @@ "node": ">= 0.6" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -355,11 +360,49 @@ "node": ">= 0.8" } }, + "node_modules/passport-kakao": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", + "dependencies": { + "passport-oauth2": "~1.1.2", + "pkginfo": "~0.3.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", + "integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -522,6 +565,11 @@ "node": ">= 0.6" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/node_modules/oauth/.npmignore b/node_modules/oauth/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/node_modules/oauth/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/node_modules/oauth/LICENSE b/node_modules/oauth/LICENSE new file mode 100644 index 0000000..f8049f8 --- /dev/null +++ b/node_modules/oauth/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) <2010-2012> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/oauth/Makefile b/node_modules/oauth/Makefile new file mode 100644 index 0000000..7723a39 --- /dev/null +++ b/node_modules/oauth/Makefile @@ -0,0 +1,7 @@ +# +# Run all tests +# +test: + @@node_modules/.bin/vows tests/*tests.js --spec + +.PHONY: test install diff --git a/node_modules/oauth/Readme.md b/node_modules/oauth/Readme.md new file mode 100644 index 0000000..bfbb805 --- /dev/null +++ b/node_modules/oauth/Readme.md @@ -0,0 +1,190 @@ +node-oauth +=========== +A simple oauth API for node.js . This API allows users to authenticate against OAUTH providers, and thus act as OAuth consumers. It also has support for OAuth Echo, which is used for communicating with 3rd party media providers such as TwitPic and yFrog. + +Tested against Twitter (http://twitter.com), term.ie (http://term.ie/oauth/example/), TwitPic, and Yahoo! + +Also provides rudimentary OAuth2 support, tested against facebook, github, foursquare, google and Janrain. For more complete usage examples please take a look at connect-auth (http://github.com/ciaranj/connect-auth) + +[![Clone in Koding](http://learn.koding.com/btn/clone_d.png)][koding] +[koding]: https://koding.com/Teamwork?import=https://github.com/ciaranj/node-oauth/archive/master.zip&c=git1 +[![Pair on Thinkful](https://tf-assets-staging.s3.amazonaws.com/badges/thinkful_repo_badge.svg)][Thinkful] +[Thinkful]: http://start.thinkful.com/node/?utm_source=github&utm_medium=badge&utm_campaign=node-oauth + +Installation +============== + + $ npm install oauth + + +Examples +========== + +To run examples/tests install Mocha `$ npm install -g mocha` and run `$ mocha you-file-name.js`: + +## OAuth1.0 + +```javascript +describe('OAuth1.0',function(){ + var OAuth = require('oauth'); + + it('tests trends Twitter API v1.1',function(done){ + var oauth = new OAuth.OAuth( + 'https://api.twitter.com/oauth/request_token', + 'https://api.twitter.com/oauth/access_token', + 'your application consumer key', + 'your application secret', + '1.0A', + null, + 'HMAC-SHA1' + ); + oauth.get( + 'https://api.twitter.com/1.1/trends/place.json?id=23424977', + 'your user token for this app', //test user token + 'your user secret for this app', //test user secret + function (e, data, res){ + if (e) console.error(e); + console.log(require('util').inspect(data)); + done(); + }); + }); +}); +``` + +## OAuth2.0 +```javascript +describe('OAuth2',function(){ + var OAuth = require('oauth'); + + it('gets bearer token', function(done){ + var OAuth2 = OAuth.OAuth2; + var twitterConsumerKey = 'your key'; + var twitterConsumerSecret = 'your secret'; + var oauth2 = new OAuth2(server.config.keys.twitter.consumerKey, + twitterConsumerSecret, + 'https://api.twitter.com/', + null, + 'oauth2/token', + null); + oauth2.getOAuthAccessToken( + '', + {'grant_type':'client_credentials'}, + function (e, access_token, refresh_token, results){ + console.log('bearer: ',access_token); + done(); + }); + }); +``` + +Change History +============== +* 0.9.15 + - OAuth2: Allow specification of agent +* 0.9.14 + - OAuth2: Extend 'successful' token responses to include anything in the 2xx range. +* 0.9.13 + - OAuth2: Fixes the "createCredentials() is deprecated, use tls.createSecureContext instead" message. (thank you AJ ONeal) +* 0.9.12 + - OAuth1/2: Can now pass Buffer instance directly for PUTs+POSTs (thank you Evan Prodromou) + - OAuth1: Improve interoperability with libraries that mess with the prototype. (thank you Jose Ignacio Andres) + - OAuth2: Adds PUT support for OAuth2 (thank you Derek Brooks) + - OAuth1: Improves use_strict compatibility (thank you Ted Goddard) +* 0.9.11 + - OAuth2: No longer sends the type=webserver argument with the OAuth2 requests (thank you bendiy) + - OAuth2: Provides a default (and overrideable) User-Agent header (thanks to Andrew Martens & Daniel Mahlow) + - OAuth1: New followRedirects client option (true by default) (thanks to Pieter Joost van de Sande) + - OAuth1: Adds RSA-SHA1 support (thanks to Jeffrey D. Van Alstine & Michael Garvin & Andreas Knecht) +* 0.9.10 + - OAuth2: Addresses 2 issues that came in with 0.9.9, #129 & #125 (thank you José F. Romaniello) +* 0.9.9 + - OAuth1: Fix the mismatch between the output of querystring.stringify() and this._encodeData(). (thank you rolandboon) + - OAuth2: Adds Authorization Header and supports extra headers by default ( thanks to Brian Park) +* 0.9.8 + - OAuth1: Support overly-strict OAuth server's that require whitespace separating the Authorization Header parameters (e.g. 500px.com) (Thanks to Christian Schwarz) + - OAuth1: Fix incorrect double-encoding of PLAINTEXT OAuth connections (Thanks to Joe Rozner) + - OAuth1: Minor safety check added when checking hostnames. (Thanks to Garrick Cheung) +* 0.9.7 + - OAuth2: Pass back any extra response data for calls to getOAuthAccessToken (Thanks to Tang Bo Hao) + - OAuth2: Don't force a https request if given a http url (Thanks to Damien Mathieu) + - OAuth2: Supports specifying a grant-type of 'refresh-token' (Thanks to Luke Baker) +* 0.9.6 + - OAuth2: Support for 302 redirects (Thanks Patrick Negri). + - OAuth1/2: Some code tidying. ( Thanks to Raoul Millais ) +* 0.9.5 + - OAuth1: Allow usage of HTTP verbs other than GET for retrieving the access and request tokens (Thanks to Raoul Millais) +* 0.9.4 + - OAuth1/2: Support for OAuth providers that drop connections (don't send response lengths? [Google]) + - OAuth2: Change getOAuthAccessToken to POST rather than GET ( Possible Breaking change!!! ... re-tested against Google, Github, Facebook, FourSquare and Janrain and seems ok .. is closer to the spec (v20) ) +* 0.9.3 + - OAuth1: Adds support for following 301 redirects (Thanks bdickason) +* 0.9.2 + - OAuth1: Correct content length calculated for non-ascii post bodies (Thanks selead) + - OAuth1: Allowed for configuration of the 'access token' name used when requesting protected resources (OAuth2) +* 0.9.1 + - OAuth1: Added support for automatically following 302 redirects (Thanks neyric) + - OAuth1: Added support for OAuth Echo (Thanks Ryan LeFevre). + - OAuth1: Improved handling of 2xx responses (Thanks Neil Mansilla). +* 0.9.0 + - OAuth1/2: Compatibility fixes to bring node-oauth up to speed with node.js 0.4x [thanks to Rasmus Andersson for starting the work ] +* 0.8.4 + - OAuth1: Fixed issue #14 (Parameter ordering ignored encodings). + - OAuth1: Added support for repeated parameter names. + - OAuth1/2: Implements issue #15 (Use native SHA1 if available, 10x speed improvement!). + - OAuth2: Fixed issue #16 (Should use POST when requesting access tokens.). + - OAuth2: Fixed Issue #17 (OAuth2 spec compliance). + - OAuth1: Implemented enhancement #13 (Adds support for PUT & DELETE http verbs). + - OAuth1: Fixes issue #18 (Complex/Composite url arguments [thanks novemberborn]) +* 0.8.3 + - OAuth1: Fixed an issue where the auth header code depended on the Array's toString method (Yohei Sasaki) Updated the getOAuthRequestToken method so we can access google's OAuth secured methods. Also re-implemented and fleshed out the test suite. +* 0.8.2 + - OAuth1: The request returning methods will now write the POST body if provided (Chris Anderson), the code responsible for manipulating the headers is a bit safe now when working with other code (Paul McKellar) + - Package: Tweaked the package.json to use index.js instead of main.js +* 0.8.1 + - OAuth1: Added mechanism to get hold of a signed Node Request object, ready for attaching response listeners etc. (Perfect for streaming APIs) +* 0.8.0 + - OAuth1: Standardised method capitalisation, the old getOauthAccessToken is now getOAuthAccessToken (Breaking change to existing code) +* 0.7.7 + - OAuth1: Looks like non oauth_ parameters where appearing within the Authorization headers, which I believe to be incorrect. +* 0.7.6 + - OAuth1: Added in oauth_verifier property to getAccessToken required for 1.0A +* 0.7.5 + - Package: Added in a main.js to simplify the require'ing of OAuth +* 0.7.4 + - OAuth1: Minor change to add an error listener to the OAuth client (thanks troyk) +* 0.7.3 + - OAuth2: Now sends a Content-Length Http header to keep nginx happy :) +* 0.7.2 + - OAuth1: Fixes some broken unit tests! +* 0.7.0 + - OAuth1/2: Introduces support for HTTPS end points and callback URLS for OAuth 1.0A and Oauth 2 (Please be aware that this was a breaking change to the constructor arguments order) + +Contributors (In no particular order) +===================================== + +* Evan Prodromou +* Jose Ignacio Andres +* Ted Goddard +* Derek Brooks +* Ciaran Jessup - ciaranj@gmail.com +* Mark Wubben - http://equalmedia.com/ +* Ryan LeFevre - http://meltingice.net +* Raoul Millais +* Patrick Negri - http://github.com/pnegri +* Tang Bo Hao - http://github.com/btspoony +* Damien Mathieu - http://42.dmathieu.com +* Luke Baker - http://github.com/lukebaker +* Christian Schwarz - http://github.com/chrischw/ +* Joe Rozer - http://www.deadbytes.net +* Garrick Cheung - http://www.garrickcheung.com/ +* rolandboon - http://rolandboon.com +* Brian Park - http://github.com/yaru22 +* José F. Romaniello - http://github.com/jfromaniello +* bendiy - https://github.com/bendiy +* Andrew Martins - http://www.andrewmartens.com +* Daniel Mahlow - https://github.com/dmahlow +* Pieter Joost van de Sande - https://github.com/pjvds +* Jeffrey D. Van Alstine +* Michael Garvin +* Andreas Knecht +* AJ ONeal +* Philip Skinner - https://github.com/PhilipSkinner diff --git a/node_modules/oauth/examples/express-gdata/server.js b/node_modules/oauth/examples/express-gdata/server.js new file mode 100644 index 0000000..3c7bf7f --- /dev/null +++ b/node_modules/oauth/examples/express-gdata/server.js @@ -0,0 +1,168 @@ +var express = require('express'), + OAuth = require('oauth').OAuth, + querystring = require('querystring'); + +// Setup the Express.js server +var app = express.createServer(); +app.use(express.logger()); +app.use(express.bodyParser()); +app.use(express.cookieParser()); +app.use(express.session({ + secret: "skjghskdjfhbqigohqdiouk" +})); + +// Home Page +app.get('/', function(req, res){ + if(!req.session.oauth_access_token) { + res.redirect("/google_login"); + } + else { + res.redirect("/google_contacts"); + } +}); + +// Request an OAuth Request Token, and redirects the user to authorize it +app.get('/google_login', function(req, res) { + + var getRequestTokenUrl = "https://www.google.com/accounts/OAuthGetRequestToken"; + + // GData specifid: scopes that wa want access to + var gdataScopes = [ + querystring.escape("https://www.google.com/m8/feeds/"), + querystring.escape("https://www.google.com/calendar/feeds/") + ]; + + var oa = new OAuth(getRequestTokenUrl+"?scope="+gdataScopes.join('+'), + "https://www.google.com/accounts/OAuthGetAccessToken", + "anonymous", + "anonymous", + "1.0", + "http://localhost:3000/google_cb"+( req.param('action') && req.param('action') != "" ? "?action="+querystring.escape(req.param('action')) : "" ), + "HMAC-SHA1"); + + oa.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){ + if(error) { + console.log('error'); + console.log(error); + } + else { + // store the tokens in the session + req.session.oa = oa; + req.session.oauth_token = oauth_token; + req.session.oauth_token_secret = oauth_token_secret; + + // redirect the user to authorize the token + res.redirect("https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token="+oauth_token); + } + }) + +}); + +// Callback for the authorization page +app.get('/google_cb', function(req, res) { + + // get the OAuth access token with the 'oauth_verifier' that we received + + var oa = new OAuth(req.session.oa._requestUrl, + req.session.oa._accessUrl, + req.session.oa._consumerKey, + req.session.oa._consumerSecret, + req.session.oa._version, + req.session.oa._authorize_callback, + req.session.oa._signatureMethod); + + console.log(oa); + + oa.getOAuthAccessToken( + req.session.oauth_token, + req.session.oauth_token_secret, + req.param('oauth_verifier'), + function(error, oauth_access_token, oauth_access_token_secret, results2) { + + if(error) { + console.log('error'); + console.log(error); + } + else { + + // store the access token in the session + req.session.oauth_access_token = oauth_access_token; + req.session.oauth_access_token_secret = oauth_access_token_secret; + + res.redirect((req.param('action') && req.param('action') != "") ? req.param('action') : "/google_contacts"); + } + + }); + +}); + + +function require_google_login(req, res, next) { + if(!req.session.oauth_access_token) { + res.redirect("/google_login?action="+querystring.escape(req.originalUrl)); + return; + } + next(); +}; + +app.get('/google_contacts', require_google_login, function(req, res) { + var oa = new OAuth(req.session.oa._requestUrl, + req.session.oa._accessUrl, + req.session.oa._consumerKey, + req.session.oa._consumerSecret, + req.session.oa._version, + req.session.oa._authorize_callback, + req.session.oa._signatureMethod); + + console.log(oa); + + // Example using GData API v3 + // GData Specific Header + oa._headers['GData-Version'] = '3.0'; + + oa.getProtectedResource( + "https://www.google.com/m8/feeds/contacts/default/full?alt=json", + "GET", + req.session.oauth_access_token, + req.session.oauth_access_token_secret, + function (error, data, response) { + + var feed = JSON.parse(data); + + res.render('google_contacts.ejs', { + locals: { feed: feed } + }); + }); + +}); + +app.get('/google_calendars', require_google_login, function(req, res) { + var oa = new OAuth(req.session.oa._requestUrl, + req.session.oa._accessUrl, + req.session.oa._consumerKey, + req.session.oa._consumerSecret, + req.session.oa._version, + req.session.oa._authorize_callback, + req.session.oa._signatureMethod); + // Example using GData API v2 + // GData Specific Header + oa._headers['GData-Version'] = '2'; + + oa.getProtectedResource( + "https://www.google.com/calendar/feeds/default/allcalendars/full?alt=jsonc", + "GET", + req.session.oauth_access_token, + req.session.oauth_access_token_secret, + function (error, data, response) { + + var feed = JSON.parse(data); + + res.render('google_calendars.ejs', { + locals: { feed: feed } + }); + }); + +}); + +app.listen(3000); +console.log("listening on http://localhost:3000"); diff --git a/node_modules/oauth/examples/express-gdata/views/google_calendars.ejs b/node_modules/oauth/examples/express-gdata/views/google_calendars.ejs new file mode 100644 index 0000000..15b826f --- /dev/null +++ b/node_modules/oauth/examples/express-gdata/views/google_calendars.ejs @@ -0,0 +1,21 @@ + +

Check google_contacts

+ +

Google Calendars

+ +<% for(var i = 0 ; i < feed.data.items.length ; i++ ) { + + var calendar = feed.data.items[i]; %> +
+ +

"><%= calendar["title"] %>

+ +

canEdit: <%= calendar["canEdit"] %>

+

accessLevel: <%= calendar["accessLevel"] %>

+

timeZone: <%= calendar["timeZone"] %>

+

kind: <%= calendar["kind"] %>

+

updated: <%= calendar["updated"] %>

+

created: <%= calendar["created"] %>

+ +
+<% } %> \ No newline at end of file diff --git a/node_modules/oauth/examples/express-gdata/views/google_contacts.ejs b/node_modules/oauth/examples/express-gdata/views/google_contacts.ejs new file mode 100644 index 0000000..a2050b2 --- /dev/null +++ b/node_modules/oauth/examples/express-gdata/views/google_contacts.ejs @@ -0,0 +1,24 @@ + +

Check google_calendars

+ +

Google Contacts

+ +<% for(var i = 0 ; i < feed.feed.entry.length ; i++ ) { + + var contact = feed.feed.entry[i]; %> + +
+ + <%= contact["title"]["$t"] %> + <% emails = contact["gd$email"] %> + +
    + <% for(var j = 0 ; j < emails.length ; j++) { %> +
  • <%= emails[j]["address" ]%>
  • + <% } %> +
+ +
+ + +<% } %> diff --git a/node_modules/oauth/examples/express-gdata/views/layout.ejs b/node_modules/oauth/examples/express-gdata/views/layout.ejs new file mode 100644 index 0000000..8d1ac6d --- /dev/null +++ b/node_modules/oauth/examples/express-gdata/views/layout.ejs @@ -0,0 +1,9 @@ + + + + + +<%- body %> + + + \ No newline at end of file diff --git a/node_modules/oauth/examples/github-example.js b/node_modules/oauth/examples/github-example.js new file mode 100644 index 0000000..1a388bd --- /dev/null +++ b/node_modules/oauth/examples/github-example.js @@ -0,0 +1,73 @@ +var http = require('http'); +var qs = require('querystring'); +// var OAuth = require('oauth'), OAuth2 = OAuth.OAuth2; +var OAuth2 = require('../lib/oauth2.js').OAuth2; + +var clientID = ''; +var clientSecret = ''; +var oauth2 = new OAuth2(clientID, + clientSecret, + 'https://github.com/', + 'login/oauth/authorize', + 'login/oauth/access_token', + null); /** Custom headers */ + +http.createServer(function (req, res) { + var p = req.url.split('/'); + pLen = p.length; + + /** + * Authorised url as per github docs: + * https://developer.github.com/v3/oauth/#redirect-users-to-request-github-access + * + * getAuthorizedUrl: https://github.com/ciaranj/node-oauth/blob/master/lib/oauth2.js#L148 + * Adding params to authorize url with fields as mentioned in github docs + * + */ + var authURL = oauth2.getAuthorizeUrl({ + redirect_uri: 'http://localhost:8080/code', + scope: ['repo', 'user'], + state: 'some random string to protect against cross-site request forgery attacks' + }); + + + /** + * Creating an anchor with authURL as href and sending as response + */ + var body = ' Get Code '; + if (pLen === 2 && p[1] === '') { + res.writeHead(200, { + 'Content-Length': body.length, + 'Content-Type': 'text/html' }); + res.end(body); + } else if (pLen === 2 && p[1].indexOf('code') === 0) { + + /** Github sends auth code so that access_token can be obtained */ + var qsObj = {}; + + /** To obtain and parse code='...' from code?code='...' */ + qsObj = qs.parse(p[1].split('?')[1]); + + /** Obtaining access_token */ + oauth2.getOAuthAccessToken( + qsObj.code, + {'redirect_uri': 'http://localhost:8080/code/'}, + function (e, access_token, refresh_token, results){ + if (e) { + console.log(e); + res.end(e); + } else if (results.error) { + console.log(results); + res.end(JSON.stringify(results)); + } + else { + console.log('Obtained access_token: ', access_token); + res.end( access_token); + } + }); + + } else { + // Unhandled url + } + +}).listen(8080); diff --git a/node_modules/oauth/examples/term.ie.oauth-HMAC-SHA1.js b/node_modules/oauth/examples/term.ie.oauth-HMAC-SHA1.js new file mode 100644 index 0000000..91af05d --- /dev/null +++ b/node_modules/oauth/examples/term.ie.oauth-HMAC-SHA1.js @@ -0,0 +1,31 @@ +var util= require('util') + +var OAuth= require('../lib/oauth').OAuth; + +var oa= new OAuth("http://term.ie/oauth/example/request_token.php", + "http://term.ie/oauth/example/access_token.php", + "key", + "secret", + "1.0", + null, + "HMAC-SHA1") + +oa.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){ + if(error) util.puts('error :' + error) + else { + util.puts('oauth_token :' + oauth_token) + util.puts('oauth_token_secret :' + oauth_token_secret) + util.puts('requestoken results :' + util.inspect(results)) + util.puts("Requesting access token") + oa.getOAuthAccessToken(oauth_token, oauth_token_secret, function(error, oauth_access_token, oauth_access_token_secret, results2) { + util.puts('oauth_access_token :' + oauth_access_token) + util.puts('oauth_token_secret :' + oauth_access_token_secret) + util.puts('accesstoken results :' + util.inspect(results2)) + util.puts("Requesting access token") + var data= ""; + oa.getProtectedResource("http://term.ie/oauth/example/echo_api.php?foo=bar&too=roo", "GET", oauth_access_token, oauth_access_token_secret, function (error, data, response) { + util.puts(data); + }); + }); + } +}) diff --git a/node_modules/oauth/examples/twitter-example.js b/node_modules/oauth/examples/twitter-example.js new file mode 100644 index 0000000..90b6adc --- /dev/null +++ b/node_modules/oauth/examples/twitter-example.js @@ -0,0 +1,75 @@ +var http = require('http'); +var OAuth = require('../lib/oauth.js').OAuth; +var nodeUrl = require('url'); +var clientID = ''; +var clientSecret = ''; +var callbackURL = ''; + +oa = new OAuth( + 'https://api.twitter.com/oauth/request_token', + 'https://api.twitter.com/oauth/access_token', + clientID, + clientSecret, + '1.0', + callbackURL, + 'HMAC-SHA1' +); + +http.createServer(function (request, response) { + oa.getOAuthRequestToken(function (error, oAuthToken, oAuthTokenSecret, results) { + var urlObj = nodeUrl.parse(request.url, true); + var authURL = 'https://twitter.com/' + + 'oauth/authenticate?oauth_token=' + oAuthToken; + var handlers = { + '/': function (request, response) { + /** + * Creating an anchor with authURL as href and sending as response + */ + var body = ' Get Code '; + response.writeHead(200, { + 'Content-Length': body.length, + 'Content-Type': 'text/html' }); + response.end(body); + }, + '/callback': function (request, response) { + /** Obtaining access_token */ + var getOAuthRequestTokenCallback = function (error, oAuthAccessToken, + oAuthAccessTokenSecret, results) { + if (error) { + console.log(error); + response.end(JSON.stringify({ + message: 'Error occured while getting access token', + error: error + })); + return; + } + + oa.get('https://api.twitter.com/1.1/account/verify_credentials.json', + oAuthAccessToken, + oAuthAccessTokenSecret, + function (error, twitterResponseData, result) { + if (error) { + console.log(error) + res.end(JSON.stringify(error)); + return; + } + try { + console.log(JSON.parse(twitterResponseData)); + } catch (parseError) { + console.log(parseError); + } + console.log(twitterResponseData); + response.end(twitterResponseData); + }); + }; + + oa.getOAuthAccessToken(urlObj.query.oauth_token, oAuthTokenSecret, + urlObj.query.oauth_verifier, + getOAuthRequestTokenCallback); + + } + }; + handlers[urlObj.pathname](request, response); + }) + +}).listen(3000); diff --git a/node_modules/oauth/index.js b/node_modules/oauth/index.js new file mode 100644 index 0000000..e20716d --- /dev/null +++ b/node_modules/oauth/index.js @@ -0,0 +1,3 @@ +exports.OAuth = require("./lib/oauth").OAuth; +exports.OAuthEcho = require("./lib/oauth").OAuthEcho; +exports.OAuth2 = require("./lib/oauth2").OAuth2; \ No newline at end of file diff --git a/node_modules/oauth/lib/_utils.js b/node_modules/oauth/lib/_utils.js new file mode 100644 index 0000000..69fc300 --- /dev/null +++ b/node_modules/oauth/lib/_utils.js @@ -0,0 +1,4 @@ +// Returns true if this is a host that closes *before* it ends?!?! +module.exports.isAnEarlyCloseHost= function( hostName ) { + return hostName && hostName.match(".*google(apis)?.com$") +} \ No newline at end of file diff --git a/node_modules/oauth/lib/oauth.js b/node_modules/oauth/lib/oauth.js new file mode 100644 index 0000000..50dccf9 --- /dev/null +++ b/node_modules/oauth/lib/oauth.js @@ -0,0 +1,581 @@ +var crypto= require('crypto'), + sha1= require('./sha1'), + http= require('http'), + https= require('https'), + URL= require('url'), + querystring= require('querystring'), + OAuthUtils= require('./_utils'); + +exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, version, authorize_callback, signatureMethod, nonceSize, customHeaders) { + this._isEcho = false; + + this._requestUrl= requestUrl; + this._accessUrl= accessUrl; + this._consumerKey= consumerKey; + this._consumerSecret= this._encodeData( consumerSecret ); + if (signatureMethod == "RSA-SHA1") { + this._privateKey = consumerSecret; + } + this._version= version; + if( authorize_callback === undefined ) { + this._authorize_callback= "oob"; + } + else { + this._authorize_callback= authorize_callback; + } + + if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1") + throw new Error("Un-supported signature method: " + signatureMethod ) + this._signatureMethod= signatureMethod; + this._nonceSize= nonceSize || 32; + this._headers= customHeaders || {"Accept" : "*/*", + "Connection" : "close", + "User-Agent" : "Node authentication"} + this._clientOptions= this._defaultClientOptions= {"requestTokenHttpMethod": "POST", + "accessTokenHttpMethod": "POST", + "followRedirects": true}; + this._oauthParameterSeperator = ","; +}; + +exports.OAuthEcho= function(realm, verify_credentials, consumerKey, consumerSecret, version, signatureMethod, nonceSize, customHeaders) { + this._isEcho = true; + + this._realm= realm; + this._verifyCredentials = verify_credentials; + this._consumerKey= consumerKey; + this._consumerSecret= this._encodeData( consumerSecret ); + if (signatureMethod == "RSA-SHA1") { + this._privateKey = consumerSecret; + } + this._version= version; + + if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1") + throw new Error("Un-supported signature method: " + signatureMethod ); + this._signatureMethod= signatureMethod; + this._nonceSize= nonceSize || 32; + this._headers= customHeaders || {"Accept" : "*/*", + "Connection" : "close", + "User-Agent" : "Node authentication"}; + this._oauthParameterSeperator = ","; +} + +exports.OAuthEcho.prototype = exports.OAuth.prototype; + +exports.OAuth.prototype._getTimestamp= function() { + return Math.floor( (new Date()).getTime() / 1000 ); +} + +exports.OAuth.prototype._encodeData= function(toEncode){ + if( toEncode == null || toEncode == "" ) return "" + else { + var result= encodeURIComponent(toEncode); + // Fix the mismatch between OAuth's RFC3986's and Javascript's beliefs in what is right and wrong ;) + return result.replace(/\!/g, "%21") + .replace(/\'/g, "%27") + .replace(/\(/g, "%28") + .replace(/\)/g, "%29") + .replace(/\*/g, "%2A"); + } +} + +exports.OAuth.prototype._decodeData= function(toDecode) { + if( toDecode != null ) { + toDecode = toDecode.replace(/\+/g, " "); + } + return decodeURIComponent( toDecode); +} + +exports.OAuth.prototype._getSignature= function(method, url, parameters, tokenSecret) { + var signatureBase= this._createSignatureBase(method, url, parameters); + return this._createSignature( signatureBase, tokenSecret ); +} + +exports.OAuth.prototype._normalizeUrl= function(url) { + var parsedUrl= URL.parse(url, true) + var port =""; + if( parsedUrl.port ) { + if( (parsedUrl.protocol == "http:" && parsedUrl.port != "80" ) || + (parsedUrl.protocol == "https:" && parsedUrl.port != "443") ) { + port= ":" + parsedUrl.port; + } + } + + if( !parsedUrl.pathname || parsedUrl.pathname == "" ) parsedUrl.pathname ="/"; + + return parsedUrl.protocol + "//" + parsedUrl.hostname + port + parsedUrl.pathname; +} + +// Is the parameter considered an OAuth parameter +exports.OAuth.prototype._isParameterNameAnOAuthParameter= function(parameter) { + var m = parameter.match('^oauth_'); + if( m && ( m[0] === "oauth_" ) ) { + return true; + } + else { + return false; + } +}; + +// build the OAuth request authorization header +exports.OAuth.prototype._buildAuthorizationHeaders= function(orderedParameters) { + var authHeader="OAuth "; + if( this._isEcho ) { + authHeader += 'realm="' + this._realm + '",'; + } + + for( var i= 0 ; i < orderedParameters.length; i++) { + // Whilst the all the parameters should be included within the signature, only the oauth_ arguments + // should appear within the authorization header. + if( this._isParameterNameAnOAuthParameter(orderedParameters[i][0]) ) { + authHeader+= "" + this._encodeData(orderedParameters[i][0])+"=\""+ this._encodeData(orderedParameters[i][1])+"\""+ this._oauthParameterSeperator; + } + } + + authHeader= authHeader.substring(0, authHeader.length-this._oauthParameterSeperator.length); + return authHeader; +} + +// Takes an object literal that represents the arguments, and returns an array +// of argument/value pairs. +exports.OAuth.prototype._makeArrayOfArgumentsHash= function(argumentsHash) { + var argument_pairs= []; + for(var key in argumentsHash ) { + if (argumentsHash.hasOwnProperty(key)) { + var value= argumentsHash[key]; + if( Array.isArray(value) ) { + for(var i=0;i= 200 && response.statusCode <= 299 ) { + callback(null, data, response); + } else { + // Follow 301 or 302 redirects with Location HTTP header + if((response.statusCode == 301 || response.statusCode == 302) && clientOptions.followRedirects && response.headers && response.headers.location) { + self._performSecureRequest( oauth_token, oauth_token_secret, method, response.headers.location, extra_params, post_body, post_content_type, callback); + } + else { + callback({ statusCode: response.statusCode, data: data }, data, response); + } + } + } + } + + request.on('response', function (response) { + response.setEncoding('utf8'); + response.on('data', function (chunk) { + data+=chunk; + }); + response.on('end', function () { + passBackControl( response ); + }); + response.on('close', function () { + if( allowEarlyClose ) { + passBackControl( response ); + } + }); + }); + + request.on("error", function(err) { + if(!callbackCalled) { + callbackCalled= true; + callback( err ) + } + }); + + if( (method == "POST" || method =="PUT") && post_body != null && post_body != "" ) { + request.write(post_body); + } + request.end(); + } + else { + if( (method == "POST" || method =="PUT") && post_body != null && post_body != "" ) { + request.write(post_body); + } + return request; + } + + return; +} + +exports.OAuth.prototype.setClientOptions= function(options) { + var key, + mergedOptions= {}, + hasOwnProperty= Object.prototype.hasOwnProperty; + + for( key in this._defaultClientOptions ) { + if( !hasOwnProperty.call(options, key) ) { + mergedOptions[key]= this._defaultClientOptions[key]; + } else { + mergedOptions[key]= options[key]; + } + } + + this._clientOptions= mergedOptions; +}; + +exports.OAuth.prototype.getOAuthAccessToken= function(oauth_token, oauth_token_secret, oauth_verifier, callback) { + var extraParams= {}; + if( typeof oauth_verifier == "function" ) { + callback= oauth_verifier; + } else { + extraParams.oauth_verifier= oauth_verifier; + } + + this._performSecureRequest( oauth_token, oauth_token_secret, this._clientOptions.accessTokenHttpMethod, this._accessUrl, extraParams, null, null, function(error, data, response) { + if( error ) callback(error); + else { + var results= querystring.parse( data ); + var oauth_access_token= results["oauth_token"]; + delete results["oauth_token"]; + var oauth_access_token_secret= results["oauth_token_secret"]; + delete results["oauth_token_secret"]; + callback(null, oauth_access_token, oauth_access_token_secret, results ); + } + }) +} + +// Deprecated +exports.OAuth.prototype.getProtectedResource= function(url, method, oauth_token, oauth_token_secret, callback) { + this._performSecureRequest( oauth_token, oauth_token_secret, method, url, null, "", null, callback ); +} + +exports.OAuth.prototype.delete= function(url, oauth_token, oauth_token_secret, callback) { + return this._performSecureRequest( oauth_token, oauth_token_secret, "DELETE", url, null, "", null, callback ); +} + +exports.OAuth.prototype.get= function(url, oauth_token, oauth_token_secret, callback) { + return this._performSecureRequest( oauth_token, oauth_token_secret, "GET", url, null, "", null, callback ); +} + +exports.OAuth.prototype._putOrPost= function(method, url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) { + var extra_params= null; + if( typeof post_content_type == "function" ) { + callback= post_content_type; + post_content_type= null; + } + if ( typeof post_body != "string" && !Buffer.isBuffer(post_body) ) { + post_content_type= "application/x-www-form-urlencoded" + extra_params= post_body; + post_body= null; + } + return this._performSecureRequest( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ); +} + + +exports.OAuth.prototype.put= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) { + return this._putOrPost("PUT", url, oauth_token, oauth_token_secret, post_body, post_content_type, callback); +} + +exports.OAuth.prototype.post= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) { + return this._putOrPost("POST", url, oauth_token, oauth_token_secret, post_body, post_content_type, callback); +} + +/** + * Gets a request token from the OAuth provider and passes that information back + * to the calling code. + * + * The callback should expect a function of the following form: + * + * function(err, token, token_secret, parsedQueryString) {} + * + * This method has optional parameters so can be called in the following 2 ways: + * + * 1) Primary use case: Does a basic request with no extra parameters + * getOAuthRequestToken( callbackFunction ) + * + * 2) As above but allows for provision of extra parameters to be sent as part of the query to the server. + * getOAuthRequestToken( extraParams, callbackFunction ) + * + * N.B. This method will HTTP POST verbs by default, if you wish to override this behaviour you will + * need to provide a requestTokenHttpMethod option when creating the client. + * + **/ +exports.OAuth.prototype.getOAuthRequestToken= function( extraParams, callback ) { + if( typeof extraParams == "function" ){ + callback = extraParams; + extraParams = {}; + } + // Callbacks are 1.0A related + if( this._authorize_callback ) { + extraParams["oauth_callback"]= this._authorize_callback; + } + this._performSecureRequest( null, null, this._clientOptions.requestTokenHttpMethod, this._requestUrl, extraParams, null, null, function(error, data, response) { + if( error ) callback(error); + else { + var results= querystring.parse(data); + + var oauth_token= results["oauth_token"]; + var oauth_token_secret= results["oauth_token_secret"]; + delete results["oauth_token"]; + delete results["oauth_token_secret"]; + callback(null, oauth_token, oauth_token_secret, results ); + } + }); +} + +exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret, method) { + + if( method === undefined ) { + var method= "GET"; + } + + var orderedParameters= this._prepareParameters(oauth_token, oauth_token_secret, method, url, {}); + var parsedUrl= URL.parse( url, false ); + + var query=""; + for( var i= 0 ; i < orderedParameters.length; i++) { + query+= orderedParameters[i][0]+"="+ this._encodeData(orderedParameters[i][1]) + "&"; + } + query= query.substring(0, query.length-1); + + return parsedUrl.protocol + "//"+ parsedUrl.host + parsedUrl.pathname + "?" + query; +}; + +exports.OAuth.prototype.authHeader= function(url, oauth_token, oauth_token_secret, method) { + if( method === undefined ) { + var method= "GET"; + } + + var orderedParameters= this._prepareParameters(oauth_token, oauth_token_secret, method, url, {}); + return this._buildAuthorizationHeaders(orderedParameters); +}; diff --git a/node_modules/oauth/lib/oauth2.js b/node_modules/oauth/lib/oauth2.js new file mode 100644 index 0000000..77241c4 --- /dev/null +++ b/node_modules/oauth/lib/oauth2.js @@ -0,0 +1,228 @@ +var querystring= require('querystring'), + crypto= require('crypto'), + https= require('https'), + http= require('http'), + URL= require('url'), + OAuthUtils= require('./_utils'); + +exports.OAuth2= function(clientId, clientSecret, baseSite, authorizePath, accessTokenPath, customHeaders) { + this._clientId= clientId; + this._clientSecret= clientSecret; + this._baseSite= baseSite; + this._authorizeUrl= authorizePath || "/oauth/authorize"; + this._accessTokenUrl= accessTokenPath || "/oauth/access_token"; + this._accessTokenName= "access_token"; + this._authMethod= "Bearer"; + this._customHeaders = customHeaders || {}; + this._useAuthorizationHeaderForGET= false; + + //our agent + this._agent = undefined; +}; + +// Allows you to set an agent to use instead of the default HTTP or +// HTTPS agents. Useful when dealing with your own certificates. +exports.OAuth2.prototype.setAgent = function(agent) { + this._agent = agent; +}; + +// This 'hack' method is required for sites that don't use +// 'access_token' as the name of the access token (for requests). +// ( http://tools.ietf.org/html/draft-ietf-oauth-v2-16#section-7 ) +// it isn't clear what the correct value should be atm, so allowing +// for specific (temporary?) override for now. +exports.OAuth2.prototype.setAccessTokenName= function ( name ) { + this._accessTokenName= name; +} + +// Sets the authorization method for Authorization header. +// e.g. Authorization: Bearer # "Bearer" is the authorization method. +exports.OAuth2.prototype.setAuthMethod = function ( authMethod ) { + this._authMethod = authMethod; +}; + + +// If you use the OAuth2 exposed 'get' method (and don't construct your own _request call ) +// this will specify whether to use an 'Authorize' header instead of passing the access_token as a query parameter +exports.OAuth2.prototype.useAuthorizationHeaderforGET = function(useIt) { + this._useAuthorizationHeaderForGET= useIt; +} + +exports.OAuth2.prototype._getAccessTokenUrl= function() { + return this._baseSite + this._accessTokenUrl; /* + "?" + querystring.stringify(params); */ +} + +// Build the authorization header. In particular, build the part after the colon. +// e.g. Authorization: Bearer # Build "Bearer " +exports.OAuth2.prototype.buildAuthHeader= function(token) { + return this._authMethod + ' ' + token; +}; + +exports.OAuth2.prototype._chooseHttpLibrary= function( parsedUrl ) { + var http_library= https; + // As this is OAUth2, we *assume* https unless told explicitly otherwise. + if( parsedUrl.protocol != "https:" ) { + http_library= http; + } + return http_library; +}; + +exports.OAuth2.prototype._request= function(method, url, headers, post_body, access_token, callback) { + + var parsedUrl= URL.parse( url, true ); + if( parsedUrl.protocol == "https:" && !parsedUrl.port ) { + parsedUrl.port= 443; + } + + var http_library= this._chooseHttpLibrary( parsedUrl ); + + + var realHeaders= {}; + for( var key in this._customHeaders ) { + realHeaders[key]= this._customHeaders[key]; + } + if( headers ) { + for(var key in headers) { + realHeaders[key] = headers[key]; + } + } + realHeaders['Host']= parsedUrl.host; + + if (!realHeaders['User-Agent']) { + realHeaders['User-Agent'] = 'Node-oauth'; + } + + if( post_body ) { + if ( Buffer.isBuffer(post_body) ) { + realHeaders["Content-Length"]= post_body.length; + } else { + realHeaders["Content-Length"]= Buffer.byteLength(post_body); + } + } else { + realHeaders["Content-length"]= 0; + } + + if( access_token && !('Authorization' in realHeaders)) { + if( ! parsedUrl.query ) parsedUrl.query= {}; + parsedUrl.query[this._accessTokenName]= access_token; + } + + var queryStr= querystring.stringify(parsedUrl.query); + if( queryStr ) queryStr= "?" + queryStr; + var options = { + host:parsedUrl.hostname, + port: parsedUrl.port, + path: parsedUrl.pathname + queryStr, + method: method, + headers: realHeaders + }; + + this._executeRequest( http_library, options, post_body, callback ); +} + +exports.OAuth2.prototype._executeRequest= function( http_library, options, post_body, callback ) { + // Some hosts *cough* google appear to close the connection early / send no content-length header + // allow this behaviour. + var allowEarlyClose= OAuthUtils.isAnEarlyCloseHost(options.host); + var callbackCalled= false; + function passBackControl( response, result ) { + if(!callbackCalled) { + callbackCalled=true; + if( !(response.statusCode >= 200 && response.statusCode <= 299) && (response.statusCode != 301) && (response.statusCode != 302) ) { + callback({ statusCode: response.statusCode, data: result }); + } else { + callback(null, result, response); + } + } + } + + var result= ""; + + //set the agent on the request options + if (this._agent) { + options.agent = this._agent; + } + + var request = http_library.request(options); + request.on('response', function (response) { + response.on("data", function (chunk) { + result+= chunk + }); + response.on("close", function (err) { + if( allowEarlyClose ) { + passBackControl( response, result ); + } + }); + response.addListener("end", function () { + passBackControl( response, result ); + }); + }); + request.on('error', function(e) { + callbackCalled= true; + callback(e); + }); + + if( (options.method == 'POST' || options.method == 'PUT') && post_body ) { + request.write(post_body); + } + request.end(); +} + +exports.OAuth2.prototype.getAuthorizeUrl= function( params ) { + var params= params || {}; + params['client_id'] = this._clientId; + return this._baseSite + this._authorizeUrl + "?" + querystring.stringify(params); +} + +exports.OAuth2.prototype.getOAuthAccessToken= function(code, params, callback) { + var params= params || {}; + params['client_id'] = this._clientId; + params['client_secret'] = this._clientSecret; + var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code'; + params[codeParam]= code; + + var post_data= querystring.stringify( params ); + var post_headers= { + 'Content-Type': 'application/x-www-form-urlencoded' + }; + + + this._request("POST", this._getAccessTokenUrl(), post_headers, post_data, null, function(error, data, response) { + if( error ) callback(error); + else { + var results; + try { + // As of http://tools.ietf.org/html/draft-ietf-oauth-v2-07 + // responses should be in JSON + results= JSON.parse( data ); + } + catch(e) { + // .... However both Facebook + Github currently use rev05 of the spec + // and neither seem to specify a content-type correctly in their response headers :( + // clients of these services will suffer a *minor* performance cost of the exception + // being thrown + results= querystring.parse( data ); + } + var access_token= results["access_token"]; + var refresh_token= results["refresh_token"]; + delete results["refresh_token"]; + callback(null, access_token, refresh_token, results); // callback results =-= + } + }); +} + +// Deprecated +exports.OAuth2.prototype.getProtectedResource= function(url, access_token, callback) { + this._request("GET", url, {}, "", access_token, callback ); +} + +exports.OAuth2.prototype.get= function(url, access_token, callback) { + if( this._useAuthorizationHeaderForGET ) { + var headers= {'Authorization': this.buildAuthHeader(access_token) } + access_token= null; + } + else { + headers= {}; + } + this._request("GET", url, headers, "", access_token, callback ); +} diff --git a/node_modules/oauth/lib/sha1.js b/node_modules/oauth/lib/sha1.js new file mode 100644 index 0000000..d73277a --- /dev/null +++ b/node_modules/oauth/lib/sha1.js @@ -0,0 +1,334 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS 180-1 + * Version 2.2 Copyright Paul Johnston 2000 - 2009. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 1; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); } +function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); } +function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); } +function hex_hmac_sha1(k, d) + { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); } +function b64_hmac_sha1(k, d) + { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); } +function any_hmac_sha1(k, d, e) + { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); } + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA1 of a raw string + */ +function rstr_sha1(s) +{ + return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8)); +} + +/* + * Calculate the HMAC-SHA1 of a key and some data (raw strings) + */ +function rstr_hmac_sha1(key, data) +{ + var bkey = rstr2binb(key); + if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8); + return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160)); +} + +/* + * Convert a raw string to a hex string + */ +function rstr2hex(input) +{ + try { hexcase } catch(e) { hexcase=0; } + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var output = ""; + var x; + for(var i = 0; i < input.length; i++) + { + x = input.charCodeAt(i); + output += hex_tab.charAt((x >>> 4) & 0x0F) + + hex_tab.charAt( x & 0x0F); + } + return output; +} + +/* + * Convert a raw string to a base-64 string + */ +function rstr2b64(input) +{ + try { b64pad } catch(e) { b64pad=''; } + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var output = ""; + var len = input.length; + for(var i = 0; i < len; i += 3) + { + var triplet = (input.charCodeAt(i) << 16) + | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) + | (i + 2 < len ? input.charCodeAt(i+2) : 0); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > input.length * 8) output += b64pad; + else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); + } + } + return output; +} + +/* + * Convert a raw string to an arbitrary string encoding + */ +function rstr2any(input, encoding) +{ + var divisor = encoding.length; + var remainders = Array(); + var i, q, x, quotient; + + /* Convert to an array of 16-bit big-endian values, forming the dividend */ + var dividend = Array(Math.ceil(input.length / 2)); + for(i = 0; i < dividend.length; i++) + { + dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); + } + + /* + * Repeatedly perform a long division. The binary array forms the dividend, + * the length of the encoding is the divisor. Once computed, the quotient + * forms the dividend for the next step. We stop when the dividend is zero. + * All remainders are stored for later use. + */ + while(dividend.length > 0) + { + quotient = Array(); + x = 0; + for(i = 0; i < dividend.length; i++) + { + x = (x << 16) + dividend[i]; + q = Math.floor(x / divisor); + x -= q * divisor; + if(quotient.length > 0 || q > 0) + quotient[quotient.length] = q; + } + remainders[remainders.length] = x; + dividend = quotient; + } + + /* Convert the remainders to the output string */ + var output = ""; + for(i = remainders.length - 1; i >= 0; i--) + output += encoding.charAt(remainders[i]); + + /* Append leading zero equivalents */ + var full_length = Math.ceil(input.length * 8 / + (Math.log(encoding.length) / Math.log(2))) + for(i = output.length; i < full_length; i++) + output = encoding[0] + output; + + return output; +} + +/* + * Encode a string as utf-8. + * For efficiency, this assumes the input is valid utf-16. + */ +function str2rstr_utf8(input) +{ + var output = ""; + var i = -1; + var x, y; + + while(++i < input.length) + { + /* Decode utf-16 surrogate pairs */ + x = input.charCodeAt(i); + y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; + if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) + { + x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); + i++; + } + + /* Encode output as utf-8 */ + if(x <= 0x7F) + output += String.fromCharCode(x); + else if(x <= 0x7FF) + output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), + 0x80 | ( x & 0x3F)); + else if(x <= 0xFFFF) + output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), + 0x80 | ((x >>> 6 ) & 0x3F), + 0x80 | ( x & 0x3F)); + else if(x <= 0x1FFFFF) + output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), + 0x80 | ((x >>> 12) & 0x3F), + 0x80 | ((x >>> 6 ) & 0x3F), + 0x80 | ( x & 0x3F)); + } + return output; +} + +/* + * Encode a string as utf-16 + */ +function str2rstr_utf16le(input) +{ + var output = ""; + for(var i = 0; i < input.length; i++) + output += String.fromCharCode( input.charCodeAt(i) & 0xFF, + (input.charCodeAt(i) >>> 8) & 0xFF); + return output; +} + +function str2rstr_utf16be(input) +{ + var output = ""; + for(var i = 0; i < input.length; i++) + output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, + input.charCodeAt(i) & 0xFF); + return output; +} + +/* + * Convert a raw string to an array of big-endian words + * Characters >255 have their high-byte silently ignored. + */ +function rstr2binb(input) +{ + var output = Array(input.length >> 2); + for(var i = 0; i < output.length; i++) + output[i] = 0; + for(var i = 0; i < input.length * 8; i += 8) + output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); + return output; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2rstr(input) +{ + var output = ""; + for(var i = 0; i < input.length * 32; i += 8) + output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF); + return output; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function binb_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for(var j = 0; j < 80; j++) + { + if(j < 16) w[j] = x[i + j]; + else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = bit_rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); + +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if(t < 20) return (b & c) | ((~b) & d); + if(t < 40) return b ^ c ^ d; + if(t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function bit_rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +exports.HMACSHA1= function(key, data) { + return b64_hmac_sha1(key, data); +} \ No newline at end of file diff --git a/node_modules/oauth/package.json b/node_modules/oauth/package.json new file mode 100644 index 0000000..fa35b6f --- /dev/null +++ b/node_modules/oauth/package.json @@ -0,0 +1,15 @@ +{ "name" : "oauth" +, "description" : "Library for interacting with OAuth 1.0, 1.0A, 2 and Echo. Provides simplified client access and allows for construction of more complex apis and OAuth providers." +, "version" : "0.9.15" +, "directories" : { "lib" : "./lib" } +, "main" : "index.js" +, "author" : "Ciaran Jessup " +, "repository" : { "type":"git", "url":"http://github.com/ciaranj/node-oauth.git" } +, "devDependencies": { + "vows": "0.5.x" + } +, "scripts": { + "test": "make test" + } +, "license": "MIT" +} diff --git a/node_modules/oauth/tests/oauth2tests.js b/node_modules/oauth/tests/oauth2tests.js new file mode 100644 index 0000000..8be23e3 --- /dev/null +++ b/node_modules/oauth/tests/oauth2tests.js @@ -0,0 +1,304 @@ +var vows = require('vows'), + assert = require('assert'), + DummyResponse= require('./shared').DummyResponse, + DummyRequest= require('./shared').DummyRequest, + https = require('https'), + OAuth2= require('../lib/oauth2').OAuth2, + url = require('url'); + +vows.describe('OAuth2').addBatch({ + 'Given an OAuth2 instance with clientId and clientSecret, ': { + topic: new OAuth2("clientId", "clientSecret"), + 'When dealing with the response from the OP': { + 'we should treat a 201 response as a success': function(oa) { + var callbackCalled= false; + var http_library= { + request: function() { + return new DummyRequest(new DummyResponse(201)); + } + }; + oa._executeRequest( http_library, {}, null, function(err, result, response) { + callbackCalled= true; + assert.equal(err, null); + }); + assert.ok(callbackCalled); + }, + 'we should treat a 200 response as a success': function(oa) { + var callbackCalled= false; + var http_library= { + request: function() { + return new DummyRequest(new DummyResponse(200)); + } + }; + oa._executeRequest( http_library, {}, null, function(err, result, response) { + callbackCalled= true; + assert.equal(err, null); + }); + assert.ok(callbackCalled); + } + }, + 'When handling the access token response': { + 'we should correctly extract the token if received as form-data': function (oa) { + oa._request= function( method, url, fo, bar, bleh, callback) { + callback(null, "access_token=access&refresh_token=refresh"); + }; + oa.getOAuthAccessToken("", {}, function(error, access_token, refresh_token) { + assert.equal( access_token, "access"); + assert.equal( refresh_token, "refresh"); + }); + }, + 'we should not include access token in both querystring and headers (favours headers if specified)': function (oa) { + oa._request = new OAuth2("clientId", "clientSecret")._request.bind(oa); + oa._executeRequest= function( http_library, options, post_body, callback) { + callback(null, url.parse(options.path, true).query, options.headers); + }; + + oa._request("GET", "http://foo/", {"Authorization":"Bearer BadNews"}, null, "accessx", function(error, query, headers) { + assert.ok( !('access_token' in query), "access_token also in query"); + assert.ok( 'Authorization' in headers, "Authorization not in headers"); + }); + }, + 'we should include access token in the querystring if no Authorization header present to override it': function (oa) { + oa._request = new OAuth2("clientId", "clientSecret")._request.bind(oa); + oa._executeRequest= function( http_library, options, post_body, callback) { + callback(null, url.parse(options.path, true).query, options.headers); + }; + oa._request("GET", "http://foo/", {}, null, "access", function(error, query, headers) { + assert.ok( 'access_token' in query, "access_token not present in query"); + assert.ok( !('Authorization' in headers), "Authorization in headers"); + }); + }, + 'we should correctly extract the token if received as a JSON literal': function (oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + callback(null, '{"access_token":"access","refresh_token":"refresh"}'); + }; + oa.getOAuthAccessToken("", {}, function(error, access_token, refresh_token) { + assert.equal( access_token, "access"); + assert.equal( refresh_token, "refresh"); + }); + }, + 'we should return the received data to the calling method': function (oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + callback(null, '{"access_token":"access","refresh_token":"refresh","extra_1":1, "extra_2":"foo"}'); + }; + oa.getOAuthAccessToken("", {}, function(error, access_token, refresh_token, results) { + assert.equal( access_token, "access"); + assert.equal( refresh_token, "refresh"); + assert.isNotNull( results ); + assert.equal( results.extra_1, 1); + assert.equal( results.extra_2, "foo"); + }); + } + }, + 'When no grant_type parameter is specified': { + 'we should pass the value of the code argument as the code parameter': function(oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + assert.isTrue( post_body.indexOf("code=xsds23") != -1 ); + }; + oa.getOAuthAccessToken("xsds23", {} ); + } + }, + 'When an invalid grant_type parameter is specified': { + 'we should pass the value of the code argument as the code parameter': function(oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + assert.isTrue( post_body.indexOf("code=xsds23") != -1 ); + }; + oa.getOAuthAccessToken("xsds23", {grant_type:"refresh_toucan"} ); + } + }, + 'When a grant_type parameter of value "refresh_token" is specified': { + 'we should pass the value of the code argument as the refresh_token parameter, should pass a grant_type parameter, but shouldn\'t pass a code parameter' : function(oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + assert.isTrue( post_body.indexOf("refresh_token=sdsds2") != -1 ); + assert.isTrue( post_body.indexOf("grant_type=refresh_token") != -1 ); + assert.isTrue( post_body.indexOf("code=") == -1 ); + }; + oa.getOAuthAccessToken("sdsds2", {grant_type:"refresh_token"} ); + } + }, + 'When we use the authorization header': { + 'and call get with the default authorization method': { + 'we should pass the authorization header with Bearer method and value of the access_token, _request should be passed a null access_token' : function(oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + assert.equal(headers["Authorization"], "Bearer abcd5"); + assert.isNull( access_token ); + }; + oa.useAuthorizationHeaderforGET(true); + oa.get("", "abcd5"); + } + }, + 'and call get with the authorization method set to Basic': { + 'we should pass the authorization header with Basic method and value of the access_token, _request should be passed a null access_token' : function(oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + assert.equal(headers["Authorization"], "Basic cdg2"); + assert.isNull( access_token ); + }; + oa.useAuthorizationHeaderforGET(true); + oa.setAuthMethod("Basic"); + oa.get("", "cdg2"); + } + } + }, + 'When we do not use the authorization header': { + 'and call get': { + 'we should pass NOT provide an authorization header and the access_token should be being passed to _request' : function(oa) { + oa._request= function(method, url, headers, post_body, access_token, callback) { + assert.isUndefined(headers["Authorization"]); + assert.equal( access_token, "abcd5" ); + }; + oa.useAuthorizationHeaderforGET(false); + oa.get("", "abcd5"); + } + } + } + }, + 'Given an OAuth2 instance with clientId, clientSecret and customHeaders': { + topic: new OAuth2("clientId", "clientSecret", undefined, undefined, undefined, + { 'SomeHeader': '123' }), + 'When GETing': { + 'we should see the custom headers mixed into headers property in options passed to http-library' : function(oa) { + oa._executeRequest= function( http_library, options, callback ) { + assert.equal(options.headers["SomeHeader"], "123"); + }; + oa.get("", {}); + }, + } + }, + 'Given an OAuth2 instance with a clientId and clientSecret': { + topic: new OAuth2("clientId", "clientSecret"), + 'When POSTing': { + 'we should see a given string being sent to the request' : function(oa) { + var bodyWritten= false; + oa._chooseHttpLibrary= function() { + return { + request: function(options) { + assert.equal(options.headers["Content-Type"], "text/plain"); + assert.equal(options.headers["Content-Length"], 26); + assert.equal(options.method, "POST"); + return { + end: function() {}, + on: function() {}, + write: function(body) { + bodyWritten= true; + assert.isNotNull(body); + assert.equal(body, "THIS_IS_A_POST_BODY_STRING") + } + } + } + }; + } + oa._request("POST", "", {"Content-Type":"text/plain"}, "THIS_IS_A_POST_BODY_STRING"); + assert.ok( bodyWritten ); + }, + 'we should see a given buffer being sent to the request' : function(oa) { + var bodyWritten= false; + oa._chooseHttpLibrary= function() { + return { + request: function(options) { + assert.equal(options.headers["Content-Type"], "application/octet-stream"); + assert.equal(options.headers["Content-Length"], 4); + assert.equal(options.method, "POST"); + return { + end: function() {}, + on: function() {}, + write: function(body) { + bodyWritten= true; + assert.isNotNull(body); + assert.equal(4, body.length) + } + } + } + }; + } + oa._request("POST", "", {"Content-Type":"application/octet-stream"}, new Buffer([1,2,3,4])); + assert.ok( bodyWritten ); + } + }, + 'When PUTing': { + 'we should see a given string being sent to the request' : function(oa) { + var bodyWritten= false; + oa._chooseHttpLibrary= function() { + return { + request: function(options) { + assert.equal(options.headers["Content-Type"], "text/plain"); + assert.equal(options.headers["Content-Length"], 25); + assert.equal(options.method, "PUT"); + return { + end: function() {}, + on: function() {}, + write: function(body) { + bodyWritten= true; + assert.isNotNull(body); + assert.equal(body, "THIS_IS_A_PUT_BODY_STRING") + } + } + } + }; + } + oa._request("PUT", "", {"Content-Type":"text/plain"}, "THIS_IS_A_PUT_BODY_STRING"); + assert.ok( bodyWritten ); + }, + 'we should see a given buffer being sent to the request' : function(oa) { + var bodyWritten= false; + oa._chooseHttpLibrary= function() { + return { + request: function(options) { + assert.equal(options.headers["Content-Type"], "application/octet-stream"); + assert.equal(options.headers["Content-Length"], 4); + assert.equal(options.method, "PUT"); + return { + end: function() {}, + on: function() {}, + write: function(body) { + bodyWritten= true; + assert.isNotNull(body); + assert.equal(4, body.length) + } + } + } + }; + } + oa._request("PUT", "", {"Content-Type":"application/octet-stream"}, new Buffer([1,2,3,4])); + assert.ok( bodyWritten ); + } + } + }, + 'When the user passes in the User-Agent in customHeaders': { + topic: new OAuth2("clientId", "clientSecret", undefined, undefined, undefined, + { 'User-Agent': '123Agent' }), + 'When calling get': { + 'we should see the User-Agent mixed into headers property in options passed to http-library' : function(oa) { + oa._executeRequest= function( http_library, options, callback ) { + assert.equal(options.headers["User-Agent"], "123Agent"); + }; + oa.get("", {}); + } + } + }, + 'When the user does not pass in a User-Agent in customHeaders': { + topic: new OAuth2("clientId", "clientSecret", undefined, undefined, undefined, + undefined), + 'When calling get': { + 'we should see the default User-Agent mixed into headers property in options passed to http-library' : function(oa) { + oa._executeRequest= function( http_library, options, callback ) { + assert.equal(options.headers["User-Agent"], "Node-oauth"); + }; + oa.get("", {}); + } + } + }, + 'When specifying an agent, that agent is passed to the HTTP request method' : { + topic : new OAuth2('clientId', 'clientSecret', undefined, undefined, undefined, undefined), + 'When calling _executeRequest': { + 'we whould see the agent being put into the options' : function(oa) { + oa.setAgent('awesome agent'); + oa._executeRequest({ + request : function(options, cb) { + assert.equal(options.agent, 'awesome agent'); + return new DummyRequest(new DummyResponse(200)); + } + }, {}, null, function() {}); + } + } + } +}).export(module); diff --git a/node_modules/oauth/tests/oauthtests.js b/node_modules/oauth/tests/oauthtests.js new file mode 100644 index 0000000..d36bfed --- /dev/null +++ b/node_modules/oauth/tests/oauthtests.js @@ -0,0 +1,1064 @@ +var vows = require('vows'), + assert = require('assert'), + DummyResponse= require('./shared').DummyResponse, + DummyRequest= require('./shared').DummyRequest, + events = require('events'), + OAuth= require('../lib/oauth').OAuth, + OAuthEcho= require('../lib/oauth').OAuthEcho, + crypto = require('crypto'); + +//Valid RSA keypair used to test RSA-SHA1 signature method +var RsaPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + +"MIICXQIBAAKBgQDizE4gQP5nPQhzof/Vp2U2DDY3UY/Gxha2CwKW0URe7McxtnmE\n" + +"CrZnT1n/YtfrrCNxY5KMP4o8hMrxsYEe05+1ZGFT68ztms3puUxilU5E3BQMhz1t\n" + +"JMJEGcTt8nZUlM4utli7fHgDtWbhvqvYjRMGn3AjyLOfY8XZvnFkGjipvQIDAQAB\n" + +"AoGAKgk6FcpWHOZ4EY6eL4iGPt1Gkzw/zNTcUsN5qGCDLqDuTq2Gmk2t/zn68VXt\n" + +"tVXDf/m3qN0CDzOBtghzaTZKLGhnSewQ98obMWgPcvAsb4adEEeW1/xigbMiaW2X\n" + +"cu6GhZxY16edbuQ40LRrPoVK94nXQpj8p7w4IQ301Sm8PSECQQD1ZlOj4ugvfhEt\n" + +"exi4WyAaM45fylmN290UXYqZ8SYPI/VliDytIlMfyq5Rv+l+dud1XDPrWOQ0ImgV\n" + +"HJn7uvoZAkEA7JhHNmHF9dbdF9Koj86K2Cl6c8KUu7U7d2BAuB6pPkt8+D8+y4St\n" + +"PaCmN4oP4X+sf5rqBYoXywHlqEei2BdpRQJBAMYgR4cZu7wcXGIL8HlnmROObHSK\n" + +"OqN9z5CRtUV0nPW8YnQG+nYOMG6KhRMbjri750OpnYF100kEPmRNI0VKQIECQE8R\n" + +"fQsRleTYz768ahTVQ9WF1ySErMwmfx8gDcD6jjkBZVxZVpURXAwyehopi7Eix/VF\n" + +"QlxjkBwKIEQi3Ks297kCQQCL9by1bueKDMJO2YX1Brm767pkDKkWtGfPS+d3xMtC\n" + +"KJHHCqrS1V+D5Q89x5wIRHKxE5UMTc0JNa554OxwFORX\n" + +"-----END RSA PRIVATE KEY-----"; + +var RsaPublicKey = "-----BEGIN PUBLIC KEY-----\n" + +"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDizE4gQP5nPQhzof/Vp2U2DDY3\n" + +"UY/Gxha2CwKW0URe7McxtnmECrZnT1n/YtfrrCNxY5KMP4o8hMrxsYEe05+1ZGFT\n" + +"68ztms3puUxilU5E3BQMhz1tJMJEGcTt8nZUlM4utli7fHgDtWbhvqvYjRMGn3Aj\n" + +"yLOfY8XZvnFkGjipvQIDAQAB\n" + +"-----END PUBLIC KEY-----"; + +vows.describe('OAuth').addBatch({ + 'When newing OAuth': { + topic: new OAuth(null, null, null, null, null, null, "PLAINTEXT"), + 'followRedirects is enabled by default': function (oa) { + assert.equal(oa._clientOptions.followRedirects, true) + } + }, + 'When generating the signature base string described in http://oauth.net/core/1.0/#sig_base_example': { + topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + 'we get the expected result string': function (oa) { + var result= oa._createSignatureBase("GET", "http://photos.example.net/photos", + "file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original") + assert.equal( result, "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"); + } + }, + 'When generating the signature with RSA-SHA1': { + topic: new OAuth(null, null, null, RsaPrivateKey, null, null, "RSA-SHA1"), + 'we get a valid oauth signature': function (oa) { + var signatureBase = "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"; + var oauthSignature = oa._createSignature(signatureBase, "xyz4992k83j47x0b"); + + assert.equal( oauthSignature, "qS4rhWog7GPgo4ZCJvUdC/1ZAax/Q4Ab9yOBvgxSopvmKUKp5rso+Zda46GbyN2hnYDTiA/g3P/d/YiPWa454BEBb/KWFV83HpLDIoqUUhJnlXX9MqRQQac0oeope4fWbGlfTdL2PXjSFJmvfrzybERD/ZufsFtVrQKS3QBpYiw="); + + //now check that given the public key we can verify this signature + var verifier = crypto.createVerify("RSA-SHA1").update(signatureBase); + var valid = verifier.verify(RsaPublicKey, oauthSignature, 'base64'); + assert.ok( valid, "Signature could not be verified with RSA public key"); + } + }, + 'When generating the signature base string with PLAINTEXT': { + topic: new OAuth(null, null, null, null, null, null, "PLAINTEXT"), + 'we get the expected result string': function (oa) { + var result= oa._getSignature("GET", "http://photos.example.net/photos", + "file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=PLAINTEXT&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original", + "test"); + assert.equal( result, "&test"); + } + }, + 'When normalising a url': { + topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + 'default ports should be stripped': function(oa) { + assert.equal( oa._normalizeUrl("https://somehost.com:443/foo/bar"), "https://somehost.com/foo/bar" ); + }, + 'should leave in non-default ports from urls for use in signature generation': function(oa) { + assert.equal( oa._normalizeUrl("https://somehost.com:446/foo/bar"), "https://somehost.com:446/foo/bar" ); + assert.equal( oa._normalizeUrl("http://somehost.com:81/foo/bar"), "http://somehost.com:81/foo/bar" ); + }, + 'should add a trailing slash when no path at all is present': function(oa) { + assert.equal( oa._normalizeUrl("http://somehost.com"), "http://somehost.com/") + } + }, + 'When making an array out of the arguments hash' : { + topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + 'flatten out arguments that are arrays' : function(oa) { + var parameters= {"z": "a", + "a": ["1", "2"], + "1": "c" }; + var parameterResults= oa._makeArrayOfArgumentsHash(parameters); + assert.equal(parameterResults.length, 4); + assert.equal(parameterResults[0][0], "1"); + assert.equal(parameterResults[1][0], "z"); + assert.equal(parameterResults[2][0], "a"); + assert.equal(parameterResults[3][0], "a"); + } + }, + 'When ordering the request parameters' : { + topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + 'Order them by name' : function(oa) { + var parameters= {"z": "a", + "a": "b", + "1": "c" }; + var parameterResults= oa._sortRequestParams(oa._makeArrayOfArgumentsHash(parameters)) + assert.equal(parameterResults[0][0], "1"); + assert.equal(parameterResults[1][0], "a"); + assert.equal(parameterResults[2][0], "z"); + }, + 'If two parameter names are the same then order by the value': function(oa) { + var parameters= {"z": "a", + "a": ["z", "b", "b", "a", "y"], + "1": "c" }; + var parameterResults= oa._sortRequestParams(oa._makeArrayOfArgumentsHash(parameters)) + assert.equal(parameterResults[0][0], "1"); + assert.equal(parameterResults[1][0], "a"); + assert.equal(parameterResults[1][1], "a"); + assert.equal(parameterResults[2][0], "a"); + assert.equal(parameterResults[2][1], "b"); + assert.equal(parameterResults[3][0], "a"); + assert.equal(parameterResults[3][1], "b"); + assert.equal(parameterResults[4][0], "a"); + assert.equal(parameterResults[4][1], "y"); + assert.equal(parameterResults[5][0], "a"); + assert.equal(parameterResults[5][1], "z"); + assert.equal(parameterResults[6][0], "z"); + } + }, + 'When normalising the request parameters': { + topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + 'the resulting parameters should be encoded and ordered as per http://tools.ietf.org/html/rfc5849#section-3.1 (3.4.1.3.2)' : function(oa) { + var parameters= {"b5" : "=%3D", + "a3": ["a", "2 q"], + "c@": "", + "a2": "r b", + "oauth_consumer_key": "9djdj82h48djs9d2", + "oauth_token":"kkk9d7dh3k39sjv7", + "oauth_signature_method": "HMAC-SHA1", + "oauth_timestamp": "137131201", + "oauth_nonce": "7d8f3e4a", + "c2" : ""}; + var normalisedParameterString= oa._normaliseRequestParams(parameters); + assert.equal(normalisedParameterString, "a2=r%20b&a3=2%20q&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9djdj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7"); + } + }, + 'When preparing the parameters for use in signing': { + topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + 'We need to be wary of node\'s auto object creation from foo[bar] style url parameters' : function(oa) { + var result= oa._prepareParameters( "", "", "", "http://foo.com?foo[bar]=xxx&bar[foo]=yyy", {} ); + assert.equal( result[0][0], "bar[foo]") + assert.equal( result[0][1], "yyy") + assert.equal( result[1][0], "foo[bar]") + assert.equal( result[1][1], "xxx") + } + }, + 'When signing a url': { + topic: function() { + var oa= new OAuth(null, null, "consumerkey", "consumersecret", "1.0", null, "HMAC-SHA1"); + oa._getTimestamp= function(){ return "1272399856"; } + oa._getNonce= function(){ return "ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp"; } + return oa; + }, + 'Provide a valid signature when no token present': function(oa) { + assert.equal( oa.signUrl("http://somehost.com:3323/foo/poop?bar=foo"), "http://somehost.com:3323/foo/poop?bar=foo&oauth_consumer_key=consumerkey&oauth_nonce=ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1272399856&oauth_version=1.0&oauth_signature=7ytO8vPSLut2GzHjU9pn1SV9xjc%3D"); + }, + 'Provide a valid signature when a token is present': function(oa) { + assert.equal( oa.signUrl("http://somehost.com:3323/foo/poop?bar=foo", "token"), "http://somehost.com:3323/foo/poop?bar=foo&oauth_consumer_key=consumerkey&oauth_nonce=ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1272399856&oauth_token=token&oauth_version=1.0&oauth_signature=9LwCuCWw5sURtpMroIolU3YwsdI%3D"); + }, + 'Provide a valid signature when a token and a token secret is present': function(oa) { + assert.equal( oa.signUrl("http://somehost.com:3323/foo/poop?bar=foo", "token", "tokensecret"), "http://somehost.com:3323/foo/poop?bar=foo&oauth_consumer_key=consumerkey&oauth_nonce=ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1272399856&oauth_token=token&oauth_version=1.0&oauth_signature=zeOR0Wsm6EG6XSg0Vw%2FsbpoSib8%3D"); + } + }, + 'When getting a request token': { + topic: function() { + var oa= new OAuth(null, null, "consumerkey", "consumersecret", "1.0", null, "HMAC-SHA1"); + oa._getTimestamp= function(){ return "1272399856"; } + oa._getNonce= function(){ return "ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp"; } + oa._performSecureRequest= function(){ return this.requestArguments = arguments; } + return oa; + }, + 'Use the HTTP method in the client options': function(oa) { + oa.setClientOptions({ requestTokenHttpMethod: "GET" }); + oa.getOAuthRequestToken(function() {}); + assert.equal(oa.requestArguments[2], "GET"); + }, + 'Use a POST by default': function(oa) { + oa.setClientOptions({}); + oa.getOAuthRequestToken(function() {}); + assert.equal(oa.requestArguments[2], "POST"); + } + }, + 'When getting an access token': { + topic: function() { + var oa= new OAuth(null, null, "consumerkey", "consumersecret", "1.0", null, "HMAC-SHA1"); + oa._getTimestamp= function(){ return "1272399856"; } + oa._getNonce= function(){ return "ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp"; } + oa._performSecureRequest= function(){ return this.requestArguments = arguments; } + return oa; + }, + 'Use the HTTP method in the client options': function(oa) { + oa.setClientOptions({ accessTokenHttpMethod: "GET" }); + oa.getOAuthAccessToken(function() {}); + assert.equal(oa.requestArguments[2], "GET"); + }, + 'Use a POST by default': function(oa) { + oa.setClientOptions({}); + oa.getOAuthAccessToken(function() {}); + assert.equal(oa.requestArguments[2], "POST"); + } + }, + 'When get authorization header' : { + topic: function() { + var oa= new OAuth(null, null, "consumerkey", "consumersecret", "1.0", null, "HMAC-SHA1"); + oa._getTimestamp= function(){ return "1272399856"; } + oa._getNonce= function(){ return "ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp"; } + return oa; + }, + 'Provide a valid signature when a token and a token secret is present': function(oa) { + assert.equal( oa.authHeader("http://somehost.com:3323/foo/poop?bar=foo", "token", "tokensecret"), 'OAuth oauth_consumer_key="consumerkey",oauth_nonce="ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1272399856",oauth_token="token",oauth_version="1.0",oauth_signature="zeOR0Wsm6EG6XSg0Vw%2FsbpoSib8%3D"'); + }, + 'Support variable whitespace separating the arguments': function(oa) { + oa._oauthParameterSeperator= ", "; + assert.equal( oa.authHeader("http://somehost.com:3323/foo/poop?bar=foo", "token", "tokensecret"), 'OAuth oauth_consumer_key="consumerkey", oauth_nonce="ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1272399856", oauth_token="token", oauth_version="1.0", oauth_signature="zeOR0Wsm6EG6XSg0Vw%2FsbpoSib8%3D"'); + } + }, + 'When get the OAuth Echo authorization header': { + topic: function () { + var realm = "http://foobar.com/"; + var verifyCredentials = "http://api.foobar.com/verify.json"; + var oa = new OAuthEcho(realm, verifyCredentials, "consumerkey", "consumersecret", "1.0A", "HMAC-SHA1"); + oa._getTimestamp= function(){ return "1272399856"; } + oa._getNonce= function(){ return "ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp"; } + return oa; + }, + 'Provide a valid signature when a token and token secret is present': function (oa) { + assert.equal( oa.authHeader("http://somehost.com:3323/foo/poop?bar=foo", "token", "tokensecret"), 'OAuth realm="http://foobar.com/",oauth_consumer_key="consumerkey",oauth_nonce="ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1272399856",oauth_token="token",oauth_version="1.0A",oauth_signature="0rr1LhSxACX2IEWRq3uCb4IwtOs%3D"'); + } + }, + 'When non standard ports are used': { + topic: function() { + var oa= new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + mockProvider= {}; + + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers.Host, "somehost.com:8080"); + assert.equal(hostname, "somehost.com"); + assert.equal(port, "8080"); + return { + on: function() {}, + end: function() {} + }; + } + return oa; + }, + 'getProtectedResource should correctly define the host headers': function(oa) { + oa.getProtectedResource("http://somehost.com:8080", "GET", "oauth_token", null, function(){}) + } + }, + 'When building the OAuth Authorization header': { + topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), + 'All provided oauth arguments should be concatentated correctly' : function(oa) { + var parameters= [ + ["oauth_timestamp", "1234567"], + ["oauth_nonce", "ABCDEF"], + ["oauth_version", "1.0"], + ["oauth_signature_method", "HMAC-SHA1"], + ["oauth_consumer_key", "asdasdnm2321b3"]]; + assert.equal(oa._buildAuthorizationHeaders(parameters), 'OAuth oauth_timestamp="1234567",oauth_nonce="ABCDEF",oauth_version="1.0",oauth_signature_method="HMAC-SHA1",oauth_consumer_key="asdasdnm2321b3"'); + }, + '*Only* Oauth arguments should be concatentated, others should be disregarded' : function(oa) { + var parameters= [ + ["foo", "2343"], + ["oauth_timestamp", "1234567"], + ["oauth_nonce", "ABCDEF"], + ["bar", "dfsdfd"], + ["oauth_version", "1.0"], + ["oauth_signature_method", "HMAC-SHA1"], + ["oauth_consumer_key", "asdasdnm2321b3"], + ["foobar", "asdasdnm2321b3"]]; + assert.equal(oa._buildAuthorizationHeaders(parameters), 'OAuth oauth_timestamp="1234567",oauth_nonce="ABCDEF",oauth_version="1.0",oauth_signature_method="HMAC-SHA1",oauth_consumer_key="asdasdnm2321b3"'); + }, + '_buildAuthorizationHeaders should not depends on Array.prototype.toString' : function(oa) { + var _toString = Array.prototype.toString; + Array.prototype.toString = function(){ return '[Array] ' + this.length; }; // toString overwrite example used in jsdom. + var parameters= [ + ["foo", "2343"], + ["oauth_timestamp", "1234567"], + ["oauth_nonce", "ABCDEF"], + ["bar", "dfsdfd"], + ["oauth_version", "1.0"], + ["oauth_signature_method", "HMAC-SHA1"], + ["oauth_consumer_key", "asdasdnm2321b3"], + ["foobar", "asdasdnm2321b3"]]; + assert.equal(oa._buildAuthorizationHeaders(parameters), 'OAuth oauth_timestamp="1234567",oauth_nonce="ABCDEF",oauth_version="1.0",oauth_signature_method="HMAC-SHA1",oauth_consumer_key="asdasdnm2321b3"'); + Array.prototype.toString = _toString; + } + }, + 'When performing the Secure Request' : { + topic: new OAuth("http://foo.com/RequestToken", + "http://foo.com/AccessToken", + "anonymous", "anonymous", + "1.0A", "http://foo.com/callback", "HMAC-SHA1"), + 'using the POST method' : { + 'Any passed extra_params should form part of the POST body': function(oa) { + var post_body_written= false; + var op= oa._createClient; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return { + write: function(post_body){ + post_body_written= true; + assert.equal(post_body,"scope=foobar%2C1%2C2"); + } + }; + } + oa._performSecureRequest("token", "token_secret", 'POST', 'http://foo.com/protected_resource', {"scope": "foobar,1,2"}); + assert.equal(post_body_written, true); + } + finally { + oa._createClient= op; + } + } + } + }, + 'When performing a secure' : { + topic: new OAuth("http://foo.com/RequestToken", + "http://foo.com/AccessToken", + "anonymous", "anonymous", + "1.0A", "http://foo.com/callback", "HMAC-SHA1"), + 'POST' : { + 'if no callback is passed' : { + 'it should return a request object': function(oa) { + var request= oa.post("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain") + assert.isObject(request); + assert.equal(request.method, "POST"); + request.end(); + } + }, + 'if a callback is passed' : { + "it should call the internal request's end method and return nothing": function(oa) { + var callbackCalled= false; + var op= oa._createClient; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return { + write: function(){}, + on: function() {}, + end: function() { + callbackCalled= true; + } + }; + } + var request= oa.post("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain", function(e,d){}) + assert.equal(callbackCalled, true); + assert.isUndefined(request); + } + finally { + oa._createClient= op; + } + } + }, + 'if the post_body is a buffer' : { + "It should be passed through as is, and the original content-type (if specified) should be passed through": function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "image/jpeg") + return { + write: function(data){ + callbackCalled= true; + assert.equal(data.length, 4); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.post("http://foo.com/blah", "token", "token_secret", new Buffer([10,20,30,40]), "image/jpeg") + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + }, + "It should be passed through as is, and no content-type is specified.": function(oa) { + //Should probably actually set application/octet-stream, but to avoid a change in behaviour + // will just document (here) that the library will set it to application/x-www-form-urlencoded + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded") + return { + write: function(data){ + callbackCalled= true; + assert.equal(data.length, 4); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.post("http://foo.com/blah", "token", "token_secret", new Buffer([10,20,30,40])) + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + }, + 'if the post_body is not a string or a buffer' : { + "It should be url encoded and the content type set to be x-www-form-urlencoded" : function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded") + return { + write: function(data){ + callbackCalled= true; + assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.post("http://foo.com/blah", "token", "token_secret", {"foo":"1,2,3", "bar":"1+2"}) + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + }, + 'if the post_body is a string' : { + "and it contains non ascii (7/8bit) characters" : { + "the content length should be the byte count, and not the string length" : function(oa) { + var testString= "Tôi yêu node"; + var testStringLength= testString.length; + var testStringBytesLength= Buffer.byteLength(testString); + assert.notEqual(testStringLength, testStringBytesLength); // Make sure we're testing a string that differs between byte-length and char-length! + + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-length"], testStringBytesLength); + return { + write: function(data){ + callbackCalled= true; + assert.equal(data, testString); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.post("http://foo.com/blah", "token", "token_secret", "Tôi yêu node") + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + }, + "and no post_content_type is specified" : { + "It should be written as is, with a content length specified, and the encoding should be set to be x-www-form-urlencoded" : function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded"); + assert.equal(headers["Content-length"], 23); + return { + write: function(data){ + callbackCalled= true; + assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.post("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2") + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + }, + "and a post_content_type is specified" : { + "It should be written as is, with a content length specified, and the encoding should be set to be as specified" : function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "unicorn/encoded"); + assert.equal(headers["Content-length"], 23); + return { + write: function(data){ + callbackCalled= true; + assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.post("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2", "unicorn/encoded") + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + } + } + }, + 'GET' : { + 'if no callback is passed' : { + 'it should return a request object': function(oa) { + var request= oa.get("http://foo.com/blah", "token", "token_secret") + assert.isObject(request); + assert.equal(request.method, "GET"); + request.end(); + } + }, + 'if a callback is passed' : { + "it should call the internal request's end method and return nothing": function(oa) { + var callbackCalled= false; + var op= oa._createClient; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return { + on: function() {}, + end: function() { + callbackCalled= true; + } + }; + } + var request= oa.get("http://foo.com/blah", "token", "token_secret", function(e,d) {}) + assert.equal(callbackCalled, true); + assert.isUndefined(request); + } + finally { + oa._createClient= op; + } + } + }, + }, + 'PUT' : { + 'if no callback is passed' : { + 'it should return a request object': function(oa) { + var request= oa.put("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain") + assert.isObject(request); + assert.equal(request.method, "PUT"); + request.end(); + } + }, + 'if a callback is passed' : { + "it should call the internal request's end method and return nothing": function(oa) { + var callbackCalled= 0; + var op= oa._createClient; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return { + on: function() {}, + write: function(data) { + callbackCalled++; + }, + end: function() { + callbackCalled++; + } + }; + } + var request= oa.put("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain", function(e,d){}) + assert.equal(callbackCalled, 2); + assert.isUndefined(request); + } + finally { + oa._createClient= op; + } + } + }, + 'if the post_body is a buffer' : { + "It should be passed through as is, and the original content-type (if specified) should be passed through": function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "image/jpeg") + return { + write: function(data){ + callbackCalled= true; + assert.equal(data.length, 4); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.put("http://foo.com/blah", "token", "token_secret", new Buffer([10,20,30,40]), "image/jpeg") + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + }, + "It should be passed through as is, and no content-type is specified.": function(oa) { + //Should probably actually set application/octet-stream, but to avoid a change in behaviour + // will just document (here) that the library will set it to application/x-www-form-urlencoded + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded") + return { + write: function(data){ + callbackCalled= true; + assert.equal(data.length, 4); + }, + on: function() {}, + end: function() { + } + }; + } + var request= oa.put("http://foo.com/blah", "token", "token_secret", new Buffer([10,20,30,40])) + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + }, + 'if the post_body is not a string' : { + "It should be url encoded and the content type set to be x-www-form-urlencoded" : function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded") + return { + write: function(data) { + callbackCalled= true; + assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); + } + }; + } + var request= oa.put("http://foo.com/blah", "token", "token_secret", {"foo":"1,2,3", "bar":"1+2"}) + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + }, + 'if the post_body is a string' : { + "and no post_content_type is specified" : { + "It should be written as is, with a content length specified, and the encoding should be set to be x-www-form-urlencoded" : function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded"); + assert.equal(headers["Content-length"], 23); + return { + write: function(data) { + callbackCalled= true; + assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); + } + }; + } + var request= oa.put("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2") + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + }, + "and a post_content_type is specified" : { + "It should be written as is, with a content length specified, and the encoding should be set to be as specified" : function(oa) { + var op= oa._createClient; + try { + var callbackCalled= false; + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + assert.equal(headers["Content-Type"], "unicorn/encoded"); + assert.equal(headers["Content-length"], 23); + return { + write: function(data) { + callbackCalled= true; + assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); + } + }; + } + var request= oa.put("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2", "unicorn/encoded") + assert.equal(callbackCalled, true); + } + finally { + oa._createClient= op; + } + } + } + } + }, + 'DELETE' : { + 'if no callback is passed' : { + 'it should return a request object': function(oa) { + var request= oa.delete("http://foo.com/blah", "token", "token_secret") + assert.isObject(request); + assert.equal(request.method, "DELETE"); + request.end(); + } + }, + 'if a callback is passed' : { + "it should call the internal request's end method and return nothing": function(oa) { + var callbackCalled= false; + var op= oa._createClient; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return { + on: function() {}, + end: function() { + callbackCalled= true; + } + }; + } + var request= oa.delete("http://foo.com/blah", "token", "token_secret", function(e,d) {}) + assert.equal(callbackCalled, true); + assert.isUndefined(request); + } + finally { + oa._createClient= op; + } + } + } + }, + 'Request With a Callback' : { + 'and a 200 response code is received' : { + 'it should callback successfully' : function(oa) { + var op= oa._createClient; + var callbackCalled = false; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse(200) ); + } + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(error) { + // callback + callbackCalled= true; + assert.equal(error, undefined); + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + } + } + }, + 'and a 210 response code is received' : { + 'it should callback successfully' : function(oa) { + var op= oa._createClient; + var callbackCalled = false; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse(210) ); + } + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(error) { + // callback + callbackCalled= true; + assert.equal(error, undefined); + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + } + } + }, + 'And A 301 redirect is received' : { + 'and there is a location header' : { + 'it should (re)perform the secure request but with the new location' : function(oa) { + var op= oa._createClient; + var psr= oa._performSecureRequest; + var responseCounter = 1; + var callbackCalled = false; + var DummyResponse =function() { + if( responseCounter == 1 ){ + this.statusCode= 301; + this.headers= {location:"http://redirectto.com"}; + responseCounter++; + } + else { + this.statusCode= 200; + } + } + DummyResponse.prototype= events.EventEmitter.prototype; + DummyResponse.prototype.setEncoding= function() {} + + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse() ); + } + oa._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) { + if( responseCounter == 1 ) { + assert.equal(url, "http://originalurl.com"); + } + else { + assert.equal(url, "http://redirectto.com"); + } + return psr.call(oa, oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) + } + + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function() { + // callback + assert.equal(responseCounter, 2); + callbackCalled= true; + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + oa._performSecureRequest= psr; + } + } + }, + 'but there is no location header' : { + 'it should execute the callback, passing the HTTP Response code' : function(oa) { + var op= oa._createClient; + var callbackCalled = false; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse(301) ); + } + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(error) { + // callback + assert.equal(error.statusCode, 301); + callbackCalled= true; + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + } + } + }, + 'and followRedirect is true' : { + 'it should (re)perform the secure request but with the new location' : function(oa) { + var op= oa._createClient; + var psr= oa._performSecureRequest; + var responseCounter = 1; + var callbackCalled = false; + var DummyResponse =function() { + if( responseCounter == 1 ){ + this.statusCode= 301; + this.headers= {location:"http://redirectto.com"}; + responseCounter++; + } + else { + this.statusCode= 200; + } + } + DummyResponse.prototype= events.EventEmitter.prototype; + DummyResponse.prototype.setEncoding= function() {} + + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse() ); + } + oa._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) { + if( responseCounter == 1 ) { + assert.equal(url, "http://originalurl.com"); + } + else { + assert.equal(url, "http://redirectto.com"); + } + return psr.call(oa, oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) + } + + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function() { + // callback + assert.equal(responseCounter, 2); + callbackCalled= true; + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + oa._performSecureRequest= psr; + } + } + }, + 'and followRedirect is false' : { + 'it should not perform the secure request with the new location' : function(oa) { + var op= oa._createClient; + oa.setClientOptions({ followRedirects: false }); + var DummyResponse =function() { + this.statusCode= 301; + this.headers= {location:"http://redirectto.com"}; + } + DummyResponse.prototype= events.EventEmitter.prototype; + DummyResponse.prototype.setEncoding= function() {} + + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse() ); + } + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(res, data, response) { + // callback + assert.equal(res.statusCode, 301); + }); + } + finally { + oa._createClient= op; + oa.setClientOptions({followRedirects:true}); + } + } + } + }, + 'And A 302 redirect is received' : { + 'and there is a location header' : { + 'it should (re)perform the secure request but with the new location' : function(oa) { + var op= oa._createClient; + var psr= oa._performSecureRequest; + var responseCounter = 1; + var callbackCalled = false; + var DummyResponse =function() { + if( responseCounter == 1 ){ + this.statusCode= 302; + this.headers= {location:"http://redirectto.com"}; + responseCounter++; + } + else { + this.statusCode= 200; + } + } + DummyResponse.prototype= events.EventEmitter.prototype; + DummyResponse.prototype.setEncoding= function() {} + + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse() ); + } + oa._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) { + if( responseCounter == 1 ) { + assert.equal(url, "http://originalurl.com"); + } + else { + assert.equal(url, "http://redirectto.com"); + } + return psr.call(oa, oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) + } + + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function() { + // callback + assert.equal(responseCounter, 2); + callbackCalled= true; + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + oa._performSecureRequest= psr; + } + } + }, + 'but there is no location header' : { + 'it should execute the callback, passing the HTTP Response code' : function(oa) { + var op= oa._createClient; + var callbackCalled = false; + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse(302) ); + } + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(error) { + // callback + assert.equal(error.statusCode, 302); + callbackCalled= true; + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + } + } + }, + 'and followRedirect is true' : { + 'it should (re)perform the secure request but with the new location' : function(oa) { + var op= oa._createClient; + var psr= oa._performSecureRequest; + var responseCounter = 1; + var callbackCalled = false; + var DummyResponse =function() { + if( responseCounter == 1 ){ + this.statusCode= 302; + this.headers= {location:"http://redirectto.com"}; + responseCounter++; + } + else { + this.statusCode= 200; + } + } + DummyResponse.prototype= events.EventEmitter.prototype; + DummyResponse.prototype.setEncoding= function() {} + + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse() ); + } + oa._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) { + if( responseCounter == 1 ) { + assert.equal(url, "http://originalurl.com"); + } + else { + assert.equal(url, "http://redirectto.com"); + } + return psr.call(oa, oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) + } + + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function() { + // callback + assert.equal(responseCounter, 2); + callbackCalled= true; + }); + assert.equal(callbackCalled, true) + } + finally { + oa._createClient= op; + oa._performSecureRequest= psr; + } + } + }, + 'and followRedirect is false' : { + 'it should not perform the secure request with the new location' : function(oa) { + var op= oa._createClient; + oa.setClientOptions({ followRedirects: false }); + var DummyResponse =function() { + this.statusCode= 302; + this.headers= {location:"http://redirectto.com"}; + } + DummyResponse.prototype= events.EventEmitter.prototype; + DummyResponse.prototype.setEncoding= function() {} + + try { + oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { + return new DummyRequest( new DummyResponse() ); + } + oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(res, data, response) { + // callback + assert.equal(res.statusCode, 302); + }); + } + finally { + oa._createClient= op; + oa.setClientOptions({followRedirects:true}); + } + } + } + } + } + } +}).export(module); diff --git a/node_modules/oauth/tests/sha1tests.js b/node_modules/oauth/tests/sha1tests.js new file mode 100644 index 0000000..18ba0ae --- /dev/null +++ b/node_modules/oauth/tests/sha1tests.js @@ -0,0 +1,13 @@ +var vows = require('vows'), + assert = require('assert'); + +vows.describe('SHA1 Hashing').addBatch({ + 'When using the SHA1 Hashing function': { + topic: require('../lib/sha1'), + 'we get the specified digest as described in http://oauth.net/core/1.0/#sig_base_example (A.5.2)': function (sha1) { + assert.equal (sha1.HMACSHA1( "kd94hf93k423kf44&pfkkdhi9sl3r4s00", + "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"), + "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); + } + } +}).export(module); \ No newline at end of file diff --git a/node_modules/oauth/tests/shared.js b/node_modules/oauth/tests/shared.js new file mode 100644 index 0000000..f4c8094 --- /dev/null +++ b/node_modules/oauth/tests/shared.js @@ -0,0 +1,26 @@ +var events = require('events'); + +exports.DummyResponse = function( statusCode ) { + this.statusCode= statusCode; + this.headers= {}; +} +exports.DummyResponse.prototype= events.EventEmitter.prototype; +exports.DummyResponse.prototype.setEncoding= function() {} + +exports.DummyRequest =function( response ) { + this.response= response; + this.responseSent= false; +} +exports.DummyRequest.prototype= events.EventEmitter.prototype; +exports.DummyRequest.prototype.write= function(post_body){} +exports.DummyRequest.prototype.write= function(post_body){ + this.responseSent= true; + this.emit('response',this.response); +} +exports.DummyRequest.prototype.end= function(){ + if(!this.responseSent) { + this.responseSent= true; + this.emit('response',this.response); + } + this.response.emit('end'); +} \ No newline at end of file diff --git a/node_modules/passport-kakao/.eslintrc.js b/node_modules/passport-kakao/.eslintrc.js new file mode 100644 index 0000000..30009ff --- /dev/null +++ b/node_modules/passport-kakao/.eslintrc.js @@ -0,0 +1,51 @@ +module.exports = { + env: { + es6: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ], + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', + }, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 2018, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + settings: { + react: { + version: 'detect', + }, + }, + rules: { + semi: ['warn', 'never'], + 'sort-imports': ['off'], + quotes: ['warn', 'single'], + 'no-extra-boolean-cast': ['off'], + '@typescript-eslint/member-delimiter-style': [ + 'warn', + { + multiline: { + delimiter: 'none', + requireLast: false, + }, + singleline: { + requireLast: false, + }, + }, + ], + '@typescript-eslint/no-empty-interface': ['off'], + '@typescript-eslint/explicit-function-return-type': ['off'], + '@typescript-eslint/no-explicit-any': ['off'], + '@typescript-eslint/no-non-null-assertion': ['off'], + '@typescript-eslint/interface-name-prefix': ['warn'], + }, +} diff --git a/node_modules/passport-kakao/.prettierrc b/node_modules/passport-kakao/.prettierrc new file mode 100644 index 0000000..ea8c3c7 --- /dev/null +++ b/node_modules/passport-kakao/.prettierrc @@ -0,0 +1,12 @@ +{ + "trailingComma": "es5", + "semi": false, + "singleQuote": true, + "arrowParens": "avoid", + "bracketSpacing": true, + "endOfLine": "auto", + "htmlWhitespaceSensitivity": "css", + "proseWrap": "preserve", + "quoteProps": "as-needed", + "tabWidth": 2 +} diff --git a/node_modules/passport-kakao/.travis.yml b/node_modules/passport-kakao/.travis.yml new file mode 100644 index 0000000..21a1d2e --- /dev/null +++ b/node_modules/passport-kakao/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - '0.10' +before_script: + - npm install -g jasmine-node + diff --git a/node_modules/passport-kakao/README.md b/node_modules/passport-kakao/README.md new file mode 100644 index 0000000..3ca9d50 --- /dev/null +++ b/node_modules/passport-kakao/README.md @@ -0,0 +1,204 @@ +# passport-kakao + +kakao oauth2 로그인과 passport 모듈 연결체. + +## install + +```sh +npm install passport-kakao +``` + +## how to use + +- https://developers.kakao.com/ 에서 애플리케이션을 등록한다. +- 방금 추가한 애플리케이션의 설정 - 사용자 관리에 들어가서 사용을 ON으로 한 뒤 저장한다. +- 설정 - 일반에서, 플랫폼 추가를 누른 후 웹 플랫폼을 추가한다. +- 웹 플랫폼 설정의 사이트 도메인에 자신의 사이트 도메인을 추가한다. (ex : http://localhost:3000) +- 프로그램 상에서는 아래와 같이 사용한다. + +> clientSecret을 활성화 한 경우 해당 파라메터를 같이 넘겨줘야한다. + +```javascript +const passport = require('passport') +const KakaoStrategy = require('passport-kakao').Strategy + +passport.use(new KakaoStrategy({ + clientID : clientID, + clientSecret: clientSecret, // clientSecret을 사용하지 않는다면 넘기지 말거나 빈 스트링을 넘길 것 + callbackURL : callbackURL + }, + (accessToken, refreshToken, profile, done) => { + // 사용자의 정보는 profile에 들어있다. + User.findOrCreate(..., (err, user) => { + if (err) { return done(err) } + return done(null, user) + }) + } +)) +``` + +> 기본 callbackPath는 `/oauth` 이고 https://developers.kakao.com 에서 수정할 수 있다. 하지만 callbackURL은 `사이트 도메인/oauth` 로 설정하는 것을 권장함. (ex : http://myhomepage.com:3000/oauth ) + +## + +## profile property + +profile에는 아래의 property들이 설정되어 넘겨진다. + +| key | value | 비고 | +| -------- | ------ | ------------------------------------------ | +| provider | String | kakao 고정 | +| id | Number | 사용자의 kakao id | +| \_raw | String | 사용자 정보 조회로 얻어진 json string | +| \_json | Object | 사용자 정보 조회로 얻어진 json 원본 데이터 | + +## simple sample + +### 설치 & 실행 + +1. `./sample/sample.js` 의 `appKey` 를 https://developers.kakao.com 에서 발급받은 JS appKey 값으로 셋팅. +2. command line 에서 아래의 커맨드 실행 +3. 브라우져를 열고 `127.0.0.1:3000/login` 을 입력 후 이후 과정을 진행한다. + +``` +cd ./sample +npm install +node app +``` + +## mean.io 와 쉽게 연동하기 + +수정해야하는 파일들은 아래와 같다. + +| file path | 설명 | +| -------------------------------- | ------------------------------ | +| server/config/env/development.js | 개발환경 설정파일 | +| server/config/env/production.js | 운영환경 설정파일 | +| server/config/models/user.js | 사용자 모델 | +| server/config/passport.js | passport script | +| server/routes/users.js | 사용자 로그인 관련 routes file | +| public/auth/views/index.html | 로그인 화면 | + +(1) **mean.io app을 생성** 한다. (ex : mean init kakaoTest) + +(2) 해당 모듈을 연동할 mean.io app에 설치한다.(npm install passport-kakao --save) + +(3) **server/config/env/development.js** 와 **production.js** 에 kakao 관련 설정을 아래와 같이 추가한다. + +```javascript +'use strict' + +module.exports = { + db: 'mongodb', + app: { + name: 'passport-kakao', + }, + // 그외 설정들...., + kakao: { + clientID: 'kakao app rest api key', + callbackURL: 'http://localhost:3000/oauth', + }, +} +``` + +(4) **server/config/models/users.js** 의 사용자 스키마 정의에 **kakao: {}** 를 추가한다. + +(5) **server/config/passport.js** 파일에 아래 구문을 추가한다. + +```javascript +// 최상단 require되는 구문에 추가 +var KakaoStrategy = require('passport-kakao').Strategy + +passport.use( + new KakaoStrategy( + { + clientID: config.kakao.clientID, + callbackURL: config.kakao.callbackURL, + }, + function(accessToken, refreshToken, profile, done) { + User.findOne( + { + 'kakao.id': profile.id, + }, + function(err, user) { + if (err) { + return done(err) + } + if (!user) { + user = new User({ + name: profile.username, + username: profile.id, + roles: ['authenticated'], + provider: 'kakao', + kakao: profile._json, + }) + + user.save(function(err) { + if (err) { + console.log(err) + } + return done(err, user) + }) + } else { + return done(err, user) + } + } + ) + } + ) +) +``` + +(6) **server/routes/users.js** 에 아래와 같은 구문을 추가한다. + +```javascript +app.get( + '/auth/kakao', + passport.authenticate('kakao', { + failureRedirect: '#!/login', + }), + users.signin +) + +app.get( + '/oauth', + passport.authenticate('kakao', { + failureRedirect: '#!/login', + }), + users.authCallback +) +``` + +(7) **public/auth/views/index.html** 에 kakao login을 연결한다. + +```html + +
+
+
+ + + + + + + + + + + +
+
+
+
+
+
+``` + +(8) grunt로 mean.io app 실행 후, 실제 로그인 연동 테스트를 해본다. + +## 기타 + +passport-oauth 모듈과 passport-facebook 모듈을 참고함. diff --git a/node_modules/passport-kakao/dist/Strategy.js b/node_modules/passport-kakao/dist/Strategy.js new file mode 100644 index 0000000..4d77d20 --- /dev/null +++ b/node_modules/passport-kakao/dist/Strategy.js @@ -0,0 +1,81 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var util_1 = require("util"); +var passport_oauth2_1 = __importDefault(require("passport-oauth2")); +var DEFAULT_CLIENT_SECRET = 'kakao'; +var OAUTH_HOST = 'https://kauth.kakao.com'; +var USER_PROFILE_URL = 'https://kapi.kakao.com/v2/user/me'; +exports.buildOptions = function (options) { + options.authorizationURL = OAUTH_HOST + "/oauth/authorize"; + options.tokenURL = OAUTH_HOST + "/oauth/token"; + if (!options.clientSecret) { + options.clientSecret = DEFAULT_CLIENT_SECRET; + } + options.scopeSeparator = options.scopeSeparator || ','; + options.customHeaders = options.customHeaders || {}; + if (!options.customHeaders['User-Agent']) { + options.customHeaders['User-Agent'] = options.userAgent || 'passport-kakao'; + } + return options; +}; +/** + * KaKaoStrategy 생성자 함수.
+ * @param options.clientID 필수. kakao rest app key. + * @param options.callbackURL 필수. 로그인 처리 후 호출할 URL + * @param verify + * @constructor + */ +function Strategy(options, verify) { + if (options === void 0) { options = {}; } + passport_oauth2_1.default.call(this, exports.buildOptions(options), verify); + this.name = 'kakao'; + this._userProfileURL = USER_PROFILE_URL; +} +/** + * `OAuth2Stragegy`를 상속 받는다. + */ +util_1.inherits(Strategy, passport_oauth2_1.default); +/** + * kakao 사용자 정보를 얻는다.
+ * 사용자 정보를 성공적으로 조회하면 아래의 object가 done 콜백함수 호출과 함꼐 넘어간다. + * + * - `provider` kakao 고정 + * - `id` kakao user id number + * - `username` 사용자의 kakao nickname + * - `_raw` json string 원문 + * _ `_json` json 원 데이터 + * + * @param {String} accessToken + * @param {Function} done + */ +Strategy.prototype.userProfile = function (accessToken, done) { + this._oauth2.get(this._userProfileURL, accessToken, function (err, body) { + if (err) { + return done(err); + } + try { + var json = JSON.parse(body); + // 카카오톡이나 카카오스토리에 연동한 적이 없는 계정의 경우 + // properties가 비어있다고 한다. 없을 경우의 처리 + var properties = json.properties || { + nickname: '미연동 계정', + }; + var profile = { + provider: 'kakao', + id: json.id, + username: properties.nickname, + displayName: properties.nickname, + _raw: body, + _json: json, + }; + return done(null, profile); + } + catch (e) { + return done(e); + } + }); +}; +exports.default = Strategy; diff --git a/node_modules/passport-kakao/dist/passport-kakao.js b/node_modules/passport-kakao/dist/passport-kakao.js new file mode 100644 index 0000000..b3b0fb5 --- /dev/null +++ b/node_modules/passport-kakao/dist/passport-kakao.js @@ -0,0 +1,8 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var Strategy_1 = __importDefault(require("./Strategy")); +exports.Strategy = Strategy_1.default; +exports.default = Strategy_1.default; diff --git a/node_modules/passport-kakao/dist/types/models.js b/node_modules/passport-kakao/dist/types/models.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/node_modules/passport-kakao/dist/types/models.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node_modules/passport-kakao/package.json b/node_modules/passport-kakao/package.json new file mode 100644 index 0000000..c763033 --- /dev/null +++ b/node_modules/passport-kakao/package.json @@ -0,0 +1,45 @@ +{ + "name": "passport-kakao", + "version": "1.0.1", + "description": "kakao oauth2 login module", + "main": "./dist/passport-kakao", + "keywords": [ + "passport", + "kakao", + "kakaotalk", + "oauth2" + ], + "repository": { + "type": "git", + "url": "git://github.com/rotoshine/passport-kakao.git" + }, + "author": "rotoshine@gmail.com", + "license": "MIT", + "dependencies": { + "pkginfo": "~0.3.0", + "passport-oauth2": "~1.1.2" + }, + "devDependencies": { + "@types/chai": "^4.2.3", + "@types/mocha": "^5.2.7", + "@types/node": "^12.7.11", + "@typescript-eslint/eslint-plugin": "^2.30.0", + "@typescript-eslint/parser": "^2.30.0", + "chai": "^4.2.0", + "eslint": "^6.8.0", + "mocha": "^6.2.1", + "prettier": "^2.0.5", + "rimraf": "^3.0.2", + "ts-node": "^8.4.1", + "tslint": "^5.20.0", + "tslint-config-prettier": "^1.18.0", + "typescript": "^3.6.3" + }, + "scripts": { + "test": "mocha -r node_modules/ts-node/register ./tests/**/*.spec.ts", + "clean": "rimraf dist/*", + "build": "npm run clean && npx tsc", + "lint": "eslint -c .eslintrc.js src/**/*.ts", + "format": "prettier --write \"src/*.{ts,tsx,json,md}\"" + } +} diff --git a/node_modules/passport-kakao/sample/README.md b/node_modules/passport-kakao/sample/README.md new file mode 100644 index 0000000..f788a23 --- /dev/null +++ b/node_modules/passport-kakao/sample/README.md @@ -0,0 +1,5 @@ +## how to test + +- create `.env` file. +- setup `API_KEY` and `CLIENT_SECRET_KEY`. +- run `npm install & node sample.js` diff --git a/node_modules/passport-kakao/sample/package-lock.json b/node_modules/passport-kakao/sample/package-lock.json new file mode 100644 index 0000000..1ee3077 --- /dev/null +++ b/node_modules/passport-kakao/sample/package-lock.json @@ -0,0 +1,397 @@ +{ + "name": "passport-kakao-sample", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/node_modules/passport-kakao/sample/package.json b/node_modules/passport-kakao/sample/package.json new file mode 100644 index 0000000..8fd0f57 --- /dev/null +++ b/node_modules/passport-kakao/sample/package.json @@ -0,0 +1,12 @@ +{ + "name": "passport-kakao-sample", + "repository": { + "type": "git", + "url": "git://github.com/rotoshine/passport-kakao.git" + }, + "dependencies": { + "dotenv": "^8.2.0", + "express": ">= 4.4.5", + "passport": ">= 0.0.0" + } +} diff --git a/node_modules/passport-kakao/sample/sample.js b/node_modules/passport-kakao/sample/sample.js new file mode 100644 index 0000000..96ce188 --- /dev/null +++ b/node_modules/passport-kakao/sample/sample.js @@ -0,0 +1,49 @@ +require('dotenv').config() + +const passport = require('passport') +const express = require('express') +const KakaoStrategy = require('../dist/passport-kakao.js').Strategy + +const appKey = process.env.API_KEY +const appSecret = process.env.CLIENT_SECRET_KEY + +// passport 에 Kakao Oauth 추가 +passport.use( + new KakaoStrategy( + { + clientID: appKey, + clientSecret: appSecret, + callbackURL: 'http://localhost:3000/oauth', + }, + function (accessToken, refreshToken, params, profile, done) { + // authorization 에 성공했을때의 액션 + console.log(`accessToken : ${accessToken}`) + console.log(`사용자 profile: ${JSON.stringify(profile._json)}`) + + save(accessToken, refreshToken, profile) + return done(null, profile._json) + } + ) +) +passport.serializeUser(function (user, done) { + done(null, user) +}) +passport.deserializeUser(function (obj, done) { + done(null, obj) +}) + +// express 앱 설정 +var app = express() +app.use(passport.initialize()) +app.get('/login', passport.authenticate('kakao', { state: 'myStateValue' })) +app.get('/oauth', passport.authenticate('kakao'), function (req, res) { + // 로그인 시작시 state 값을 받을 수 있음 + res.send('state :' + req.query.state) +}) +app.listen(3000) +console.log('> server start! ') + +// 사용자 구현 부분 +function save() { + //save 로직 구현 +} diff --git a/node_modules/passport-kakao/src/Strategy.ts b/node_modules/passport-kakao/src/Strategy.ts new file mode 100644 index 0000000..0e06bd0 --- /dev/null +++ b/node_modules/passport-kakao/src/Strategy.ts @@ -0,0 +1,93 @@ +import { inherits } from 'util' +import OAuth2Strategy from 'passport-oauth2' + +import { StrategyOptions, Profile } from './types/models' + +const DEFAULT_CLIENT_SECRET = 'kakao' +const OAUTH_HOST = 'https://kauth.kakao.com' +const USER_PROFILE_URL = 'https://kapi.kakao.com/v2/user/me' + +export const buildOptions = (options: StrategyOptions) => { + options.authorizationURL = `${OAUTH_HOST}/oauth/authorize` + options.tokenURL = `${OAUTH_HOST}/oauth/token` + + if (!options.clientSecret) { + options.clientSecret = DEFAULT_CLIENT_SECRET + } + + options.scopeSeparator = options.scopeSeparator || ',' + options.customHeaders = options.customHeaders || {} + + if (!options.customHeaders['User-Agent']) { + options.customHeaders['User-Agent'] = options.userAgent || 'passport-kakao' + } + + return options +} +/** + * KaKaoStrategy 생성자 함수.
+ * @param options.clientID 필수. kakao rest app key. + * @param options.callbackURL 필수. 로그인 처리 후 호출할 URL + * @param verify + * @constructor + */ +function Strategy(options: StrategyOptions = {}, verify: any) { + OAuth2Strategy.call(this, buildOptions(options), verify) + this.name = 'kakao' + this._userProfileURL = USER_PROFILE_URL +} + +/** + * `OAuth2Stragegy`를 상속 받는다. + */ +inherits(Strategy, OAuth2Strategy) + +/** + * kakao 사용자 정보를 얻는다.
+ * 사용자 정보를 성공적으로 조회하면 아래의 object가 done 콜백함수 호출과 함꼐 넘어간다. + * + * - `provider` kakao 고정 + * - `id` kakao user id number + * - `username` 사용자의 kakao nickname + * - `_raw` json string 원문 + * _ `_json` json 원 데이터 + * + * @param {String} accessToken + * @param {Function} done + */ +Strategy.prototype.userProfile = function ( + accessToken: string, + done: (error: Error, profile?: Profile) => void +) { + this._oauth2.get( + this._userProfileURL, + accessToken, + (err: Error, body: string) => { + if (err) { + return done(err) + } + + try { + const json = JSON.parse(body) + // 카카오톡이나 카카오스토리에 연동한 적이 없는 계정의 경우 + // properties가 비어있다고 한다. 없을 경우의 처리 + const properties = json.properties || { + nickname: '미연동 계정', + } + const profile: Profile = { + provider: 'kakao', + id: json.id, + username: properties.nickname, + displayName: properties.nickname, + _raw: body, + _json: json, + } + return done(null, profile) + } catch (e) { + return done(e) + } + } + ) +} + +export default Strategy diff --git a/node_modules/passport-kakao/src/passport-kakao.ts b/node_modules/passport-kakao/src/passport-kakao.ts new file mode 100644 index 0000000..3121fb3 --- /dev/null +++ b/node_modules/passport-kakao/src/passport-kakao.ts @@ -0,0 +1,5 @@ +import Strategy from './Strategy' + +export default Strategy + +export { Strategy } diff --git a/node_modules/passport-kakao/src/types/models.ts b/node_modules/passport-kakao/src/types/models.ts new file mode 100644 index 0000000..f864086 --- /dev/null +++ b/node_modules/passport-kakao/src/types/models.ts @@ -0,0 +1,19 @@ +export interface StrategyOptions { + authorizationURL?: string + tokenURL?: string + clientSecret?: string + scopeSeparator?: string + customHeaders?: { + 'User-Agent'?: string + } + userAgent?: string +} + +export interface Profile { + provider: 'kakao' + id?: string | number + username?: string + displayName?: string + _raw: string + _json: string +} diff --git a/node_modules/passport-kakao/tests/passport-kakao.spec.ts b/node_modules/passport-kakao/tests/passport-kakao.spec.ts new file mode 100644 index 0000000..139bafc --- /dev/null +++ b/node_modules/passport-kakao/tests/passport-kakao.spec.ts @@ -0,0 +1,24 @@ +import { expect } from 'chai' +import KakaoStrategy, { buildOptions } from '../src/Strategy' + +describe('passport-kakao', () => { + it('passport-kakao 객체가 제대로 생성이 되어 있어야 한다.', () => { + expect(KakaoStrategy).to.not.equals(null) + }) + it('Strategy option의 clientSecret 값이 없을 경우 default 값이 설정되어야 한다.', () => { + const options = buildOptions({}) + + expect(options).to.not.equals(null) + expect(options.clientSecret).to.be.equals('kakao') + expect(options.scopeSeparator).to.be.equals(',') + expect(options.customHeaders['User-Agent']).to.be.equals('passport-kakao') + }) + it('Strategy option의 User-Agent값이 있을 경우 customHeaders의 User-Agent가 해당 값으로 설정되어야 한다.', () => { + const options = buildOptions({ + customHeaders: { + 'User-Agent': 'HELLO ROTO', + }, + }) + expect(options.customHeaders['User-Agent']).to.be.equals('HELLO ROTO') + }) +}) diff --git a/node_modules/passport-kakao/tsconfig.json b/node_modules/passport-kakao/tsconfig.json new file mode 100644 index 0000000..a460e0b --- /dev/null +++ b/node_modules/passport-kakao/tsconfig.json @@ -0,0 +1,64 @@ +{ + "include": ["./src"], + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, + "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": false /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +} diff --git a/node_modules/passport-oauth2/LICENSE b/node_modules/passport-oauth2/LICENSE new file mode 100644 index 0000000..214ac8b --- /dev/null +++ b/node_modules/passport-oauth2/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2011-2014 Jared Hanson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/passport-oauth2/README.md b/node_modules/passport-oauth2/README.md new file mode 100644 index 0000000..4590112 --- /dev/null +++ b/node_modules/passport-oauth2/README.md @@ -0,0 +1,93 @@ +# passport-oauth2 + +[![Build](https://travis-ci.org/jaredhanson/passport-oauth2.png)](https://travis-ci.org/jaredhanson/passport-oauth2) +[![Coverage](https://coveralls.io/repos/jaredhanson/passport-oauth2/badge.png)](https://coveralls.io/r/jaredhanson/passport-oauth2) +[![Quality](https://codeclimate.com/github/jaredhanson/passport-oauth2.png)](https://codeclimate.com/github/jaredhanson/passport-oauth2) +[![Dependencies](https://david-dm.org/jaredhanson/passport-oauth2.png)](https://david-dm.org/jaredhanson/passport-oauth2) +[![Tips](http://img.shields.io/gittip/jaredhanson.png)](https://www.gittip.com/jaredhanson/) + +General-purpose OAuth 2.0 authentication strategy for [Passport](http://passportjs.org/). + +This module lets you authenticate using OAuth 2.0 in your Node.js applications. +By plugging into Passport, OAuth 2.0 authentication can be easily and +unobtrusively integrated into any application or framework that supports +[Connect](http://www.senchalabs.org/connect/)-style middleware, including +[Express](http://expressjs.com/). + +Note that this strategy provides generic OAuth 2.0 support. In many cases, a +provider-specific strategy can be used instead, which cuts down on unnecessary +configuration, and accommodates any provider-specific quirks. See the +[list](https://github.com/jaredhanson/passport/wiki/Strategies) for supported +providers. + +Developers who need to implement authentication against an OAuth 2.0 provider +that is not already supported are encouraged to sub-class this strategy. If you +choose to open source the new provider-specific strategy, please add it to the +list so other people can find it. + +## Install + + $ npm install passport-oauth2 + +## Usage + +#### Configure Strategy + +The OAuth 2.0 authentication strategy authenticates users using a third-party +account and OAuth 2.0 tokens. The provider's OAuth 2.0 endpoints, as well as +the client identifer and secret, are specified as options. The strategy +requires a `verify` callback, which receives an access token and profile, +and calls `done` providing a user. + + passport.use(new OAuth2Strategy({ + authorizationURL: 'https://www.example.com/oauth2/authorize', + tokenURL: 'https://www.example.com/oauth2/token', + clientID: EXAMPLE_CLIENT_ID, + clientSecret: EXAMPLE_CLIENT_SECRET, + callbackURL: "http://localhost:3000/auth/example/callback" + }, + function(accessToken, refreshToken, profile, done) { + User.findOrCreate({ exampleId: profile.id }, function (err, user) { + return done(err, user); + }); + } + )); + +#### Authenticate Requests + +Use `passport.authenticate()`, specifying the `'oauth2'` strategy, to +authenticate requests. + +For example, as route middleware in an [Express](http://expressjs.com/) +application: + + app.get('/auth/example', + passport.authenticate('oauth2')); + + app.get('/auth/example/callback', + passport.authenticate('oauth2', { failureRedirect: '/login' }), + function(req, res) { + // Successful authentication, redirect home. + res.redirect('/'); + }); + +## Related Modules + +- [passport-oauth1](https://github.com/jaredhanson/passport-oauth1) — OAuth 1.0 authentication strategy +- [passport-http-bearer](https://github.com/jaredhanson/passport-http-bearer) — Bearer token authentication strategy for APIs +- [OAuth2orize](https://github.com/jaredhanson/oauth2orize) — OAuth 2.0 authorization server toolkit + +## Tests + + $ npm install + $ npm test + +## Credits + + - [Jared Hanson](http://github.com/jaredhanson) + +## License + +[The MIT License](http://opensource.org/licenses/MIT) + +Copyright (c) 2011-2014 Jared Hanson <[http://jaredhanson.net/](http://jaredhanson.net/)> diff --git a/node_modules/passport-oauth2/lib/errors/authorizationerror.js b/node_modules/passport-oauth2/lib/errors/authorizationerror.js new file mode 100644 index 0000000..be36934 --- /dev/null +++ b/node_modules/passport-oauth2/lib/errors/authorizationerror.js @@ -0,0 +1,44 @@ +/** + * `AuthorizationError` error. + * + * AuthorizationError represents an error in response to an authorization + * request. For details, refer to RFC 6749, section 4.1.2.1. + * + * References: + * - [The OAuth 2.0 Authorization Framework](http://tools.ietf.org/html/rfc6749) + * + * @constructor + * @param {String} [message] + * @param {String} [code] + * @param {String} [uri] + * @param {Number} [status] + * @api public + */ +function AuthorizationError(message, code, uri, status) { + if (!status) { + switch (code) { + case 'access_denied': status = 403; break; + case 'server_error': status = 502; break; + case 'temporarily_unavailable': status = 503; break; + } + } + + Error.call(this); + Error.captureStackTrace(this, arguments.callee); + this.name = 'AuthorizationError'; + this.message = message; + this.code = code || 'server_error'; + this.uri = uri; + this.status = status || 500; +} + +/** + * Inherit from `Error`. + */ +AuthorizationError.prototype.__proto__ = Error.prototype; + + +/** + * Expose `AuthorizationError`. + */ +module.exports = AuthorizationError; diff --git a/node_modules/passport-oauth2/lib/errors/internaloautherror.js b/node_modules/passport-oauth2/lib/errors/internaloautherror.js new file mode 100644 index 0000000..5c197ea --- /dev/null +++ b/node_modules/passport-oauth2/lib/errors/internaloautherror.js @@ -0,0 +1,49 @@ +/** + * `InternalOAuthError` error. + * + * InternalOAuthError wraps errors generated by node-oauth. By wrapping these + * objects, error messages can be formatted in a manner that aids in debugging + * OAuth issues. + * + * @constructor + * @param {String} [message] + * @param {Object|Error} [err] + * @api public + */ +function InternalOAuthError(message, err) { + Error.call(this); + Error.captureStackTrace(this, arguments.callee); + this.name = 'InternalOAuthError'; + this.message = message; + this.oauthError = err; +} + +/** + * Inherit from `Error`. + */ +InternalOAuthError.prototype.__proto__ = Error.prototype; + +/** + * Returns a string representing the error. + * + * @return {String} + * @api public + */ +InternalOAuthError.prototype.toString = function() { + var m = this.name; + if (this.message) { m += ': ' + this.message; } + if (this.oauthError) { + if (this.oauthError instanceof Error) { + m = this.oauthError.toString(); + } else if (this.oauthError.statusCode && this.oauthError.data) { + m += ' (status: ' + this.oauthError.statusCode + ' data: ' + this.oauthError.data + ')'; + } + } + return m; +}; + + +/** + * Expose `InternalOAuthError`. + */ +module.exports = InternalOAuthError; diff --git a/node_modules/passport-oauth2/lib/errors/tokenerror.js b/node_modules/passport-oauth2/lib/errors/tokenerror.js new file mode 100644 index 0000000..eba4104 --- /dev/null +++ b/node_modules/passport-oauth2/lib/errors/tokenerror.js @@ -0,0 +1,36 @@ +/** + * `TokenError` error. + * + * TokenError represents an error received from a token endpoint. For details, + * refer to RFC 6749, section 5.2. + * + * References: + * - [The OAuth 2.0 Authorization Framework](http://tools.ietf.org/html/rfc6749) + * + * @constructor + * @param {String} [message] + * @param {String} [code] + * @param {String} [uri] + * @param {Number} [status] + * @api public + */ +function TokenError(message, code, uri, status) { + Error.call(this); + Error.captureStackTrace(this, arguments.callee); + this.name = 'TokenError'; + this.message = message; + this.code = code || 'invalid_request'; + this.uri = uri; + this.status = status || 500; +} + +/** + * Inherit from `Error`. + */ +TokenError.prototype.__proto__ = Error.prototype; + + +/** + * Expose `TokenError`. + */ +module.exports = TokenError; diff --git a/node_modules/passport-oauth2/lib/index.js b/node_modules/passport-oauth2/lib/index.js new file mode 100644 index 0000000..8a2fc05 --- /dev/null +++ b/node_modules/passport-oauth2/lib/index.js @@ -0,0 +1,25 @@ +/** + * Module dependencies. + */ +var Strategy = require('./strategy') + , AuthorizationError = require('./errors/authorizationerror') + , TokenError = require('./errors/tokenerror') + , InternalOAuthError = require('./errors/internaloautherror'); + + +/** + * Expose `Strategy` directly from package. + */ +exports = module.exports = Strategy; + +/** + * Export constructors. + */ +exports.Strategy = Strategy; + +/** + * Export errors. + */ +exports.AuthorizationError = AuthorizationError; +exports.TokenError = TokenError; +exports.InternalOAuthError = InternalOAuthError; diff --git a/node_modules/passport-oauth2/lib/strategy.js b/node_modules/passport-oauth2/lib/strategy.js new file mode 100644 index 0000000..3d60608 --- /dev/null +++ b/node_modules/passport-oauth2/lib/strategy.js @@ -0,0 +1,356 @@ +/** + * Module dependencies. + */ +var passport = require('passport-strategy') + , url = require('url') + , uid = require('uid2') + , util = require('util') + , utils = require('./utils') + , OAuth2 = require('oauth').OAuth2 + , AuthorizationError = require('./errors/authorizationerror') + , TokenError = require('./errors/tokenerror') + , InternalOAuthError = require('./errors/internaloautherror'); + + +/** + * Creates an instance of `OAuth2Strategy`. + * + * The OAuth 2.0 authentication strategy authenticates requests using the OAuth + * 2.0 framework. + * + * OAuth 2.0 provides a facility for delegated authentication, whereby users can + * authenticate using a third-party service such as Facebook. Delegating in + * this manner involves a sequence of events, including redirecting the user to + * the third-party service for authorization. Once authorization has been + * granted, the user is redirected back to the application and an authorization + * code can be used to obtain credentials. + * + * Applications must supply a `verify` callback, for which the function + * signature is: + * + * function(accessToken, refreshToken, profile, done) { ... } + * + * The verify callback is responsible for finding or creating the user, and + * invoking `done` with the following arguments: + * + * done(err, user, info); + * + * `user` should be set to `false` to indicate an authentication failure. + * Additional `info` can optionally be passed as a third argument, typically + * used to display informational messages. If an exception occured, `err` + * should be set. + * + * Options: + * + * - `authorizationURL` URL used to obtain an authorization grant + * - `tokenURL` URL used to obtain an access token + * - `clientID` identifies client to service provider + * - `clientSecret` secret used to establish ownership of the client identifer + * - `callbackURL` URL to which the service provider will redirect the user after obtaining authorization + * - `passReqToCallback` when `true`, `req` is the first argument to the verify callback (default: `false`) + * + * Examples: + * + * passport.use(new OAuth2Strategy({ + * authorizationURL: 'https://www.example.com/oauth2/authorize', + * tokenURL: 'https://www.example.com/oauth2/token', + * clientID: '123-456-789', + * clientSecret: 'shhh-its-a-secret' + * callbackURL: 'https://www.example.net/auth/example/callback' + * }, + * function(accessToken, refreshToken, profile, done) { + * User.findOrCreate(..., function (err, user) { + * done(err, user); + * }); + * } + * )); + * + * @constructor + * @param {Object} options + * @param {Function} verify + * @api public + */ +function OAuth2Strategy(options, verify) { + if (typeof options == 'function') { + verify = options; + options = undefined; + } + options = options || {}; + + if (!verify) { throw new TypeError('OAuth2Strategy requires a verify callback'); } + if (!options.authorizationURL) { throw new TypeError('OAuth2Strategy requires a authorizationURL option'); } + if (!options.tokenURL) { throw new TypeError('OAuth2Strategy requires a tokenURL option'); } + if (!options.clientID) { throw new TypeError('OAuth2Strategy requires a clientID option'); } + if (!options.clientSecret) { throw new TypeError('OAuth2Strategy requires a clientSecret option'); } + + passport.Strategy.call(this); + this.name = 'oauth2'; + this._verify = verify; + + // NOTE: The _oauth2 property is considered "protected". Subclasses are + // allowed to use it when making protected resource requests to retrieve + // the user profile. + this._oauth2 = new OAuth2(options.clientID, options.clientSecret, + '', options.authorizationURL, options.tokenURL, options.customHeaders); + + this._callbackURL = options.callbackURL; + this._scope = options.scope; + this._scopeSeparator = options.scopeSeparator || ' '; + this._state = options.state; + this._key = options.sessionKey || ('oauth2:' + url.parse(options.authorizationURL).hostname); + this._trustProxy = options.proxy; + this._passReqToCallback = options.passReqToCallback; + this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile; +} + +/** + * Inherit from `passport.Strategy`. + */ +util.inherits(OAuth2Strategy, passport.Strategy); + + +/** + * Authenticate request by delegating to a service provider using OAuth 2.0. + * + * @param {Object} req + * @api protected + */ +OAuth2Strategy.prototype.authenticate = function(req, options) { + options = options || {}; + var self = this; + + if (req.query && req.query.error) { + if (req.query.error == 'access_denied') { + return this.fail({ message: req.query.error_description }); + } else { + return this.error(new AuthorizationError(req.query.error_description, req.query.error, req.query.error_uri)); + } + } + + var callbackURL = options.callbackURL || this._callbackURL; + if (callbackURL) { + var parsed = url.parse(callbackURL); + if (!parsed.protocol) { + // The callback URL is relative, resolve a fully qualified URL from the + // URL of the originating request. + callbackURL = url.resolve(utils.originalURL(req, { proxy: this._trustProxy }), callbackURL); + } + } + + if (req.query && req.query.code) { + var code = req.query.code; + + if (this._state) { + if (!req.session) { return this.error(new Error('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?')); } + + var key = this._key; + if (!req.session[key]) { + return this.fail({ message: 'Unable to verify authorization request state.' }, 403); + } + var state = req.session[key].state; + if (!state) { + return this.fail({ message: 'Unable to verify authorization request state.' }, 403); + } + + delete req.session[key].state; + if (Object.keys(req.session[key]).length === 0) { + delete req.session[key]; + } + + if (state !== req.query.state) { + return this.fail({ message: 'Invalid authorization request state.' }, 403); + } + } + + var params = this.tokenParams(options); + params.grant_type = 'authorization_code'; + params.redirect_uri = callbackURL; + + this._oauth2.getOAuthAccessToken(code, params, + function(err, accessToken, refreshToken, params) { + if (err) { return self.error(self._createOAuthError('Failed to obtain access token', err)); } + + self._loadUserProfile(accessToken, function(err, profile) { + if (err) { return self.error(err); } + + function verified(err, user, info) { + if (err) { return self.error(err); } + if (!user) { return self.fail(info); } + self.success(user, info); + } + + try { + if (self._passReqToCallback) { + var arity = self._verify.length; + if (arity == 6) { + self._verify(req, accessToken, refreshToken, params, profile, verified); + } else { // arity == 5 + self._verify(req, accessToken, refreshToken, profile, verified); + } + } else { + var arity = self._verify.length; + if (arity == 5) { + self._verify(accessToken, refreshToken, params, profile, verified); + } else { // arity == 4 + self._verify(accessToken, refreshToken, profile, verified); + } + } + } catch (ex) { + return self.error(ex); + } + }); + } + ); + } else { + var params = this.authorizationParams(options); + params.response_type = 'code'; + params.redirect_uri = callbackURL; + var scope = options.scope || this._scope; + if (scope) { + if (Array.isArray(scope)) { scope = scope.join(this._scopeSeparator); } + params.scope = scope; + } + var state = options.state; + if (state) { + params.state = state; + } else if (this._state) { + if (!req.session) { return this.error(new Error('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?')); } + + var key = this._key; + state = uid(24); + if (!req.session[key]) { req.session[key] = {}; } + req.session[key].state = state; + params.state = state; + } + + var location = this._oauth2.getAuthorizeUrl(params); + this.redirect(location); + } +}; + +/** + * Retrieve user profile from service provider. + * + * OAuth 2.0-based authentication strategies can overrride this function in + * order to load the user's profile from the service provider. This assists + * applications (and users of those applications) in the initial registration + * process by automatically submitting required information. + * + * @param {String} accessToken + * @param {Function} done + * @api protected + */ +OAuth2Strategy.prototype.userProfile = function(accessToken, done) { + return done(null, {}); +}; + +/** + * Return extra parameters to be included in the authorization request. + * + * Some OAuth 2.0 providers allow additional, non-standard parameters to be + * included when requesting authorization. Since these parameters are not + * standardized by the OAuth 2.0 specification, OAuth 2.0-based authentication + * strategies can overrride this function in order to populate these parameters + * as required by the provider. + * + * @param {Object} options + * @return {Object} + * @api protected + */ +OAuth2Strategy.prototype.authorizationParams = function(options) { + return {}; +}; + +/** + * Return extra parameters to be included in the token request. + * + * Some OAuth 2.0 providers allow additional, non-standard parameters to be + * included when requesting an access token. Since these parameters are not + * standardized by the OAuth 2.0 specification, OAuth 2.0-based authentication + * strategies can overrride this function in order to populate these parameters + * as required by the provider. + * + * @return {Object} + * @api protected + */ +OAuth2Strategy.prototype.tokenParams = function(options) { + return {}; +}; + +/** + * Parse error response from OAuth 2.0 endpoint. + * + * OAuth 2.0-based authentication strategies can overrride this function in + * order to parse error responses received from the token endpoint, allowing the + * most informative message to be displayed. + * + * If this function is not overridden, the body will be parsed in accordance + * with RFC 6749, section 5.2. + * + * @param {String} body + * @param {Number} status + * @return {Error} + * @api protected + */ +OAuth2Strategy.prototype.parseErrorResponse = function(body, status) { + var json = JSON.parse(body); + if (json.error) { + return new TokenError(json.error_description, json.error, json.error_uri); + } + return null; +}; + +/** + * Load user profile, contingent upon options. + * + * @param {String} accessToken + * @param {Function} done + * @api private + */ +OAuth2Strategy.prototype._loadUserProfile = function(accessToken, done) { + var self = this; + + function loadIt() { + return self.userProfile(accessToken, done); + } + function skipIt() { + return done(null); + } + + if (typeof this._skipUserProfile == 'function' && this._skipUserProfile.length > 1) { + // async + this._skipUserProfile(accessToken, function(err, skip) { + if (err) { return done(err); } + if (!skip) { return loadIt(); } + return skipIt(); + }); + } else { + var skip = (typeof this._skipUserProfile == 'function') ? this._skipUserProfile() : this._skipUserProfile; + if (!skip) { return loadIt(); } + return skipIt(); + } +}; + +/** + * Create an OAuth error. + * + * @param {String} message + * @param {Object|Error} err + * @api private + */ +OAuth2Strategy.prototype._createOAuthError = function(message, err) { + var e; + if (err.statusCode && err.data) { + try { + e = this.parseErrorResponse(err.data, err.statusCode); + } catch (_) {} + } + if (!e) { e = new InternalOAuthError(message, err); } + return e; +}; + + +/** + * Expose `OAuth2Strategy`. + */ +module.exports = OAuth2Strategy; diff --git a/node_modules/passport-oauth2/lib/utils.js b/node_modules/passport-oauth2/lib/utils.js new file mode 100644 index 0000000..493300a --- /dev/null +++ b/node_modules/passport-oauth2/lib/utils.js @@ -0,0 +1,30 @@ +/** + * Reconstructs the original URL of the request. + * + * This function builds a URL that corresponds the original URL requested by the + * client, including the protocol (http or https) and host. + * + * If the request passed through any proxies that terminate SSL, the + * `X-Forwarded-Proto` header is used to detect if the request was encrypted to + * the proxy, assuming that the proxy has been flagged as trusted. + * + * @param {http.IncomingMessage} req + * @param {Object} [options] + * @return {String} + * @api private + */ +exports.originalURL = function(req, options) { + options = options || {}; + var app = req.app; + if (app && app.get && app.get('trust proxy')) { + options.proxy = true; + } + var trustProxy = options.proxy; + + var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase() + , tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0]) + , host = (trustProxy && req.headers['x-forwarded-host']) || req.headers.host + , protocol = tls ? 'https' : 'http' + , path = req.url || ''; + return protocol + '://' + host + path; +}; diff --git a/node_modules/passport-oauth2/package.json b/node_modules/passport-oauth2/package.json new file mode 100644 index 0000000..f200c1e --- /dev/null +++ b/node_modules/passport-oauth2/package.json @@ -0,0 +1,50 @@ +{ + "name": "passport-oauth2", + "version": "1.1.2", + "description": "OAuth 2.0 authentication strategy for Passport.", + "keywords": [ + "passport", + "auth", + "authn", + "authentication", + "authz", + "authorization", + "oauth", + "oauth2" + ], + "author": { + "name": "Jared Hanson", + "email": "jaredhanson@gmail.com", + "url": "http://www.jaredhanson.net/" + }, + "repository": { + "type": "git", + "url": "git://github.com/jaredhanson/passport-oauth2.git" + }, + "bugs": { + "url": "http://github.com/jaredhanson/passport-oauth2/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + } + ], + "main": "./lib", + "dependencies": { + "passport-strategy": "1.x.x", + "oauth": "0.9.x", + "uid2": "0.0.x" + }, + "devDependencies": { + "mocha": "1.x.x", + "chai": "1.x.x", + "chai-passport-strategy": "0.2.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "scripts": { + "test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js" + } +} diff --git a/node_modules/passport-strategy/.jshintrc b/node_modules/passport-strategy/.jshintrc new file mode 100644 index 0000000..a07354b --- /dev/null +++ b/node_modules/passport-strategy/.jshintrc @@ -0,0 +1,20 @@ +{ + "node": true, + + "bitwise": true, + "camelcase": true, + "curly": true, + "forin": true, + "immed": true, + "latedef": true, + "newcap": true, + "noarg": true, + "noempty": true, + "nonew": true, + "quotmark": "single", + "undef": true, + "unused": true, + "trailing": true, + + "laxcomma": true +} diff --git a/node_modules/passport-strategy/.travis.yml b/node_modules/passport-strategy/.travis.yml new file mode 100644 index 0000000..45f8624 --- /dev/null +++ b/node_modules/passport-strategy/.travis.yml @@ -0,0 +1,15 @@ +language: "node_js" +node_js: + - "0.4" + - "0.6" + - "0.8" + - "0.10" + +before_install: + - "npm install istanbul -g" + - "npm install coveralls -g" + +script: "make ci-travis" + +after_success: + - "make submit-coverage-to-coveralls" diff --git a/node_modules/passport-strategy/LICENSE b/node_modules/passport-strategy/LICENSE new file mode 100644 index 0000000..ec885b5 --- /dev/null +++ b/node_modules/passport-strategy/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2011-2013 Jared Hanson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/passport-strategy/README.md b/node_modules/passport-strategy/README.md new file mode 100644 index 0000000..71de07f --- /dev/null +++ b/node_modules/passport-strategy/README.md @@ -0,0 +1,61 @@ +# passport-strategy + +[![Build](https://travis-ci.org/jaredhanson/passport-strategy.png)](http://travis-ci.org/jaredhanson/passport-strategy) +[![Coverage](https://coveralls.io/repos/jaredhanson/passport-strategy/badge.png)](https://coveralls.io/r/jaredhanson/passport-strategy) +[![Dependencies](https://david-dm.org/jaredhanson/passport-strategy.png)](http://david-dm.org/jaredhanson/passport-strategy) + + +An abstract class implementing [Passport](http://passportjs.org/)'s strategy +API. + +## Install + + $ npm install passport-strategy + +## Usage + +This module exports an abstract `Strategy` class that is intended to be +subclassed when implementing concrete authentication strategies. Once +implemented, such strategies can be used by applications that utilize Passport +middleware for authentication. + +#### Subclass Strategy + +Create a new `CustomStrategy` constructor which inherits from `Strategy`: + +```javascript +var util = require('util') + , Strategy = require('passport-strategy'); + +function CustomStrategy(...) { + Strategy.call(this); +} + +util.inherits(CustomStrategy, Strategy); +``` + +#### Implement Authentication + +Implement `autheticate()`, performing the necessary operations required by the +authentication scheme or protocol being implemented. + +```javascript +CustomStrategy.prototype.authenticate = function(req, options) { + // TODO: authenticate request +} +``` + +## Tests + + $ npm install + $ npm test + +## Credits + + - [Jared Hanson](http://github.com/jaredhanson) + +## License + +[The MIT License](http://opensource.org/licenses/MIT) + +Copyright (c) 2011-2013 Jared Hanson <[http://jaredhanson.net/](http://jaredhanson.net/)> diff --git a/node_modules/passport-strategy/lib/index.js b/node_modules/passport-strategy/lib/index.js new file mode 100644 index 0000000..a6fdfa7 --- /dev/null +++ b/node_modules/passport-strategy/lib/index.js @@ -0,0 +1,15 @@ +/** + * Module dependencies. + */ +var Strategy = require('./strategy'); + + +/** + * Expose `Strategy` directly from package. + */ +exports = module.exports = Strategy; + +/** + * Export constructors. + */ +exports.Strategy = Strategy; diff --git a/node_modules/passport-strategy/lib/strategy.js b/node_modules/passport-strategy/lib/strategy.js new file mode 100644 index 0000000..5a7eb28 --- /dev/null +++ b/node_modules/passport-strategy/lib/strategy.js @@ -0,0 +1,28 @@ +/** + * Creates an instance of `Strategy`. + * + * @constructor + * @api public + */ +function Strategy() { +} + +/** + * Authenticate request. + * + * This function must be overridden by subclasses. In abstract form, it always + * throws an exception. + * + * @param {Object} req The request to authenticate. + * @param {Object} [options] Strategy-specific options. + * @api public + */ +Strategy.prototype.authenticate = function(req, options) { + throw new Error('Strategy#authenticate must be overridden by subclass'); +}; + + +/** + * Expose `Strategy`. + */ +module.exports = Strategy; diff --git a/node_modules/passport-strategy/package.json b/node_modules/passport-strategy/package.json new file mode 100644 index 0000000..666d265 --- /dev/null +++ b/node_modules/passport-strategy/package.json @@ -0,0 +1,50 @@ +{ + "name": "passport-strategy", + "version": "1.0.0", + "description": "An abstract class implementing Passport's strategy API.", + "keywords": [ + "passport", + "strategy" + ], + "repository": { + "type": "git", + "url": "git://github.com/jaredhanson/passport-strategy.git" + }, + "bugs": { + "url": "http://github.com/jaredhanson/passport-strategy/issues" + }, + "author": { + "name": "Jared Hanson", + "email": "jaredhanson@gmail.com", + "url": "http://www.jaredhanson.net/" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + } + ], + "main": "./lib", + "dependencies": { + }, + "devDependencies": { + "mocha": "1.x.x", + "chai": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "scripts": { + "test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js" + }, + "testling": { + "browsers": [ + "chrome/latest" + ], + "harness" : "mocha", + "files": [ + "test/bootstrap/testling.js", + "test/*.test.js" + ] + } +} diff --git a/node_modules/pkginfo/.npmignore b/node_modules/pkginfo/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/node_modules/pkginfo/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/node_modules/pkginfo/LICENSE b/node_modules/pkginfo/LICENSE new file mode 100644 index 0000000..ed4a4e7 --- /dev/null +++ b/node_modules/pkginfo/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Charlie Robbins. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/pkginfo/README.md b/node_modules/pkginfo/README.md new file mode 100644 index 0000000..7a363bf --- /dev/null +++ b/node_modules/pkginfo/README.md @@ -0,0 +1,86 @@ +# node-pkginfo + +An easy way to expose properties on a module from a package.json + +## Installation + +### Installing npm (node package manager) +``` + curl http://npmjs.org/install.sh | sh +``` + +### Installing pkginfo +``` + [sudo] npm install pkginfo +``` + +## Motivation +How often when writing node.js modules have you written the following line(s) of code? + +* Hard code your version string into your code + +``` js + exports.version = '0.1.0'; +``` + +* Programmatically expose the version from the package.json + +``` js + exports.version = require('/path/to/package.json').version; +``` + +In other words, how often have you wanted to expose basic information from your package.json onto your module programmatically? **WELL NOW YOU CAN!** + +## Usage + +Using `pkginfo` is idiot-proof, just require and invoke it. + +``` js + var pkginfo = require('pkginfo')(module); + + console.dir(module.exports); +``` + +By invoking the `pkginfo` module all of the properties in your `package.json` file will be automatically exposed on the callee module (i.e. the parent module of `pkginfo`). + +Here's a sample of the output: + +``` + { name: 'simple-app', + description: 'A test fixture for pkginfo', + version: '0.1.0', + author: 'Charlie Robbins ', + keywords: [ 'test', 'fixture' ], + main: './index.js', + scripts: { test: 'vows test/*-test.js --spec' }, + engines: { node: '>= 0.4.0' } } +``` + +### Expose specific properties +If you don't want to expose **all** properties on from your `package.json` on your module then simple pass those properties to the `pkginfo` function: + +``` js + var pkginfo = require('pkginfo')(module, 'version', 'author'); + + console.dir(module.exports); +``` + +``` + { version: '0.1.0', + author: 'Charlie Robbins ' } +``` + +If you're looking for further usage see the [examples][0] included in this repository. + +## Run Tests +Tests are written in [vows][1] and give complete coverage of all APIs. + +``` + vows test/*-test.js --spec +``` + +[0]: https://github.com/indexzero/node-pkginfo/tree/master/examples +[1]: http://vowsjs.org + +#### Author: [Charlie Robbins](http://nodejitsu.com) +#### License: MIT diff --git a/node_modules/pkginfo/docs/docco.css b/node_modules/pkginfo/docs/docco.css new file mode 100644 index 0000000..bd54134 --- /dev/null +++ b/node_modules/pkginfo/docs/docco.css @@ -0,0 +1,194 @@ +/*--------------------- Layout and Typography ----------------------------*/ +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + color: #252519; + margin: 0; padding: 0; +} +a { + color: #261a3b; +} + a:visited { + color: #261a3b; + } +p { + margin: 0 0 15px 0; +} +h4, h5, h6 { + color: #333; + margin: 6px 0 6px 0; + font-size: 13px; +} + h2, h3 { + margin-bottom: 0; + color: #000; + } + h1 { + margin-top: 40px; + margin-bottom: 15px; + color: #000; + } +#container { + position: relative; +} +#background { + position: fixed; + top: 0; left: 525px; right: 0; bottom: 0; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + z-index: -1; +} +#jump_to, #jump_page { + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 10px Arial; + text-transform: uppercase; + cursor: pointer; + text-align: right; +} +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 5px 10px; +} + #jump_wrapper { + padding: 0; + display: none; + } + #jump_to:hover #jump_wrapper { + display: block; + } + #jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; + } + #jump_page .source { + display: block; + padding: 5px 10px; + text-decoration: none; + border-top: 1px solid #eee; + } + #jump_page .source:hover { + background: #f5f5ff; + } + #jump_page .source:first-child { + } +table td { + border: 0; + outline: 0; +} + td.docs, th.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; + } + .docs pre { + margin: 15px 0 15px; + padding-left: 15px; + } + .docs p tt, .docs p code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; + } + .pilwrap { + position: relative; + } + .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + } + td.docs:hover .pilcrow { + opacity: 1; + } + td.code, th.code { + padding: 14px 15px 16px 25px; + width: 100%; + vertical-align: top; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + } + pre, tt, code { + font-size: 12px; line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; + } + + +/*---------------------- Syntax Highlighting -----------------------------*/ +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +body .hll { background-color: #ffffcc } +body .c { color: #408080; font-style: italic } /* Comment */ +body .err { border: 1px solid #FF0000 } /* Error */ +body .k { color: #954121 } /* Keyword */ +body .o { color: #666666 } /* Operator */ +body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +body .cp { color: #BC7A00 } /* Comment.Preproc */ +body .c1 { color: #408080; font-style: italic } /* Comment.Single */ +body .cs { color: #408080; font-style: italic } /* Comment.Special */ +body .gd { color: #A00000 } /* Generic.Deleted */ +body .ge { font-style: italic } /* Generic.Emph */ +body .gr { color: #FF0000 } /* Generic.Error */ +body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +body .gi { color: #00A000 } /* Generic.Inserted */ +body .go { color: #808080 } /* Generic.Output */ +body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +body .gs { font-weight: bold } /* Generic.Strong */ +body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +body .gt { color: #0040D0 } /* Generic.Traceback */ +body .kc { color: #954121 } /* Keyword.Constant */ +body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ +body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ +body .kp { color: #954121 } /* Keyword.Pseudo */ +body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ +body .kt { color: #B00040 } /* Keyword.Type */ +body .m { color: #666666 } /* Literal.Number */ +body .s { color: #219161 } /* Literal.String */ +body .na { color: #7D9029 } /* Name.Attribute */ +body .nb { color: #954121 } /* Name.Builtin */ +body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +body .no { color: #880000 } /* Name.Constant */ +body .nd { color: #AA22FF } /* Name.Decorator */ +body .ni { color: #999999; font-weight: bold } /* Name.Entity */ +body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +body .nf { color: #0000FF } /* Name.Function */ +body .nl { color: #A0A000 } /* Name.Label */ +body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +body .nt { color: #954121; font-weight: bold } /* Name.Tag */ +body .nv { color: #19469D } /* Name.Variable */ +body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +body .w { color: #bbbbbb } /* Text.Whitespace */ +body .mf { color: #666666 } /* Literal.Number.Float */ +body .mh { color: #666666 } /* Literal.Number.Hex */ +body .mi { color: #666666 } /* Literal.Number.Integer */ +body .mo { color: #666666 } /* Literal.Number.Oct */ +body .sb { color: #219161 } /* Literal.String.Backtick */ +body .sc { color: #219161 } /* Literal.String.Char */ +body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ +body .s2 { color: #219161 } /* Literal.String.Double */ +body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +body .sh { color: #219161 } /* Literal.String.Heredoc */ +body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +body .sx { color: #954121 } /* Literal.String.Other */ +body .sr { color: #BB6688 } /* Literal.String.Regex */ +body .s1 { color: #219161 } /* Literal.String.Single */ +body .ss { color: #19469D } /* Literal.String.Symbol */ +body .bp { color: #954121 } /* Name.Builtin.Pseudo */ +body .vc { color: #19469D } /* Name.Variable.Class */ +body .vg { color: #19469D } /* Name.Variable.Global */ +body .vi { color: #19469D } /* Name.Variable.Instance */ +body .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/node_modules/pkginfo/docs/pkginfo.html b/node_modules/pkginfo/docs/pkginfo.html new file mode 100644 index 0000000..bf615fa --- /dev/null +++ b/node_modules/pkginfo/docs/pkginfo.html @@ -0,0 +1,101 @@ + pkginfo.js

pkginfo.js

/*
+ * pkginfo.js: Top-level include for the pkginfo module
+ *
+ * (C) 2011, Charlie Robbins
+ *
+ */
+ 
+var fs = require('fs'),
+    path = require('path');

function pkginfo ([options, 'property', 'property' ..])

+ +

@pmodule {Module} Parent module to read from.

+ +

@options {Object|Array|string} Optional Options used when exposing properties.

+ +

@arguments {string...} Optional Specified properties to expose.

+ +

Exposes properties from the package.json file for the parent module on +it's exports. Valid usage:

+ +

require('pkginfo')()

+ +

require('pkginfo')('version', 'author');

+ +

require('pkginfo')(['version', 'author']);

+ +

require('pkginfo')({ include: ['version', 'author'] });

var pkginfo = module.exports = function (pmodule, options) {
+  var args = [].slice.call(arguments, 2).filter(function (arg) {
+    return typeof arg === 'string';
+  });
+  

Parse variable arguments

  if (Array.isArray(options)) {

If the options passed in is an Array assume that +it is the Array of properties to expose from the +on the package.json file on the parent module.

    options = { include: options };
+  }
+  else if (typeof options === 'string') {

Otherwise if the first argument is a string, then +assume that it is the first property to expose from +the package.json file on the parent module.

    options = { include: [options] };
+  }
+  

Setup default options

  options = options || { include: [] };
+  
+  if (args.length > 0) {

If additional string arguments have been passed in +then add them to the properties to expose on the +parent module.

    options.include = options.include.concat(args);
+  }
+  
+  var pkg = pkginfo.read(pmodule, options.dir).package;
+  Object.keys(pkg).forEach(function (key) {
+    if (options.include.length > 0 && !~options.include.indexOf(key)) {
+      return;
+    }
+    
+    if (!pmodule.exports[key]) {
+      pmodule.exports[key] = pkg[key];
+    }
+  });
+  
+  return pkginfo;
+};

function find (dir)

+ +

@pmodule {Module} Parent module to read from.

+ +

@dir {string} Optional Directory to start search from.

+ +

Searches up the directory tree from dir until it finds a directory +which contains a package.json file.

pkginfo.find = function (pmodule, dir) {
+  dir = dir || pmodule.filename;
+  dir = path.dirname(dir); 
+  
+  var files = fs.readdirSync(dir);
+  
+  if (~files.indexOf('package.json')) {
+    return path.join(dir, 'package.json');
+  }
+  
+  if (dir === '/') {
+    throw new Error('Could not find package.json up from: ' + dir);
+  }
+  
+  return pkginfo.find(dir);
+};

function read (pmodule, dir)

+ +

@pmodule {Module} Parent module to read from.

+ +

@dir {string} Optional Directory to start search from.

+ +

Searches up the directory tree from dir until it finds a directory +which contains a package.json file and returns the package information.

pkginfo.read = function (pmodule, dir) { 
+  dir = pkginfo.find(pmodule, dir);
+  
+  var data = fs.readFileSync(dir).toString();
+      
+  return {
+    dir: dir, 
+    package: JSON.parse(data)
+  };
+};

Call pkginfo on this module and expose version.

pkginfo(module, {
+  dir: __dirname,
+  include: ['version'],
+  target: pkginfo
+});
+
+
\ No newline at end of file diff --git a/node_modules/pkginfo/examples/all-properties.js b/node_modules/pkginfo/examples/all-properties.js new file mode 100644 index 0000000..fd1d831 --- /dev/null +++ b/node_modules/pkginfo/examples/all-properties.js @@ -0,0 +1,19 @@ +/* + * all-properties.js: Sample of including all properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/node_modules/pkginfo/examples/array-argument.js b/node_modules/pkginfo/examples/array-argument.js new file mode 100644 index 0000000..b1b6848 --- /dev/null +++ b/node_modules/pkginfo/examples/array-argument.js @@ -0,0 +1,20 @@ +/* + * array-argument.js: Sample of including specific properties from a package.json file + * using Array argument syntax. + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, ['version', 'author']); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/node_modules/pkginfo/examples/multiple-properties.js b/node_modules/pkginfo/examples/multiple-properties.js new file mode 100644 index 0000000..b4b5fd6 --- /dev/null +++ b/node_modules/pkginfo/examples/multiple-properties.js @@ -0,0 +1,19 @@ +/* + * multiple-properties.js: Sample of including multiple properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, 'version', 'author'); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/node_modules/pkginfo/examples/object-argument.js b/node_modules/pkginfo/examples/object-argument.js new file mode 100644 index 0000000..28420c8 --- /dev/null +++ b/node_modules/pkginfo/examples/object-argument.js @@ -0,0 +1,22 @@ +/* + * object-argument.js: Sample of including specific properties from a package.json file + * using Object argument syntax. + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, { + include: ['version', 'author'] + }); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/node_modules/pkginfo/examples/package.json b/node_modules/pkginfo/examples/package.json new file mode 100644 index 0000000..1f2f01c --- /dev/null +++ b/node_modules/pkginfo/examples/package.json @@ -0,0 +1,10 @@ +{ + "name": "simple-app", + "description": "A test fixture for pkginfo", + "version": "0.1.0", + "author": "Charlie Robbins ", + "keywords": ["test", "fixture"], + "main": "./index.js", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.4.0" } +} diff --git a/node_modules/pkginfo/examples/single-property.js b/node_modules/pkginfo/examples/single-property.js new file mode 100644 index 0000000..4f44561 --- /dev/null +++ b/node_modules/pkginfo/examples/single-property.js @@ -0,0 +1,19 @@ +/* + * single-property.js: Sample of including a single specific properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, 'version'); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/node_modules/pkginfo/examples/subdir/package.json b/node_modules/pkginfo/examples/subdir/package.json new file mode 100644 index 0000000..aa85410 --- /dev/null +++ b/node_modules/pkginfo/examples/subdir/package.json @@ -0,0 +1,11 @@ +{ + "name": "simple-app-subdir", + "description": "A test fixture for pkginfo", + "version": "0.1.0", + "author": "Charlie Robbins ", + "keywords": ["test", "fixture"], + "main": "./index.js", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.4.0" }, + "subdironly": "true" +} diff --git a/node_modules/pkginfo/examples/target-dir.js b/node_modules/pkginfo/examples/target-dir.js new file mode 100644 index 0000000..88770e6 --- /dev/null +++ b/node_modules/pkginfo/examples/target-dir.js @@ -0,0 +1,20 @@ +/* + * multiple-properties.js: Sample of including multiple properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + path = require('path'), + pkginfo = require('../lib/pkginfo')(module, { dir: path.resolve(__dirname, 'subdir' )}); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/node_modules/pkginfo/lib/pkginfo.js b/node_modules/pkginfo/lib/pkginfo.js new file mode 100644 index 0000000..c5dc020 --- /dev/null +++ b/node_modules/pkginfo/lib/pkginfo.js @@ -0,0 +1,136 @@ +/* + * pkginfo.js: Top-level include for the pkginfo module + * + * (C) 2011, Charlie Robbins + * + */ + +var fs = require('fs'), + path = require('path'); + +// +// ### function pkginfo ([options, 'property', 'property' ..]) +// #### @pmodule {Module} Parent module to read from. +// #### @options {Object|Array|string} **Optional** Options used when exposing properties. +// #### @arguments {string...} **Optional** Specified properties to expose. +// Exposes properties from the package.json file for the parent module on +// it's exports. Valid usage: +// +// `require('pkginfo')()` +// +// `require('pkginfo')('version', 'author');` +// +// `require('pkginfo')(['version', 'author']);` +// +// `require('pkginfo')({ include: ['version', 'author'] });` +// +var pkginfo = module.exports = function (pmodule, options) { + var args = [].slice.call(arguments, 2).filter(function (arg) { + return typeof arg === 'string'; + }); + + // + // **Parse variable arguments** + // + if (Array.isArray(options)) { + // + // If the options passed in is an Array assume that + // it is the Array of properties to expose from the + // on the package.json file on the parent module. + // + options = { include: options }; + } + else if (typeof options === 'string') { + // + // Otherwise if the first argument is a string, then + // assume that it is the first property to expose from + // the package.json file on the parent module. + // + options = { include: [options] }; + } + + // + // **Setup default options** + // + options = options || {}; + + // ensure that includes have been defined + options.include = options.include || []; + + if (args.length > 0) { + // + // If additional string arguments have been passed in + // then add them to the properties to expose on the + // parent module. + // + options.include = options.include.concat(args); + } + + var pkg = pkginfo.read(pmodule, options.dir).package; + Object.keys(pkg).forEach(function (key) { + if (options.include.length > 0 && !~options.include.indexOf(key)) { + return; + } + + if (!pmodule.exports[key]) { + pmodule.exports[key] = pkg[key]; + } + }); + + return pkginfo; +}; + +// +// ### function find (dir) +// #### @pmodule {Module} Parent module to read from. +// #### @dir {string} **Optional** Directory to start search from. +// Searches up the directory tree from `dir` until it finds a directory +// which contains a `package.json` file. +// +pkginfo.find = function (pmodule, dir) { + if (! dir) { + dir = path.dirname(pmodule.filename); + } + + var files = fs.readdirSync(dir); + + if (~files.indexOf('package.json')) { + return path.join(dir, 'package.json'); + } + + if (dir === '/') { + throw new Error('Could not find package.json up from: ' + dir); + } + else if (!dir || dir === '.') { + throw new Error('Cannot find package.json from unspecified directory'); + } + + return pkginfo.find(pmodule, path.dirname(dir)); +}; + +// +// ### function read (pmodule, dir) +// #### @pmodule {Module} Parent module to read from. +// #### @dir {string} **Optional** Directory to start search from. +// Searches up the directory tree from `dir` until it finds a directory +// which contains a `package.json` file and returns the package information. +// +pkginfo.read = function (pmodule, dir) { + dir = pkginfo.find(pmodule, dir); + + var data = fs.readFileSync(dir).toString(); + + return { + dir: dir, + package: JSON.parse(data) + }; +}; + +// +// Call `pkginfo` on this module and expose version. +// +pkginfo(module, { + dir: __dirname, + include: ['version'], + target: pkginfo +}); \ No newline at end of file diff --git a/node_modules/pkginfo/package.json b/node_modules/pkginfo/package.json new file mode 100644 index 0000000..908329c --- /dev/null +++ b/node_modules/pkginfo/package.json @@ -0,0 +1,21 @@ +{ + "name": "pkginfo", + "version": "0.3.1", + "license": "MIT", + "description": "An easy way to expose properties on a module from a package.json", + "author": "Charlie Robbins ", + "repository": { + "type": "git", + "url": "http://github.com/indexzero/node-pkginfo.git" + }, + "bugs": { + "url": "https://github.com/indexzero/node-pkginfo/issues" + }, + "keywords": ["info", "tools", "package.json"], + "devDependencies": { + "vows": "0.7.x" + }, + "main": "./lib/pkginfo.js", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.4.0" } +} diff --git a/node_modules/pkginfo/test/pkginfo-test.js b/node_modules/pkginfo/test/pkginfo-test.js new file mode 100644 index 0000000..a59f077 --- /dev/null +++ b/node_modules/pkginfo/test/pkginfo-test.js @@ -0,0 +1,83 @@ +/* + * pkginfo-test.js: Tests for the pkginfo module. + * + * (C) 2011, Charlie Robbins + * + */ + +var assert = require('assert'), + exec = require('child_process').exec, + fs = require('fs'), + path = require('path'), + vows = require('vows'), + pkginfo = require('../lib/pkginfo'); + +function assertProperties (source, target) { + assert.lengthOf(source, target.length + 1); + target.forEach(function (prop) { + assert.isTrue(!!~source.indexOf(prop)); + }); +} + +function compareWithExample(targetPath) { + var examplePaths = ['package.json']; + + if (targetPath) { + examplePaths.unshift(targetPath); + } + + return function(exposed) { + var pkg = fs.readFileSync(path.join.apply(null, [__dirname, '..', 'examples'].concat(examplePaths))).toString(), + keys = Object.keys(JSON.parse(pkg)); + + assertProperties(exposed, keys); + }; +} + +function testExposes (options) { + return { + topic: function () { + exec('node ' + path.join(__dirname, '..', 'examples', options.script), this.callback); + }, + "should expose that property correctly": function (err, stdout, stderr) { + assert.isNull(err); + + var exposed = stderr.match(/'(\w+)'/ig).map(function (p) { + return p.substring(1, p.length - 1); + }); + + return !options.assert + ? assertProperties(exposed, options.properties) + : options.assert(exposed); + } + } +} + +vows.describe('pkginfo').addBatch({ + "When using the pkginfo module": { + "and passed a single `string` argument": testExposes({ + script: 'single-property.js', + properties: ['version'] + }), + "and passed multiple `string` arguments": testExposes({ + script: 'multiple-properties.js', + properties: ['version', 'author'] + }), + "and passed an `object` argument": testExposes({ + script: 'object-argument.js', + properties: ['version', 'author'] + }), + "and passed an `array` argument": testExposes({ + script: 'array-argument.js', + properties: ['version', 'author'] + }), + "and read from a specified directory": testExposes({ + script: 'target-dir.js', + assert: compareWithExample('subdir') + }), + "and passed no arguments": testExposes({ + script: 'all-properties.js', + assert: compareWithExample() + }) + } +}).export(module); diff --git a/node_modules/uid2/HISTORY.md b/node_modules/uid2/HISTORY.md new file mode 100644 index 0000000..e1d9b3c --- /dev/null +++ b/node_modules/uid2/HISTORY.md @@ -0,0 +1,21 @@ +# 0.0.4 - August 24, 2021 + +- Add _README.md_ +- Add `repository` field to _package.json_ +- Add `license` field to _package.json_ +- Removed unused var, added param documentation + +# 0.0.3 - July 6, 2013 + +- Add MIT License +- Change string generation to not rely internally on base64 byte buffer encoding +- Change string generation to only use the 62 latin alphanumeric chars +- Switch from `crypto.randomBytes()` to `crypto.pseudoRandomBytes()` + +# 0.0.2 - February 25, 2013 + +- Make unique ids safe for use in URLs (Using 62 latin alphanumeric chars, `-` and `_`) + +# 0.0.1 - February 4, 2013 + +- Initial Release diff --git a/node_modules/uid2/LICENSE b/node_modules/uid2/LICENSE new file mode 100644 index 0000000..bdfab69 --- /dev/null +++ b/node_modules/uid2/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Marco Aurelio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/uid2/README.md b/node_modules/uid2/README.md new file mode 100644 index 0000000..53b0a53 --- /dev/null +++ b/node_modules/uid2/README.md @@ -0,0 +1,32 @@ +# uid2 + +[![NPM version](https://badge.fury.io/js/uid2.svg)](http://badge.fury.io/js/uid2) + +Generate unique ids. Pass in a `length` and it returns a `string`. + + +## Installation + + npm install uid2 + +## Examples + +Without a callback it is synchronous: + +```js +uid(10) +// => "hbswt489ts" +``` + +With a callback it is asynchronous: + +```js +uid(10, function (err, id) { + if (err) throw err; + // id => "hbswt489ts" +}); +``` + +## License + +MIT diff --git a/node_modules/uid2/index.js b/node_modules/uid2/index.js new file mode 100644 index 0000000..3f6d54d --- /dev/null +++ b/node_modules/uid2/index.js @@ -0,0 +1,55 @@ +/** + * Module dependencies + */ + +var crypto = require('crypto'); + +/** + * 62 characters in the ascii range that can be used in URLs without special + * encoding. + */ +var UIDCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + +/** + * Make a Buffer into a string ready for use in URLs + * + * @param {String} bytes a Buffer containing the bytes to convert + * @returns {String} UID + * @api private + */ +function tostr(bytes) { + var r, i; + + r = []; + for (i = 0; i < bytes.length; i++) { + r.push(UIDCHARS[bytes[i] % UIDCHARS.length]); + } + + return r.join(''); +} + +/** + * Generate an Unique Id + * + * @param {Number} length The number of chars of the uid + * @param {Number} cb (optional) Callback for async uid generation + * @api public + */ + +function uid(length, cb) { + + if (typeof cb === 'undefined') { + return tostr(crypto.pseudoRandomBytes(length)); + } else { + crypto.pseudoRandomBytes(length, function(err, bytes) { + if (err) return cb(err); + cb(null, tostr(bytes)); + }) + } +} + +/** + * Exports + */ + +module.exports = uid; diff --git a/node_modules/uid2/package.json b/node_modules/uid2/package.json new file mode 100644 index 0000000..07167a6 --- /dev/null +++ b/node_modules/uid2/package.json @@ -0,0 +1,12 @@ +{ + "name": "uid2", + "description": "strong uid", + "tags": ["uid"], + "version": "0.0.4", + "repository": { + "type": "git", + "url": "https://github.com/coreh/uid2.git" + }, + "license": "MIT", + "dependencies": {} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1795824..d6a2060 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dependencies": { "body-parser": "1.19.0", "express": "4.17.1", - "mysql": "2.18.1" + "mysql": "2.18.1", + "passport-kakao": "1.0.1" } }, "node_modules/accepts": { @@ -346,6 +347,11 @@ "node": ">= 0.6" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -365,11 +371,49 @@ "node": ">= 0.8" } }, + "node_modules/passport-kakao": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", + "dependencies": { + "passport-oauth2": "~1.1.2", + "pkginfo": "~0.3.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", + "integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -532,6 +576,11 @@ "node": ">= 0.6" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -820,6 +869,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -833,11 +887,40 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "passport-kakao": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", + "requires": { + "passport-oauth2": "~1.1.2", + "pkginfo": "~0.3.0" + } + }, + "passport-oauth2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", + "integrity": "sha1-vXFjsbYJA3GGjcTvb58uHkzEuUg=", + "requires": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -972,6 +1055,11 @@ "mime-types": "~2.1.24" } }, + "uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 1c18b3c..9f5e0d9 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "dependencies": { "body-parser": "1.19.0", "express": "4.17.1", - "mysql": "2.18.1" + "mysql": "2.18.1", + "passport-kakao": "1.0.1" } } From 0c2c8a09cfad9e4d26c36821b67ab6456736de61 Mon Sep 17 00:00:00 2001 From: Ahn YoungSeon Date: Fri, 26 Nov 2021 21:33:43 +0900 Subject: [PATCH 02/77] =?UTF-8?q?#5=20kakao=20oauth=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- main.js | 19 +- node_modules/.package-lock.json | 21 + node_modules/passport/CHANGELOG.md | 25 + node_modules/passport/LICENSE | 20 + node_modules/passport/README.md | 174 +++++++ node_modules/passport/lib/authenticator.js | 468 ++++++++++++++++++ .../lib/errors/authenticationerror.js | 20 + .../passport/lib/framework/connect.js | 22 + node_modules/passport/lib/http/request.js | 83 ++++ node_modules/passport/lib/index.js | 26 + .../passport/lib/middleware/authenticate.js | 363 ++++++++++++++ .../passport/lib/middleware/initialize.js | 68 +++ node_modules/passport/lib/sessionmanager.js | 39 ++ .../passport/lib/strategies/session.js | 83 ++++ node_modules/passport/package.json | 55 ++ .../passport/sponsors/loginradius.png | Bin 0 -> 10888 bytes node_modules/passport/sponsors/workos.png | Bin 0 -> 7174 bytes node_modules/pause/.npmignore | 4 + node_modules/pause/History.md | 5 + node_modules/pause/Makefile | 7 + node_modules/pause/Readme.md | 29 ++ node_modules/pause/index.js | 29 ++ node_modules/pause/package.json | 13 + package-lock.json | 36 ++ package.json | 1 + {route => routes}/indexRouter.js | 0 template/home.js | 2 +- 28 files changed, 1611 insertions(+), 3 deletions(-) create mode 100644 node_modules/passport/CHANGELOG.md create mode 100644 node_modules/passport/LICENSE create mode 100644 node_modules/passport/README.md create mode 100644 node_modules/passport/lib/authenticator.js create mode 100644 node_modules/passport/lib/errors/authenticationerror.js create mode 100644 node_modules/passport/lib/framework/connect.js create mode 100644 node_modules/passport/lib/http/request.js create mode 100644 node_modules/passport/lib/index.js create mode 100644 node_modules/passport/lib/middleware/authenticate.js create mode 100644 node_modules/passport/lib/middleware/initialize.js create mode 100644 node_modules/passport/lib/sessionmanager.js create mode 100644 node_modules/passport/lib/strategies/session.js create mode 100644 node_modules/passport/package.json create mode 100644 node_modules/passport/sponsors/loginradius.png create mode 100644 node_modules/passport/sponsors/workos.png create mode 100644 node_modules/pause/.npmignore create mode 100644 node_modules/pause/History.md create mode 100644 node_modules/pause/Makefile create mode 100644 node_modules/pause/Readme.md create mode 100644 node_modules/pause/index.js create mode 100644 node_modules/pause/package.json rename {route => routes}/indexRouter.js (100%) diff --git a/README.md b/README.md index bb15ba3..a4be6cf 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ entry point: main.js -dependency: express, mysql, body-parser +dependency: express, mysql, body-parser, passport-kakao #### database 생성 diff --git a/main.js b/main.js index 5c6a558..1789c34 100644 --- a/main.js +++ b/main.js @@ -2,7 +2,24 @@ const express = require('express'); const app = express(); const port = 3000; const bodyParser = require('body-parser'); -const indexRouter = require('./route/indexRouter.js') +const indexRouter = require('./routes/indexRouter.js') +const passport = require('passport') +const KaKaoStrategy = require('passport-kakao').Strategy + +passport.use(new KaKaoStrategy({ + clientID: '989236d695e051a00be147f5a2d11274', + clientSecret: '', + callbackURL: 'localhost:3000' + }, + (accessToken, refreshToken, profile, done) => { + console.log(profile); + // 사용자의 정보는 profile에 들어있다. + // User.findOrCreate(..., (err, user) => { + // if (err) { return done(err) } + // return done(null, user) + // }) + } +)); // parse application/x-www-form-urlencoded diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 933b825..a01b458 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -360,6 +360,22 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz", + "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, "node_modules/passport-kakao": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", @@ -395,6 +411,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "node_modules/pkginfo": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", diff --git a/node_modules/passport/CHANGELOG.md b/node_modules/passport/CHANGELOG.md new file mode 100644 index 0000000..2d10efd --- /dev/null +++ b/node_modules/passport/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.5.0] - 2021-09-23 +### Changed + +- `initialize()` middleware extends request with `login()`, `logIn()`, +`logout()`, `logOut()`, `isAuthenticated()`, and `isUnauthenticated()` +functions. + +### Removed + +- `login()`, `logIn()`, `logout()`, `logOut()`, `isAuthenticated()`, and +`isUnauthenticated()` functions no longer added to `http.IncomingMessage.prototype`. + +### Fixed + +- `userProperty` option to `initialize()` middleware only affects the current +request, rather than all requests processed via singleton Passport instance, +eliminating a race condition in situations where `initialize()` middleware is +used multiple times in an application with `userProperty` set to different +values. diff --git a/node_modules/passport/LICENSE b/node_modules/passport/LICENSE new file mode 100644 index 0000000..ba9eb28 --- /dev/null +++ b/node_modules/passport/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2011-2019 Jared Hanson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/passport/README.md b/node_modules/passport/README.md new file mode 100644 index 0000000..ffd80c0 --- /dev/null +++ b/node_modules/passport/README.md @@ -0,0 +1,174 @@ +[![passport banner](http://cdn.auth0.com/img/passport-banner-github.png)](http://passportjs.org) + +# Passport + +Passport is [Express](http://expressjs.com/)-compatible authentication +middleware for [Node.js](http://nodejs.org/). + +Passport's sole purpose is to authenticate requests, which it does through an +extensible set of plugins known as _strategies_. Passport does not mount +routes or assume any particular database schema, which maximizes flexibility and +allows application-level decisions to be made by the developer. The API is +simple: you provide Passport a request to authenticate, and Passport provides +hooks for controlling what occurs when authentication succeeds or fails. + +--- + +

+ Sponsors +
+
+ LoginRadius is built for the developer community to integrate robust Authentication and Single Sign-On in just a few lines of code.
FREE Signup +

+
+ Your app, enterprise-ready.
Start selling to enterprise customers with just a few lines of code. Add Single Sign-On (and more) in minutes instead of months.
+

+ +--- + +Status: +[![Build](https://travis-ci.org/jaredhanson/passport.svg?branch=master)](https://travis-ci.org/jaredhanson/passport) +[![Coverage](https://coveralls.io/repos/jaredhanson/passport/badge.svg?branch=master)](https://coveralls.io/r/jaredhanson/passport) +[![Dependencies](https://david-dm.org/jaredhanson/passport.svg)](https://david-dm.org/jaredhanson/passport) + + +## Install + +``` +$ npm install passport +``` + +## Usage + +#### Strategies + +Passport uses the concept of strategies to authenticate requests. Strategies +can range from verifying username and password credentials, delegated +authentication using [OAuth](http://oauth.net/) (for example, via [Facebook](http://www.facebook.com/) +or [Twitter](http://twitter.com/)), or federated authentication using [OpenID](http://openid.net/). + +Before authenticating requests, the strategy (or strategies) used by an +application must be configured. + +```javascript +passport.use(new LocalStrategy( + function(username, password, done) { + User.findOne({ username: username }, function (err, user) { + if (err) { return done(err); } + if (!user) { return done(null, false); } + if (!user.verifyPassword(password)) { return done(null, false); } + return done(null, user); + }); + } +)); +``` + +There are 480+ strategies. Find the ones you want at: [passportjs.org](http://passportjs.org) + +#### Sessions + +Passport will maintain persistent login sessions. In order for persistent +sessions to work, the authenticated user must be serialized to the session, and +deserialized when subsequent requests are made. + +Passport does not impose any restrictions on how your user records are stored. +Instead, you provide functions to Passport which implements the necessary +serialization and deserialization logic. In a typical application, this will be +as simple as serializing the user ID, and finding the user by ID when +deserializing. + +```javascript +passport.serializeUser(function(user, done) { + done(null, user.id); +}); + +passport.deserializeUser(function(id, done) { + User.findById(id, function (err, user) { + done(err, user); + }); +}); +``` + +#### Middleware + +To use Passport in an [Express](http://expressjs.com/) or +[Connect](http://senchalabs.github.com/connect/)-based application, configure it +with the required `passport.initialize()` middleware. If your application uses +persistent login sessions (recommended, but not required), `passport.session()` +middleware must also be used. + +```javascript +var app = express(); +app.use(require('serve-static')(__dirname + '/../../public')); +app.use(require('cookie-parser')()); +app.use(require('body-parser').urlencoded({ extended: true })); +app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true })); +app.use(passport.initialize()); +app.use(passport.session()); +``` + +#### Authenticate Requests + +Passport provides an `authenticate()` function, which is used as route +middleware to authenticate requests. + +```javascript +app.post('/login', + passport.authenticate('local', { failureRedirect: '/login' }), + function(req, res) { + res.redirect('/'); + }); +``` + +## Strategies + +Passport has a comprehensive set of **over 480** authentication strategies +covering social networking, enterprise integration, API services, and more. + +## Search all strategies + +There is a **Strategy Search** at [passportjs.org](http://passportjs.org) + +The following table lists commonly used strategies: + +|Strategy | Protocol |Developer | +|---------------------------------------------------------------|--------------------------|------------------------------------------------| +|[Local](https://github.com/jaredhanson/passport-local) | HTML form |[Jared Hanson](https://github.com/jaredhanson) | +|[OpenID](https://github.com/jaredhanson/passport-openid) | OpenID |[Jared Hanson](https://github.com/jaredhanson) | +|[BrowserID](https://github.com/jaredhanson/passport-browserid) | BrowserID |[Jared Hanson](https://github.com/jaredhanson) | +|[Facebook](https://github.com/jaredhanson/passport-facebook) | OAuth 2.0 |[Jared Hanson](https://github.com/jaredhanson) | +|[Google](https://github.com/jaredhanson/passport-google) | OpenID |[Jared Hanson](https://github.com/jaredhanson) | +|[Google](https://github.com/jaredhanson/passport-google-oauth) | OAuth / OAuth 2.0 |[Jared Hanson](https://github.com/jaredhanson) | +|[Twitter](https://github.com/jaredhanson/passport-twitter) | OAuth |[Jared Hanson](https://github.com/jaredhanson) | +|[Azure Active Directory](https://github.com/AzureAD/passport-azure-ad) | OAuth 2.0 / OpenID / SAML |[Azure](https://github.com/azuread) | + +## Examples + +- For a complete, working example, refer to the [example](https://github.com/passport/express-4.x-local-example) +that uses [passport-local](https://github.com/jaredhanson/passport-local). +- **Local Strategy**: Refer to the following tutorials for setting up user authentication via LocalStrategy (`passport-local`): + - Mongo + - Express v3x - [Tutorial](http://mherman.org/blog/2016/09/25/node-passport-and-postgres/#.V-govpMrJE5) / [working example](https://github.com/mjhea0/passport-local-knex) + - Express v4x - [Tutorial](http://mherman.org/blog/2015/01/31/local-authentication-with-passport-and-express-4/) / [working example](https://github.com/mjhea0/passport-local-express4) + - Postgres + - [Tutorial](http://mherman.org/blog/2015/01/31/local-authentication-with-passport-and-express-4/) / [working example](https://github.com/mjhea0/passport-local-express4) +- **Social Authentication**: Refer to the following tutorials for setting up various social authentication strategies: + - Express v3x - [Tutorial](http://mherman.org/blog/2013/11/10/social-authentication-with-passport-dot-js/) / [working example](https://github.com/mjhea0/passport-examples) + - Express v4x - [Tutorial](http://mherman.org/blog/2015/09/26/social-authentication-in-node-dot-js-with-passport) / [working example](https://github.com/mjhea0/passport-social-auth) + +## Related Modules + +- [Locomotive](https://github.com/jaredhanson/locomotive) — Powerful MVC web framework +- [OAuthorize](https://github.com/jaredhanson/oauthorize) — OAuth service provider toolkit +- [OAuth2orize](https://github.com/jaredhanson/oauth2orize) — OAuth 2.0 authorization server toolkit +- [connect-ensure-login](https://github.com/jaredhanson/connect-ensure-login) — middleware to ensure login sessions + +The [modules](https://github.com/jaredhanson/passport/wiki/Modules) page on the +[wiki](https://github.com/jaredhanson/passport/wiki) lists other useful modules +that build upon or integrate with Passport. + +## License + +[The MIT License](http://opensource.org/licenses/MIT) + +Copyright (c) 2011-2019 Jared Hanson <[http://jaredhanson.net/](http://jaredhanson.net/)> diff --git a/node_modules/passport/lib/authenticator.js b/node_modules/passport/lib/authenticator.js new file mode 100644 index 0000000..03f9878 --- /dev/null +++ b/node_modules/passport/lib/authenticator.js @@ -0,0 +1,468 @@ +/** + * Module dependencies. + */ +var SessionStrategy = require('./strategies/session') + , SessionManager = require('./sessionmanager'); + + +/** + * `Authenticator` constructor. + * + * @api public + */ +function Authenticator() { + this._key = 'passport'; + this._strategies = {}; + this._serializers = []; + this._deserializers = []; + this._infoTransformers = []; + this._framework = null; + + this.init(); +} + +/** + * Initialize authenticator. + * + * @api protected + */ +Authenticator.prototype.init = function() { + this.framework(require('./framework/connect')()); + this.use(new SessionStrategy({ key: this._key }, this.deserializeUser.bind(this))); + this._sm = new SessionManager({ key: this._key }, this.serializeUser.bind(this)); +}; + +/** + * Utilize the given `strategy` with optional `name`, overridding the strategy's + * default name. + * + * Examples: + * + * passport.use(new TwitterStrategy(...)); + * + * passport.use('api', new http.BasicStrategy(...)); + * + * @param {String|Strategy} name + * @param {Strategy} strategy + * @return {Authenticator} for chaining + * @api public + */ +Authenticator.prototype.use = function(name, strategy) { + if (!strategy) { + strategy = name; + name = strategy.name; + } + if (!name) { throw new Error('Authentication strategies must have a name'); } + + this._strategies[name] = strategy; + return this; +}; + +/** + * Un-utilize the `strategy` with given `name`. + * + * In typical applications, the necessary authentication strategies are static, + * configured once and always available. As such, there is often no need to + * invoke this function. + * + * However, in certain situations, applications may need dynamically configure + * and de-configure authentication strategies. The `use()`/`unuse()` + * combination satisfies these scenarios. + * + * Examples: + * + * passport.unuse('legacy-api'); + * + * @param {String} name + * @return {Authenticator} for chaining + * @api public + */ +Authenticator.prototype.unuse = function(name) { + delete this._strategies[name]; + return this; +}; + +/** + * Setup Passport to be used under framework. + * + * By default, Passport exposes middleware that operate using Connect-style + * middleware using a `fn(req, res, next)` signature. Other popular frameworks + * have different expectations, and this function allows Passport to be adapted + * to operate within such environments. + * + * If you are using a Connect-compatible framework, including Express, there is + * no need to invoke this function. + * + * Examples: + * + * passport.framework(require('hapi-passport')()); + * + * @param {Object} name + * @return {Authenticator} for chaining + * @api public + */ +Authenticator.prototype.framework = function(fw) { + this._framework = fw; + return this; +}; + +/** + * Passport's primary initialization middleware. + * + * This middleware must be in use by the Connect/Express application for + * Passport to operate. + * + * Options: + * - `userProperty` Property to set on `req` upon login, defaults to _user_ + * + * Examples: + * + * app.use(passport.initialize()); + * + * app.use(passport.initialize({ userProperty: 'currentUser' })); + * + * @param {Object} options + * @return {Function} middleware + * @api public + */ +Authenticator.prototype.initialize = function(options) { + options = options || {}; + return this._framework.initialize(this, options); +}; + +/** + * Middleware that will authenticate a request using the given `strategy` name, + * with optional `options` and `callback`. + * + * Examples: + * + * passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })(req, res); + * + * passport.authenticate('local', function(err, user) { + * if (!user) { return res.redirect('/login'); } + * res.end('Authenticated!'); + * })(req, res); + * + * passport.authenticate('basic', { session: false })(req, res); + * + * app.get('/auth/twitter', passport.authenticate('twitter'), function(req, res) { + * // request will be redirected to Twitter + * }); + * app.get('/auth/twitter/callback', passport.authenticate('twitter'), function(req, res) { + * res.json(req.user); + * }); + * + * @param {String} strategy + * @param {Object} options + * @param {Function} callback + * @return {Function} middleware + * @api public + */ +Authenticator.prototype.authenticate = function(strategy, options, callback) { + return this._framework.authenticate(this, strategy, options, callback); +}; + +/** + * Middleware that will authorize a third-party account using the given + * `strategy` name, with optional `options`. + * + * If authorization is successful, the result provided by the strategy's verify + * callback will be assigned to `req.account`. The existing login session and + * `req.user` will be unaffected. + * + * This function is particularly useful when connecting third-party accounts + * to the local account of a user that is currently authenticated. + * + * Examples: + * + * passport.authorize('twitter-authz', { failureRedirect: '/account' }); + * + * @param {String} strategy + * @param {Object} options + * @return {Function} middleware + * @api public + */ +Authenticator.prototype.authorize = function(strategy, options, callback) { + options = options || {}; + options.assignProperty = 'account'; + + var fn = this._framework.authorize || this._framework.authenticate; + return fn(this, strategy, options, callback); +}; + +/** + * Middleware that will restore login state from a session. + * + * Web applications typically use sessions to maintain login state between + * requests. For example, a user will authenticate by entering credentials into + * a form which is submitted to the server. If the credentials are valid, a + * login session is established by setting a cookie containing a session + * identifier in the user's web browser. The web browser will send this cookie + * in subsequent requests to the server, allowing a session to be maintained. + * + * If sessions are being utilized, and a login session has been established, + * this middleware will populate `req.user` with the current user. + * + * Note that sessions are not strictly required for Passport to operate. + * However, as a general rule, most web applications will make use of sessions. + * An exception to this rule would be an API server, which expects each HTTP + * request to provide credentials in an Authorization header. + * + * Examples: + * + * app.use(connect.cookieParser()); + * app.use(connect.session({ secret: 'keyboard cat' })); + * app.use(passport.initialize()); + * app.use(passport.session()); + * + * Options: + * - `pauseStream` Pause the request stream before deserializing the user + * object from the session. Defaults to _false_. Should + * be set to true in cases where middleware consuming the + * request body is configured after passport and the + * deserializeUser method is asynchronous. + * + * @param {Object} options + * @return {Function} middleware + * @api public + */ +Authenticator.prototype.session = function(options) { + return this.authenticate('session', options); +}; + +// TODO: Make session manager pluggable +/* +Authenticator.prototype.sessionManager = function(mgr) { + this._sm = mgr; + return this; +} +*/ + +/** + * Registers a function used to serialize user objects into the session. + * + * Examples: + * + * passport.serializeUser(function(user, done) { + * done(null, user.id); + * }); + * + * @api public + */ +Authenticator.prototype.serializeUser = function(fn, req, done) { + if (typeof fn === 'function') { + return this._serializers.push(fn); + } + + // private implementation that traverses the chain of serializers, attempting + // to serialize a user + var user = fn; + + // For backwards compatibility + if (typeof req === 'function') { + done = req; + req = undefined; + } + + var stack = this._serializers; + (function pass(i, err, obj) { + // serializers use 'pass' as an error to skip processing + if ('pass' === err) { + err = undefined; + } + // an error or serialized object was obtained, done + if (err || obj || obj === 0) { return done(err, obj); } + + var layer = stack[i]; + if (!layer) { + return done(new Error('Failed to serialize user into session')); + } + + + function serialized(e, o) { + pass(i + 1, e, o); + } + + try { + var arity = layer.length; + if (arity == 3) { + layer(req, user, serialized); + } else { + layer(user, serialized); + } + } catch(e) { + return done(e); + } + })(0); +}; + +/** + * Registers a function used to deserialize user objects out of the session. + * + * Examples: + * + * passport.deserializeUser(function(id, done) { + * User.findById(id, function (err, user) { + * done(err, user); + * }); + * }); + * + * @api public + */ +Authenticator.prototype.deserializeUser = function(fn, req, done) { + if (typeof fn === 'function') { + return this._deserializers.push(fn); + } + + // private implementation that traverses the chain of deserializers, + // attempting to deserialize a user + var obj = fn; + + // For backwards compatibility + if (typeof req === 'function') { + done = req; + req = undefined; + } + + var stack = this._deserializers; + (function pass(i, err, user) { + // deserializers use 'pass' as an error to skip processing + if ('pass' === err) { + err = undefined; + } + // an error or deserialized user was obtained, done + if (err || user) { return done(err, user); } + // a valid user existed when establishing the session, but that user has + // since been removed + if (user === null || user === false) { return done(null, false); } + + var layer = stack[i]; + if (!layer) { + return done(new Error('Failed to deserialize user out of session')); + } + + + function deserialized(e, u) { + pass(i + 1, e, u); + } + + try { + var arity = layer.length; + if (arity == 3) { + layer(req, obj, deserialized); + } else { + layer(obj, deserialized); + } + } catch(e) { + return done(e); + } + })(0); +}; + +/** + * Registers a function used to transform auth info. + * + * In some circumstances authorization details are contained in authentication + * credentials or loaded as part of verification. + * + * For example, when using bearer tokens for API authentication, the tokens may + * encode (either directly or indirectly in a database), details such as scope + * of access or the client to which the token was issued. + * + * Such authorization details should be enforced separately from authentication. + * Because Passport deals only with the latter, this is the responsiblity of + * middleware or routes further along the chain. However, it is not optimal to + * decode the same data or execute the same database query later. To avoid + * this, Passport accepts optional `info` along with the authenticated `user` + * in a strategy's `success()` action. This info is set at `req.authInfo`, + * where said later middlware or routes can access it. + * + * Optionally, applications can register transforms to proccess this info, + * which take effect prior to `req.authInfo` being set. This is useful, for + * example, when the info contains a client ID. The transform can load the + * client from the database and include the instance in the transformed info, + * allowing the full set of client properties to be convieniently accessed. + * + * If no transforms are registered, `info` supplied by the strategy will be left + * unmodified. + * + * Examples: + * + * passport.transformAuthInfo(function(info, done) { + * Client.findById(info.clientID, function (err, client) { + * info.client = client; + * done(err, info); + * }); + * }); + * + * @api public + */ +Authenticator.prototype.transformAuthInfo = function(fn, req, done) { + if (typeof fn === 'function') { + return this._infoTransformers.push(fn); + } + + // private implementation that traverses the chain of transformers, + // attempting to transform auth info + var info = fn; + + // For backwards compatibility + if (typeof req === 'function') { + done = req; + req = undefined; + } + + var stack = this._infoTransformers; + (function pass(i, err, tinfo) { + // transformers use 'pass' as an error to skip processing + if ('pass' === err) { + err = undefined; + } + // an error or transformed info was obtained, done + if (err || tinfo) { return done(err, tinfo); } + + var layer = stack[i]; + if (!layer) { + // if no transformers are registered (or they all pass), the default + // behavior is to use the un-transformed info as-is + return done(null, info); + } + + + function transformed(e, t) { + pass(i + 1, e, t); + } + + try { + var arity = layer.length; + if (arity == 1) { + // sync + var t = layer(info); + transformed(null, t); + } else if (arity == 3) { + layer(req, info, transformed); + } else { + layer(info, transformed); + } + } catch(e) { + return done(e); + } + })(0); +}; + +/** + * Return strategy with given `name`. + * + * @param {String} name + * @return {Strategy} + * @api private + */ +Authenticator.prototype._strategy = function(name) { + return this._strategies[name]; +}; + + +/** + * Expose `Authenticator`. + */ +module.exports = Authenticator; diff --git a/node_modules/passport/lib/errors/authenticationerror.js b/node_modules/passport/lib/errors/authenticationerror.js new file mode 100644 index 0000000..2b1da14 --- /dev/null +++ b/node_modules/passport/lib/errors/authenticationerror.js @@ -0,0 +1,20 @@ +/** + * `AuthenticationError` error. + * + * @constructor + * @api private + */ +function AuthenticationError(message, status) { + Error.call(this); + Error.captureStackTrace(this, arguments.callee); + this.name = 'AuthenticationError'; + this.message = message; + this.status = status || 401; +} + +// Inherit from `Error`. +AuthenticationError.prototype.__proto__ = Error.prototype; + + +// Expose constructor. +module.exports = AuthenticationError; diff --git a/node_modules/passport/lib/framework/connect.js b/node_modules/passport/lib/framework/connect.js new file mode 100644 index 0000000..7d2db6e --- /dev/null +++ b/node_modules/passport/lib/framework/connect.js @@ -0,0 +1,22 @@ +/** + * Module dependencies. + */ +var initialize = require('../middleware/initialize') + , authenticate = require('../middleware/authenticate'); + +/** + * Framework support for Connect/Express. + * + * This module provides support for using Passport with Express. It exposes + * middleware that conform to the `fn(req, res, next)` signature. + * + * @return {Object} + * @api protected + */ +exports = module.exports = function() { + + return { + initialize: initialize, + authenticate: authenticate + }; +}; diff --git a/node_modules/passport/lib/http/request.js b/node_modules/passport/lib/http/request.js new file mode 100644 index 0000000..3e6cc4e --- /dev/null +++ b/node_modules/passport/lib/http/request.js @@ -0,0 +1,83 @@ +var req = exports = module.exports = {}; + +/** + * Initiate a login session for `user`. + * + * Options: + * - `session` Save login state in session, defaults to _true_ + * + * Examples: + * + * req.logIn(user, { session: false }); + * + * req.logIn(user, function(err) { + * if (err) { throw err; } + * // session saved + * }); + * + * @param {User} user + * @param {Object} options + * @param {Function} done + * @api public + */ +req.login = +req.logIn = function(user, options, done) { + if (typeof options == 'function') { + done = options; + options = {}; + } + options = options || {}; + + var property = this._userProperty || 'user'; + var session = (options.session === undefined) ? true : options.session; + + this[property] = user; + if (session) { + if (!this._passport) { throw new Error('passport.initialize() middleware not in use'); } + if (typeof done != 'function') { throw new Error('req#login requires a callback function'); } + + var self = this; + this._passport.instance._sm.logIn(this, user, function(err) { + if (err) { self[property] = null; return done(err); } + done(); + }); + } else { + done && done(); + } +}; + +/** + * Terminate an existing login session. + * + * @api public + */ +req.logout = +req.logOut = function() { + var property = this._userProperty || 'user'; + + this[property] = null; + if (this._passport) { + this._passport.instance._sm.logOut(this); + } +}; + +/** + * Test if request is authenticated. + * + * @return {Boolean} + * @api public + */ +req.isAuthenticated = function() { + var property = this._userProperty || 'user'; + return (this[property]) ? true : false; +}; + +/** + * Test if request is unauthenticated. + * + * @return {Boolean} + * @api public + */ +req.isUnauthenticated = function() { + return !this.isAuthenticated(); +}; diff --git a/node_modules/passport/lib/index.js b/node_modules/passport/lib/index.js new file mode 100644 index 0000000..ab17469 --- /dev/null +++ b/node_modules/passport/lib/index.js @@ -0,0 +1,26 @@ +/** + * Module dependencies. + */ +var Passport = require('./authenticator') + , SessionStrategy = require('./strategies/session'); + + +/** + * Export default singleton. + * + * @api public + */ +exports = module.exports = new Passport(); + +/** + * Expose constructors. + */ +exports.Passport = +exports.Authenticator = Passport; +exports.Strategy = require('passport-strategy'); + +/** + * Expose strategies. + */ +exports.strategies = {}; +exports.strategies.SessionStrategy = SessionStrategy; diff --git a/node_modules/passport/lib/middleware/authenticate.js b/node_modules/passport/lib/middleware/authenticate.js new file mode 100644 index 0000000..c87831d --- /dev/null +++ b/node_modules/passport/lib/middleware/authenticate.js @@ -0,0 +1,363 @@ +/** + * Module dependencies. + */ +var http = require('http') + , IncomingMessageExt = require('../http/request') + , AuthenticationError = require('../errors/authenticationerror'); + + +/** + * Authenticates requests. + * + * Applies the `name`ed strategy (or strategies) to the incoming request, in + * order to authenticate the request. If authentication is successful, the user + * will be logged in and populated at `req.user` and a session will be + * established by default. If authentication fails, an unauthorized response + * will be sent. + * + * Options: + * - `session` Save login state in session, defaults to _true_ + * - `successRedirect` After successful login, redirect to given URL + * - `successMessage` True to store success message in + * req.session.messages, or a string to use as override + * message for success. + * - `successFlash` True to flash success messages or a string to use as a flash + * message for success (overrides any from the strategy itself). + * - `failureRedirect` After failed login, redirect to given URL + * - `failureMessage` True to store failure message in + * req.session.messages, or a string to use as override + * message for failure. + * - `failureFlash` True to flash failure messages or a string to use as a flash + * message for failures (overrides any from the strategy itself). + * - `assignProperty` Assign the object provided by the verify callback to given property + * + * An optional `callback` can be supplied to allow the application to override + * the default manner in which authentication attempts are handled. The + * callback has the following signature, where `user` will be set to the + * authenticated user on a successful authentication attempt, or `false` + * otherwise. An optional `info` argument will be passed, containing additional + * details provided by the strategy's verify callback - this could be information about + * a successful authentication or a challenge message for a failed authentication. + * An optional `status` argument will be passed when authentication fails - this could + * be a HTTP response code for a remote authentication failure or similar. + * + * app.get('/protected', function(req, res, next) { + * passport.authenticate('local', function(err, user, info, status) { + * if (err) { return next(err) } + * if (!user) { return res.redirect('/signin') } + * res.redirect('/account'); + * })(req, res, next); + * }); + * + * Note that if a callback is supplied, it becomes the application's + * responsibility to log-in the user, establish a session, and otherwise perform + * the desired operations. + * + * Examples: + * + * passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }); + * + * passport.authenticate('basic', { session: false }); + * + * passport.authenticate('twitter'); + * + * @param {Strategy|String|Array} name + * @param {Object} options + * @param {Function} callback + * @return {Function} + * @api public + */ +module.exports = function authenticate(passport, name, options, callback) { + if (typeof options == 'function') { + callback = options; + options = {}; + } + options = options || {}; + + var multi = true; + + // Cast `name` to an array, allowing authentication to pass through a chain of + // strategies. The first strategy to succeed, redirect, or error will halt + // the chain. Authentication failures will proceed through each strategy in + // series, ultimately failing if all strategies fail. + // + // This is typically used on API endpoints to allow clients to authenticate + // using their preferred choice of Basic, Digest, token-based schemes, etc. + // It is not feasible to construct a chain of multiple strategies that involve + // redirection (for example both Facebook and Twitter), since the first one to + // redirect will halt the chain. + if (!Array.isArray(name)) { + name = [ name ]; + multi = false; + } + + return function authenticate(req, res, next) { + // accumulator for failures from each strategy in the chain + var failures = []; + + function allFailed() { + if (callback) { + if (!multi) { + return callback(null, false, failures[0].challenge, failures[0].status); + } else { + var challenges = failures.map(function(f) { return f.challenge; }); + var statuses = failures.map(function(f) { return f.status; }); + return callback(null, false, challenges, statuses); + } + } + + // Strategies are ordered by priority. For the purpose of flashing a + // message, the first failure will be displayed. + var failure = failures[0] || {} + , challenge = failure.challenge || {} + , msg; + + if (options.failureFlash) { + var flash = options.failureFlash; + if (typeof flash == 'string') { + flash = { type: 'error', message: flash }; + } + flash.type = flash.type || 'error'; + + var type = flash.type || challenge.type || 'error'; + msg = flash.message || challenge.message || challenge; + if (typeof msg == 'string') { + req.flash(type, msg); + } + } + if (options.failureMessage) { + msg = options.failureMessage; + if (typeof msg == 'boolean') { + msg = challenge.message || challenge; + } + if (typeof msg == 'string') { + req.session.messages = req.session.messages || []; + req.session.messages.push(msg); + } + } + if (options.failureRedirect) { + return res.redirect(options.failureRedirect); + } + + // When failure handling is not delegated to the application, the default + // is to respond with 401 Unauthorized. Note that the WWW-Authenticate + // header will be set according to the strategies in use (see + // actions#fail). If multiple strategies failed, each of their challenges + // will be included in the response. + var rchallenge = [] + , rstatus, status; + + for (var j = 0, len = failures.length; j < len; j++) { + failure = failures[j]; + challenge = failure.challenge; + status = failure.status; + + rstatus = rstatus || status; + if (typeof challenge == 'string') { + rchallenge.push(challenge); + } + } + + res.statusCode = rstatus || 401; + if (res.statusCode == 401 && rchallenge.length) { + res.setHeader('WWW-Authenticate', rchallenge); + } + if (options.failWithError) { + return next(new AuthenticationError(http.STATUS_CODES[res.statusCode], rstatus)); + } + res.end(http.STATUS_CODES[res.statusCode]); + } + + (function attempt(i) { + var layer = name[i]; + // If no more strategies exist in the chain, authentication has failed. + if (!layer) { return allFailed(); } + + // Get the strategy, which will be used as prototype from which to create + // a new instance. Action functions will then be bound to the strategy + // within the context of the HTTP request/response pair. + var strategy, prototype; + if (typeof layer.authenticate == 'function') { + strategy = layer; + } else { + prototype = passport._strategy(layer); + if (!prototype) { return next(new Error('Unknown authentication strategy "' + layer + '"')); } + + strategy = Object.create(prototype); + } + + + // ----- BEGIN STRATEGY AUGMENTATION ----- + // Augment the new strategy instance with action functions. These action + // functions are bound via closure the the request/response pair. The end + // goal of the strategy is to invoke *one* of these action methods, in + // order to indicate successful or failed authentication, redirect to a + // third-party identity provider, etc. + + /** + * Authenticate `user`, with optional `info`. + * + * Strategies should call this function to successfully authenticate a + * user. `user` should be an object supplied by the application after it + * has been given an opportunity to verify credentials. `info` is an + * optional argument containing additional user information. This is + * useful for third-party authentication strategies to pass profile + * details. + * + * @param {Object} user + * @param {Object} info + * @api public + */ + strategy.success = function(user, info) { + if (callback) { + return callback(null, user, info); + } + + info = info || {}; + var msg; + + if (options.successFlash) { + var flash = options.successFlash; + if (typeof flash == 'string') { + flash = { type: 'success', message: flash }; + } + flash.type = flash.type || 'success'; + + var type = flash.type || info.type || 'success'; + msg = flash.message || info.message || info; + if (typeof msg == 'string') { + req.flash(type, msg); + } + } + if (options.successMessage) { + msg = options.successMessage; + if (typeof msg == 'boolean') { + msg = info.message || info; + } + if (typeof msg == 'string') { + req.session.messages = req.session.messages || []; + req.session.messages.push(msg); + } + } + if (options.assignProperty) { + req[options.assignProperty] = user; + return next(); + } + + req.logIn(user, options, function(err) { + if (err) { return next(err); } + + function complete() { + if (options.successReturnToOrRedirect) { + var url = options.successReturnToOrRedirect; + if (req.session && req.session.returnTo) { + url = req.session.returnTo; + delete req.session.returnTo; + } + return res.redirect(url); + } + if (options.successRedirect) { + return res.redirect(options.successRedirect); + } + next(); + } + + if (options.authInfo !== false) { + passport.transformAuthInfo(info, req, function(err, tinfo) { + if (err) { return next(err); } + req.authInfo = tinfo; + complete(); + }); + } else { + complete(); + } + }); + }; + + /** + * Fail authentication, with optional `challenge` and `status`, defaulting + * to 401. + * + * Strategies should call this function to fail an authentication attempt. + * + * @param {String} challenge + * @param {Number} status + * @api public + */ + strategy.fail = function(challenge, status) { + if (typeof challenge == 'number') { + status = challenge; + challenge = undefined; + } + + // push this failure into the accumulator and attempt authentication + // using the next strategy + failures.push({ challenge: challenge, status: status }); + attempt(i + 1); + }; + + /** + * Redirect to `url` with optional `status`, defaulting to 302. + * + * Strategies should call this function to redirect the user (via their + * user agent) to a third-party website for authentication. + * + * @param {String} url + * @param {Number} status + * @api public + */ + strategy.redirect = function(url, status) { + // NOTE: Do not use `res.redirect` from Express, because it can't decide + // what it wants. + // + // Express 2.x: res.redirect(url, status) + // Express 3.x: res.redirect(status, url) -OR- res.redirect(url, status) + // - as of 3.14.0, deprecated warnings are issued if res.redirect(url, status) + // is used + // Express 4.x: res.redirect(status, url) + // - all versions (as of 4.8.7) continue to accept res.redirect(url, status) + // but issue deprecated versions + + res.statusCode = status || 302; + res.setHeader('Location', url); + res.setHeader('Content-Length', '0'); + res.end(); + }; + + /** + * Pass without making a success or fail decision. + * + * Under most circumstances, Strategies should not need to call this + * function. It exists primarily to allow previous authentication state + * to be restored, for example from an HTTP session. + * + * @api public + */ + strategy.pass = function() { + next(); + }; + + /** + * Internal error while performing authentication. + * + * Strategies should call this function when an internal error occurs + * during the process of performing authentication; for example, if the + * user directory is not available. + * + * @param {Error} err + * @api public + */ + strategy.error = function(err) { + if (callback) { + return callback(err); + } + + next(err); + }; + + // ----- END STRATEGY AUGMENTATION ----- + + strategy.authenticate(req, options); + })(0); // attempt + }; +}; diff --git a/node_modules/passport/lib/middleware/initialize.js b/node_modules/passport/lib/middleware/initialize.js new file mode 100644 index 0000000..724982b --- /dev/null +++ b/node_modules/passport/lib/middleware/initialize.js @@ -0,0 +1,68 @@ +/** + * Module dependencies. + */ +var IncomingMessageExt = require('../http/request'); + + +/** + * Passport initialization. + * + * Intializes Passport for incoming requests, allowing authentication strategies + * to be applied. + * + * If sessions are being utilized, applications must set up Passport with + * functions to serialize a user into and out of a session. For example, a + * common pattern is to serialize just the user ID into the session (due to the + * fact that it is desirable to store the minimum amount of data in a session). + * When a subsequent request arrives for the session, the full User object can + * be loaded from the database by ID. + * + * Note that additional middleware is required to persist login state, so we + * must use the `connect.session()` middleware _before_ `passport.initialize()`. + * + * If sessions are being used, this middleware must be in use by the + * Connect/Express application for Passport to operate. If the application is + * entirely stateless (not using sessions), this middleware is not necessary, + * but its use will not have any adverse impact. + * + * Examples: + * + * app.use(connect.cookieParser()); + * app.use(connect.session({ secret: 'keyboard cat' })); + * app.use(passport.initialize()); + * app.use(passport.session()); + * + * passport.serializeUser(function(user, done) { + * done(null, user.id); + * }); + * + * passport.deserializeUser(function(id, done) { + * User.findById(id, function (err, user) { + * done(err, user); + * }); + * }); + * + * @return {Function} + * @api public + */ +module.exports = function initialize(passport, options) { + options = options || {}; + + return function initialize(req, res, next) { + req.login = + req.logIn = IncomingMessageExt.logIn; + req.logout = + req.logOut = IncomingMessageExt.logOut; + req.isAuthenticated = IncomingMessageExt.isAuthenticated; + req.isUnauthenticated = IncomingMessageExt.isUnauthenticated; + + if (options.userProperty) { + req._userProperty = options.userProperty; + } + + req._passport = {}; + req._passport.instance = passport; + + next(); + }; +}; diff --git a/node_modules/passport/lib/sessionmanager.js b/node_modules/passport/lib/sessionmanager.js new file mode 100644 index 0000000..3d5c51c --- /dev/null +++ b/node_modules/passport/lib/sessionmanager.js @@ -0,0 +1,39 @@ +function SessionManager(options, serializeUser) { + if (typeof options == 'function') { + serializeUser = options; + options = undefined; + } + options = options || {}; + + this._key = options.key || 'passport'; + this._serializeUser = serializeUser; +} + +SessionManager.prototype.logIn = function(req, user, cb) { + var self = this; + this._serializeUser(user, req, function(err, obj) { + if (err) { + return cb(err); + } + // TODO: Error if session isn't available here. + if (!req.session) { + req.session = {}; + } + if (!req.session[self._key]) { + req.session[self._key] = {}; + } + req.session[self._key].user = obj; + cb(); + }); +} + +SessionManager.prototype.logOut = function(req, cb) { + if (req.session && req.session[this._key]) { + delete req.session[this._key].user; + } + + cb && cb(); +} + + +module.exports = SessionManager; diff --git a/node_modules/passport/lib/strategies/session.js b/node_modules/passport/lib/strategies/session.js new file mode 100644 index 0000000..759516f --- /dev/null +++ b/node_modules/passport/lib/strategies/session.js @@ -0,0 +1,83 @@ +/** + * Module dependencies. + */ +var pause = require('pause') + , util = require('util') + , Strategy = require('passport-strategy'); + + +/** + * `SessionStrategy` constructor. + * + * @api public + */ +function SessionStrategy(options, deserializeUser) { + if (typeof options == 'function') { + deserializeUser = options; + options = undefined; + } + options = options || {}; + + Strategy.call(this); + this.name = 'session'; + this._key = options.key || 'passport'; + this._deserializeUser = deserializeUser; +} + +/** + * Inherit from `Strategy`. + */ +util.inherits(SessionStrategy, Strategy); + +/** + * Authenticate request based on the current session state. + * + * The session authentication strategy uses the session to restore any login + * state across requests. If a login session has been established, `req.user` + * will be populated with the current user. + * + * This strategy is registered automatically by Passport. + * + * @param {Object} req + * @param {Object} options + * @api protected + */ +SessionStrategy.prototype.authenticate = function(req, options) { + if (!req._passport) { return this.error(new Error('passport.initialize() middleware not in use')); } + options = options || {}; + + var self = this, + su; + if (req.session[this._key]) { + su = req.session[this._key].user; + } + + if (su || su === 0) { + // NOTE: Stream pausing is desirable in the case where later middleware is + // listening for events emitted from request. For discussion on the + // matter, refer to: https://github.com/jaredhanson/passport/pull/106 + + var paused = options.pauseStream ? pause(req) : null; + this._deserializeUser(su, req, function(err, user) { + if (err) { return self.error(err); } + if (!user) { + delete req.session[self._key].user; + } else { + var property = req._userProperty || 'user'; + req[property] = user; + } + self.pass(); + if (paused) { + paused.resume(); + } + }); + } else { + self.pass(); + } +}; + + +/** + * Expose `SessionStrategy`. + */ +module.exports = SessionStrategy; diff --git a/node_modules/passport/package.json b/node_modules/passport/package.json new file mode 100644 index 0000000..8ef250d --- /dev/null +++ b/node_modules/passport/package.json @@ -0,0 +1,55 @@ +{ + "name": "passport", + "version": "0.5.0", + "description": "Simple, unobtrusive authentication for Node.js.", + "keywords": [ + "express", + "connect", + "auth", + "authn", + "authentication" + ], + "author": { + "name": "Jared Hanson", + "email": "jaredhanson@gmail.com", + "url": "http://www.jaredhanson.net/" + }, + "homepage": "http://passportjs.org/", + "repository": { + "type": "git", + "url": "git://github.com/jaredhanson/passport.git" + }, + "bugs": { + "url": "http://github.com/jaredhanson/passport/issues" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + }, + "license": "MIT", + "licenses": [ + { + "type": "MIT", + "url": "http://opensource.org/licenses/MIT" + } + ], + "main": "./lib", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "devDependencies": { + "make-node": "0.3.x", + "mocha": "2.x.x", + "chai": "2.x.x", + "chai-connect-middleware": "0.3.x", + "chai-passport-strategy": "0.2.x", + "proxyquire": "1.4.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "scripts": { + "test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js" + } +} diff --git a/node_modules/passport/sponsors/loginradius.png b/node_modules/passport/sponsors/loginradius.png new file mode 100644 index 0000000000000000000000000000000000000000..884f0f47dfd4a441194317faa071895c740b3aeb GIT binary patch literal 10888 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 U+XQ|7ydEkot{_$|Vi@rM0KWR)8~^|S literal 0 HcmV?d00001 diff --git a/node_modules/passport/sponsors/workos.png b/node_modules/passport/sponsors/workos.png new file mode 100644 index 0000000000000000000000000000000000000000..3afdfbaec9865675eeb7a051f07745f695151c41 GIT binary patch literal 7174 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 literal 0 HcmV?d00001 diff --git a/node_modules/pause/.npmignore b/node_modules/pause/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/node_modules/pause/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/node_modules/pause/History.md b/node_modules/pause/History.md new file mode 100644 index 0000000..c8aa68f --- /dev/null +++ b/node_modules/pause/History.md @@ -0,0 +1,5 @@ + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/node_modules/pause/Makefile b/node_modules/pause/Makefile new file mode 100644 index 0000000..4e9c8d3 --- /dev/null +++ b/node_modules/pause/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --require should \ + --reporter spec + +.PHONY: test \ No newline at end of file diff --git a/node_modules/pause/Readme.md b/node_modules/pause/Readme.md new file mode 100644 index 0000000..1cdd68a --- /dev/null +++ b/node_modules/pause/Readme.md @@ -0,0 +1,29 @@ + +# pause + + Pause streams... + +## License + +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/pause/index.js b/node_modules/pause/index.js new file mode 100644 index 0000000..1b7b379 --- /dev/null +++ b/node_modules/pause/index.js @@ -0,0 +1,29 @@ + +module.exports = function(obj){ + var onData + , onEnd + , events = []; + + // buffer data + obj.on('data', onData = function(data, encoding){ + events.push(['data', data, encoding]); + }); + + // buffer end + obj.on('end', onEnd = function(data, encoding){ + events.push(['end', data, encoding]); + }); + + return { + end: function(){ + obj.removeListener('data', onData); + obj.removeListener('end', onEnd); + }, + resume: function(){ + this.end(); + for (var i = 0, len = events.length; i < len; ++i) { + obj.emit.apply(obj, events[i]); + } + } + }; +}; \ No newline at end of file diff --git a/node_modules/pause/package.json b/node_modules/pause/package.json new file mode 100644 index 0000000..ae472ac --- /dev/null +++ b/node_modules/pause/package.json @@ -0,0 +1,13 @@ +{ + "name": "pause", + "version": "0.0.1", + "description": "Pause streams...", + "keywords": [], + "author": "TJ Holowaychuk ", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "main": "index" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d6a2060..5285774 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "body-parser": "1.19.0", "express": "4.17.1", "mysql": "2.18.1", + "passport": "0.5.0", "passport-kakao": "1.0.1" } }, @@ -371,6 +372,22 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz", + "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, "node_modules/passport-kakao": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", @@ -406,6 +423,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "node_modules/pkginfo": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", @@ -887,6 +909,15 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "passport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz", + "integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, "passport-kakao": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", @@ -916,6 +947,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "pkginfo": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", diff --git a/package.json b/package.json index 9f5e0d9..dba0399 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "body-parser": "1.19.0", "express": "4.17.1", "mysql": "2.18.1", + "passport": "0.5.0", "passport-kakao": "1.0.1" } } diff --git a/route/indexRouter.js b/routes/indexRouter.js similarity index 100% rename from route/indexRouter.js rename to routes/indexRouter.js diff --git a/template/home.js b/template/home.js index 7fac3ca..dcecd24 100644 --- a/template/home.js +++ b/template/home.js @@ -23,7 +23,7 @@ module.exports = { `; i = i + 1; } From 8575c5216db933a7e3b206d39b3d96d0f63e377b Mon Sep 17 00:00:00 2001 From: Ahn YoungSeon Date: Fri, 17 Dec 2021 15:14:45 +0900 Subject: [PATCH 28/77] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/indexRouter.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/routes/indexRouter.js b/routes/indexRouter.js index 972d603..0564448 100644 --- a/routes/indexRouter.js +++ b/routes/indexRouter.js @@ -7,8 +7,6 @@ router.get('/', function(request, response) { console.log('path: /'); console.log(request) const body = home.HOME(request.user); - console.log('1'); - console.log(request.session); response.send(home.HTML(body)); }); From 96ccba8f0f249fa80de56704bdcafb56c1e26521 Mon Sep 17 00:00:00 2001 From: Ahn YoungSeon Date: Fri, 17 Dec 2021 15:23:43 +0900 Subject: [PATCH 29/77] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/indexRouter.js | 1 - template/home.js | 27 +++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/routes/indexRouter.js b/routes/indexRouter.js index 0564448..c604d9d 100644 --- a/routes/indexRouter.js +++ b/routes/indexRouter.js @@ -5,7 +5,6 @@ const home = require('../template/home.js'); router.get('/', function(request, response) { console.log('path: /'); - console.log(request) const body = home.HOME(request.user); response.send(home.HTML(body)); }); diff --git a/template/home.js b/template/home.js index 592a97c..00713d8 100644 --- a/template/home.js +++ b/template/home.js @@ -10,16 +10,23 @@ module.exports = { }, HOME: function(user) { console.log('HOME: ', user); + var nickname = ''; + var profile_image = ''; + var menu_list =''; if (user === undefined) { nickname = '로그인하기'; profile_image = '/public/img/loginIcon.png'; } else { nickname = user.nickname; profile_image = user.profile_image; + menu_list = ` + + + + ` } return ` - - + + Hi Alcohol - - - - Hi Alcohol + + + `; + }, + TAIL: function() { + return ` + + + ` + } +}; \ No newline at end of file From f9c1b19b67aca07f691dcee20811d5203c9dfa5e Mon Sep 17 00:00:00 2001 From: alreadynyeong Date: Fri, 17 Dec 2021 19:43:55 +0900 Subject: [PATCH 35/77] =?UTF-8?q?home=20=EA=B2=80=EC=83=89=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/home.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/template/home.js b/template/home.js index b121220..1493e1f 100644 --- a/template/home.js +++ b/template/home.js @@ -112,12 +112,12 @@ module.exports = { - +

이달의 술 추천

소다주

From e56243fb190e44a052f88a361725b227e7293111 Mon Sep 17 00:00:00 2001 From: Ahn YoungSeon Date: Fri, 17 Dec 2021 19:44:58 +0900 Subject: [PATCH 36/77] =?UTF-8?q?#23=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=A7=81=ED=81=AC=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/likeRouter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/likeRouter.js b/routes/likeRouter.js index a1a6dd6..c7ada7e 100644 --- a/routes/likeRouter.js +++ b/routes/likeRouter.js @@ -19,7 +19,7 @@ router.get('/', function(request, response) { console.log(result[0].createdate) for (var i = 0;i < result.length; i++) { list += ` - +

${result[i].title}

From 4a64292fd33a7e26010cab23a3bbc78558cf4985 Mon Sep 17 00:00:00 2001 From: Ahn YoungSeon Date: Fri, 17 Dec 2021 19:53:08 +0900 Subject: [PATCH 37/77] =?UTF-8?q?#23=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=A7=81=ED=81=AC=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/likeRouter.js | 61 ++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/routes/likeRouter.js b/routes/likeRouter.js index c7ada7e..7eb0b65 100644 --- a/routes/likeRouter.js +++ b/routes/likeRouter.js @@ -10,33 +10,40 @@ router.get('/', function(request, response) { if (request.user === undefined) { response.redirect('/'); } - db.query(`SELECT post.id, post.title, post.createdate, count(*) 'count' - FROM (SELECT * FROM post WHERE post.userId=${request.user.id}) post, liked - WHERE post.id = liked.postId group by post.id`, - function(err, result) { - console.log(result); - var list =''; - console.log(result[0].createdate) - for (var i = 0;i < result.length; i++) { - list += ` -
-
-
-

${result[i].title}

-
${request.user.nickname} | ${result[i].createdate}
-
- -
-
- ` - } - var body = template.HOME(list); - var html = template.HTML(body); - response.send(html); - }) + if (request.query.id === undefined) { + db.query(`SELECT post.id, post.title, post.createdate, count(*) 'count' + FROM (SELECT * FROM post WHERE post.userId=${request.user.id}) post, liked + WHERE post.id = liked.postId group by post.id`, + function(err, result) { + console.log(result); + var list =''; + console.log(result[0].createdate) + for (var i = 0;i < result.length; i++) { + list += ` + +
+
+

${result[i].title}

+
${request.user.nickname} | ${result[i].createdate}
+
+ +
+
+ ` + } + var body = template.HOME(list); + var html = template.HTML(body); + response.send(html); + }) + } else { + db.query(`SELECT * FROM post WHERE id=?`, [request.query.id], function(err, result) { + + }) + } + }); module.exports = router; \ No newline at end of file From a453b89a7cd3c2319280b71aeb906202d069f548 Mon Sep 17 00:00:00 2001 From: Ahn YoungSeon Date: Fri, 17 Dec 2021 21:25:13 +0900 Subject: [PATCH 38/77] =?UTF-8?q?home=20menu=20button=20link=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/board_list.js | 2 +- template/home.js | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/template/board_list.js b/template/board_list.js index 189f557..52c3d83 100644 --- a/template/board_list.js +++ b/template/board_list.js @@ -9,7 +9,7 @@ module.exports = {
diff --git a/template/home.js b/template/home.js index 1493e1f..8ba562a 100644 --- a/template/home.js +++ b/template/home.js @@ -19,11 +19,11 @@ module.exports = { } else { nickname = user.nickname; profile_image = user.profile_image; - // menu_list = ` - // - // - // - // ` + menu_list = ` + + + + ` } return ` @@ -95,10 +95,6 @@ module.exports = { ${menu_list} - - - - // login 시에만 보이게 할 예정
- ` - }, - HEAD: function() { - return ` - + @@ -74,12 +35,36 @@ module.exports = { - `; - }, - TAIL: function() { - return ` - +
+
+
+
+ +
+ +
+ +
+ +
+
+
+ +
+
+ +
+ +
+
+ + ` } + }; \ No newline at end of file From da29d17064a32c8498f2cf846579311ca231a83c Mon Sep 17 00:00:00 2001 From: alreadynyeong Date: Fri, 17 Dec 2021 23:15:36 +0900 Subject: [PATCH 43/77] =?UTF-8?q?#10=20=EA=B2=80=EC=83=89=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=20=EC=97=86=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC,=20=EC=B5=9C=EC=86=8C=20=EB=84=93=EC=9D=B4?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/search_list.css | 1 + routes/search_listRouter.js | 97 +++++++++++++++++++++++++------------ template/search_list.js | 27 +---------- 3 files changed, 69 insertions(+), 56 deletions(-) diff --git a/public/css/search_list.css b/public/css/search_list.css index fe13f33..ef2421d 100644 --- a/public/css/search_list.css +++ b/public/css/search_list.css @@ -39,6 +39,7 @@ header h2 { } main { + min-width: 500px; padding-top: 100px; } diff --git a/routes/search_listRouter.js b/routes/search_listRouter.js index ceeb093..c4d1af5 100644 --- a/routes/search_listRouter.js +++ b/routes/search_listRouter.js @@ -4,6 +4,8 @@ const search_list = require('../template/search_list.js'); var db = require('../config/db'); // db.js 폴더 경로 var url = require('url'); + + class Item { cocktail = '' @@ -11,42 +13,77 @@ class Item } router.get('/', async function(request, response){ - var _url = request.url; - var queryData = url.parse(_url, true).query; - sql2 = `select recipe.cocktail, material.material from recipe, material where recipe.id=any(select recipeId from inclusion - where materialId = any(select id from material - where material= '${queryData.keyword}' or material = any(select alcolType from product where name='${queryData.keyword}'))) - and material.id =any(select inclusion.materialId from inclusion where inclusion.recipeId= recipe.id )` + sql = `select recipe.cocktail, material.material from recipe, material where + material.id =any(select inclusion.materialId from inclusion where inclusion.recipeId= recipe.id )` + + + + db.query(sql, function(err0, result0){ + var _url = request.url; + var queryData = url.parse(_url, true).query; + console.log(queryData.query) - db.query(sql2, function(err, result){ + sql2 = `select recipe.cocktail, material.material from recipe, material where recipe.id=any(select recipeId from inclusion + where materialId = any(select id from material + where material= '${queryData.keyword}' or material = any(select alcolType from product where name='${queryData.keyword}'))) + and material.id =any(select inclusion.materialId from inclusion where inclusion.recipeId= recipe.id )` + + if(err0) throw err0; + + db.query(sql2, function(err, result){ - // console.log('sql1:',result); + if (err) throw err; + var recipe_list = []; + var index = 0; - if (err) throw err; - var recipe_list = []; - var index = 0; - for (var i = 0;i < result.length - 1;i++) { - var item = new Item(); - item.cocktail = result[i].cocktail; - item.materials[0] = result[i].material; - recipe_list[index] = item; - for (var j = i + 1; j < result.length; j++){ - if (result[i].cocktail === result[j].cocktail) { - recipe_list[index].materials[recipe_list[index].materials.length] = result[j].material; - } else { - index++; - i = j - 1; - break ; + if(result0.length===0){ + for (var i = 0;i < result0.length - 1;i++) { + var item = new Item(); + item.cocktail = result0[i].cocktail; + item.materials[0] = result0[i].material; + recipe_list[index] = item; + for (var j = i + 1; j < result0.length; j++){ + if (result0[i].cocktail === result0[j].cocktail) { + recipe_list[index].materials[recipe_list[index].materials.length] = result0[j].material; + } else { + index++; + i = j - 1; + break ; + } + } + } + + var list = search_list.LIST(recipe_list) + var html = search_list.HTML( list) + } else{ + for (var i = 0;i < result.length - 1;i++) { + var item = new Item(); + item.cocktail = result[i].cocktail; + item.materials[0] = result[i].material; + recipe_list[index] = item; + for (var j = i + 1; j < result.length; j++){ + if (result[i].cocktail === result[j].cocktail) { + recipe_list[index].materials[recipe_list[index].materials.length] = result[j].material; + } else { + index++; + i = j - 1; + break ; + } + } } + + var list = search_list.LIST(recipe_list) + var html = search_list.HTML( list) } - } - // console.log(recipe_list) - var list = search_list.LIST(recipe_list) - var html = search_list.HTML( list) - - response.send(html); - }); + + console.log(recipe_list) + + + response.send(html); + }); + }) + }); module.exports = router; \ No newline at end of file diff --git a/template/search_list.js b/template/search_list.js index b0e42f9..ba7ab4b 100644 --- a/template/search_list.js +++ b/template/search_list.js @@ -64,25 +64,8 @@ module.exports = { - ${list} - -
- 내 마음대로 주 -
>
-

-
-
체리
-
맥콜
-
사이다
-
소주
-
깔라만씨 토닉워터
-
토닉워터
-
토닉워터
-
-
- @@ -149,15 +132,7 @@ module.exports = { var i = 0; var list = ''; - var name = ` -
- ${result[i].cocktail} -
>
-

`; - // var mat = ` - //
- //
${result[i].cocktail}
- //
`; + while(i Date: Fri, 17 Dec 2021 23:16:28 +0900 Subject: [PATCH 44/77] hialcohol icon home page redirect link add --- template/board.js | 3 ++- template/board_write.js | 2 +- template/home.js | 2 +- template/likes_list.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/template/board.js b/template/board.js index 3acd921..606facf 100644 --- a/template/board.js +++ b/template/board.js @@ -43,7 +43,7 @@ module.exports = { // login 시에만 보이게 할 예정
- + @@ -78,6 +78,7 @@ module.exports = { + + + + + Hi Alcohol recipe + + + + + + + + +
+ +
+
${name}
+
${content}
+
+ +
+ + + + ` + } +}; + \ No newline at end of file From 3c790b84020b5236b2476d3298342c67a137fa2b Mon Sep 17 00:00:00 2001 From: parksewon Date: Sat, 18 Dec 2021 01:12:21 +0900 Subject: [PATCH 53/77] =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EB=A7=81=ED=81=AC?= =?UTF-8?q?=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/board_write_css.css | 11 ++++++++--- template/board.js | 5 ++++- template/board_view.js | 35 +++++++++++++++++----------------- template/board_write.js | 4 ++-- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/public/css/board_write_css.css b/public/css/board_write_css.css index 9505852..187966d 100644 --- a/public/css/board_write_css.css +++ b/public/css/board_write_css.css @@ -1,12 +1,17 @@ body { - padding: 20px; + /* padding: 20px; margin: 0 auto; - width: 40%; + width: 40%; */ + padding: 20px; + margin-left: 10%; + margin-right: 10%; background-color: #242424; font-size: 15px; } - +.board_wrap{ + text-align: center; +} .main_title{ display: flex; diff --git a/template/board.js b/template/board.js index a452ae4..fe4d1d4 100644 --- a/template/board.js +++ b/template/board.js @@ -44,7 +44,10 @@ module.exports = {
- +
+ +
+
diff --git a/template/board_view.js b/template/board_view.js index f235e26..c8f1620 100644 --- a/template/board_view.js +++ b/template/board_view.js @@ -23,7 +23,7 @@ module.exports = { - 공지사항 + 게시글 상세보기 페이지
- -
${content}
From 4ecede4365cd16cfce13cc85c6daee1e2474229a Mon Sep 17 00:00:00 2001 From: alreadynyeong Date: Sun, 19 Dec 2021 09:41:37 +0900 Subject: [PATCH 75/77] =?UTF-8?q?recipe=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/recipe.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/recipe.sql b/config/recipe.sql index fb4c08a..da45ef8 100644 --- a/config/recipe.sql +++ b/config/recipe.sql @@ -31,7 +31,7 @@ VALUES(14, '멜론 볼', '멜론 리큐어 1oz : 오렌지 주스 2oz: 멜론 INSERT INTO `recipe` VALUES(15, '선 스트로크', '보드카 4: 트리폴 섹 4: 그레이프 주스9', '잔에 얼음을 충분히 넣고, 재료를 부은 다음 잘 저어 마신다.'); INSERT INTO `recipe` -VALUES(16, '오렌지 봉 봉', '보드카 3: 크림 드 카카오 화이트 2: 오렌지 주스2: 오렌지 퀴라소 1', '잘게 부순 얼음과 재료를 블랜더에 모두 넣고 블렌딩한 다음 진에 얼음과 함께 옮겨 붓는다. 그러나 블렌더가 없을 경우에는 쉐이커에 얼음과 재료 를 넣고 힘차게 잘 흔들어서 얼음과 함께 잔에 옮겨 붓는다.'); +VALUES(16, '오렌지 봉 봉', '보드카 3: 크림 드 카카오 화이트 2: 오렌지 주스2: 오렌지 퀴라소 1', '잘게 부순 얼음과 재료를 블랜더에 모두 넣고 블렌딩한 다음 진에 얼음과 함께 옮겨 붓는다. 블렌더가 없을 경우에는 쉐이커에 얼음과 재료 를 넣고 힘차게 잘 흔들어서 얼음과 함께 잔에 옮겨 붓는다.'); INSERT INTO `recipe` VALUES(17, '발레 러스', '보드카 2: 크림 드 카시스 1: 칼린스믹스 6', '잔에 얼음과 재료를 넣고 잘 저어 마신다.'); INSERT INTO `recipe` @@ -42,7 +42,7 @@ VALUES(18, '블랙 러시안', '보드카 5: 깔루아 2', '보드카와 깔루 INSERT INTO `recipe` VALUES(19, '데킬라 선라이즈', '적당량', '데킬라에 오렌지주스 적당량을 넣고 저은 후 그레나딘 시럽을 잔 안 쪽으로 천천히 따른다.'); INSERT INTO `recipe` -VALUES(20, '데킬라 선셋', '데킬라 1: 레몬주스 1', '데킬라, 레몬주스, 시럽을 잘게 부순 얼음 3/4 컵과 함께께 섞어준 후 레몬 슬라이스로 장식한다.'); +VALUES(20, '데킬라 선셋', '데킬라 1: 레몬주스 1', '데킬라, 레몬주스, 시럽을 잘게 부순 얼음 3/4 컵과 함께 섞어준 후 레몬 슬라이스로 장식한다.'); INSERT INTO `recipe` VALUES(21, '마가리타', '데킬라 2: 쿠앵트로 1: 라임주스 1', '데킬라, 쿠앵트로, 라임주주스를 넣어 흔든 후 소금으로 프로스팅 한 잔에 따른다.'); @@ -76,7 +76,7 @@ VALUES(31, '아이리시 커피', '적당량', '잔에 설탕을 넣은 후 뜨 -- 진 INSERT INTO `recipe` -VALUES(32, '싱가포리 슬링', '드라이진 2: 체리브랜디 1: 레몬주스 1/2', '드라이진과 체리브랜디에 레몬주스 1/2개분을 넣고 흔든 후 적당량의 소다수로 채운 후 슬라이스로 장식한다.'); +VALUES(32, '싱가포르 슬링', '드라이진 2: 체리브랜디 1: 레몬주스 1/2', '드라이진과 체리브랜디에 레몬주스 1/2개분을 넣고 흔든 후 적당량의 소다수로 채운 후 슬라이스로 장식한다.'); INSERT INTO `recipe` VALUES(33, '김렛', '드라이진 3: 라임주스1', '드라이진과 라임주스를 넣고 흔든다.'); INSERT INTO `recipe` From 577b53955df68103e9d9bc5c1e30de19432799b1 Mon Sep 17 00:00:00 2001 From: parksewon Date: Sun, 19 Dec 2021 09:44:10 +0900 Subject: [PATCH 76/77] =?UTF-8?q?#13=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/boardDeleteProcessRouter.js | 21 +++++++++++++++------ routes/boardRouter.js | 2 +- routes/boardUpdateProcessRouter.js | 4 +++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/routes/boardDeleteProcessRouter.js b/routes/boardDeleteProcessRouter.js index 7eeeb59..85b3790 100644 --- a/routes/boardDeleteProcessRouter.js +++ b/routes/boardDeleteProcessRouter.js @@ -10,13 +10,22 @@ const db = require('../config/db.js'); router.get('/', function(request, response) { queryData = request.query; + + if(!request.isAuthenticated()){ + response.send(''); + }else{ + if( userId !== request.user.kakaoid){ + response.send(''); + }else{ + db.query(`DELETE FROM post WHERE id=?`, [queryData.id], function(err, result){ + if (err) console.error("err : " + err); + response.redirect('/board'); + }) + } - db.query(`DELETE FROM post WHERE id=?`, [queryData.id], function(err, result){ - if (err) console.error("err : " + err); - response.redirect('/board'); - }) - - + } }); diff --git a/routes/boardRouter.js b/routes/boardRouter.js index 75c4943..43ed820 100644 --- a/routes/boardRouter.js +++ b/routes/boardRouter.js @@ -63,7 +63,7 @@ router.get('/view', function(request, response){ var title = result2[0].title; var userId = result2[0].userId; - var date = result2[0].createdate; + var date = result2[0].updatedate; var like_num = 10000; // 좋아요 연결 후 반영하기 var content = result2[0].content; diff --git a/routes/boardUpdateProcessRouter.js b/routes/boardUpdateProcessRouter.js index fe80718..1d1e6ca 100644 --- a/routes/boardUpdateProcessRouter.js +++ b/routes/boardUpdateProcessRouter.js @@ -4,6 +4,7 @@ const res = require('express/lib/response'); const { fstat } = require('fs'); const router = express.Router(); const mysql = require('mysql'); +const { DATE } = require('mysql/lib/protocol/constants/types'); const db = require('../config/db.js'); @@ -14,8 +15,9 @@ router.post('/', function(request, response) { let title = body.title; let content = body.content; + let now = new Date(); - db.query(`UPDATE post SET title=?, content=?, updatedate=? WHERE id=?`, [title, content, null, queryData.id], function(err, result){ + db.query(`UPDATE post SET title=?, content=?, updatedate=? WHERE id=?`, [title, content, now, queryData.id], function(err, result){ if (err) console.error("err : " + err); response.redirect('/board/view?id=' + queryData.id); }) From 422f7b6ac06de58eb2fb0b9e2696be21e8d9c2eb Mon Sep 17 00:00:00 2001 From: alreadynyeong Date: Sun, 19 Dec 2021 12:47:17 +0900 Subject: [PATCH 77/77] =?UTF-8?q?#38=20=EB=A9=94=EB=89=B4=EC=99=80=20?= =?UTF-8?q?=EC=A7=80=EB=8F=84=EA=B0=80=20=EA=B2=B9=EC=B9=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/menu.css | 1 + 1 file changed, 1 insertion(+) diff --git a/public/css/menu.css b/public/css/menu.css index d478998..9e3a263 100644 --- a/public/css/menu.css +++ b/public/css/menu.css @@ -71,6 +71,7 @@ position: absolute; top: 0; left: -50%; + z-index: 2; } .close_btn { padding-left: 20px;