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
-
+
**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"
- }
-}