Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First commit.

  • Loading branch information...
commit 3e6ef9b9a2ca6602a5829cb4ca971b75c9bf549c 0 parents
@jaredhanson authored
2  .gitignore
@@ -0,0 +1,2 @@
+.DS_Store
+node_modules
8 .npmignore
@@ -0,0 +1,8 @@
+*.md
+.DS_Store
+.git*
+Makefile
+docs/
+examples/
+support/
+test/
20 LICENSE
@@ -0,0 +1,20 @@
+(The MIT License)
+
+Copyright (c) 2011 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.
19 Makefile
@@ -0,0 +1,19 @@
+NODE = node
+TEST = vows
+TESTS ?= test/*-test.js
+
+test:
+ @NODE_ENV=test NODE_PATH=lib $(TEST) $(TEST_FLAGS) $(TESTS)
+
+docs: docs/api.html
+
+docs/api.html: lib/passport-foursquare/*.js
+ dox \
+ --title Passport-Foursquare \
+ --desc "Foursquare authentication strategy for Passport" \
+ $(shell find lib/passport-foursquare/* -type f) > $@
+
+docclean:
+ rm -f docs/*.{1,html}
+
+.PHONY: test docs docclean
27 README.md
@@ -0,0 +1,27 @@
+# Passport-Foursquare
+
+[Passport](https://github.com/jaredhanson/passport) strategy for authenticating
+with [Foursquare](https://foursquare.com/) using the OAuth 2.0 API.
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2011 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.
15 lib/passport-foursquare/index.js
@@ -0,0 +1,15 @@
+/**
+ * Module dependencies.
+ */
+var Strategy = require('./strategy');
+
+
+/**
+ * Framework version.
+ */
+exports.version = '0.1.0';
+
+/**
+ * Expose constructors.
+ */
+exports.Strategy = Strategy;
106 lib/passport-foursquare/strategy.js
@@ -0,0 +1,106 @@
+/**
+ * Module dependencies.
+ */
+var util = require('util')
+ , OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
+
+
+/**
+ * `Strategy` constructor.
+ *
+ * The Fourquare authentication strategy authenticates requests by delegating to
+ * Foursquare using the OAuth 2.0 protocol.
+ *
+ * Applications must supply a `verify` callback which accepts an `accessToken`,
+ * `refreshToken` and service-specific `profile`, and then calls the `done`
+ * callback supplying a `user`, which should be set to `false` if the
+ * credentials are not valid. If an exception occured, `err` should be set.
+ *
+ * Options:
+ * - `clientID` your Facebook application's App ID
+ * - `clientSecret` your Facebook application's App Secret
+ * - `callbackURL` URL to which Facebook will redirect the user after granting authorization
+ *
+ * Examples:
+ *
+ * passport.use(new FoursquareStrategy({
+ * clientID: '123-456-789',
+ * clientSecret: 'shhh-its-a-secret'
+ * callbackURL: 'https://www.example.net/auth/foursquare/callback'
+ * },
+ * function(accessToken, refreshToken, profile, done) {
+ * User.findOrCreate(..., function (err, user) {
+ * done(err, user);
+ * });
+ * }
+ * ));
+ *
+ * @param {Object} options
+ * @param {Function} verify
+ * @api public
+ */
+function Strategy(options, verify) {
+ options = options || {};
+ options.authorizationURL = options.authorizationURL || 'https://foursquare.com/oauth2/authorize';
+ options.tokenURL = options.tokenURL || 'https://foursquare.com/oauth2/access_token';
+
+ OAuth2Strategy.call(this, options, verify);
+ this.name = 'foursquare';
+
+ // NOTE: Due to OAuth 2.0 implementations arising at different points and
+ // drafts in the specification process, the parameter used to denote the
+ // access token is not always consistent. As of OAuth 2.0 draft 22,
+ // the parameter is named "access_token". However, Foursquare's
+ // implementation expects it to be named "oauth_token". For further
+ // information, refer to: https://developer.foursquare.com/docs/oauth.html
+ this._oauth2.setAccessTokenName("oauth_token");
+}
+
+/**
+ * Inherit from `OAuth2Strategy`.
+ */
+util.inherits(Strategy, OAuth2Strategy);
+
+
+/**
+ * Retrieve user profile from Foursquare.
+ *
+ * This function constructs a normalized profile, with the following properties:
+ *
+ * - `provider` always set to `foursquare`
+ * - `id` unique identifier for this user.
+ * - `name.familyName` user's last name
+ * - `name.givenName` user's first name
+ * - `gender` the user's gender: `male` or `female`
+ * - `emails` the proxied or contact email address granted by the user
+ *
+ * @param {String} accessToken
+ * @param {Function} done
+ * @api protected
+ */
+Strategy.prototype.userProfile = function(accessToken, done) {
+ this._oauth2.getProtectedResource('https://api.foursquare.com/v2/users/self', accessToken, function (err, body, res) {
+ if (err) { return done(err); }
+
+ try {
+ o = JSON.parse(body);
+
+ var profile = { provider: 'foursquare' };
+ profile.id = o.response.user.id;
+ profile.name = { familyName: o.response.user.lastName,
+ givenName: o.response.user.firstName };
+ profile.gender = o.response.user.gender;
+ profile.emails = [{ value: o.response.user.contact.email }];
+
+ done(null, profile);
+ } catch(e) {
+ done(e);
+ }
+ });
+}
+
+
+/**
+ * Expose `Strategy`.
+ */
+module.exports = Strategy;
16 package.json
@@ -0,0 +1,16 @@
+{
+ "name": "passport-foursquare",
+ "version": "0.1.0",
+ "description": "Foursquare authentication strategy for Passport.",
+ "author": "Jared Hanson <jaredhanson@gmail.com> (http://www.jaredhanson.net/)",
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/jaredhanson/passport-foursquare.git"
+ },
+ "main": "./lib/passport-foursquare",
+ "dependencies": {
+ "passport-oauth": ">= 0.0.1"
+ },
+ "engines": { "node": ">= 0.4.0" },
+ "keywords": ["passport", "foursquare", "auth", "authn", "authentication", "identity"]
+}
15 test/index-test.js
@@ -0,0 +1,15 @@
+var vows = require('vows');
+var assert = require('assert');
+var util = require('util');
+var foursquare = require('passport-foursquare');
+
+
+vows.describe('passport-foursquare').addBatch({
+
+ 'module': {
+ 'should report a version': function (x) {
+ assert.isString(foursquare.version);
+ },
+ },
+
+}).export(module);
105 test/strategy-test.js
@@ -0,0 +1,105 @@
+var vows = require('vows');
+var assert = require('assert');
+var util = require('util');
+var FoursquareStrategy = require('passport-foursquare/strategy');
+
+
+vows.describe('FoursquareStrategy').addBatch({
+
+ 'strategy': {
+ topic: function() {
+ return new FoursquareStrategy({
+ clientID: 'ABC123',
+ clientSecret: 'secret'
+ },
+ function() {});
+ },
+
+ 'should be named foursquare': function (strategy) {
+ assert.equal(strategy.name, 'foursquare');
+ },
+ },
+
+ 'strategy when loading user profile': {
+ topic: function() {
+ var strategy = new FoursquareStrategy({
+ clientID: 'ABC123',
+ clientSecret: 'secret'
+ },
+ function() {});
+
+ // mock
+ strategy._oauth2.getProtectedResource = function(url, accessToken, callback) {
+ var body = '{"meta":{"code":200},"notifications":[{"type":"notificationTray","item":{"unreadCount":0}}],"response":{"user":{"id":"1419","firstName":"Jared","lastName":"Hanson","photo":"https://playfoursquare.s3.amazonaws.com/userpix_thumbs/1419_1238423817.jpg","gender":"male","homeCity":"Oakland, CA","relationship":"self","type":"user","pings":false,"contact":{"phone":"5105551234","email":"jaredhanson@example.com","twitter":"jaredhanson","facebook":"500308595"}}}}';
+
+ callback(null, body, undefined);
+ }
+
+ return strategy;
+ },
+
+ 'when told to load user profile': {
+ topic: function(strategy) {
+ var self = this;
+ function done(err, profile) {
+ self.callback(err, profile);
+ }
+
+ process.nextTick(function () {
+ strategy.userProfile('access-token', done);
+ });
+ },
+
+ 'should not error' : function(err, req) {
+ assert.isNull(err);
+ },
+ 'should load profile' : function(err, profile) {
+ assert.equal(profile.provider, 'foursquare');
+ assert.equal(profile.id, '1419');
+ assert.equal(profile.name.familyName, 'Hanson');
+ assert.equal(profile.name.givenName, 'Jared');
+ assert.equal(profile.gender, 'male');
+ assert.length(profile.emails, 1);
+ assert.equal(profile.emails[0].value, 'jaredhanson@example.com');
+ },
+ },
+ },
+
+ 'strategy when loading user profile and encountering an error': {
+ topic: function() {
+ var strategy = new FoursquareStrategy({
+ clientID: 'ABC123',
+ clientSecret: 'secret'
+ },
+ function() {});
+
+ // mock
+ strategy._oauth2.getProtectedResource = function(url, accessToken, callback) {
+ callback(new Error('something-went-wrong'));
+ }
+
+ return strategy;
+ },
+
+ 'when told to load user profile': {
+ topic: function(strategy) {
+ var self = this;
+ function done(err, profile) {
+ self.callback(err, profile);
+ }
+
+ process.nextTick(function () {
+ strategy.userProfile('access-token', done);
+ });
+ },
+
+ 'should error' : function(err, req) {
+ assert.isNotNull(err);
+ },
+ 'should not load profile' : function(err, profile) {
+ assert.isUndefined(profile);
+ },
+ },
+ },
+
+}).export(module);
Please sign in to comment.
Something went wrong with that request. Please try again.