diff --git a/.gitignore b/.gitignore index 9c78d669..5c390675 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .idea/ private/config.json .sass-cache/ +node_modules/ + diff --git a/.meteor/.finished-upgraders b/.meteor/.finished-upgraders index 61ee3132..8f397c7d 100644 --- a/.meteor/.finished-upgraders +++ b/.meteor/.finished-upgraders @@ -10,3 +10,10 @@ notices-for-facebook-graph-api-2 1.2.0-meteor-platform-split 1.2.0-cordova-changes 1.2.0-breaking-changes +1.3.0-split-minifiers-package +1.3.5-remove-old-dev-bundle-link +1.4.0-remove-old-dev-bundle-link +1.4.1-add-shell-server-package +1.4.3-split-account-service-packages +1.5-add-dynamic-import-package +1.7-split-underscore-from-meteor-base diff --git a/.meteor/.gitignore b/.meteor/.gitignore index 40830374..501f92e4 100644 --- a/.meteor/.gitignore +++ b/.meteor/.gitignore @@ -1 +1,2 @@ +dev_bundle local diff --git a/.meteor/packages b/.meteor/packages index 6b5dbbf5..8e5321f3 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -3,31 +3,37 @@ # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. -accounts-github -service-configuration +accounts-github@1.4.2 +service-configuration@1.0.11 iron:router -accounts-facebook +accounts-facebook@1.3.2 momentjs:moment -reactive-var -underscore -accounts-password +reactive-var@1.0.11 +underscore@1.0.10 +accounts-password@1.5.1 mizzao:user-status chart:chart -fourseven:scss semantic:ui flemay:less-autoprefixer -standard-minifiers -meteor-base -mobile-experience -mongo -blaze-html-templates -session -jquery -tracker -logging -reload -random -ejson -spacebars -check +meteor-base@1.4.0 +mobile-experience@1.0.5 +mongo@1.6.0 +blaze-html-templates@1.0.4 +session@1.2.0 +jquery@1.11.10 +tracker@1.2.0 +logging@1.1.20 +reload@1.2.0 +random@1.1.0 +ejson@1.1.0 +spacebars@1.0.12 +check@1.3.1 +standard-minifier-css@1.5.2 +standard-minifier-js@2.4.0 +shell-server@0.4.0 +facebook-config-ui@1.0.2 +github-config-ui@1.0.1 +dynamic-import@0.5.0 +fourseven:scss +accounts-google diff --git a/.meteor/release b/.meteor/release index 3a05e0a2..91e05fc1 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@1.2.1 +METEOR@1.8.0.2 diff --git a/.meteor/versions b/.meteor/versions index 392b1db0..56255630 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,97 +1,119 @@ -accounts-base@1.2.2 -accounts-facebook@1.0.6 -accounts-github@1.0.6 -accounts-oauth@1.1.8 -accounts-password@1.1.4 -autoupdate@1.2.4 -babel-compiler@5.8.24_1 -babel-runtime@0.1.4 -base64@1.0.4 -binary-heap@1.0.4 -blaze@2.1.3 -blaze-html-templates@1.0.1 -blaze-tools@1.0.4 -boilerplate-generator@1.0.4 -caching-compiler@1.0.0 -caching-html-compiler@1.0.2 -callback-hook@1.0.4 +accounts-base@1.4.3 +accounts-facebook@1.3.2 +accounts-github@1.4.2 +accounts-google@1.3.2 +accounts-oauth@1.1.16 +accounts-password@1.5.1 +allow-deny@1.1.0 +autoupdate@1.5.1 +babel-compiler@7.2.4 +babel-runtime@1.3.0 +base64@1.0.11 +binary-heap@1.0.11 +blaze@2.3.3 +blaze-html-templates@1.1.2 +blaze-tools@1.0.10 +boilerplate-generator@1.6.0 +caching-compiler@1.2.1 +caching-html-compiler@1.1.3 +callback-hook@1.1.0 chart:chart@1.0.1-beta.4 -check@1.1.0 -coffeescript@1.0.11 -ddp@1.2.2 -ddp-client@1.2.1 -ddp-common@1.2.2 -ddp-rate-limiter@1.0.0 -ddp-server@1.2.2 -deps@1.0.9 -diff-sequence@1.0.1 -ecmascript@0.1.6 -ecmascript-runtime@0.2.6 -ejson@1.0.7 -email@1.0.8 -facebook@1.2.2 -fastclick@1.0.7 +check@1.3.1 +coffeescript@2.3.2_1 +coffeescript-compiler@2.3.2_1 +ddp@1.4.0 +ddp-client@2.3.3 +ddp-common@1.4.0 +ddp-rate-limiter@1.0.7 +ddp-server@2.2.0 +deps@1.0.12 +diff-sequence@1.1.1 +dynamic-import@0.5.1 +ecmascript@0.12.4 +ecmascript-runtime@0.7.0 +ecmascript-runtime-client@0.8.0 +ecmascript-runtime-server@0.7.1 +ejson@1.1.0 +email@1.2.3 +es5-shim@4.8.0 +facebook-config-ui@1.0.2 +facebook-oauth@1.5.0 +fetch@0.1.0 flemay:less-autoprefixer@1.2.0 -fourseven:scss@3.4.1 -geojson-utils@1.0.4 -github@1.1.4 -hot-code-push@1.0.0 -html-tools@1.0.5 -htmljs@1.0.5 -http@1.1.1 -id-map@1.0.4 +fourseven:scss@4.10.0 +geojson-utils@1.0.10 +github-config-ui@1.0.1 +github-oauth@1.2.2 +google-oauth@1.2.6 +hot-code-push@1.0.4 +html-tools@1.0.11 +htmljs@1.0.11 +http@1.4.2 +id-map@1.1.0 +inter-process-messaging@0.1.0 iron:controller@1.0.12 iron:core@1.0.11 iron:dynamic-template@1.0.12 iron:layout@1.0.12 iron:location@1.0.11 iron:middleware-stack@1.1.0 -iron:router@1.0.12 -iron:url@1.0.11 -jquery@1.11.4 -launch-screen@1.0.4 -livedata@1.0.15 -localstorage@1.0.5 -logging@1.0.8 -meteor@1.1.10 -meteor-base@1.0.1 -minifiers@1.1.7 -minimongo@1.0.10 -mizzao:timesync@0.3.4 -mizzao:user-status@0.6.6 -mobile-experience@1.0.1 -mobile-status-bar@1.0.6 -momentjs:moment@2.10.6 -mongo@1.1.3 -mongo-id@1.0.1 -npm-bcrypt@0.7.8_2 -npm-mongo@1.4.39_1 -oauth@1.1.6 -oauth2@1.1.5 -observe-sequence@1.0.7 -ordered-dict@1.0.4 -promise@0.5.1 -random@1.0.5 -rate-limit@1.0.0 -reactive-dict@1.1.3 -reactive-var@1.0.6 -reload@1.1.4 -retry@1.0.4 -routepolicy@1.0.6 -semantic:ui@2.1.6 -semantic:ui-data@2.1.6 -service-configuration@1.0.5 -session@1.1.1 -sha@1.0.4 -spacebars@1.0.7 -spacebars-compiler@1.0.7 -srp@1.0.4 -standard-minifiers@1.0.2 -templating@1.1.5 -templating-tools@1.0.0 -tracker@1.0.9 -ui@1.0.8 -underscore@1.0.4 -url@1.0.5 -webapp@1.2.3 -webapp-hashing@1.0.5 +iron:router@1.1.2 +iron:url@1.1.0 +jquery@1.11.11 +launch-screen@1.1.1 +livedata@1.0.18 +localstorage@1.2.0 +logging@1.1.20 +meteor@1.9.2 +meteor-base@1.4.0 +minifier-css@1.4.1 +minifier-js@2.4.0 +minimongo@1.4.5 +mizzao:timesync@0.5.0 +mizzao:user-status@0.6.8 +mobile-experience@1.0.5 +mobile-status-bar@1.0.14 +modern-browsers@0.1.3 +modules@0.13.0 +modules-runtime@0.10.3 +momentjs:moment@2.24.0 +mongo@1.6.0 +mongo-decimal@0.1.1 +mongo-dev-server@1.1.0 +mongo-id@1.0.7 +npm-bcrypt@0.9.3 +npm-mongo@3.1.1 +oauth@1.2.7 +oauth2@1.2.1 +observe-sequence@1.0.16 +ordered-dict@1.1.0 +promise@0.11.2 +random@1.1.0 +rate-limit@1.0.9 +reactive-dict@1.2.1 +reactive-var@1.0.11 +reload@1.2.0 +retry@1.1.0 +routepolicy@1.1.0 +semantic:ui@2.3.1 +semantic:ui-data@2.3.1 +service-configuration@1.0.11 +session@1.2.0 +sha@1.0.9 +shell-server@0.4.0 +socket-stream-client@0.2.2 +spacebars@1.0.15 +spacebars-compiler@1.1.3 +srp@1.0.12 +standard-minifier-css@1.5.2 +standard-minifier-js@2.4.0 +templating@1.3.2 +templating-compiler@1.3.3 +templating-runtime@1.3.2 +templating-tools@1.1.2 +tracker@1.2.0 +ui@1.0.13 +underscore@1.0.10 +url@1.2.0 +webapp@1.7.2 +webapp-hashing@1.0.9 diff --git a/README.md b/README.md index f6f76b9c..611fc317 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,34 @@ To configure login, edit `private/config.json`. Alternatively, you can configure In `private/config.json` (or the corresponding environment variables), provide the appropriate application id/secret combinations for either Facebook or Github authentication, or choose to disable them. +##### GitHub OAuth +1. Navigate to settings page of GitHub user/organization +1. Click "OAuth Apps" +1. Click "New OAuth App" and fill out the information (name, email, logo) + * For Authorization callback URL, put `{YOUR URL}/_oauth/github`, and `localhost:3000/_oauth/github` for debugging +1. Copy the `client id` and `secret` to `private/config.json` + +##### Facebook OAuth +1. Go to the [Facebook App Dashboard](https://developers.facebook.com/apps) +1. Click "Add a New App" + * For scenario, choose "Integrate Facebook login" +1. Put your info (event name, email, logo) +1. On the sidebar, select "Facebook login" > "Settings" +1. Put `{YOUR URL}/_oauth/facebook?close` and `{YOUR URL}/_oauth/facebook` in "Valid OAuth Redirect URLs" +1. From "Settings" > "Basic", copy the `app ID` and `secret` to `private/config.json` +1. Unclear if necessary, but you can use this [privacy policy generator](https://termsfeed.com/privacy-policy/generator/) to generate a policy for your app, before making the app "live" (you can only test from localhost if the app is in "development") + +##### Google OAuth +1. Go to [Google Sign-In](https://developers.google.com/identity/sign-in/web/sign-in) +1. Click on "Configure a project" +1. Create a new project for HelpQ + * For "Where are you calling from?", select "Web Server" + * Put `{YOUR URL}/_oauth/google?close` and `{YOUR URL}/_oauth/google` in "Valid OAuth Redirect URLs" + * Add localhost for testing, if needed +1. Click the link to go to the GCP console once you're done +1. Put your info (event name, email, logo) on the "OAuth Consent Screen" tab +1. Copy `client id` and `secret` to `private/config.json` + #### Branding For front end branding, edit `client/stylesheets/scss/_branding.scss` diff --git a/client/app.html b/client/app.html index 163f6796..7b06b7ae 100644 --- a/client/app.html +++ b/client/app.html @@ -7,8 +7,8 @@ type="image/png" href="assets/images/favicon.png"> - - + + diff --git a/client/components/login/login.html b/client/components/login/login.html index f17399e3..8a0f3b39 100644 --- a/client/components/login/login.html +++ b/client/components/login/login.html @@ -29,5 +29,10 @@
{{/if}} + + {{#if enabled.google}} +
+ + {{/if}} diff --git a/client/components/login/login.js b/client/components/login/login.js index 0ee310ef..2a8eade4 100644 --- a/client/components/login/login.js +++ b/client/components/login/login.js @@ -13,6 +13,11 @@ Template.login.events({ loginStyle: 'redirect' }); }, + 'click #login-google': function(){ + Meteor.loginWithGoogle({ + loginStyle: 'redirect', + }); + }, 'click #login-password': function(e, t){ loginPassword(t); }, diff --git a/client/stylesheets/scss/_branding.scss b/client/stylesheets/scss/_branding.scss index 76476f68..3f13feda 100644 --- a/client/stylesheets/scss/_branding.scss +++ b/client/stylesheets/scss/_branding.scss @@ -32,6 +32,9 @@ $facebook-blue: #3b5998; // Github Gray $github-gray: #e4e4e4; +// Google Red +$google-red: #DB3236; + // =============================================================================== //------------------------------ diff --git a/client/stylesheets/scss/elements/_pushbuttons.scss b/client/stylesheets/scss/elements/_pushbuttons.scss index 3630c1f1..e427671e 100644 --- a/client/stylesheets/scss/elements/_pushbuttons.scss +++ b/client/stylesheets/scss/elements/_pushbuttons.scss @@ -73,5 +73,9 @@ button.push { border-bottom-color: lighten($facebook-blue, 10); } + &.google { + background: $google-red; + border-bottom-color: lighten($google-red, 10); + } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..9b1d55cd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,456 @@ +{ + "name": "helpq", + "version": "0.2.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", + "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", + "requires": { + "regenerator-runtime": "^0.12.0" + } + }, + "bcrypt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.0.tgz", + "integrity": "sha512-gjicxsD4e5U3nH0EqiEb5y+fKpsZ7F52wcnmNfu45nxnolWVAYh7NgbdfilY+5x1v6cLspxmzz4hf+ju2pFxhA==", + "requires": { + "nan": "2.10.0", + "node-pre-gyp": "0.10.2" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.3.3", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true + }, + "needle": { + "version": "2.2.1", + "bundled": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.2", + "bundled": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "readable-stream": { + "version": "2.3.5", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "sax": { + "version": "1.2.4", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.0.3", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "tar": { + "version": "4.4.4", + "bundled": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..f59da8a7 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "helpq", + "version": "0.2.1", + "description": "an extensible real-time queue application, for mentorship @ hackathons and classrooms http://ehz.io/HELPq-data", + "main": "client/app.js", + "directories": { + "lib": "lib" + }, + "dependencies": { + "@babel/runtime": "^7.3.4", + "bcrypt": "^3.0.0" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ehzhang/HELPq.git" + }, + "author": "ehzhang", + "license": "MIT", + "bugs": { + "url": "https://github.com/ehzhang/HELPq/issues" + }, + "homepage": "https://github.com/ehzhang/HELPq#readme" +} diff --git a/packages/facebook/.gitignore b/packages/facebook/.gitignore new file mode 100755 index 00000000..677a6fc2 --- /dev/null +++ b/packages/facebook/.gitignore @@ -0,0 +1 @@ +.build* diff --git a/packages/facebook/README.md b/packages/facebook/README.md new file mode 100755 index 00000000..f4c9f1c5 --- /dev/null +++ b/packages/facebook/README.md @@ -0,0 +1,3 @@ +# facebook + +An implementation of the Facebook OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. XXX link \ No newline at end of file diff --git a/packages/facebook/facebook_client.js b/packages/facebook/facebook_client.js new file mode 100755 index 00000000..6aa79125 --- /dev/null +++ b/packages/facebook/facebook_client.js @@ -0,0 +1,46 @@ +Facebook = {}; + +// Request Facebook credentials for the user +// +// @param options {optional} +// @param credentialRequestCompleteCallback {Function} Callback function to call on +// completion. Takes one argument, credentialToken on success, or Error on +// error. +Facebook.requestCredential = function (options, credentialRequestCompleteCallback) { + // support both (options, callback) and (callback). + if (!credentialRequestCompleteCallback && typeof options === 'function') { + credentialRequestCompleteCallback = options; + options = {}; + } + + var config = ServiceConfiguration.configurations.findOne({service: 'facebook'}); + if (!config) { + credentialRequestCompleteCallback && credentialRequestCompleteCallback( + new ServiceConfiguration.ConfigError()); + return; + } + + var credentialToken = Random.secret(); + var mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent); + var display = mobile ? 'touch' : 'popup'; + + var scope = "email"; + if (options && options.requestPermissions) + scope = options.requestPermissions.join(','); + + var loginStyle = OAuth._loginStyle('facebook', config, options); + + var loginUrl = + 'https://www.facebook.com/v2.8/dialog/oauth?client_id=' + config.appId + + '&redirect_uri=' + OAuth._redirectUri('facebook', config) + + '&display=' + display + '&scope=' + scope + + '&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl); + + OAuth.launchLogin({ + loginService: "facebook", + loginStyle: loginStyle, + loginUrl: loginUrl, + credentialRequestCompleteCallback: credentialRequestCompleteCallback, + credentialToken: credentialToken + }); +}; diff --git a/packages/facebook/facebook_configure.html b/packages/facebook/facebook_configure.html new file mode 100755 index 00000000..18ddeeb9 --- /dev/null +++ b/packages/facebook/facebook_configure.html @@ -0,0 +1,38 @@ + diff --git a/packages/facebook/facebook_configure.js b/packages/facebook/facebook_configure.js new file mode 100755 index 00000000..412ee505 --- /dev/null +++ b/packages/facebook/facebook_configure.js @@ -0,0 +1,12 @@ +Template.configureLoginServiceDialogForFacebook.helpers({ + siteUrl: function () { + return Meteor.absoluteUrl(); + } +}); + +Template.configureLoginServiceDialogForFacebook.fields = function () { + return [ + {property: 'appId', label: 'App ID'}, + {property: 'secret', label: 'App Secret'} + ]; +}; diff --git a/packages/facebook/facebook_server.js b/packages/facebook/facebook_server.js new file mode 100755 index 00000000..c30eb575 --- /dev/null +++ b/packages/facebook/facebook_server.js @@ -0,0 +1,95 @@ +Facebook = {}; + +OAuth.registerService('facebook', 2, null, function(query) { + + var response = getTokenResponse(query); + var accessToken = response.accessToken; + + // include all fields from facebook + // http://developers.facebook.com/docs/reference/login/public-profile-and-friend-list/ + var whitelisted = ['id', 'email', 'name', 'first_name', + 'last_name', 'link', 'gender', 'locale', 'age_range']; + + var identity = getIdentity(accessToken, whitelisted); + + var serviceData = { + accessToken: accessToken, + expiresAt: (+new Date) + (1000 * response.expiresIn) + }; + + + var fields = _.pick(identity, whitelisted); + _.extend(serviceData, fields); + + return { + serviceData: serviceData, + options: {profile: {name: identity.name}} + }; +}); + +// checks whether a string parses as JSON +var isJSON = function (str) { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } +}; + +// returns an object containing: +// - accessToken +// - expiresIn: lifetime of token in seconds +var getTokenResponse = function (query) { + var config = ServiceConfiguration.configurations.findOne({service: 'facebook'}); + if (!config) + throw new ServiceConfiguration.ConfigError(); + + var responseContent; + try { + // Request an access token + responseContent = HTTP.get( + "https://graph.facebook.com/v2.8/oauth/access_token", { + params: { + client_id: config.appId, + redirect_uri: OAuth._redirectUri('facebook', config), + client_secret: OAuth.openSecret(config.secret), + code: query.code + } + }).data; + } catch (err) { + throw _.extend(new Error("Failed to complete OAuth handshake with Facebook. " + err.message), + {response: err.response}); + } + + // Handle response + var fbAccessToken = responseContent.access_token; + var fbExpires = responseContent.expires_in; + + if (!fbAccessToken) { + throw new Error("Failed to complete OAuth handshake with facebook " + + "-- can't find access token in HTTP response. " + responseContent); + } + return { + accessToken: fbAccessToken, + expiresIn: fbExpires + }; +}; + +var getIdentity = function (accessToken, fields) { + try { + return HTTP.get("https://graph.facebook.com/v2.8/me", { + params: { + access_token: accessToken, + fields: fields.join(",") + } + }).data; + } catch (err) { + throw _.extend(new Error("Failed to fetch identity from Facebook. " + err.message), + {response: err.response}); + } +}; + +Facebook.retrieveCredential = function(credentialToken, credentialSecret) { + return OAuth.retrieveCredential(credentialToken, credentialSecret); +}; diff --git a/packages/facebook/package.js b/packages/facebook/package.js new file mode 100755 index 00000000..0698dadd --- /dev/null +++ b/packages/facebook/package.js @@ -0,0 +1,23 @@ +Package.describe({ + summary: "Facebook OAuth flow", + version: "1.2.2" +}); + +Package.onUse(function(api) { + api.use('oauth2', ['client', 'server']); + api.use('oauth', ['client', 'server']); + api.use('http', ['server']); + api.use('templating', 'client'); + api.use('underscore', 'server'); + api.use('random', 'client'); + api.use('service-configuration', ['client', 'server']); + + api.export('Facebook'); + + api.addFiles( + ['facebook_configure.html', 'facebook_configure.js'], + 'client'); + + api.addFiles('facebook_server.js', 'server'); + api.addFiles('facebook_client.js', 'client'); +}); diff --git a/private/config.json.template b/private/config.json.template index 51a57dae..573b8932 100644 --- a/private/config.json.template +++ b/private/config.json.template @@ -22,5 +22,5 @@ "queueEnabled": true, "expirationDelay": 1800000 }, - "defaultMentor": true + "defaultMentor": false }