diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2b17982 --- /dev/null +++ b/.env.example @@ -0,0 +1,14 @@ +DB_ENCRYPTION_SALT="somesecretsaltthatis32characters" +REDIS_URL="redis://127.0.0.1:6379" +FROM="login@handshakejs.com" +SMTP_ADDRESS=smtp.sendgrid.net +SMTP_PORT=587 +SMTP_USERNAME=username +SMTP_PASSWORD=password +BACKUP_SMTP_ADDRESS=smtp.mandrillapp.com +BACKUP_SMTP_PORT=587 +BACKUP_SMTP_USERNAME=username +BACKUP_SMTP_PASSWORD=password +SUBJECT_TEMPLATE="Your code: {{authcode}}. Please enter it to login." +TEXT_TEMPLATE="Your code: {{authcode}}. Please enter it to login." +HTML_TEMPLATE="Your code: {{authcode}}. Please enter it to login." diff --git a/.gitignore b/.gitignore index b6df505..abc0ee7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ tmp/ tmp/pids/* log/ .env* +!.env.example .ruby-version node_modules diff --git a/.godir b/.godir new file mode 100644 index 0000000..8ddeef6 --- /dev/null +++ b/.godir @@ -0,0 +1 @@ +handshakejs-api diff --git a/Procfile b/Procfile index e1d4131..141a460 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: node app.js +web: handshakejs-api diff --git a/README.md b/README.md index 57babf5..b2e8c22 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [handshakejs](https://handshakejs.herokuapp.com) API Documentation -![](https://rawgithub.com/handshakejs/handshakejs-script/master/handshakejs.svg) +![](https://raw.githubusercontent.com/handshakejs/handshakejs-api/sm-go/handshakejs-logo.png) **API platform for authenticating users without requiring a password.** @@ -9,31 +9,50 @@ ### Heroku ```bash -git clone https://github.com/sendgrid/handshakejs-api.git +git clone https://github.com/scottmotte/handshakejs-api.git cd handshakejs-api -heroku create handshakejs +heroku create handshakejs-api -b https://github.com/kr/heroku-buildpack-go.git heroku addons:add sendgrid heroku addons:add redistogo -git push heroku master -heroku config:set FROM=login@yourapp.com +heroku config ``` -Next, create your first app. +Note the REDISTOGOURL, SENGRID_PASSWORD, and SENDGRID_USERNAME. + +``` +heroku config:set DB_ENCRYPTION_SALT="somesecretsaltthatis32characters" +heroku config:set REDIS_URL=REDISTOGOURL +heroku config:set FROM=you@youremail.com +heroku config:set SMTP_ADDRESS=smtp.sendgrid.net +heroku config:set SMTP_PORT=587 +heroku config:set SMTP_USERNAME=SENDGRID_USERNAME +heroku config:set SMTP_PASSWORD=SENDGRID_PASSWORD +heroku config:set SUBJECT_TEMPLATE="Your code: {{authcode}}. Please enter it to login." +heroku config:set TEXT_TEMPLATE="Your code: {{authcode}}. Please enter it to login." +heroku config:set HTML_TEMPLATE="Your code: {{authcode}}. Please enter it to login." +heroku config +``` + +Finally, deploy it. -```bash -curl -X POST https://handshakejs.herokuapp.com/api/v0/apps/create.json \ --d "email=you@email.com" \ --d "app_name=your_app_name" ``` +git push heroku master +``` + +Next, create your first app. Replace email and app_name with your information. + + Nice, that's all it takes to get your authentication system running. Now let's plug that into our app using the embeddable JavaScript. +### Old, this will move to a separate spot probably with instructions. + Place a script tag wherever you want the login form displayed. ```html + data-root_url="https://handshakejs-api.herokuapp.com"> ``` Get the latest [handshake.js here](https://github.com/sendgrid/handshakejs-script/blob/master/build/handshake.js). Replace the `data-app_name` with your own. @@ -63,21 +82,6 @@ Then you'd setup a route in your app at /login/success to do something like this end ``` -### Click to cloud (beta) - -You can optionally install using `click-to-cloud`. Click to cloud is a binary I'm building to make it easier to deploy -small application to cloud Paas like Heroku. I personally, use this approach, but your mileage may vary. - -First, [install click-to-cloud](https://github.com/scottmotte/click-to-cloud#installation) on your machine. - -Second, run the following command. - -```bash -click-to-cloud --repo https://github.com/sendgrid/handshakejs-api.git -``` - -That's it. That will install your application to Heroku. - ## API Overview The [handshakejs.herokuapp.com](https://handshakejs.herokuapp.com) API is based around REST. It uses standard HTTP authentication. [JSON](https://www.json.org/) is returned in all responses from the API, including errors. @@ -92,7 +96,7 @@ I've tried to make it as easy to use as possible, but if you have any feedback p ### API Endpoint -* https://handshakejs.herokuapp.com/api/v0 +* https://handshakejs-api.herokuapp.com/api/v1 ## Apps @@ -105,35 +109,41 @@ Pass an email and app_name to create your app at handshakejs.herokuapp.com. #### Definition ```bash -POST https://handshakejs.herokuapp.com/api/v0/apps/create.json +ANY https://handshakejs-api.herokuapp.com/api/v1/apps/create.json?app_name=[app_name]&email=[email]&salt=[salt] ``` #### Required Parameters -* email * app_name #### Optional Parameters +* email * salt #### Example Request -```bash -curl -X POST https://handshakejs.herokuapp.com/api/v0/apps/create.json \ --d "email=test@example.com" \ --d "app_name=myapp" -``` + #### Example Response ```javascript { - success: true, - app: { - email: "test@example.com", - app_name: "myapp", - salt: "the_default_generated_salt_that_you_should_keep_secret" - } + "apps": [{ + "email": "test@example.com", + "app_name": "myapp", + "salt": "the_default_generated_salt_that_you_should_keep_secret" + }] +} +``` + +#### Example Error +```javascript +{ + errors: [{ + "code": "not_unique", + "field": "app_name", + "message": "app_name must be unique" + }] } ``` @@ -146,7 +156,7 @@ Request a login. #### Definition ```bash -POST https://handshakejs.herokuapp.com/api/v0/login/request.json +ANY https://handshakejs-api.herokuapp.com/api/v0/login/request.json?email=[email]&app_name=[app_name] ``` #### Required Parameters @@ -156,21 +166,27 @@ POST https://handshakejs.herokuapp.com/api/v0/login/request.json #### Example Request -```bash -curl -X POST https://handshakejs.herokuapp.com/api/v0/login/request.json \ --d "email=test@example.com" \ --d "app_name=your_app_name" -``` + #### Example Response ```javascript { - success: true, - identity: { - email: "test@example.com", - app_name: "your_app_name", - authcode_expired_at: "1382833591309" - } + "identities": [{ + "email": "test@example.com", + "app_name": "your_app_name", + "authcode_expired_at": "1382833591309" + }] +} +``` + +#### Example Error +```javascript +{ + "errors": [{ + "code": "required", + "field": "email", + "message": "email cannot be blank" + }] } ``` @@ -181,7 +197,7 @@ Confirm a login. Email and authcode must match to get a success response back. #### Definition ```bash -POST https://handshakejs.herokuapp.com/api/v0/login/confirm.json +ANY https://handshakejs-api.herokuapp.com/api/v1/login/confirm.json?email=[email]&authcode=[authcode]&app_name=[app_name] ``` #### Required Parameters @@ -192,32 +208,26 @@ POST https://handshakejs.herokuapp.com/api/v0/login/confirm.json #### Example Request -```bash -curl -X POST https://handshakejs.herokuapp.com/api/v0/login/confirm.json \ --d "email=test@example.com" \ --d "authcode=7389" \ --d "app_name=your_app_name" -``` + #### Example Response ```javascript { - success: true, - identity: { - email: "test@example.com", - app_name: "your_app_name", - authcode: "7389" - } + "identities": [{ + "email": "test@example.com", + "app_name": "your_app_name", + "hash": "523f325279fd3446a78894b55cf4d777" + }] } ``` -## Database Schema with Redis - -apps - collection of keys with all the app_names in there. SADD - -apps/myappname - hash with all the data in there. HSET or HMSET - -apps/theappname/identities - collection of keys with all the identities' emails in there. SADD - -apps/theappname/identities/emailaddress HSET or HMSET - +#### Example Error +```javascript +{ + "errors": [{ + "code": "incorrect", + "field": "authcode", + "message": "the authcode was incorrect" + }] +} +``` diff --git a/app.go b/app.go new file mode 100644 index 0000000..204731b --- /dev/null +++ b/app.go @@ -0,0 +1,231 @@ +package main + +import ( + "github.com/go-martini/martini" + "github.com/handshakejs/handshakejserrors" + "github.com/handshakejs/handshakejslogic" + "github.com/handshakejs/handshakejstransport" + "github.com/hoisie/mustache" + "github.com/joho/godotenv" + "github.com/martini-contrib/binding" + "github.com/martini-contrib/render" + "net/http" + "os" +) + +const ( + LOGIC_ERROR_CODE_UNKNOWN = "unknown" +) + +var ( + DB_ENCRYPTION_SALT string + REDIS_URL string + FROM string + SMTP_ADDRESS string + SMTP_PORT string + SMTP_USERNAME string + SMTP_PASSWORD string + BACKUP_SMTP_ADDRESS string + BACKUP_SMTP_PORT string + BACKUP_SMTP_USERNAME string + BACKUP_SMTP_PASSWORD string + SUBJECT_TEMPLATE string + TEXT_TEMPLATE string + HTML_TEMPLATE string +) + +func CrossDomain() martini.Handler { + return func(res http.ResponseWriter) { + res.Header().Add("Access-Control-Allow-Origin", "*") + } +} + +type App struct { + AppName string `form:"app_name" json:"app_name"` + Email string `form:"email" json:"email"` + Salt string `form:"salt" json:"salt"` +} + +type Identity struct { + AppName string `form:"app_name" json:"app_name"` + Email string `form:"email" json:"email"` + Authcode string `form:"authcode" json:"authcode"` +} + +func main() { + loadEnvs() + + logic_options := handshakejslogic.Options{DbEncryptionSalt: DB_ENCRYPTION_SALT} + transport_options := handshakejstransport.Options{SmtpAddress: SMTP_ADDRESS, SmtpPort: SMTP_PORT, SmtpUsername: SMTP_USERNAME, SmtpPassword: SMTP_PASSWORD} + + handshakejslogic.Setup(REDIS_URL, logic_options) + handshakejstransport.Setup(transport_options) + + m := martini.Classic() + m.Use(render.Renderer()) + m.Use(CrossDomain()) + + m.Any("/api/v1/apps/create.json", binding.Bind(App{}), AppsCreate) + m.Any("/api/v1/login/request.json", binding.Bind(Identity{}), IdentitiesCreate) + m.Any("/api/v1/login/confirm.json", binding.Bind(Identity{}), IdentitiesConfirm) + + m.Run() +} + +func ErrorPayload(logic_error *handshakejserrors.LogicError) map[string]interface{} { + error_object := map[string]interface{}{"code": logic_error.Code, "field": logic_error.Field, "message": logic_error.Message} + errors := []interface{}{} + errors = append(errors, error_object) + payload := map[string]interface{}{"errors": errors} + + return payload +} + +func AppsPayload(app map[string]interface{}) map[string]interface{} { + apps := []interface{}{} + apps = append(apps, app) + payload := map[string]interface{}{"apps": apps} + + return payload +} + +func IdentitiesCreatePayload(identity map[string]interface{}) map[string]interface{} { + email := identity["email"].(string) + app_name := identity["app_name"].(string) + authcode_expired_at := identity["authcode_expired_at"].(string) + + identities := []interface{}{} + output_identity := map[string]interface{}{"email": email, "app_name": app_name, "authcode_expired_at": authcode_expired_at} + identities = append(identities, output_identity) + payload := map[string]interface{}{"identities": identities} + + return payload +} + +func IdentitiesConfirmPayload(identity map[string]interface{}) map[string]interface{} { + email := identity["email"].(string) + app_name := identity["app_name"].(string) + hash := identity["hash"].(string) + + identities := []interface{}{} + output_identity := map[string]interface{}{"email": email, "app_name": app_name, "hash": hash} + identities = append(identities, output_identity) + payload := map[string]interface{}{"identities": identities} + + return payload +} + +func AppsCreate(app App, req *http.Request, r render.Render) { + email := app.Email + app_name := app.AppName + salt := app.Salt + + params := map[string]interface{}{"email": email, "app_name": app_name, "salt": salt} + result, logic_error := handshakejslogic.AppsCreate(params) + if logic_error != nil { + payload := ErrorPayload(logic_error) + statuscode := determineStatusCodeFromLogicError(logic_error) + r.JSON(statuscode, payload) + } else { + payload := AppsPayload(result) + r.JSON(200, payload) + } +} + +func IdentitiesCreate(identity Identity, req *http.Request, r render.Render) { + email := identity.Email + app_name := identity.AppName + + params := map[string]interface{}{"email": email, "app_name": app_name} + result, logic_error := handshakejslogic.IdentitiesCreate(params) + if logic_error != nil { + payload := ErrorPayload(logic_error) + statuscode := determineStatusCodeFromLogicError(logic_error) + r.JSON(statuscode, payload) + } else { + logic_error := deliverAuthcodeEmail(result) + if logic_error != nil { + payload := ErrorPayload(logic_error) + statuscode := determineStatusCodeFromLogicError(logic_error) + r.JSON(statuscode, payload) + } else { + payload := IdentitiesCreatePayload(result) + r.JSON(200, payload) + } + } +} + +func IdentitiesConfirm(identity Identity, req *http.Request, r render.Render) { + email := identity.Email + app_name := identity.AppName + authcode := identity.Authcode + + params := map[string]interface{}{"email": email, "app_name": app_name, "authcode": authcode} + result, logic_error := handshakejslogic.IdentitiesConfirm(params) + if logic_error != nil { + payload := ErrorPayload(logic_error) + statuscode := determineStatusCodeFromLogicError(logic_error) + r.JSON(statuscode, payload) + } else { + payload := IdentitiesConfirmPayload(result) + r.JSON(200, payload) + } +} + +func determineStatusCodeFromLogicError(logic_error *handshakejserrors.LogicError) int { + code := 400 + if logic_error.Code == LOGIC_ERROR_CODE_UNKNOWN { + code = 500 + } + + return code +} + +func deliverAuthcodeEmail(identity map[string]interface{}) *handshakejserrors.LogicError { + email := identity["email"].(string) + subject := renderTemplate(SUBJECT_TEMPLATE, identity) + text := renderTemplate(TEXT_TEMPLATE, identity) + html := renderTemplate(HTML_TEMPLATE, identity) + + logic_error := handshakejstransport.ViaEmail(email, FROM, subject, text, html) + if logic_error != nil { + if BACKUP_SMTP_ADDRESS != "" { + backup_transport_options := handshakejstransport.Options{SmtpAddress: BACKUP_SMTP_ADDRESS, SmtpPort: BACKUP_SMTP_PORT, SmtpUsername: BACKUP_SMTP_USERNAME, SmtpPassword: BACKUP_SMTP_PASSWORD} + handshakejstransport.Setup(backup_transport_options) + + logic_error := handshakejstransport.ViaEmail(email, FROM, subject, text, html) + if logic_error != nil { + return logic_error + } + } else { + return logic_error + } + } + + return nil +} + +func renderTemplate(template_string string, identity map[string]interface{}) string { + data := mustache.Render(template_string, identity) + + return data +} + +func loadEnvs() { + godotenv.Load() + + DB_ENCRYPTION_SALT = os.Getenv("DB_ENCRYPTION_SALT") + REDIS_URL = os.Getenv("REDIS_URL") + FROM = os.Getenv("FROM") + SMTP_ADDRESS = os.Getenv("SMTP_ADDRESS") + SMTP_PORT = os.Getenv("SMTP_PORT") + SMTP_USERNAME = os.Getenv("SMTP_USERNAME") + SMTP_PASSWORD = os.Getenv("SMTP_PASSWORD") + BACKUP_SMTP_ADDRESS = os.Getenv("BACKUP_SMTP_ADDRESS") + BACKUP_SMTP_PORT = os.Getenv("BACKUP_SMTP_PORT") + BACKUP_SMTP_USERNAME = os.Getenv("BACKUP_SMTP_USERNAME") + BACKUP_SMTP_PASSWORD = os.Getenv("BACKUP_SMTP_PASSWORD") + SUBJECT_TEMPLATE = os.Getenv("SUBJECT_TEMPLATE") + TEXT_TEMPLATE = os.Getenv("TEXT_TEMPLATE") + HTML_TEMPLATE = os.Getenv("HTML_TEMPLATE") +} diff --git a/app.js b/app.js deleted file mode 100644 index 2d00dbd..0000000 --- a/app.js +++ /dev/null @@ -1,338 +0,0 @@ -var dotenv = require('dotenv'); -dotenv.load(); - -var crypto = require('crypto'); -var redis = require('redis'); -var sanitize = require('validator').sanitize; -var Validator = require('validator').Validator; - -var e = module.exports; -e.ENV = process.env.NODE_ENV || 'development'; - -// Constants -var DATABASE_URL = process.env.DATABASE_URL; -var FROM = process.env.FROM || "login@emailauth.io"; -var SUBJECT = process.env.SUBJECT || "Your code: {{authcode}}. Please enter it to login."; -var BODY = process.env.BODY || "Your code: {{authcode}}. Please enter it to login."; -var AUTHCODE_LIFE_IN_MS = process.env.AUTHCODE_LIFE_IN_MS || "120000"; -var SMTP_ADDRESS = process.env.SMTP_ADDRESS || "smtp.sendgrid.net"; -var SMTP_PORT = process.env.SMTP_PORT || 25; -var SMTP_USERNAME = process.env.SMTP_USERNAME || process.env.SENDGRID_USERNAME; -var SMTP_PASSWORD = process.env.SMTP_PASSWORD || process.env.SENDGRID_PASSWORD; -var REDIS_URL = process.env.REDIS_URL || process.env.REDISTOGO_URL || "redis://localhost:6379"; -var SALT_LENGTH = process.env.SALT_LENGTH || 10; -var PBKDF2_ITERATIONS = process.env.PBKDF2_ITERATIONS || 1000; -var PBKDF2_KEY_LENGTH = process.env.PBKDF2_KEY_LENGTH || 16; - -// Libraries -var redis_url = require("url").parse(REDIS_URL); -var db = redis.createClient(redis_url.port, redis_url.hostname); -if (redis_url.auth) { - db.auth(redis_url.auth.split(":")[1]); -} - -var sendgrid = require('sendgrid')(SMTP_USERNAME, SMTP_PASSWORD); - -var port = parseInt(process.env.PORT) || 3000; -var Hapi = require('hapi'); -server = new Hapi.Server(+port, '0.0.0.0', { cors: true }); - -// Setup validation -Validator.prototype.error = function (msg) { - this._errors.push(new Error(msg)); - return this; -} -Validator.prototype.errors = function () { - return this._errors; -} - -var randomAuthcode = function() { - var authcode = ""; - - for(var i=1;i <= 4;i++) { - authcode += parseInt(Math.random(1000) * 10)+""; - } - - return authcode; -} - -// Models -//// App -var App = module.exports.App = function(self){ - var self = self || 0; - this._validator = new Validator(); - this.app_name = sanitize(self.app_name).trim().toLowerCase() || ""; - this.email = sanitize(self.email).trim().toLowerCase() || ""; - this.salt = self.salt || crypto.randomBytes(SALT_LENGTH).toString('hex'); - - return this; -}; - -App.prototype.toJson = function(fn) { - var _this = this; - - return { - email: _this.email, - app_name: _this.app_name, - salt: _this.salt - } -}; - -App.prototype.create = function(fn){ - var _this = this; - var key = "apps/"+_this.app_name; - - this._validator.check(_this.email, "Invalid email.").isEmail(); - this._validator.check(_this.app_name, "App_name must be alphanumeric, underscore, or dashes.").is(/^[a-z0-9\_\-]+$/); - - console.log(_this); - - this._validator.check(_this.salt, "Salt must be alphanumeric, underscore, or dashes.").is(/^[a-z0-9\_\-]+$/); - - var errors = this._validator.errors(); - delete(this._validator); - - if (errors.length) { - fn(errors, null); - } else { - db.EXISTS(key, function(err, res) { - if (err) { return fn(err, null); } - - if (res == 1) { - var err = new Error("That app_name already exists."); - fn(err, null); - } else { - db.SADD("apps", _this.app_name); - db.HMSET(key, _this, function(err, res) { - fn(err, _this); - }); - } - }); - } - - return this; -}; - -//// Identity -var Identity = module.exports.Identity = function(self){ - var self = self || 0; - this._validator = new Validator(); - this.email = sanitize(self.email).trim().toLowerCase() || ""; - this.authcode = randomAuthcode() || ""; - this.authcode_expired_at = +new Date + parseInt(AUTHCODE_LIFE_IN_MS); - this.app_name = sanitize(self.app_name).trim().toLowerCase() || ""; - - return this; -}; - -Identity.prototype.toJson = function(fn) { - var _this = this; - - return { - email: _this.email, - app_name: _this.app_name, - authcode_expired_at: _this.authcode_expired_at - } -}; - -Identity.prototype.create = function(fn){ - var _this = this; - var app_name_key = "apps/"+_this.app_name; - var key = app_name_key+"/identities/"+_this.email; - - this._validator.check(_this.email, "Invalid email.").isEmail(); - - var errors = this._validator.errors(); - delete(this._validator); - - if (errors.length) { - fn(errors, null); - } else { - db.EXISTS(app_name_key, function(err, res) { - if (err) { return fn(err, null); } - - if (res == 1) { - db.SADD(app_name_key+"/identities", _this.email); - db.HMSET(key, _this, function(err, res) { - fn(err, _this); - }); - } else { - var err = new Error("Sorry, we couldn't find an app by that app_name."); - fn(err, null); - } - }); - } - - return this; -}; - -Identity.confirm = function(identity, fn) { - identity.email = sanitize(identity.email).trim().toLowerCase(); - var app_key = "apps/"+identity.app_name; - var key = app_key+"/identities/"+identity.email; - - db.EXISTS(key, function(err, res) { - if (err) { return fn(err, null); } - - if (res == 1) { - db.HGETALL(key, function(err, res) { - if (err) { return fn(err, null); } - - var current_ms_epoch_time = +new Date; - if ( - res.authcode && - res.authcode.length && - res.authcode === identity.authcode - ) { - if (res.authcode_expired_at < current_ms_epoch_time) { - err = new Error("Sorry, that authcode has expired. Request another."); - return fn(err, null); - } - - db.HSET(key, "authcode", ""); // clear authcode on success login/confirm - db.HGETALL(app_key, function(err, app) { - if (err) { return fn(err, null); } - - crypto.pbkdf2(identity.email, app.salt, PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH, function(err, hash) { - if (err) { return fn(err, null); } - - identity.hash = hash.toString('hex'); - - return fn(null, identity); - }); - }); - - } else { - err = new Error("Sorry, the authcode did not match.") - return fn(err, null); - } - }); - } else { - var err = new Error("Sorry, we couldn't find a login request using that email."); - return fn(err, null); - } - }); -}; - -// Routes -var apps = { - create: { - handler: function (request) { - var payload = request.payload; - var email = payload.email; - var app_name = payload.app_name; - var salt = payload.salt; - var app = new App({ - email: email, - app_name: app_name, - salt: salt - }); - - app.create(function(err, res) { - if (err) { - var message = err.length ? err[0].message : err.message; - request.reply({success: false, error: {message: message}}); - } else { - request.reply({success: true, app: res.toJson()}); - } - }); - } - } -}; - -var login = { - request: { - handler: function (request) { - var payload = request.payload; - var email = payload.email; - var app_name = payload.app_name; - var identity = new Identity({ - email: email, - app_name: app_name - }); - - identity.create(function(err, res) { - if (err) { - var message = err.length ? err[0].message : err.message; - request.reply({success: false, error: {message: message}}); - } else { - var identity = res.toJson(); - var email = new sendgrid.Email({ - to: identity.email, - from: FROM, - subject: SUBJECT, - html: BODY - }); - email.addSubVal('{{authcode}}', res.authcode); - sendgrid.send(email, function(err, json) { - if (err) { - request.reply({success: false, error: {message: err.message}}); - } else { - request.reply({success: true, identity: identity}); - } - }); - } - }); - } - }, - - confirm: { - handler: function (request) { - var payload = request.payload; - var confirm_payload = { - email: payload.email, - authcode: payload.authcode, - app_name: payload.app_name - } - - Identity.confirm(confirm_payload, function(err, res) { - if (err) { - request.reply({success: false, error: {message: err.message}}); - } else { - request.reply({success: true, identity: res}); - } - }); - } - } -}; - - -server.route({ - method : 'POST', - path : '/api/v0/apps/create', - config : apps.create -}); - -server.route({ - method : 'POST', - path : '/api/v0/apps/create.json', - config : apps.create -}); - -server.route({ - method : 'POST', - path : '/api/v0/login/request', - config : login.request -}); - -server.route({ - method : 'POST', - path : '/api/v0/login/request.json', - config : login.request -}); - -server.route({ - method : 'POST', - path : '/api/v0/login/confirm', - config : login.confirm -}); - -server.route({ - method : 'POST', - path : '/api/v0/login/confirm.json', - config : login.confirm -}); - -server.start(function() { - console.log('Handshake.js server started at: ' + server.info.uri); -}); diff --git a/click-to-cloud.json b/click-to-cloud.json deleted file mode 100644 index d57d86e..0000000 --- a/click-to-cloud.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "heroku": [ - "heroku create", - "heroku addons:add sendgrid", - "heroku addons:add redistogo", - "git push heroku master" - ] -} diff --git a/handshakejs-logo.png b/handshakejs-logo.png new file mode 100644 index 0000000..29420e0 Binary files /dev/null and b/handshakejs-logo.png differ diff --git a/package.json b/package.json deleted file mode 100644 index a14cb3b..0000000 --- a/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "handshake", - "version": "0.0.1", - "engines": { - "node": "0.10.x", - "npm": "1.2.x" - }, - "main": "app.js", - "description": "Email only authentication", - "dependencies": { - "dotenv": "0.2.0", - "hapi": "1.6.2", - "redis": "0.9.0", - "request": "2.21.0", - "sendgrid": "0.3.1", - "validator": "1.5.1" - } -}