From 01d957677f160e4b5e43ce043b05e246493b34ea Mon Sep 17 00:00:00 2001
From: Willem van Dreumel <willemvd@users.noreply.github.com>
Date: Wed, 22 Feb 2017 08:14:37 +0100
Subject: [PATCH] Oauth2 consumer (#679)

* initial stuff for oauth2 login, fails on:
* login button on the signIn page to start the OAuth2 flow and a callback for each provider
Only GitHub is implemented for now
* show login button only when the OAuth2 consumer is configured (and activated)
* create macaron group for oauth2 urls
* prevent net/http in modules (other then oauth2)
* use a new data sessions oauth2 folder for storing the oauth2 session data
* add missing 2FA when this is enabled on the user
* add password option for OAuth2 user , for use with git over http and login to the GUI
* add tip for registering a GitHub OAuth application
* at startup of Gitea register all configured providers and also on adding/deleting of new providers
* custom handling of errors in oauth2 request init + show better tip
* add ExternalLoginUser model and migration script to add it to database
* link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed
* remove the linked external account from the user his settings
* if user is unknown we allow him to register a new account or link it to some existing account
* sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers)

* from gorilla/sessions docs:
"Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!"
(we're using gorilla/sessions for storing oauth2 sessions)

* use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
---
 cmd/web.go                                    |  17 +-
 models/error.go                               |  40 ++
 models/external_login_user.go                 |  74 ++
 models/login_source.go                        | 160 ++++-
 models/migrations/migrations.go               |   2 +
 models/migrations/v18.go                      |  25 +
 models/user.go                                |  21 +
 modules/auth/auth.go                          |   4 +-
 modules/auth/auth_form.go                     |   5 +-
 modules/auth/oauth2/oauth2.go                 | 105 +++
 modules/auth/user_form.go                     |   2 +-
 options/locale/locale_en-US.ini               |  15 +
 public/css/index.css                          |  21 +
 public/img/github.png                         | Bin 0 -> 1151 bytes
 public/js/index.js                            |   8 +-
 routers/admin/auths.go                        |  29 +-
 routers/init.go                               |   1 +
 routers/repo/http.go                          |  12 +-
 routers/user/auth.go                          | 358 +++++++++-
 routers/user/setting.go                       |  46 +-
 templates/admin/auth/edit.tmpl                |  26 +
 templates/admin/auth/new.tmpl                 |  27 +
 templates/admin/user/edit.tmpl                |   2 +-
 templates/user/auth/link_account.tmpl         |  13 +
 templates/user/auth/signin.tmpl               |  43 +-
 templates/user/auth/signin_inner.tmpl         |  57 ++
 templates/user/auth/signup.tmpl               |  55 +-
 templates/user/auth/signup_inner.tmpl         |  59 ++
 templates/user/settings/account_link.tmpl     |  48 ++
 templates/user/settings/navbar.tmpl           |   3 +
 templates/user/settings/password.tmpl         |   4 +-
 vendor/github.com/gorilla/context/LICENSE     |  27 +
 vendor/github.com/gorilla/context/README.md   |  10 +
 vendor/github.com/gorilla/context/context.go  | 143 ++++
 vendor/github.com/gorilla/context/doc.go      |  88 +++
 vendor/github.com/gorilla/mux/LICENSE         |  27 +
 vendor/github.com/gorilla/mux/README.md       | 299 ++++++++
 .../github.com/gorilla/mux/context_gorilla.go |  26 +
 .../github.com/gorilla/mux/context_native.go  |  24 +
 vendor/github.com/gorilla/mux/doc.go          | 235 +++++++
 vendor/github.com/gorilla/mux/mux.go          | 542 +++++++++++++++
 vendor/github.com/gorilla/mux/regexp.go       | 316 +++++++++
 vendor/github.com/gorilla/mux/route.go        | 636 +++++++++++++++++
 .../github.com/gorilla/securecookie/LICENSE   |  27 +
 .../github.com/gorilla/securecookie/README.md |  78 +++
 vendor/github.com/gorilla/securecookie/doc.go |  61 ++
 .../github.com/gorilla/securecookie/fuzz.go   |  25 +
 .../gorilla/securecookie/securecookie.go      | 646 ++++++++++++++++++
 vendor/github.com/gorilla/sessions/LICENSE    |  27 +
 vendor/github.com/gorilla/sessions/README.md  |  81 +++
 vendor/github.com/gorilla/sessions/doc.go     | 199 ++++++
 vendor/github.com/gorilla/sessions/lex.go     | 102 +++
 .../github.com/gorilla/sessions/sessions.go   | 241 +++++++
 vendor/github.com/gorilla/sessions/store.go   | 295 ++++++++
 vendor/github.com/markbates/goth/LICENSE.txt  |  22 +
 vendor/github.com/markbates/goth/README.md    | 143 ++++
 vendor/github.com/markbates/goth/doc.go       |  10 +
 .../markbates/goth/gothic/gothic.go           | 219 ++++++
 vendor/github.com/markbates/goth/provider.go  |  75 ++
 .../markbates/goth/providers/github/github.go | 224 ++++++
 .../goth/providers/github/session.go          |  56 ++
 vendor/github.com/markbates/goth/session.go   |  21 +
 vendor/github.com/markbates/goth/user.go      |  30 +
 vendor/golang.org/x/oauth2/AUTHORS            |   3 +
 vendor/golang.org/x/oauth2/CONTRIBUTING.md    |  31 +
 vendor/golang.org/x/oauth2/CONTRIBUTORS       |   3 +
 vendor/golang.org/x/oauth2/LICENSE            |  27 +
 vendor/golang.org/x/oauth2/README.md          |  65 ++
 .../golang.org/x/oauth2/client_appengine.go   |  25 +
 vendor/golang.org/x/oauth2/internal/oauth2.go |  76 +++
 vendor/golang.org/x/oauth2/internal/token.go  | 227 ++++++
 .../golang.org/x/oauth2/internal/transport.go |  69 ++
 vendor/golang.org/x/oauth2/oauth2.go          | 341 +++++++++
 vendor/golang.org/x/oauth2/token.go           | 158 +++++
 vendor/golang.org/x/oauth2/transport.go       | 132 ++++
 vendor/vendor.json                            |  18 +
 76 files changed, 7275 insertions(+), 137 deletions(-)
 create mode 100644 models/external_login_user.go
 create mode 100644 models/migrations/v18.go
 create mode 100644 modules/auth/oauth2/oauth2.go
 create mode 100644 public/img/github.png
 create mode 100644 templates/user/auth/link_account.tmpl
 create mode 100644 templates/user/auth/signin_inner.tmpl
 create mode 100644 templates/user/auth/signup_inner.tmpl
 create mode 100644 templates/user/settings/account_link.tmpl
 create mode 100644 vendor/github.com/gorilla/context/LICENSE
 create mode 100644 vendor/github.com/gorilla/context/README.md
 create mode 100644 vendor/github.com/gorilla/context/context.go
 create mode 100644 vendor/github.com/gorilla/context/doc.go
 create mode 100644 vendor/github.com/gorilla/mux/LICENSE
 create mode 100644 vendor/github.com/gorilla/mux/README.md
 create mode 100644 vendor/github.com/gorilla/mux/context_gorilla.go
 create mode 100644 vendor/github.com/gorilla/mux/context_native.go
 create mode 100644 vendor/github.com/gorilla/mux/doc.go
 create mode 100644 vendor/github.com/gorilla/mux/mux.go
 create mode 100644 vendor/github.com/gorilla/mux/regexp.go
 create mode 100644 vendor/github.com/gorilla/mux/route.go
 create mode 100644 vendor/github.com/gorilla/securecookie/LICENSE
 create mode 100644 vendor/github.com/gorilla/securecookie/README.md
 create mode 100644 vendor/github.com/gorilla/securecookie/doc.go
 create mode 100644 vendor/github.com/gorilla/securecookie/fuzz.go
 create mode 100644 vendor/github.com/gorilla/securecookie/securecookie.go
 create mode 100644 vendor/github.com/gorilla/sessions/LICENSE
 create mode 100644 vendor/github.com/gorilla/sessions/README.md
 create mode 100644 vendor/github.com/gorilla/sessions/doc.go
 create mode 100644 vendor/github.com/gorilla/sessions/lex.go
 create mode 100644 vendor/github.com/gorilla/sessions/sessions.go
 create mode 100644 vendor/github.com/gorilla/sessions/store.go
 create mode 100644 vendor/github.com/markbates/goth/LICENSE.txt
 create mode 100644 vendor/github.com/markbates/goth/README.md
 create mode 100644 vendor/github.com/markbates/goth/doc.go
 create mode 100644 vendor/github.com/markbates/goth/gothic/gothic.go
 create mode 100644 vendor/github.com/markbates/goth/provider.go
 create mode 100644 vendor/github.com/markbates/goth/providers/github/github.go
 create mode 100644 vendor/github.com/markbates/goth/providers/github/session.go
 create mode 100644 vendor/github.com/markbates/goth/session.go
 create mode 100644 vendor/github.com/markbates/goth/user.go
 create mode 100644 vendor/golang.org/x/oauth2/AUTHORS
 create mode 100644 vendor/golang.org/x/oauth2/CONTRIBUTING.md
 create mode 100644 vendor/golang.org/x/oauth2/CONTRIBUTORS
 create mode 100644 vendor/golang.org/x/oauth2/LICENSE
 create mode 100644 vendor/golang.org/x/oauth2/README.md
 create mode 100644 vendor/golang.org/x/oauth2/client_appengine.go
 create mode 100644 vendor/golang.org/x/oauth2/internal/oauth2.go
 create mode 100644 vendor/golang.org/x/oauth2/internal/token.go
 create mode 100644 vendor/golang.org/x/oauth2/internal/transport.go
 create mode 100644 vendor/golang.org/x/oauth2/oauth2.go
 create mode 100644 vendor/golang.org/x/oauth2/token.go
 create mode 100644 vendor/golang.org/x/oauth2/transport.go

diff --git a/cmd/web.go b/cmd/web.go
index f9b4bdd270..982af4a39e 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -41,6 +41,7 @@ import (
 	"github.com/go-macaron/toolbox"
 	"github.com/urfave/cli"
 	macaron "gopkg.in/macaron.v1"
+	context2 "github.com/gorilla/context"
 )
 
 // CmdWeb represents the available web sub-command.
@@ -210,6 +211,13 @@ func runWeb(ctx *cli.Context) error {
 		m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
 		m.Get("/reset_password", user.ResetPasswd)
 		m.Post("/reset_password", user.ResetPasswdPost)
+		m.Group("/oauth2", func() {
+			m.Get("/:provider", user.SignInOAuth)
+			m.Get("/:provider/callback", user.SignInOAuthCallback)
+		})
+		m.Get("/link_account", user.LinkAccount)
+		m.Post("/link_account_signin", bindIgnErr(auth.SignInForm{}), user.LinkAccountPostSignIn)
+		m.Post("/link_account_signup", bindIgnErr(auth.RegisterForm{}), user.LinkAccountPostRegister)
 		m.Group("/two_factor", func() {
 			m.Get("", user.TwoFactor)
 			m.Post("", bindIgnErr(auth.TwoFactorAuthForm{}), user.TwoFactorPost)
@@ -236,6 +244,7 @@ func runWeb(ctx *cli.Context) error {
 			Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost)
 		m.Post("/applications/delete", user.SettingsDeleteApplication)
 		m.Route("/delete", "GET,POST", user.SettingsDelete)
+		m.Combo("/account_link").Get(user.SettingsAccountLinks).Post(user.SettingsDeleteAccountLink)
 		m.Group("/two_factor", func() {
 			m.Get("", user.SettingsTwoFactor)
 			m.Post("/regenerate_scratch", user.SettingsTwoFactorRegenerateScratch)
@@ -671,11 +680,11 @@ func runWeb(ctx *cli.Context) error {
 	var err error
 	switch setting.Protocol {
 	case setting.HTTP:
-		err = runHTTP(listenAddr, m)
+		err = runHTTP(listenAddr, context2.ClearHandler(m))
 	case setting.HTTPS:
-		err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, m)
+		err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
 	case setting.FCGI:
-		err = fcgi.Serve(nil, m)
+		err = fcgi.Serve(nil, context2.ClearHandler(m))
 	case setting.UnixSocket:
 		if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) {
 			log.Fatal(4, "Failed to remove unix socket directory %s: %v", listenAddr, err)
@@ -691,7 +700,7 @@ func runWeb(ctx *cli.Context) error {
 		if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil {
 			log.Fatal(4, "Failed to set permission of unix socket: %v", err)
 		}
-		err = http.Serve(listener, m)
+		err = http.Serve(listener, context2.ClearHandler(m))
 	default:
 		log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
 	}
diff --git a/models/error.go b/models/error.go
index 85d250c84a..472c8c9426 100644
--- a/models/error.go
+++ b/models/error.go
@@ -847,3 +847,43 @@ func IsErrUploadNotExist(err error) bool {
 func (err ErrUploadNotExist) Error() string {
 	return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
 }
+
+//  ___________         __                             .__    .____                 .__          ____ ___
+//  \_   _____/__  ____/  |_  ___________  ____ _____  |  |   |    |    ____   ____ |__| ____   |    |   \______ ___________
+//   |    __)_\  \/  /\   __\/ __ \_  __ \/    \\__  \ |  |   |    |   /  _ \ / ___\|  |/    \  |    |   /  ___// __ \_  __ \
+//   |        \>    <  |  | \  ___/|  | \/   |  \/ __ \|  |__ |    |__(  <_> ) /_/  >  |   |  \ |    |  /\___ \\  ___/|  | \/
+//  /_______  /__/\_ \ |__|  \___  >__|  |___|  (____  /____/ |_______ \____/\___  /|__|___|  / |______//____  >\___  >__|
+//          \/      \/           \/           \/     \/               \/    /_____/         \/               \/     \/
+
+// ErrExternalLoginUserAlreadyExist represents a "ExternalLoginUserAlreadyExist" kind of error.
+type ErrExternalLoginUserAlreadyExist struct {
+	ExternalID    string
+	UserID        int64
+	LoginSourceID int64
+}
+
+// IsErrExternalLoginUserAlreadyExist checks if an error is a ExternalLoginUserAlreadyExist.
+func IsErrExternalLoginUserAlreadyExist(err error) bool {
+	_, ok := err.(ErrExternalLoginUserAlreadyExist)
+	return ok
+}
+
+func (err ErrExternalLoginUserAlreadyExist) Error() string {
+	return fmt.Sprintf("external login user already exists [externalID: %s, userID: %d, loginSourceID: %d]", err.ExternalID, err.UserID, err.LoginSourceID)
+}
+
+// ErrExternalLoginUserNotExist represents a "ExternalLoginUserNotExist" kind of error.
+type ErrExternalLoginUserNotExist struct {
+	UserID        int64
+	LoginSourceID int64
+}
+
+// IsErrExternalLoginUserNotExist checks if an error is a ExternalLoginUserNotExist.
+func IsErrExternalLoginUserNotExist(err error) bool {
+	_, ok := err.(ErrExternalLoginUserNotExist)
+	return ok
+}
+
+func (err ErrExternalLoginUserNotExist) Error() string {
+	return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID)
+}
diff --git a/models/external_login_user.go b/models/external_login_user.go
new file mode 100644
index 0000000000..ade1b8a13b
--- /dev/null
+++ b/models/external_login_user.go
@@ -0,0 +1,74 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import "github.com/markbates/goth"
+
+// ExternalLoginUser makes the connecting between some existing user and additional external login sources
+type ExternalLoginUser struct {
+	ExternalID    string `xorm:"NOT NULL"`
+	UserID        int64 `xorm:"NOT NULL"`
+	LoginSourceID int64 `xorm:"NOT NULL"`
+}
+
+// GetExternalLogin checks if a externalID in loginSourceID scope already exists
+func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) {
+	return x.Get(externalLoginUser)
+}
+
+// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource
+func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) {
+	externalAccounts := make([]*ExternalLoginUser, 0, 5)
+	err := x.Where("user_id=?", user.ID).
+		Desc("login_source_id").
+		Find(&externalAccounts)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return externalAccounts, nil
+}
+
+// LinkAccountToUser link the gothUser to the user
+func LinkAccountToUser(user *User, gothUser goth.User) error {
+	loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider)
+	if err != nil {
+		return err
+	}
+
+	externalLoginUser := &ExternalLoginUser{
+		ExternalID:    gothUser.UserID,
+		UserID:        user.ID,
+		LoginSourceID: loginSource.ID,
+	}
+	has, err := x.Get(externalLoginUser)
+	if err != nil {
+		return err
+	} else if has {
+		return ErrExternalLoginUserAlreadyExist{gothUser.UserID, user.ID, loginSource.ID}
+	}
+
+	_, err = x.Insert(externalLoginUser)
+	return err
+}
+
+// RemoveAccountLink will remove all external login sources for the given user
+func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) {
+	deleted, err := x.Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID})
+	if err != nil {
+		return deleted, err
+	}
+	if deleted < 1 {
+		return deleted, ErrExternalLoginUserNotExist{user.ID, loginSourceID}
+	}
+	return deleted, err
+}
+
+// RemoveAllAccountLinks will remove all external login sources for the given user
+func RemoveAllAccountLinks(user *User) error {
+	_, err := x.Delete(&ExternalLoginUser{UserID: user.ID})
+	return err
+}
diff --git a/models/login_source.go b/models/login_source.go
index 3fe5e172e3..8d5d08dea6 100644
--- a/models/login_source.go
+++ b/models/login_source.go
@@ -22,6 +22,7 @@ import (
 	"code.gitea.io/gitea/modules/auth/ldap"
 	"code.gitea.io/gitea/modules/auth/pam"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/auth/oauth2"
 )
 
 // LoginType represents an login type.
@@ -30,19 +31,21 @@ type LoginType int
 // Note: new type must append to the end of list to maintain compatibility.
 const (
 	LoginNoType LoginType = iota
-	LoginPlain            // 1
-	LoginLDAP             // 2
-	LoginSMTP             // 3
-	LoginPAM              // 4
-	LoginDLDAP            // 5
+	LoginPlain   // 1
+	LoginLDAP    // 2
+	LoginSMTP    // 3
+	LoginPAM     // 4
+	LoginDLDAP   // 5
+	LoginOAuth2  // 6
 )
 
 // LoginNames contains the name of LoginType values.
 var LoginNames = map[LoginType]string{
-	LoginLDAP:  "LDAP (via BindDN)",
-	LoginDLDAP: "LDAP (simple auth)", // Via direct bind
-	LoginSMTP:  "SMTP",
-	LoginPAM:   "PAM",
+	LoginLDAP:   "LDAP (via BindDN)",
+	LoginDLDAP:  "LDAP (simple auth)", // Via direct bind
+	LoginSMTP:   "SMTP",
+	LoginPAM:    "PAM",
+	LoginOAuth2: "OAuth2",
 }
 
 // SecurityProtocolNames contains the name of SecurityProtocol values.
@@ -57,6 +60,7 @@ var (
 	_ core.Conversion = &LDAPConfig{}
 	_ core.Conversion = &SMTPConfig{}
 	_ core.Conversion = &PAMConfig{}
+	_ core.Conversion = &OAuth2Config{}
 )
 
 // LDAPConfig holds configuration for LDAP login source.
@@ -115,6 +119,23 @@ func (cfg *PAMConfig) ToDB() ([]byte, error) {
 	return json.Marshal(cfg)
 }
 
+// OAuth2Config holds configuration for the OAuth2 login source.
+type OAuth2Config struct {
+	Provider     string
+	ClientID     string
+	ClientSecret string
+}
+
+// FromDB fills up an OAuth2Config from serialized format.
+func (cfg *OAuth2Config) FromDB(bs []byte) error {
+	return json.Unmarshal(bs, cfg)
+}
+
+// ToDB exports an SMTPConfig to a serialized format.
+func (cfg *OAuth2Config) ToDB() ([]byte, error) {
+	return json.Marshal(cfg)
+}
+
 // LoginSource represents an external way for authorizing users.
 type LoginSource struct {
 	ID        int64 `xorm:"pk autoincr"`
@@ -162,6 +183,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
 			source.Cfg = new(SMTPConfig)
 		case LoginPAM:
 			source.Cfg = new(PAMConfig)
+		case LoginOAuth2:
+			source.Cfg = new(OAuth2Config)
 		default:
 			panic("unrecognized login source type: " + com.ToStr(*val))
 		}
@@ -203,6 +226,11 @@ func (source *LoginSource) IsPAM() bool {
 	return source.Type == LoginPAM
 }
 
+// IsOAuth2 returns true of this source is of the OAuth2 type.
+func (source *LoginSource) IsOAuth2() bool {
+	return source.Type == LoginOAuth2
+}
+
 // HasTLS returns true of this source supports TLS.
 func (source *LoginSource) HasTLS() bool {
 	return ((source.IsLDAP() || source.IsDLDAP()) &&
@@ -250,6 +278,11 @@ func (source *LoginSource) PAM() *PAMConfig {
 	return source.Cfg.(*PAMConfig)
 }
 
+// OAuth2 returns OAuth2Config for this source, if of OAuth2 type.
+func (source *LoginSource) OAuth2() *OAuth2Config {
+	return source.Cfg.(*OAuth2Config)
+}
+
 // CreateLoginSource inserts a LoginSource in the DB if not already
 // existing with the given name.
 func CreateLoginSource(source *LoginSource) error {
@@ -261,12 +294,16 @@ func CreateLoginSource(source *LoginSource) error {
 	}
 
 	_, err = x.Insert(source)
+	if err == nil && source.IsOAuth2() {
+		oAuth2Config := source.OAuth2()
+		oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret)
+	}
 	return err
 }
 
 // LoginSources returns a slice of all login sources found in DB.
 func LoginSources() ([]*LoginSource, error) {
-	auths := make([]*LoginSource, 0, 5)
+	auths := make([]*LoginSource, 0, 6)
 	return auths, x.Find(&auths)
 }
 
@@ -285,6 +322,11 @@ func GetLoginSourceByID(id int64) (*LoginSource, error) {
 // UpdateSource updates a LoginSource record in DB.
 func UpdateSource(source *LoginSource) error {
 	_, err := x.Id(source.ID).AllCols().Update(source)
+	if err == nil && source.IsOAuth2() {
+		oAuth2Config := source.OAuth2()
+		oauth2.RemoveProvider(source.Name)
+		oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret)
+	}
 	return err
 }
 
@@ -296,6 +338,18 @@ func DeleteSource(source *LoginSource) error {
 	} else if count > 0 {
 		return ErrLoginSourceInUse{source.ID}
 	}
+
+	count, err = x.Count(&ExternalLoginUser{LoginSourceID: source.ID})
+	if err != nil {
+		return err
+	} else if count > 0 {
+		return ErrLoginSourceInUse{source.ID}
+	}
+
+	if source.IsOAuth2() {
+		oauth2.RemoveProvider(source.Name)
+	}
+
 	_, err = x.Id(source.ID).Delete(new(LoginSource))
 	return err
 }
@@ -444,7 +498,7 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
 		idx := strings.Index(login, "@")
 		if idx == -1 {
 			return nil, ErrUserNotExist{0, login, 0}
-		} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), login[idx+1:]) {
+		} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), login[idx + 1:]) {
 			return nil, ErrUserNotExist{0, login, 0}
 		}
 	}
@@ -526,6 +580,27 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
 	return user, CreateUser(user)
 }
 
+//  ________      _____          __  .__     ________
+//  \_____  \    /  _  \  __ ___/  |_|  |__  \_____  \
+//   /   |   \  /  /_\  \|  |  \   __\  |  \  /  ____/
+//  /    |    \/    |    \  |  /|  | |   Y  \/       \
+//  \_______  /\____|__  /____/ |__| |___|  /\_______ \
+//          \/         \/                 \/         \/
+
+// OAuth2Provider describes the display values of a single OAuth2 provider
+type OAuth2Provider struct {
+	Name string
+	DisplayName string
+	Image string
+}
+
+// OAuth2Providers contains the map of registered OAuth2 providers in Gitea (based on goth)
+// key is used to map the OAuth2Provider with the goth provider type (also in LoginSource.OAuth2Config.Provider)
+// value is used to store display data
+var OAuth2Providers = map[string]OAuth2Provider{
+	"github":   {Name: "github", DisplayName:"GitHub", Image: "/img/github.png"},
+}
+
 // ExternalUserLogin attempts a login using external source types.
 func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
 	if !source.IsActived {
@@ -560,7 +635,7 @@ func UserSignIn(username, password string) (*User, error) {
 
 	if hasUser {
 		switch user.LoginType {
-		case LoginNoType, LoginPlain:
+		case LoginNoType, LoginPlain, LoginOAuth2:
 			if user.ValidatePassword(password) {
 				return user, nil
 			}
@@ -580,12 +655,16 @@ func UserSignIn(username, password string) (*User, error) {
 		}
 	}
 
-	sources := make([]*LoginSource, 0, 3)
+	sources := make([]*LoginSource, 0, 5)
 	if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
 		return nil, err
 	}
 
 	for _, source := range sources {
+		if source.IsOAuth2() {
+			// don't try to authenticate against OAuth2 sources
+			continue
+		}
 		authUser, err := ExternalUserLogin(nil, username, password, source, true)
 		if err == nil {
 			return authUser, nil
@@ -596,3 +675,58 @@ func UserSignIn(username, password string) (*User, error) {
 
 	return nil, ErrUserNotExist{user.ID, user.Name, 0}
 }
+
+// GetActiveOAuth2ProviderLoginSources returns all actived LoginOAuth2 sources
+func GetActiveOAuth2ProviderLoginSources() ([]*LoginSource, error) {
+	sources := make([]*LoginSource, 0, 1)
+	if err := x.UseBool().Find(&sources, &LoginSource{IsActived: true, Type: LoginOAuth2}); err != nil {
+		return nil, err
+	}
+	return sources, nil
+}
+
+// GetActiveOAuth2LoginSourceByName returns a OAuth2 LoginSource based on the given name
+func GetActiveOAuth2LoginSourceByName(name string) (*LoginSource, error) {
+	loginSource := &LoginSource{
+		Name:      name,
+		Type:      LoginOAuth2,
+		IsActived: true,
+	}
+
+	has, err := x.UseBool().Get(loginSource)
+	if !has || err != nil {
+		return nil, err
+	}
+
+	return loginSource, nil
+}
+
+// GetActiveOAuth2Providers returns the map of configured active OAuth2 providers
+// key is used as technical name (like in the callbackURL)
+// values to display
+func GetActiveOAuth2Providers() (map[string]OAuth2Provider, error) {
+	// Maybe also seperate used and unused providers so we can force the registration of only 1 active provider for each type
+
+	loginSources, err := GetActiveOAuth2ProviderLoginSources()
+	if err != nil {
+		return nil, err
+	}
+
+	providers := make(map[string]OAuth2Provider)
+	for _, source := range loginSources {
+		providers[source.Name] = OAuth2Providers[source.OAuth2().Provider]
+	}
+
+	return providers, nil
+}
+
+// InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library
+func InitOAuth2() {
+	oauth2.Init()
+	loginSources, _ := GetActiveOAuth2ProviderLoginSources()
+
+	for _, source := range loginSources {
+		oAuth2Config := source.OAuth2()
+		oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret)
+	}
+}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index e54d502b7d..2a1c70af77 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -84,6 +84,8 @@ var migrations = []Migration{
 	NewMigration("create repo unit table and add units for all repos", addUnitsToTables),
 	// v17 -> v18
 	NewMigration("set protect branches updated with created", setProtectedBranchUpdatedWithCreated),
+	// v18 -> v19
+	NewMigration("add external login user", addExternalLoginUser),
 }
 
 // Migrate database to current version
diff --git a/models/migrations/v18.go b/models/migrations/v18.go
new file mode 100644
index 0000000000..be51615341
--- /dev/null
+++ b/models/migrations/v18.go
@@ -0,0 +1,25 @@
+// Copyright 2016 Gitea. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"fmt"
+
+	"github.com/go-xorm/xorm"
+)
+
+// ExternalLoginUser makes the connecting between some existing user and additional external login sources
+type ExternalLoginUser struct {
+	ExternalID    string `xorm:"NOT NULL"`
+	UserID        int64 `xorm:"NOT NULL"`
+	LoginSourceID int64 `xorm:"NOT NULL"`
+}
+
+func addExternalLoginUser(x *xorm.Engine) error {
+	if err := x.Sync2(new(ExternalLoginUser)); err != nil {
+		return fmt.Errorf("Sync2: %v", err)
+	}
+	return nil
+}
diff --git a/models/user.go b/models/user.go
index c9ddbe7c8b..d0f711448d 100644
--- a/models/user.go
+++ b/models/user.go
@@ -196,6 +196,11 @@ func (u *User) IsLocal() bool {
 	return u.LoginType <= LoginPlain
 }
 
+// IsOAuth2 returns true if user login type is LoginOAuth2.
+func (u *User) IsOAuth2() bool {
+	return u.LoginType == LoginOAuth2
+}
+
 // HasForkedRepo checks if user has already forked a repository with given ID.
 func (u *User) HasForkedRepo(repoID int64) bool {
 	_, has := HasForkedRepo(u.ID, repoID)
@@ -397,6 +402,11 @@ func (u *User) ValidatePassword(passwd string) bool {
 	return subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(newUser.Passwd)) == 1
 }
 
+// IsPasswordSet checks if the password is set or left empty
+func (u *User) IsPasswordSet() bool {
+	return !u.ValidatePassword("")
+}
+
 // UploadAvatar saves custom avatar for user.
 // FIXME: split uploads to different subdirs in case we have massive users.
 func (u *User) UploadAvatar(data []byte) error {
@@ -947,6 +957,12 @@ func deleteUser(e *xorm.Session, u *User) error {
 		return fmt.Errorf("clear assignee: %v", err)
 	}
 
+	// ***** START: ExternalLoginUser *****
+	if err = RemoveAllAccountLinks(u); err != nil {
+		return fmt.Errorf("ExternalLoginUser: %v", err)
+	}
+	// ***** END: ExternalLoginUser *****
+
 	if _, err = e.Id(u.ID).Delete(new(User)); err != nil {
 		return fmt.Errorf("Delete: %v", err)
 	}
@@ -1190,6 +1206,11 @@ func GetUserByEmail(email string) (*User, error) {
 	return nil, ErrUserNotExist{0, email, 0}
 }
 
+// GetUser checks if a user already exists
+func GetUser(user *User) (bool, error) {
+	return x.Get(user)
+}
+
 // SearchUserOptions contains the options for searching
 type SearchUserOptions struct {
 	Keyword  string
diff --git a/modules/auth/auth.go b/modules/auth/auth.go
index 33ba777966..4a31ff944e 100644
--- a/modules/auth/auth.go
+++ b/modules/auth/auth.go
@@ -179,7 +179,7 @@ func AssignForm(form interface{}, data map[string]interface{}) {
 func getRuleBody(field reflect.StructField, prefix string) string {
 	for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
 		if strings.HasPrefix(rule, prefix) {
-			return rule[len(prefix) : len(rule)-1]
+			return rule[len(prefix): len(rule) - 1]
 		}
 	}
 	return ""
@@ -237,7 +237,7 @@ func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaro
 		}
 
 		if errs[0].FieldNames[0] == field.Name {
-			data["Err_"+field.Name] = true
+			data["Err_" + field.Name] = true
 
 			trName := field.Tag.Get("locale")
 			if len(trName) == 0 {
diff --git a/modules/auth/auth_form.go b/modules/auth/auth_form.go
index 78ba2b8997..5dae55c1ef 100644
--- a/modules/auth/auth_form.go
+++ b/modules/auth/auth_form.go
@@ -12,7 +12,7 @@ import (
 // AuthenticationForm form for authentication
 type AuthenticationForm struct {
 	ID                int64
-	Type              int    `binding:"Range(2,5)"`
+	Type              int    `binding:"Range(2,6)"`
 	Name              string `binding:"Required;MaxSize(30)"`
 	Host              string
 	Port              int
@@ -36,6 +36,9 @@ type AuthenticationForm struct {
 	TLS               bool
 	SkipVerify        bool
 	PAMServiceName    string
+	Oauth2Provider    string
+	Oauth2Key	  string
+	Oauth2Secret      string
 }
 
 // Validate validates fields
diff --git a/modules/auth/oauth2/oauth2.go b/modules/auth/oauth2/oauth2.go
new file mode 100644
index 0000000000..bf486d5a0b
--- /dev/null
+++ b/modules/auth/oauth2/oauth2.go
@@ -0,0 +1,105 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/log"
+	"github.com/gorilla/sessions"
+	"github.com/markbates/goth"
+	"github.com/markbates/goth/gothic"
+	"net/http"
+	"os"
+	"github.com/satori/go.uuid"
+	"path/filepath"
+	"github.com/markbates/goth/providers/github"
+)
+
+var (
+	sessionUsersStoreKey = "gitea-oauth2-sessions"
+	providerHeaderKey    = "gitea-oauth2-provider"
+)
+
+// Init initialize the setup of the OAuth2 library
+func Init() {
+	sessionDir := filepath.Join(setting.AppDataPath, "sessions", "oauth2")
+	if err := os.MkdirAll(sessionDir, 0700); err != nil {
+		log.Fatal(4, "Fail to create dir %s: %v", sessionDir, err)
+	}
+
+	gothic.Store = sessions.NewFilesystemStore(sessionDir, []byte(sessionUsersStoreKey))
+
+	gothic.SetState = func(req *http.Request) string {
+		return uuid.NewV4().String()
+	}
+
+	gothic.GetProviderName = func(req *http.Request) (string, error) {
+		return req.Header.Get(providerHeaderKey), nil
+	}
+
+}
+
+// Auth OAuth2 auth service
+func Auth(provider string, request *http.Request, response http.ResponseWriter) error {
+	// not sure if goth is thread safe (?) when using multiple providers
+	request.Header.Set(providerHeaderKey, provider)
+
+	// don't use the default gothic begin handler to prevent issues when some error occurs
+	// normally the gothic library will write some custom stuff to the response instead of our own nice error page
+	//gothic.BeginAuthHandler(response, request)
+
+	url, err := gothic.GetAuthURL(response, request)
+	if err == nil {
+		http.Redirect(response, request, url, http.StatusTemporaryRedirect)
+	}
+	return err
+}
+
+// ProviderCallback handles OAuth callback, resolve to a goth user and send back to original url
+// this will trigger a new authentication request, but because we save it in the session we can use that
+func ProviderCallback(provider string, request *http.Request, response http.ResponseWriter) (goth.User, error) {
+	// not sure if goth is thread safe (?) when using multiple providers
+	request.Header.Set(providerHeaderKey, provider)
+
+	user, err := gothic.CompleteUserAuth(response, request)
+	if err != nil {
+		return user, err
+	}
+
+	return user, nil
+}
+
+// RegisterProvider register a OAuth2 provider in goth lib
+func RegisterProvider(providerName, providerType, clientID, clientSecret string) {
+	provider := createProvider(providerName, providerType, clientID, clientSecret)
+
+	if provider != nil {
+		goth.UseProviders(provider)
+	}
+}
+
+// RemoveProvider removes the given OAuth2 provider from the goth lib
+func RemoveProvider(providerName string) {
+	delete(goth.GetProviders(), providerName)
+}
+
+// used to create different types of goth providers
+func createProvider(providerName, providerType, clientID, clientSecret string) goth.Provider {
+	callbackURL := setting.AppURL + "user/oauth2/" + providerName + "/callback"
+
+	var provider goth.Provider
+
+	switch providerType {
+	case "github":
+		provider = github.New(clientID, clientSecret, callbackURL, "user:email")
+	}
+
+	// always set the name if provider is created so we can support multiple setups of 1 provider
+	if provider != nil {
+		provider.SetName(providerName)
+	}
+
+	return provider
+}
diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go
index e2c45a21e9..0750d2219c 100644
--- a/modules/auth/user_form.go
+++ b/modules/auth/user_form.go
@@ -143,7 +143,7 @@ func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) bindi
 
 // ChangePasswordForm form for changing password
 type ChangePasswordForm struct {
-	OldPassword string `form:"old_password" binding:"Required;MinSize(1);MaxSize(255)"`
+	OldPassword string `form:"old_password" binding:"MaxSize(255)"`
 	Password    string `form:"password" binding:"Required;MaxSize(255)"`
 	Retype      string `form:"retype"`
 }
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index d33e5ea984..661a44caf5 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -5,8 +5,11 @@ dashboard = Dashboard
 explore = Explore
 help = Help
 sign_in = Sign In
+sign_in_with = Sign in with
 sign_out = Sign Out
 sign_up = Sign Up
+link_account = Link Account
+link_account_signin_or_signup = Login with existing credentials to link your existing account to these new account, or sign up for a new account
 register = Register
 website = Website
 version = Version
@@ -277,6 +280,7 @@ applications = Applications
 orgs = Organizations
 delete = Delete Account
 twofa = Two-Factor Authentication
+account_link = External Accounts
 uid = Uid
 
 public_profile = Public Profile
@@ -379,6 +383,13 @@ then_enter_passcode = Then enter the passcode the application gives you:
 passcode_invalid = That passcode is invalid. Try again.
 twofa_enrolled = Your account has now been enrolled in two-factor authentication. Make sure to save your scratch token (%s), as it will only be shown once!
 
+manage_account_links = Manage account links
+manage_account_links_desc = External accounts linked to this account
+account_links_not_available = There are no external accounts linked to this account
+remove_account_link = Remove linked account
+remove_account_link_desc = Delete this account link will remove all related access for your account. Do you want to continue?
+remove_account_link_success = Account link has been removed successfully!
+
 delete_account = Delete Your Account
 delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undone!
 confirm_delete_account = Confirm Deletion
@@ -1106,8 +1117,12 @@ auths.allowed_domains_helper = Leave it empty to not restrict any domains. Multi
 auths.enable_tls = Enable TLS Encryption
 auths.skip_tls_verify = Skip TLS Verify
 auths.pam_service_name = PAM Service Name
+auths.oauth2_provider = OAuth2 provider
+auths.oauth2_clientID = Client ID (Key)
+auths.oauth2_clientSecret = Client Secret
 auths.enable_auto_register = Enable Auto Registration
 auths.tips = Tips
+auths.tip.github = Register a new OAuth application on https://github.com/settings/applications/new and use <host>/user/oauth2/<Authentication Name>/callback as "Authorization callback URL"
 auths.edit = Edit Authentication Setting
 auths.activated = This authentication is activated
 auths.new_success = New authentication '%s' has been added successfully.
diff --git a/public/css/index.css b/public/css/index.css
index 34764b6894..a35351e177 100644
--- a/public/css/index.css
+++ b/public/css/index.css
@@ -2983,3 +2983,24 @@ footer .ui.language .menu {
 .ui.user.list .item .description a:hover {
   text-decoration: underline;
 }
+.user.link-account:not(.icon) {
+  padding-top: 15px;
+  padding-bottom: 5px;
+}
+.signin .oauth2 div {
+  display: inline-block;
+}
+.signin .oauth2 div p {
+  margin: 10px 5px 0 0;
+  float: left;
+}
+.signin .oauth2 a {
+  margin-right: 5px;
+}
+.signin .oauth2 a:last-child {
+  margin-right: 0px;
+}
+.signin .oauth2 img {
+  width: 32px;
+  height: 32px;
+}
\ No newline at end of file
diff --git a/public/img/github.png b/public/img/github.png
new file mode 100644
index 0000000000000000000000000000000000000000..1fa19c55d2f71505edf0f4d70840e817e7861c06
GIT binary patch
literal 1151
zcmV-_1c3XAP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF000C=Nkl<Zcmdqn
z1F$Pe6vpw%efHY6ZQHhO+qP}nws#B9wr!gi-~QLBRM2O1dv>aRaZb<q!tNwUdRuns
zWmdx}xF4UPFD76P1aO+m&=;TLew>Wef~;-qbQN~T3m5@KGy*SRcU1Y@_%gT?qoIV1
z#+_K&r^Z*p<CqVnWIi6pO7+=zBV3KSP(7K8tI;U!=C?#Y$P@jrWg3m=a48l-o>_=X
zkV~rhX7~W|+y`hDck^w~4$6pjXcK4iU7?KWf;Lf`Z;tj*Mzu%tC~tV5KpFQ5a$y3v
z3i_Y8%G&()sD>)o4V&Xsyo>oz!sg>$oQln{8&p9xws&p52?j$IbSrY-#c&;_K{1<-
z>#$gnal1hk3`P@|0B(UCJXEH}Vt4|z5QNG24X@!|T!~8nPIDPv!*7@j0o0%t7ArH}
z!;phpDmA|b7C;V8bNtGD5>^fI9<(Y>!j=xVJq>cO0Be*h^gQHfKaXal`$3MLFI8j}
zEP@;z>`?%RK#mq+m10F6feg;@D1b8{gGY+Z?I%J8&v+ETGmybVG|De<5M<QFqX4=<
zMhE2=coQ;Mgl#+uU>hug4BpHyFaa{S*~3NIn;?S;|J-qX$Y3m*dKf@cjDZZ+4>&jn
zGPuFxrTH5mgL4879)%3n^f-XEAcIE(4!(gLjSf8Dj4y+40uBa2j(*HQ06#*G1_c~U
zh8(?>fdJly98C$FXMezJ83^Dt$kBqpy1tfy06v9W83M@6d%#cD0OmoChGZasA&{ea
zfpHdCjb<J<-wf4|qlsZ=QagJbz%J$)Ee9V#2KRX!z<rRxM*#<SKnBB5<>`NuhCv2*
zgk20d&cgtX3$-Z1!7`B1P&D_b`Q{h~87&iKN$Dkz0(dFXlAis20KiQiG=B>e!X5cV
z)`LQN0Zr@aGyerBr1c65c7Q?}goBYw>5p26V+a&dhhl|Kfr|JUJL6EiP+|7i3zuTG
zgjRd5LO&>?Q;H4H5Tl?1qp?BW?bdPJiQ#w+SK$yeF*Mr>N8={EgE3G{MxkM;fzO4C
zn2VG1ZnrcBLylfY&iFKc1xmoV<pyquJ}$opa(ZjjLKRF!>kv=*)|djts1F)e8hB5r
zgarjJhg+RiUQL_*j}HcWx(@t2RKhm}Pt|*%G7gLo!2VE7p0^C#vckvACIz?O7H?uO
zW?(2j!YODHA%HcY7z{y6>);z<4phYZDSg>l42ozDHVV4~N0fP^J3Ipc)Z&P!J8=$F
z#5^47c>w3cwIi26C47kOkn=2nOXA*{lPfGZPR6hJ3}2uFW?-?D1E|KyNj)WZ#YD&;
z%>X81*QDQ=R>ZF!2JkCZOlvNl!-bgRK>%}bF>>{>8nXgkK^;`WauMn;4V6%bSFwT*
zYjPXlbu5AkEFGclQc!_vyp9cg{9$4RT!#S=-ie{+I}iroI{ZE=#`q{01pu+n%tOqP
Rd2;{&002ovPDHLkV1i^k^r8R&

literal 0
HcmV?d00001

diff --git a/public/js/index.js b/public/js/index.js
index fb35eadf8a..1470a44b97 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -1019,9 +1019,9 @@ function initAdmin() {
     // New authentication
     if ($('.admin.new.authentication').length > 0) {
         $('#auth_type').change(function () {
-            $('.ldap, .dldap, .smtp, .pam, .has-tls').hide();
+            $('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls').hide();
 
-            $('.ldap input[required], .dldap input[required], .smtp input[required], .pam input[required], .has-tls input[required]').removeAttr('required');
+            $('.ldap input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required] .has-tls input[required]').removeAttr('required');
 
             var authType = $(this).val();
             switch (authType) {
@@ -1042,6 +1042,10 @@ function initAdmin() {
                     $('.dldap').show();
                     $('.dldap div.required:not(.ldap) input').attr('required', 'required');
                     break;
+                case '6':     // OAuth2
+                    $('.oauth2').show();
+                    $('.oauth2 input').attr('required', 'required');
+                    break;
             }
 
             if (authType == '2' || authType == '5') {
diff --git a/routers/admin/auths.go b/routers/admin/auths.go
index d437dc4b80..345494b4f6 100644
--- a/routers/admin/auths.go
+++ b/routers/admin/auths.go
@@ -53,6 +53,7 @@ var (
 		{models.LoginNames[models.LoginDLDAP], models.LoginDLDAP},
 		{models.LoginNames[models.LoginSMTP], models.LoginSMTP},
 		{models.LoginNames[models.LoginPAM], models.LoginPAM},
+		{models.LoginNames[models.LoginOAuth2], models.LoginOAuth2},
 	}
 	securityProtocols = []dropdownItem{
 		{models.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted], ldap.SecurityProtocolUnencrypted},
@@ -75,6 +76,14 @@ func NewAuthSource(ctx *context.Context) {
 	ctx.Data["AuthSources"] = authSources
 	ctx.Data["SecurityProtocols"] = securityProtocols
 	ctx.Data["SMTPAuths"] = models.SMTPAuths
+	ctx.Data["OAuth2Providers"] = models.OAuth2Providers
+
+	// only the first as default
+	for key := range models.OAuth2Providers {
+		ctx.Data["oauth2_provider"] = key
+		break
+	}
+
 	ctx.HTML(200, tplAuthNew)
 }
 
@@ -113,6 +122,14 @@ func parseSMTPConfig(form auth.AuthenticationForm) *models.SMTPConfig {
 	}
 }
 
+func parseOAuth2Config(form auth.AuthenticationForm) *models.OAuth2Config {
+	return &models.OAuth2Config{
+		Provider:     form.Oauth2Provider,
+		ClientID:     form.Oauth2Key,
+		ClientSecret: form.Oauth2Secret,
+	}
+}
+
 // NewAuthSourcePost response for adding an auth source
 func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
 	ctx.Data["Title"] = ctx.Tr("admin.auths.new")
@@ -124,6 +141,7 @@ func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
 	ctx.Data["AuthSources"] = authSources
 	ctx.Data["SecurityProtocols"] = securityProtocols
 	ctx.Data["SMTPAuths"] = models.SMTPAuths
+	ctx.Data["OAuth2Providers"] = models.OAuth2Providers
 
 	hasTLS := false
 	var config core.Conversion
@@ -138,6 +156,8 @@ func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
 		config = &models.PAMConfig{
 			ServiceName: form.PAMServiceName,
 		}
+	case models.LoginOAuth2:
+		config = parseOAuth2Config(form)
 	default:
 		ctx.Error(400)
 		return
@@ -178,6 +198,7 @@ func EditAuthSource(ctx *context.Context) {
 
 	ctx.Data["SecurityProtocols"] = securityProtocols
 	ctx.Data["SMTPAuths"] = models.SMTPAuths
+	ctx.Data["OAuth2Providers"] = models.OAuth2Providers
 
 	source, err := models.GetLoginSourceByID(ctx.ParamsInt64(":authid"))
 	if err != nil {
@@ -187,16 +208,20 @@ func EditAuthSource(ctx *context.Context) {
 	ctx.Data["Source"] = source
 	ctx.Data["HasTLS"] = source.HasTLS()
 
+	if source.IsOAuth2() {
+		ctx.Data["CurrentOAuth2Provider"] = models.OAuth2Providers[source.OAuth2().Provider]
+	}
 	ctx.HTML(200, tplAuthEdit)
 }
 
-// EditAuthSourcePost resposne for editing auth source
+// EditAuthSourcePost response for editing auth source
 func EditAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
 	ctx.Data["Title"] = ctx.Tr("admin.auths.edit")
 	ctx.Data["PageIsAdmin"] = true
 	ctx.Data["PageIsAdminAuthentications"] = true
 
 	ctx.Data["SMTPAuths"] = models.SMTPAuths
+	ctx.Data["OAuth2Providers"] = models.OAuth2Providers
 
 	source, err := models.GetLoginSourceByID(ctx.ParamsInt64(":authid"))
 	if err != nil {
@@ -221,6 +246,8 @@ func EditAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
 		config = &models.PAMConfig{
 			ServiceName: form.PAMServiceName,
 		}
+	case models.LoginOAuth2:
+		config = parseOAuth2Config(form)
 	default:
 		ctx.Error(400)
 		return
diff --git a/routers/init.go b/routers/init.go
index c56f0be1dd..3b1618a383 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -54,6 +54,7 @@ func GlobalInit() {
 			log.Fatal(4, "Failed to initialize ORM engine: %v", err)
 		}
 		models.HasEngine = true
+		models.InitOAuth2()
 
 		models.LoadRepoConfig()
 		models.NewRepoContext()
diff --git a/routers/repo/http.go b/routers/repo/http.go
index 780babd40d..4c143f66d0 100644
--- a/routers/repo/http.go
+++ b/routers/repo/http.go
@@ -59,7 +59,7 @@ func HTTP(ctx *context.Context) {
 	isWiki := false
 	if strings.HasSuffix(reponame, ".wiki") {
 		isWiki = true
-		reponame = reponame[:len(reponame)-5]
+		reponame = reponame[:len(reponame) - 5]
 	}
 
 	repoUser, err := models.GetUserByName(username)
@@ -191,9 +191,9 @@ func HTTP(ctx *context.Context) {
 
 		var lastLine int64
 		for {
-			head := input[lastLine : lastLine+2]
+			head := input[lastLine: lastLine + 2]
 			if head[0] == '0' && head[1] == '0' {
-				size, err := strconv.ParseInt(string(input[lastLine+2:lastLine+4]), 16, 32)
+				size, err := strconv.ParseInt(string(input[lastLine + 2:lastLine + 4]), 16, 32)
 				if err != nil {
 					log.Error(4, "%v", err)
 					return
@@ -204,7 +204,7 @@ func HTTP(ctx *context.Context) {
 					break
 				}
 
-				line := input[lastLine : lastLine+size]
+				line := input[lastLine: lastLine + size]
 				idx := bytes.IndexRune(line, '\000')
 				if idx > -1 {
 					line = line[:idx]
@@ -370,7 +370,7 @@ func gitCommand(dir string, args ...string) []byte {
 
 func getGitConfig(option, dir string) string {
 	out := string(gitCommand(dir, "config", option))
-	return out[0 : len(out)-1]
+	return out[0: len(out) - 1]
 }
 
 func getConfigSetting(service, dir string) bool {
@@ -501,7 +501,7 @@ func updateServerInfo(dir string) []byte {
 }
 
 func packetWrite(str string) []byte {
-	s := strconv.FormatInt(int64(len(str)+4), 16)
+	s := strconv.FormatInt(int64(len(str) + 4), 16)
 	if len(s)%4 != 0 {
 		s = strings.Repeat("0", 4-len(s)%4) + s
 	}
diff --git a/routers/user/auth.go b/routers/user/auth.go
index 85a51620ea..5b9297d349 100644
--- a/routers/user/auth.go
+++ b/routers/user/auth.go
@@ -17,6 +17,10 @@ import (
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
+	"net/http"
+	"code.gitea.io/gitea/modules/auth/oauth2"
+	"github.com/markbates/goth"
+	"strings"
 )
 
 const (
@@ -30,6 +34,7 @@ const (
 	tplResetPassword  base.TplName = "user/auth/reset_passwd"
 	tplTwofa          base.TplName = "user/auth/twofa"
 	tplTwofaScratch   base.TplName = "user/auth/twofa_scratch"
+	tplLinkAccount    base.TplName = "user/auth/link_account"
 )
 
 // AutoSignIn reads cookie and try to auto-login.
@@ -61,7 +66,7 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
 	}
 
 	if val, _ := ctx.GetSuperSecureCookie(
-		base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); val != u.Name {
+		base.EncodeMD5(u.Rands + u.Passwd), setting.CookieRememberName); val != u.Name {
 		return false, nil
 	}
 
@@ -109,6 +114,13 @@ func SignIn(ctx *context.Context) {
 		return
 	}
 
+	oauth2Providers, err := models.GetActiveOAuth2Providers()
+	if err != nil {
+		ctx.Handle(500, "UserSignIn", err)
+		return
+	}
+	ctx.Data["OAuth2Providers"] = oauth2Providers
+
 	ctx.HTML(200, tplSignIn)
 }
 
@@ -116,6 +128,13 @@ func SignIn(ctx *context.Context) {
 func SignInPost(ctx *context.Context, form auth.SignInForm) {
 	ctx.Data["Title"] = ctx.Tr("sign_in")
 
+	oauth2Providers, err := models.GetActiveOAuth2Providers()
+	if err != nil {
+		ctx.Handle(500, "UserSignIn", err)
+		return
+	}
+	ctx.Data["OAuth2Providers"] = oauth2Providers
+
 	if ctx.HasError() {
 		ctx.HTML(200, tplSignIn)
 		return
@@ -277,7 +296,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
 	if remember {
 		days := 86400 * setting.LogInRememberDays
 		ctx.SetCookie(setting.CookieUserName, u.Name, days, setting.AppSubURL)
-		ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
+		ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands + u.Passwd),
 			setting.CookieRememberName, u.Name, days, setting.AppSubURL)
 	}
 
@@ -309,6 +328,333 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
 	}
 }
 
+// SignInOAuth handles the OAuth2 login buttons
+func SignInOAuth(ctx *context.Context) {
+	provider := ctx.Params(":provider")
+
+	loginSource, err := models.GetActiveOAuth2LoginSourceByName(provider)
+	if err != nil {
+		ctx.Handle(500, "SignIn", err)
+		return
+	}
+
+	// try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user
+	user, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req.Request, ctx.Resp)
+	if err == nil && user != nil {
+		// we got the user without going through the whole OAuth2 authentication flow again
+		handleOAuth2SignIn(user, gothUser, ctx, err)
+		return
+	}
+
+	err = oauth2.Auth(loginSource.Name, ctx.Req.Request, ctx.Resp)
+	if err != nil {
+		ctx.Handle(500, "SignIn", err)
+	}
+	// redirect is done in oauth2.Auth
+}
+
+// SignInOAuthCallback handles the callback from the given provider
+func SignInOAuthCallback(ctx *context.Context) {
+	provider := ctx.Params(":provider")
+
+	// first look if the provider is still active
+	loginSource, err := models.GetActiveOAuth2LoginSourceByName(provider)
+	if err != nil {
+		ctx.Handle(500, "SignIn", err)
+		return
+	}
+
+	if loginSource == nil {
+		ctx.Handle(500, "SignIn", errors.New("No valid provider found, check configured callback url in provider"))
+		return
+	}
+
+	u, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req.Request, ctx.Resp)
+
+	handleOAuth2SignIn(u, gothUser, ctx, err)
+}
+
+func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context, err error) {
+	if err != nil {
+		ctx.Handle(500, "UserSignIn", err)
+		return
+	}
+
+	if u == nil {
+		// no existing user is found, request attach or new account
+		ctx.Session.Set("linkAccountGothUser", gothUser)
+		ctx.Redirect(setting.AppSubURL + "/user/link_account")
+		return
+	}
+
+	// If this user is enrolled in 2FA, we can't sign the user in just yet.
+	// Instead, redirect them to the 2FA authentication page.
+	_, err = models.GetTwoFactorByUID(u.ID)
+	if err != nil {
+		if models.IsErrTwoFactorNotEnrolled(err) {
+			ctx.Session.Set("uid", u.ID)
+			ctx.Session.Set("uname", u.Name)
+
+			// Clear whatever CSRF has right now, force to generate a new one
+			ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL)
+
+			// Register last login
+			u.SetLastLogin()
+			if err := models.UpdateUser(u); err != nil {
+				ctx.Handle(500, "UpdateUser", err)
+				return
+			}
+
+			if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
+				ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
+				ctx.Redirect(redirectTo)
+				return
+			}
+
+			ctx.Redirect(setting.AppSubURL + "/")
+		} else {
+			ctx.Handle(500, "UserSignIn", err)
+		}
+		return
+	}
+
+	// User needs to use 2FA, save data and redirect to 2FA page.
+	ctx.Session.Set("twofaUid", u.ID)
+	ctx.Session.Set("twofaRemember", false)
+	ctx.Redirect(setting.AppSubURL + "/user/two_factor")
+}
+
+// OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful
+// login the user
+func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Request, response http.ResponseWriter) (*models.User, goth.User, error) {
+	gothUser, err := oauth2.ProviderCallback(loginSource.Name, request, response)
+
+	if err != nil {
+		return nil, goth.User{}, err
+	}
+
+	user := &models.User{
+		LoginName:   gothUser.UserID,
+		LoginType:   models.LoginOAuth2,
+		LoginSource: loginSource.ID,
+	}
+
+	hasUser, err := models.GetUser(user)
+	if err != nil {
+		return nil, goth.User{}, err
+	}
+
+	if hasUser {
+		return user, goth.User{}, nil
+	}
+
+	// search in external linked users
+	externalLoginUser := &models.ExternalLoginUser{
+		ExternalID:    gothUser.UserID,
+		LoginSourceID: loginSource.ID,
+	}
+	hasUser, err = models.GetExternalLogin(externalLoginUser)
+	if err != nil {
+		return nil, goth.User{}, err
+	}
+	if hasUser {
+		user, err = models.GetUserByID(externalLoginUser.UserID)
+		return user, goth.User{}, err
+	}
+
+	// no user found to login
+	return nil, gothUser, nil
+
+}
+
+// LinkAccount shows the page where the user can decide to login or create a new account
+func LinkAccount(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("link_account")
+	ctx.Data["LinkAccountMode"] = true
+	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
+	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
+	ctx.Data["ShowRegistrationButton"] = false
+
+	// use this to set the right link into the signIn and signUp templates in the link_account template
+	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
+	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
+
+	gothUser := ctx.Session.Get("linkAccountGothUser")
+	if gothUser == nil {
+		ctx.Handle(500, "UserSignIn", errors.New("not in LinkAccount session"))
+		return
+	}
+
+	ctx.Data["user_name"] = gothUser.(goth.User).NickName
+	ctx.Data["email"] = gothUser.(goth.User).Email
+
+	ctx.HTML(200, tplLinkAccount)
+}
+
+// LinkAccountPostSignIn handle the coupling of external account with another account using signIn
+func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
+	ctx.Data["Title"] = ctx.Tr("link_account")
+	ctx.Data["LinkAccountMode"] = true
+	ctx.Data["LinkAccountModeSignIn"] = true
+	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
+	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
+	ctx.Data["ShowRegistrationButton"] = false
+
+	// use this to set the right link into the signIn and signUp templates in the link_account template
+	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
+	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
+
+	gothUser := ctx.Session.Get("linkAccountGothUser")
+	if gothUser == nil {
+		ctx.Handle(500, "UserSignIn", errors.New("not in LinkAccount session"))
+		return
+	}
+
+	if ctx.HasError() {
+		ctx.HTML(200, tplLinkAccount)
+		return
+	}
+
+	u, err := models.UserSignIn(signInForm.UserName, signInForm.Password)
+	if err != nil {
+		if models.IsErrUserNotExist(err) {
+			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplLinkAccount, &signInForm)
+		} else {
+			ctx.Handle(500, "UserLinkAccount", err)
+		}
+		return
+	}
+
+	// If this user is enrolled in 2FA, we can't sign the user in just yet.
+	// Instead, redirect them to the 2FA authentication page.
+	_, err = models.GetTwoFactorByUID(u.ID)
+	if err != nil {
+		if models.IsErrTwoFactorNotEnrolled(err) {
+			models.LinkAccountToUser(u, gothUser.(goth.User))
+			handleSignIn(ctx, u, signInForm.Remember)
+		} else {
+			ctx.Handle(500, "UserLinkAccount", err)
+		}
+		return
+	}
+
+	// User needs to use 2FA, save data and redirect to 2FA page.
+	ctx.Session.Set("twofaUid", u.ID)
+	ctx.Session.Set("twofaRemember", signInForm.Remember)
+	ctx.Session.Set("linkAccount", true)
+
+	ctx.Redirect(setting.AppSubURL + "/user/two_factor")
+}
+
+// LinkAccountPostRegister handle the creation of a new account for an external account using signUp
+func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) {
+	ctx.Data["Title"] = ctx.Tr("link_account")
+	ctx.Data["LinkAccountMode"] = true
+	ctx.Data["LinkAccountModeRegister"] = true
+	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
+	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
+	ctx.Data["ShowRegistrationButton"] = false
+
+	// use this to set the right link into the signIn and signUp templates in the link_account template
+	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
+	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
+
+	gothUser := ctx.Session.Get("linkAccountGothUser")
+	if gothUser == nil {
+		ctx.Handle(500, "UserSignUp", errors.New("not in LinkAccount session"))
+		return
+	}
+
+	if ctx.HasError() {
+		ctx.HTML(200, tplLinkAccount)
+		return
+	}
+
+	if setting.Service.DisableRegistration {
+		ctx.Error(403)
+		return
+	}
+
+	if setting.Service.EnableCaptcha && !cpt.VerifyReq(ctx.Req) {
+		ctx.Data["Err_Captcha"] = true
+		ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplLinkAccount, &form)
+		return
+	}
+
+	if (len(strings.TrimSpace(form.Password)) > 0 || len(strings.TrimSpace(form.Retype)) > 0) && form.Password != form.Retype {
+		ctx.Data["Err_Password"] = true
+		ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplLinkAccount, &form)
+		return
+	}
+	if len(strings.TrimSpace(form.Password)) > 0 && len(form.Password) < setting.MinPasswordLength {
+		ctx.Data["Err_Password"] = true
+		ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplLinkAccount, &form)
+		return
+	}
+
+	loginSource, err := models.GetActiveOAuth2LoginSourceByName(gothUser.(goth.User).Provider)
+	if err != nil {
+		ctx.Handle(500, "CreateUser", err)
+	}
+
+	u := &models.User{
+		Name:        form.UserName,
+		Email:       form.Email,
+		Passwd:      form.Password,
+		IsActive:    !setting.Service.RegisterEmailConfirm,
+		LoginType:   models.LoginOAuth2,
+		LoginSource: loginSource.ID,
+		LoginName:   gothUser.(goth.User).UserID,
+	}
+
+	if err := models.CreateUser(u); err != nil {
+		switch {
+		case models.IsErrUserAlreadyExist(err):
+			ctx.Data["Err_UserName"] = true
+			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplLinkAccount, &form)
+		case models.IsErrEmailAlreadyUsed(err):
+			ctx.Data["Err_Email"] = true
+			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplLinkAccount, &form)
+		case models.IsErrNameReserved(err):
+			ctx.Data["Err_UserName"] = true
+			ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(models.ErrNameReserved).Name), tplLinkAccount, &form)
+		case models.IsErrNamePatternNotAllowed(err):
+			ctx.Data["Err_UserName"] = true
+			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplLinkAccount, &form)
+		default:
+			ctx.Handle(500, "CreateUser", err)
+		}
+		return
+	}
+	log.Trace("Account created: %s", u.Name)
+
+	// Auto-set admin for the only user.
+	if models.CountUsers() == 1 {
+		u.IsAdmin = true
+		u.IsActive = true
+		if err := models.UpdateUser(u); err != nil {
+			ctx.Handle(500, "UpdateUser", err)
+			return
+		}
+	}
+
+	// Send confirmation email
+	if setting.Service.RegisterEmailConfirm && u.ID > 1 {
+		models.SendActivateAccountMail(ctx.Context, u)
+		ctx.Data["IsSendRegisterMail"] = true
+		ctx.Data["Email"] = u.Email
+		ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60
+		ctx.HTML(200, TplActivate)
+
+		if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
+			log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
+		}
+		return
+	}
+
+	ctx.Redirect(setting.AppSubURL + "/user/login")
+}
+
 // SignOut sign out from login status
 func SignOut(ctx *context.Context) {
 	ctx.Session.Delete("uid")
@@ -328,11 +674,7 @@ func SignUp(ctx *context.Context) {
 
 	ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
 
-	if setting.Service.DisableRegistration {
-		ctx.Data["DisableRegistration"] = true
-		ctx.HTML(200, tplSignUp)
-		return
-	}
+	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
 
 	ctx.HTML(200, tplSignUp)
 }
@@ -540,7 +882,7 @@ func ForgotPasswdPost(ctx *context.Context) {
 		return
 	}
 
-	if !u.IsLocal() {
+	if !u.IsLocal() && !u.IsOAuth2() {
 		ctx.Data["Err_Email"] = true
 		ctx.RenderWithErr(ctx.Tr("auth.non_local_account"), tplForgotPassword, nil)
 		return
diff --git a/routers/user/setting.go b/routers/user/setting.go
index 8fc2be0ce4..de5a1baca4 100644
--- a/routers/user/setting.go
+++ b/routers/user/setting.go
@@ -37,6 +37,7 @@ const (
 	tplSettingsApplications base.TplName = "user/settings/applications"
 	tplSettingsTwofa        base.TplName = "user/settings/twofa"
 	tplSettingsTwofaEnroll  base.TplName = "user/settings/twofa_enroll"
+	tplSettingsAccountLink  base.TplName = "user/settings/account_link"
 	tplSettingsDelete       base.TplName = "user/settings/delete"
 	tplSecurity             base.TplName = "user/security"
 )
@@ -200,7 +201,7 @@ func SettingsPasswordPost(ctx *context.Context, form auth.ChangePasswordForm) {
 		return
 	}
 
-	if !ctx.User.ValidatePassword(form.OldPassword) {
+	if ctx.User.IsPasswordSet() && !ctx.User.ValidatePassword(form.OldPassword) {
 		ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
 	} else if form.Password != form.Retype {
 		ctx.Flash.Error(ctx.Tr("form.password_not_match"))
@@ -631,6 +632,49 @@ func SettingsTwoFactorEnrollPost(ctx *context.Context, form auth.TwoFactorAuthFo
 	ctx.Redirect(setting.AppSubURL + "/user/settings/two_factor")
 }
 
+// SettingsAccountLinks render the account links settings page
+func SettingsAccountLinks(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("settings")
+	ctx.Data["PageIsSettingsAccountLink"] = true
+
+	accountLinks, err := models.ListAccountLinks(ctx.User)
+	if err != nil {
+		ctx.Handle(500, "ListAccountLinks", err)
+		return
+	}
+
+	// map the provider display name with the LoginSource
+	sources := make(map[*models.LoginSource]string)
+	for _, externalAccount := range accountLinks {
+		if loginSource, err := models.GetLoginSourceByID(externalAccount.LoginSourceID); err == nil {
+			var providerDisplayName string
+			if loginSource.IsOAuth2() {
+				providerTechnicalName := loginSource.OAuth2().Provider
+				providerDisplayName = models.OAuth2Providers[providerTechnicalName].DisplayName
+			} else {
+				providerDisplayName = loginSource.Name
+			}
+			sources[loginSource] = providerDisplayName
+		}
+	}
+	ctx.Data["AccountLinks"] = sources
+
+	ctx.HTML(200, tplSettingsAccountLink)
+}
+
+// SettingsDeleteAccountLink delete a single account link
+func SettingsDeleteAccountLink(ctx *context.Context) {
+	if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil {
+		ctx.Flash.Error("RemoveAccountLink: " + err.Error())
+	} else {
+		ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success"))
+	}
+
+	ctx.JSON(200, map[string]interface{}{
+		"redirect": setting.AppSubURL + "/user/settings/account_link",
+	})
+}
+
 // SettingsDelete render user suicide page and response for delete user himself
 func SettingsDelete(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("settings")
diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl
index 0879b274b1..84b62f6e87 100644
--- a/templates/admin/auth/edit.tmpl
+++ b/templates/admin/auth/edit.tmpl
@@ -142,6 +142,32 @@
 							</div>
 						{{end}}
 
+						<!-- OAuth2 -->
+						{{if .Source.IsOAuth2}}
+							{{ $cfg:=.Source.OAuth2 }}
+							<div class="inline required field">
+								<label>{{.i18n.Tr "admin.auths.oauth2_provider"}}</label>
+								<div class="ui selection type dropdown">
+									<input type="hidden" id="oauth2_provider" name="oauth2_provider" value="{{$cfg.Provider}}" required>
+									<div class="text">{{.CurrentOAuth2Provider.DisplayName}}</div>
+									<i class="dropdown icon"></i>
+									<div class="menu">
+										{{range $key, $value := .OAuth2Providers}}
+											<div class="item" data-value="{{$key}}">{{$value.DisplayName}}</div>
+										{{end}}
+									</div>
+								</div>
+							</div>
+							<div class="required field">
+								<label for="oauth2_key">{{.i18n.Tr "admin.auths.oauth2_clientID"}}</label>
+								<input id="oauth2_key" name="oauth2_key" value="{{$cfg.ClientID}}" required>
+							</div>
+							<div class="required field">
+								<label for="oauth2_secret">{{.i18n.Tr "admin.auths.oauth2_clientSecret"}}</label>
+								<input id="oauth2_secret" name="oauth2_secret" value="{{$cfg.ClientSecret}}" required>
+							</div>
+						{{end}}
+
 						<div class="inline field {{if not .Source.IsSMTP}}hide{{end}}">
 							<div class="ui checkbox">
 								<label><strong>{{.i18n.Tr "admin.auths.enable_tls"}}</strong></label>
diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl
index 1edec0cb34..24257a1b65 100644
--- a/templates/admin/auth/new.tmpl
+++ b/templates/admin/auth/new.tmpl
@@ -133,6 +133,31 @@
 							<input id="pam_service_name" name="pam_service_name" value="{{.pam_service_name}}" />
 						</div>
 
+						<!-- OAuth2 -->
+						<div class="oauth2 field {{if not (eq .type 6)}}hide{{end}}">
+							<div class="inline required field">
+								<label>{{.i18n.Tr "admin.auths.oauth2_provider"}}</label>
+								<div class="ui selection type dropdown">
+									<input type="hidden" id="oauth2_provider" name="oauth2_provider" value="{{.oauth2_provider}}">
+									<div class="text">{{.oauth2_provider}}</div>
+									<i class="dropdown icon"></i>
+									<div class="menu">
+										{{range $key, $value := .OAuth2Providers}}
+											<div class="item" data-value="{{$key}}">{{$value.DisplayName}}</div>
+										{{end}}
+									</div>
+								</div>
+							</div>
+							<div class="required field">
+								<label for="oauth2_key">{{.i18n.Tr "admin.auths.oauth2_clientID"}}</label>
+								<input id="oauth2_key" name="oauth2_key" value="{{.oauth2_key}}">
+							</div>
+							<div class="required field">
+								<label for="oauth2_secret">{{.i18n.Tr "admin.auths.oauth2_clientSecret"}}</label>
+								<input id="oauth2_secret" name="oauth2_secret" value="{{.oauth2_secret}}">
+							</div>
+						</div>
+
 						<div class="ldap field">
 							<div class="ui checkbox">
 								<label><strong>{{.i18n.Tr "admin.auths.attributes_in_bind"}}</strong></label>
@@ -170,6 +195,8 @@
 				<div class="ui attached segment">
 					<h5>GMail Settings:</h5>
 					<p>Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true</p>
+					<h5>OAuth GitHub:</h5>
+					<p>{{.i18n.Tr "admin.auths.tip.github"}}</p>
 				</div>
 			</div>
 		</div>
diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl
index 2d9c12e822..7c97198d53 100644
--- a/templates/admin/user/edit.tmpl
+++ b/templates/admin/user/edit.tmpl
@@ -43,7 +43,7 @@
 							<input id="email" name="email" type="email" value="{{.User.Email}}" autofocus required>
 						</div>
 						<input class="fake" type="password">
-						<div class="local field {{if .Err_Password}}error{{end}} {{if not (eq .User.LoginSource 0)}}hide{{end}}">
+						<div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}hide{{end}}">
 							<label for="password">{{.i18n.Tr "password"}}</label>
 							<input id="password" name="password" type="password">
 							<p class="help">{{.i18n.Tr "admin.users.password_helper"}}</p>
diff --git a/templates/user/auth/link_account.tmpl b/templates/user/auth/link_account.tmpl
new file mode 100644
index 0000000000..5dc54ca5a4
--- /dev/null
+++ b/templates/user/auth/link_account.tmpl
@@ -0,0 +1,13 @@
+{{template "base/head" .}}
+<div class="user link-account">
+	<div class="ui middle very relaxed page grid">
+		<div class="column">
+			<p class="large center">
+				{{.i18n.Tr "link_account_signin_or_signup"}}
+			</p>
+		</div>
+	</div>
+</div>
+{{template "user/auth/signin_inner" .}}
+{{template "user/auth/signup_inner" .}}
+{{template "base/footer" .}}
diff --git a/templates/user/auth/signin.tmpl b/templates/user/auth/signin.tmpl
index 85b7e70277..5ed8612e3f 100644
--- a/templates/user/auth/signin.tmpl
+++ b/templates/user/auth/signin.tmpl
@@ -1,44 +1,3 @@
 {{template "base/head" .}}
-<div class="user signin">
-	<div class="ui middle very relaxed page grid">
-		<div class="column">
-			<form class="ui form" action="{{.Link}}" method="post">
-				{{.CsrfTokenHtml}}
-				<h3 class="ui top attached header">
-					{{.i18n.Tr "sign_in"}}
-				</h3>
-				<div class="ui attached segment">
-					{{template "base/alert" .}}
-					<div class="required inline field {{if .Err_UserName}}error{{end}}">
-						<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
-						<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
-					</div>
-					<div class="required inline field {{if .Err_Password}}error{{end}}">
-						<label for="password">{{.i18n.Tr "password"}}</label>
-						<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
-					</div>
-					<div class="inline field">
-						<label></label>
-						<div class="ui checkbox">
-							<label>{{.i18n.Tr "auth.remember_me"}}</label>
-							<input name="remember" type="checkbox">
-						</div>
-					</div>
-
-					<div class="inline field">
-						<label></label>
-						<button class="ui green button">{{.i18n.Tr "sign_in"}}</button>
-						<a href="{{AppSubUrl}}/user/forget_password">{{.i18n.Tr "auth.forget_password"}}</a>
-					</div>
-					{{if .ShowRegistrationButton}}
-						<div class="inline field">
-							<label></label>
-							<a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a>
-						</div>
-					{{end}}
-				</div>
-			</form>
-		</div>
-	</div>
-</div>
+{{template "user/auth/signin_inner" .}}
 {{template "base/footer" .}}
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl
new file mode 100644
index 0000000000..34fcea08fc
--- /dev/null
+++ b/templates/user/auth/signin_inner.tmpl
@@ -0,0 +1,57 @@
+<div class="user signin{{if .LinkAccountMode}} icon{{end}}">
+	<div class="ui middle very relaxed page grid">
+		<div class="column">
+			<form class="ui form" action="{{if not .LinkAccountMode}}{{.Link}}{{else}}{{.SignInLink}}{{end}}" method="post">
+				{{.CsrfTokenHtml}}
+				<h3 class="ui top attached header">
+					{{.i18n.Tr "sign_in"}}
+				</h3>
+				<div class="ui attached segment">
+					{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}}
+					{{template "base/alert" .}}
+					{{end}}
+					<div class="required inline field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
+						<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
+						<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
+					</div>
+					<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
+						<label for="password">{{.i18n.Tr "password"}}</label>
+						<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
+					</div>
+					{{if not .LinkAccountMode}}
+					<div class="inline field">
+						<label></label>
+						<div class="ui checkbox">
+							<label>{{.i18n.Tr "auth.remember_me"}}</label>
+							<input name="remember" type="checkbox">
+						</div>
+					</div>
+					{{end}}
+
+					<div class="inline field">
+						<label></label>
+						<button class="ui green button">{{.i18n.Tr "sign_in"}}</button>
+						<a href="{{AppSubUrl}}/user/forget_password">{{.i18n.Tr "auth.forget_password"}}</a>
+					</div>
+
+					{{if .ShowRegistrationButton}}
+						<div class="inline field">
+							<label></label>
+							<a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a>
+						</div>
+					{{end}}
+
+					{{if .OAuth2Providers}}
+					<div class="ui attached segment">
+						<div class="oauth2 center">
+							<div>
+								<p>{{.i18n.Tr "sign_in_with"}}</p>{{range $key, $value := .OAuth2Providers}}<a href="{{AppSubUrl}}/user/oauth2/{{$key}}"><img alt="{{$value.DisplayName}}" title="{{$value.DisplayName}}" src="{{AppSubUrl}}{{$value.Image}}"></a>{{end}}
+							</div>
+						</div>
+					</div>
+					{{end}}
+				</div>
+			</form>
+		</div>
+	</div>
+</div>
\ No newline at end of file
diff --git a/templates/user/auth/signup.tmpl b/templates/user/auth/signup.tmpl
index aeae4f957e..b814b466c8 100644
--- a/templates/user/auth/signup.tmpl
+++ b/templates/user/auth/signup.tmpl
@@ -1,56 +1,3 @@
 {{template "base/head" .}}
-<div class="user signup">
-	<div class="ui middle very relaxed page grid">
-		<div class="column">
-			<form class="ui form" action="{{.Link}}" method="post">
-				{{.CsrfTokenHtml}}
-				<h3 class="ui top attached header">
-					{{if .IsSocialLogin}}{{.i18n.Tr "social_sign_in" | Str2html}}{{else}}{{.i18n.Tr "sign_up"}}{{end}}
-				</h3>
-				<div class="ui attached segment">
-					{{template "base/alert" .}}
-					{{if .DisableRegistration}}
-						<p>{{.i18n.Tr "auth.disable_register_prompt"}}</p>
-					{{else}}
-						<div class="required inline field {{if .Err_UserName}}error{{end}}">
-							<label for="user_name">{{.i18n.Tr "username"}}</label>
-							<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
-						</div>
-						<div class="required inline field {{if .Err_Email}}error{{end}}">
-							<label for="email">{{.i18n.Tr "email"}}</label>
-							<input id="email" name="email" type="email" value="{{.email}}" required>
-						</div>
-						<div class="required inline field {{if .Err_Password}}error{{end}}">
-							<label for="password">{{.i18n.Tr "password"}}</label>
-							<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
-						</div>
-						<div class="required inline field {{if .Err_Password}}error{{end}}">
-							<label for="retype">{{.i18n.Tr "re_type"}}</label>
-							<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required>
-						</div>
-						{{if .EnableCaptcha}}
-							<div class="inline field">
-								<label></label>
-								{{.Captcha.CreateHtml}}
-							</div>
-							<div class="required inline field {{if .Err_Captcha}}error{{end}}">
-								<label for="captcha">{{.i18n.Tr "captcha"}}</label>
-								<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
-							</div>
-						{{end}}
-
-						<div class="inline field">
-							<label></label>
-							<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button>
-						</div>
-						<div class="inline field">
-							<label></label>
-							<a href="{{AppSubUrl}}/user/login">{{if .IsSocialLogin}}{{.i18n.Tr "auth.social_register_helper_msg"}}{{else}}{{.i18n.Tr "auth.register_helper_msg"}}{{end}}</a>
-						</div>
-					{{end}}
-				</div>
-			</form>
-		</div>
-	</div>
-</div>
+{{template "user/auth/signup_inner" .}}
 {{template "base/footer" .}}
diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl
new file mode 100644
index 0000000000..869f3d5342
--- /dev/null
+++ b/templates/user/auth/signup_inner.tmpl
@@ -0,0 +1,59 @@
+<div class="user signup{{if .LinkAccountMode}} icon{{end}}">
+	<div class="ui middle very relaxed page grid">
+		<div class="column">
+			<form class="ui form" action="{{if not .LinkAccountMode}}{{.Link}}{{else}}{{.SignUpLink}}{{end}}" method="post">
+				{{.CsrfTokenHtml}}
+				<h3 class="ui top attached header">
+					{{.i18n.Tr "sign_up"}}
+				</h3>
+				<div class="ui attached segment">
+					{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}}
+					{{template "base/alert" .}}
+					{{end}}
+					{{if .DisableRegistration}}
+						<p>{{.i18n.Tr "auth.disable_register_prompt"}}</p>
+					{{else}}
+						<div class="required inline field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
+							<label for="user_name">{{.i18n.Tr "username"}}</label>
+							<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
+						</div>
+						<div class="required inline field {{if .Err_Email}}error{{end}}">
+							<label for="email">{{.i18n.Tr "email"}}</label>
+							<input id="email" name="email" type="email" value="{{.email}}" required>
+						</div>
+						<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
+							<label for="password">{{.i18n.Tr "password"}}</label>
+							<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
+						</div>
+						<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
+							<label for="retype">{{.i18n.Tr "re_type"}}</label>
+							<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required>
+						</div>
+						{{if .EnableCaptcha}}
+							<div class="inline field">
+								<label></label>
+								{{.Captcha.CreateHtml}}
+							</div>
+							<div class="required inline field {{if .Err_Captcha}}error{{end}}">
+								<label for="captcha">{{.i18n.Tr "captcha"}}</label>
+								<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
+							</div>
+						{{end}}
+
+						<div class="inline field">
+							<label></label>
+							<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button>
+						</div>
+
+						{{if not .LinkAccountMode}}
+						<div class="inline field">
+							<label></label>
+							<a href="{{AppSubUrl}}/user/login">{{.i18n.Tr "auth.register_helper_msg"}}</a>
+						</div>
+						{{end}}
+					{{end}}
+				</div>
+			</form>
+		</div>
+	</div>
+</div>
\ No newline at end of file
diff --git a/templates/user/settings/account_link.tmpl b/templates/user/settings/account_link.tmpl
new file mode 100644
index 0000000000..fa14cb525b
--- /dev/null
+++ b/templates/user/settings/account_link.tmpl
@@ -0,0 +1,48 @@
+{{template "base/head" .}}
+<div class="user settings account_link">
+	<div class="ui container">
+		<div class="ui grid">
+			{{template "user/settings/navbar" .}}
+			<div class="twelve wide column content">
+				{{template "base/alert" .}}
+				<h4 class="ui top attached header">
+					{{.i18n.Tr "settings.manage_account_links"}}
+				</h4>
+				<div class="ui attached segment">
+					<div class="ui key list">
+						<div class="item">
+							{{.i18n.Tr "settings.manage_account_links_desc"}}
+						</div>
+						{{if .AccountLinks}}
+						{{range $loginSource, $provider := .AccountLinks}}
+							<div class="item ui grid">
+								<div class="column">
+									<strong>{{$provider}}</strong>
+									{{if $loginSource.IsActived}}<span class="text red">{{$.i18n.Tr "settings.active"}}</span>{{end}}
+									<div class="ui right">
+										<button class="ui red tiny button delete-button" data-url="{{$.Link}}" data-id="{{$loginSource.ID}}">
+											{{$.i18n.Tr "settings.delete_key"}}
+										</button>
+									</div>
+								</div>
+							</div>
+						{{end}}
+						{{end}}
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+
+<div class="ui small basic delete modal">
+	<div class="ui icon header">
+		<i class="trash icon"></i>
+		{{.i18n.Tr "settings.remove_account_link"}}
+	</div>
+	<div class="content">
+		<p>{{.i18n.Tr "settings.remove_account_link_desc"}}</p>
+	</div>
+	{{template "base/delete_modal_actions" .}}
+</div>
+{{template "base/footer" .}}
diff --git a/templates/user/settings/navbar.tmpl b/templates/user/settings/navbar.tmpl
index 7449fdbf7e..d8b69cf5d0 100644
--- a/templates/user/settings/navbar.tmpl
+++ b/templates/user/settings/navbar.tmpl
@@ -22,6 +22,9 @@
 		<a class="{{if .PageIsSettingsTwofa}}active{{end}} item" href="{{AppSubUrl}}/user/settings/two_factor">
 			{{.i18n.Tr "settings.twofa"}}
 		</a>
+		<a class="{{if .PageIsSettingsAccountLink}}active{{end}} item" href="{{AppSubUrl}}/user/settings/account_link">
+			{{.i18n.Tr "settings.account_link"}}
+		</a>
 		<a class="{{if .PageIsSettingsDelete}}active{{end}} item" href="{{AppSubUrl}}/user/settings/delete">
 			{{.i18n.Tr "settings.delete"}}
 		</a>
diff --git a/templates/user/settings/password.tmpl b/templates/user/settings/password.tmpl
index dc8b19062f..b59d1ff56f 100644
--- a/templates/user/settings/password.tmpl
+++ b/templates/user/settings/password.tmpl
@@ -9,13 +9,15 @@
 					{{.i18n.Tr "settings.change_password"}}
 				</h4>
 				<div class="ui attached segment">
-					{{if .SignedUser.IsLocal}}
+					{{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}}
 					<form class="ui form" action="{{.Link}}" method="post">
 						{{.CsrfTokenHtml}}
+						{{if .SignedUser.IsPasswordSet}}
 						<div class="required field {{if .Err_OldPassword}}error{{end}}">
 							<label for="old_password">{{.i18n.Tr "settings.old_password"}}</label>
 							<input id="old_password" name="old_password" type="password" autocomplete="off" autofocus required>
 						</div>
+						{{end}}
 						<div class="required field {{if .Err_Password}}error{{end}}">
 							<label for="password">{{.i18n.Tr "settings.new_password"}}</label>
 							<input id="password" name="password" type="password" autocomplete="off" required>
diff --git a/vendor/github.com/gorilla/context/LICENSE b/vendor/github.com/gorilla/context/LICENSE
new file mode 100644
index 0000000000..0e5fb87280
--- /dev/null
+++ b/vendor/github.com/gorilla/context/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+	 * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+	 * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+	 * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/context/README.md b/vendor/github.com/gorilla/context/README.md
new file mode 100644
index 0000000000..08f86693bc
--- /dev/null
+++ b/vendor/github.com/gorilla/context/README.md
@@ -0,0 +1,10 @@
+context
+=======
+[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
+
+gorilla/context is a general purpose registry for global request variables.
+
+> Note: gorilla/context, having been born well before `context.Context` existed, does not play well
+> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`.
+
+Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
diff --git a/vendor/github.com/gorilla/context/context.go b/vendor/github.com/gorilla/context/context.go
new file mode 100644
index 0000000000..81cb128b19
--- /dev/null
+++ b/vendor/github.com/gorilla/context/context.go
@@ -0,0 +1,143 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context
+
+import (
+	"net/http"
+	"sync"
+	"time"
+)
+
+var (
+	mutex sync.RWMutex
+	data  = make(map[*http.Request]map[interface{}]interface{})
+	datat = make(map[*http.Request]int64)
+)
+
+// Set stores a value for a given key in a given request.
+func Set(r *http.Request, key, val interface{}) {
+	mutex.Lock()
+	if data[r] == nil {
+		data[r] = make(map[interface{}]interface{})
+		datat[r] = time.Now().Unix()
+	}
+	data[r][key] = val
+	mutex.Unlock()
+}
+
+// Get returns a value stored for a given key in a given request.
+func Get(r *http.Request, key interface{}) interface{} {
+	mutex.RLock()
+	if ctx := data[r]; ctx != nil {
+		value := ctx[key]
+		mutex.RUnlock()
+		return value
+	}
+	mutex.RUnlock()
+	return nil
+}
+
+// GetOk returns stored value and presence state like multi-value return of map access.
+func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
+	mutex.RLock()
+	if _, ok := data[r]; ok {
+		value, ok := data[r][key]
+		mutex.RUnlock()
+		return value, ok
+	}
+	mutex.RUnlock()
+	return nil, false
+}
+
+// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
+func GetAll(r *http.Request) map[interface{}]interface{} {
+	mutex.RLock()
+	if context, ok := data[r]; ok {
+		result := make(map[interface{}]interface{}, len(context))
+		for k, v := range context {
+			result[k] = v
+		}
+		mutex.RUnlock()
+		return result
+	}
+	mutex.RUnlock()
+	return nil
+}
+
+// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
+// the request was registered.
+func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
+	mutex.RLock()
+	context, ok := data[r]
+	result := make(map[interface{}]interface{}, len(context))
+	for k, v := range context {
+		result[k] = v
+	}
+	mutex.RUnlock()
+	return result, ok
+}
+
+// Delete removes a value stored for a given key in a given request.
+func Delete(r *http.Request, key interface{}) {
+	mutex.Lock()
+	if data[r] != nil {
+		delete(data[r], key)
+	}
+	mutex.Unlock()
+}
+
+// Clear removes all values stored for a given request.
+//
+// This is usually called by a handler wrapper to clean up request
+// variables at the end of a request lifetime. See ClearHandler().
+func Clear(r *http.Request) {
+	mutex.Lock()
+	clear(r)
+	mutex.Unlock()
+}
+
+// clear is Clear without the lock.
+func clear(r *http.Request) {
+	delete(data, r)
+	delete(datat, r)
+}
+
+// Purge removes request data stored for longer than maxAge, in seconds.
+// It returns the amount of requests removed.
+//
+// If maxAge <= 0, all request data is removed.
+//
+// This is only used for sanity check: in case context cleaning was not
+// properly set some request data can be kept forever, consuming an increasing
+// amount of memory. In case this is detected, Purge() must be called
+// periodically until the problem is fixed.
+func Purge(maxAge int) int {
+	mutex.Lock()
+	count := 0
+	if maxAge <= 0 {
+		count = len(data)
+		data = make(map[*http.Request]map[interface{}]interface{})
+		datat = make(map[*http.Request]int64)
+	} else {
+		min := time.Now().Unix() - int64(maxAge)
+		for r := range data {
+			if datat[r] < min {
+				clear(r)
+				count++
+			}
+		}
+	}
+	mutex.Unlock()
+	return count
+}
+
+// ClearHandler wraps an http.Handler and clears request values at the end
+// of a request lifetime.
+func ClearHandler(h http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer Clear(r)
+		h.ServeHTTP(w, r)
+	})
+}
diff --git a/vendor/github.com/gorilla/context/doc.go b/vendor/github.com/gorilla/context/doc.go
new file mode 100644
index 0000000000..448d1bfcac
--- /dev/null
+++ b/vendor/github.com/gorilla/context/doc.go
@@ -0,0 +1,88 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package context stores values shared during a request lifetime.
+
+Note: gorilla/context, having been born well before `context.Context` existed,
+does not play well > with the shallow copying of the request that
+[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext)
+(added to net/http Go 1.7 onwards) performs. You should either use *just*
+gorilla/context, or moving forward, the new `http.Request.Context()`.
+
+For example, a router can set variables extracted from the URL and later
+application handlers can access those values, or it can be used to store
+sessions values to be saved at the end of a request. There are several
+others common uses.
+
+The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
+
+	http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
+
+Here's the basic usage: first define the keys that you will need. The key
+type is interface{} so a key can be of any type that supports equality.
+Here we define a key using a custom int type to avoid name collisions:
+
+	package foo
+
+	import (
+		"github.com/gorilla/context"
+	)
+
+	type key int
+
+	const MyKey key = 0
+
+Then set a variable. Variables are bound to an http.Request object, so you
+need a request instance to set a value:
+
+	context.Set(r, MyKey, "bar")
+
+The application can later access the variable using the same key you provided:
+
+	func MyHandler(w http.ResponseWriter, r *http.Request) {
+		// val is "bar".
+		val := context.Get(r, foo.MyKey)
+
+		// returns ("bar", true)
+		val, ok := context.GetOk(r, foo.MyKey)
+		// ...
+	}
+
+And that's all about the basic usage. We discuss some other ideas below.
+
+Any type can be stored in the context. To enforce a given type, make the key
+private and wrap Get() and Set() to accept and return values of a specific
+type:
+
+	type key int
+
+	const mykey key = 0
+
+	// GetMyKey returns a value for this package from the request values.
+	func GetMyKey(r *http.Request) SomeType {
+		if rv := context.Get(r, mykey); rv != nil {
+			return rv.(SomeType)
+		}
+		return nil
+	}
+
+	// SetMyKey sets a value for this package in the request values.
+	func SetMyKey(r *http.Request, val SomeType) {
+		context.Set(r, mykey, val)
+	}
+
+Variables must be cleared at the end of a request, to remove all values
+that were stored. This can be done in an http.Handler, after a request was
+served. Just call Clear() passing the request:
+
+	context.Clear(r)
+
+...or use ClearHandler(), which conveniently wraps an http.Handler to clear
+variables at the end of a request lifetime.
+
+The Routers from the packages gorilla/mux and gorilla/pat call Clear()
+so if you are using either of them you don't need to clear the context manually.
+*/
+package context
diff --git a/vendor/github.com/gorilla/mux/LICENSE b/vendor/github.com/gorilla/mux/LICENSE
new file mode 100644
index 0000000000..0e5fb87280
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+	 * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+	 * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+	 * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md
new file mode 100644
index 0000000000..fa79a6bc3a
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/README.md
@@ -0,0 +1,299 @@
+gorilla/mux
+===
+[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
+[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
+
+![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
+
+http://www.gorillatoolkit.org/pkg/mux
+
+Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
+their respective handler.
+
+The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are:
+
+* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
+* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
+* URL hosts and paths can have variables with an optional regular expression.
+* Registered URLs can be built, or "reversed", which helps maintaining references to resources.
+* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching.
+
+---
+
+* [Install](#install)
+* [Examples](#examples)
+* [Matching Routes](#matching-routes)
+* [Static Files](#static-files)
+* [Registered URLs](#registered-urls)
+* [Full Example](#full-example)
+
+---
+
+## Install
+
+With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain:
+
+```sh
+go get -u github.com/gorilla/mux
+```
+
+## Examples
+
+Let's start registering a couple of URL paths and handlers:
+
+```go
+func main() {
+	r := mux.NewRouter()
+	r.HandleFunc("/", HomeHandler)
+	r.HandleFunc("/products", ProductsHandler)
+	r.HandleFunc("/articles", ArticlesHandler)
+	http.Handle("/", r)
+}
+```
+
+Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters.
+
+Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example:
+
+```go
+r := mux.NewRouter()
+r.HandleFunc("/products/{key}", ProductHandler)
+r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
+r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
+```
+
+The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
+
+```go
+vars := mux.Vars(request)
+category := vars["category"]
+```
+
+And this is all you need to know about the basic usage. More advanced options are explained below.
+
+### Matching Routes
+
+Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:
+
+```go
+r := mux.NewRouter()
+// Only matches if domain is "www.example.com".
+r.Host("www.example.com")
+// Matches a dynamic subdomain.
+r.Host("{subdomain:[a-z]+}.domain.com")
+```
+
+There are several other matchers that can be added. To match path prefixes:
+
+```go
+r.PathPrefix("/products/")
+```
+
+...or HTTP methods:
+
+```go
+r.Methods("GET", "POST")
+```
+
+...or URL schemes:
+
+```go
+r.Schemes("https")
+```
+
+...or header values:
+
+```go
+r.Headers("X-Requested-With", "XMLHttpRequest")
+```
+
+...or query values:
+
+```go
+r.Queries("key", "value")
+```
+
+...or to use a custom matcher function:
+
+```go
+r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
+	return r.ProtoMajor == 0
+})
+```
+
+...and finally, it is possible to combine several matchers in a single route:
+
+```go
+r.HandleFunc("/products", ProductsHandler).
+  Host("www.example.com").
+  Methods("GET").
+  Schemes("http")
+```
+
+Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting".
+
+For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it:
+
+```go
+r := mux.NewRouter()
+s := r.Host("www.example.com").Subrouter()
+```
+
+Then register routes in the subrouter:
+
+```go
+s.HandleFunc("/products/", ProductsHandler)
+s.HandleFunc("/products/{key}", ProductHandler)
+s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
+```
+
+The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route.
+
+Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter.
+
+There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:
+
+```go
+r := mux.NewRouter()
+s := r.PathPrefix("/products").Subrouter()
+// "/products/"
+s.HandleFunc("/", ProductsHandler)
+// "/products/{key}/"
+s.HandleFunc("/{key}/", ProductHandler)
+// "/products/{key}/details"
+s.HandleFunc("/{key}/details", ProductDetailsHandler)
+```
+
+### Static Files
+
+Note that the path provided to `PathPrefix()` represents a "wildcard": calling
+`PathPrefix("/static/").Handler(...)` means that the handler will be passed any
+request that matches "/static/*". This makes it easy to serve static files with mux:
+
+```go
+func main() {
+	var dir string
+
+	flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
+	flag.Parse()
+	r := mux.NewRouter()
+
+	// This will serve files under http://localhost:8000/static/<filename>
+	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
+
+	srv := &http.Server{
+		Handler:      r,
+		Addr:         "127.0.0.1:8000",
+		// Good practice: enforce timeouts for servers you create!
+		WriteTimeout: 15 * time.Second,
+		ReadTimeout:  15 * time.Second,
+	}
+
+	log.Fatal(srv.ListenAndServe())
+}
+```
+
+### Registered URLs
+
+Now let's see how to build registered URLs.
+
+Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example:
+
+```go
+r := mux.NewRouter()
+r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+  Name("article")
+```
+
+To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do:
+
+```go
+url, err := r.Get("article").URL("category", "technology", "id", "42")
+```
+
+...and the result will be a `url.URL` with the following path:
+
+```
+"/articles/technology/42"
+```
+
+This also works for host variables:
+
+```go
+r := mux.NewRouter()
+r.Host("{subdomain}.domain.com").
+  Path("/articles/{category}/{id:[0-9]+}").
+  HandlerFunc(ArticleHandler).
+  Name("article")
+
+// url.String() will be "http://news.domain.com/articles/technology/42"
+url, err := r.Get("article").URL("subdomain", "news",
+                                 "category", "technology",
+                                 "id", "42")
+```
+
+All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match.
+
+Regex support also exists for matching Headers within a route. For example, we could do:
+
+```go
+r.HeadersRegexp("Content-Type", "application/(text|json)")
+```
+
+...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
+
+There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
+
+```go
+// "http://news.domain.com/"
+host, err := r.Get("article").URLHost("subdomain", "news")
+
+// "/articles/technology/42"
+path, err := r.Get("article").URLPath("category", "technology", "id", "42")
+```
+
+And if you use subrouters, host and path defined separately can be built as well:
+
+```go
+r := mux.NewRouter()
+s := r.Host("{subdomain}.domain.com").Subrouter()
+s.Path("/articles/{category}/{id:[0-9]+}").
+  HandlerFunc(ArticleHandler).
+  Name("article")
+
+// "http://news.domain.com/articles/technology/42"
+url, err := r.Get("article").URL("subdomain", "news",
+                                 "category", "technology",
+                                 "id", "42")
+```
+
+## Full Example
+
+Here's a complete, runnable example of a small `mux` based server:
+
+```go
+package main
+
+import (
+	"net/http"
+	"log"
+	"github.com/gorilla/mux"
+)
+
+func YourHandler(w http.ResponseWriter, r *http.Request) {
+	w.Write([]byte("Gorilla!\n"))
+}
+
+func main() {
+	r := mux.NewRouter()
+	// Routes consist of a path and a handler function.
+	r.HandleFunc("/", YourHandler)
+
+	// Bind to a port and pass our router in
+	log.Fatal(http.ListenAndServe(":8000", r))
+}
+```
+
+## License
+
+BSD licensed. See the LICENSE file for details.
diff --git a/vendor/github.com/gorilla/mux/context_gorilla.go b/vendor/github.com/gorilla/mux/context_gorilla.go
new file mode 100644
index 0000000000..d7adaa8fad
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/context_gorilla.go
@@ -0,0 +1,26 @@
+// +build !go1.7
+
+package mux
+
+import (
+	"net/http"
+
+	"github.com/gorilla/context"
+)
+
+func contextGet(r *http.Request, key interface{}) interface{} {
+	return context.Get(r, key)
+}
+
+func contextSet(r *http.Request, key, val interface{}) *http.Request {
+	if val == nil {
+		return r
+	}
+
+	context.Set(r, key, val)
+	return r
+}
+
+func contextClear(r *http.Request) {
+	context.Clear(r)
+}
diff --git a/vendor/github.com/gorilla/mux/context_native.go b/vendor/github.com/gorilla/mux/context_native.go
new file mode 100644
index 0000000000..209cbea7d6
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/context_native.go
@@ -0,0 +1,24 @@
+// +build go1.7
+
+package mux
+
+import (
+	"context"
+	"net/http"
+)
+
+func contextGet(r *http.Request, key interface{}) interface{} {
+	return r.Context().Value(key)
+}
+
+func contextSet(r *http.Request, key, val interface{}) *http.Request {
+	if val == nil {
+		return r
+	}
+
+	return r.WithContext(context.WithValue(r.Context(), key, val))
+}
+
+func contextClear(r *http.Request) {
+	return
+}
diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go
new file mode 100644
index 0000000000..e9573dd8ad
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/doc.go
@@ -0,0 +1,235 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package mux implements a request router and dispatcher.
+
+The name mux stands for "HTTP request multiplexer". Like the standard
+http.ServeMux, mux.Router matches incoming requests against a list of
+registered routes and calls a handler for the route that matches the URL
+or other conditions. The main features are:
+
+	* Requests can be matched based on URL host, path, path prefix, schemes,
+	  header and query values, HTTP methods or using custom matchers.
+	* URL hosts and paths can have variables with an optional regular
+	  expression.
+	* Registered URLs can be built, or "reversed", which helps maintaining
+	  references to resources.
+	* Routes can be used as subrouters: nested routes are only tested if the
+	  parent route matches. This is useful to define groups of routes that
+	  share common conditions like a host, a path prefix or other repeated
+	  attributes. As a bonus, this optimizes request matching.
+	* It implements the http.Handler interface so it is compatible with the
+	  standard http.ServeMux.
+
+Let's start registering a couple of URL paths and handlers:
+
+	func main() {
+		r := mux.NewRouter()
+		r.HandleFunc("/", HomeHandler)
+		r.HandleFunc("/products", ProductsHandler)
+		r.HandleFunc("/articles", ArticlesHandler)
+		http.Handle("/", r)
+	}
+
+Here we register three routes mapping URL paths to handlers. This is
+equivalent to how http.HandleFunc() works: if an incoming request URL matches
+one of the paths, the corresponding handler is called passing
+(http.ResponseWriter, *http.Request) as parameters.
+
+Paths can have variables. They are defined using the format {name} or
+{name:pattern}. If a regular expression pattern is not defined, the matched
+variable will be anything until the next slash. For example:
+
+	r := mux.NewRouter()
+	r.HandleFunc("/products/{key}", ProductHandler)
+	r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
+	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
+
+Groups can be used inside patterns, as long as they are non-capturing (?:re). For example:
+
+	r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler)
+
+The names are used to create a map of route variables which can be retrieved
+calling mux.Vars():
+
+	vars := mux.Vars(request)
+	category := vars["category"]
+
+And this is all you need to know about the basic usage. More advanced options
+are explained below.
+
+Routes can also be restricted to a domain or subdomain. Just define a host
+pattern to be matched. They can also have variables:
+
+	r := mux.NewRouter()
+	// Only matches if domain is "www.example.com".
+	r.Host("www.example.com")
+	// Matches a dynamic subdomain.
+	r.Host("{subdomain:[a-z]+}.domain.com")
+
+There are several other matchers that can be added. To match path prefixes:
+
+	r.PathPrefix("/products/")
+
+...or HTTP methods:
+
+	r.Methods("GET", "POST")
+
+...or URL schemes:
+
+	r.Schemes("https")
+
+...or header values:
+
+	r.Headers("X-Requested-With", "XMLHttpRequest")
+
+...or query values:
+
+	r.Queries("key", "value")
+
+...or to use a custom matcher function:
+
+	r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
+		return r.ProtoMajor == 0
+	})
+
+...and finally, it is possible to combine several matchers in a single route:
+
+	r.HandleFunc("/products", ProductsHandler).
+	  Host("www.example.com").
+	  Methods("GET").
+	  Schemes("http")
+
+Setting the same matching conditions again and again can be boring, so we have
+a way to group several routes that share the same requirements.
+We call it "subrouting".
+
+For example, let's say we have several URLs that should only match when the
+host is "www.example.com". Create a route for that host and get a "subrouter"
+from it:
+
+	r := mux.NewRouter()
+	s := r.Host("www.example.com").Subrouter()
+
+Then register routes in the subrouter:
+
+	s.HandleFunc("/products/", ProductsHandler)
+	s.HandleFunc("/products/{key}", ProductHandler)
+	s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+
+The three URL paths we registered above will only be tested if the domain is
+"www.example.com", because the subrouter is tested first. This is not
+only convenient, but also optimizes request matching. You can create
+subrouters combining any attribute matchers accepted by a route.
+
+Subrouters can be used to create domain or path "namespaces": you define
+subrouters in a central place and then parts of the app can register its
+paths relatively to a given subrouter.
+
+There's one more thing about subroutes. When a subrouter has a path prefix,
+the inner routes use it as base for their paths:
+
+	r := mux.NewRouter()
+	s := r.PathPrefix("/products").Subrouter()
+	// "/products/"
+	s.HandleFunc("/", ProductsHandler)
+	// "/products/{key}/"
+	s.HandleFunc("/{key}/", ProductHandler)
+	// "/products/{key}/details"
+	s.HandleFunc("/{key}/details", ProductDetailsHandler)
+
+Note that the path provided to PathPrefix() represents a "wildcard": calling
+PathPrefix("/static/").Handler(...) means that the handler will be passed any
+request that matches "/static/*". This makes it easy to serve static files with mux:
+
+	func main() {
+		var dir string
+
+		flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
+		flag.Parse()
+		r := mux.NewRouter()
+
+		// This will serve files under http://localhost:8000/static/<filename>
+		r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
+
+		srv := &http.Server{
+			Handler:      r,
+			Addr:         "127.0.0.1:8000",
+			// Good practice: enforce timeouts for servers you create!
+			WriteTimeout: 15 * time.Second,
+			ReadTimeout:  15 * time.Second,
+		}
+
+		log.Fatal(srv.ListenAndServe())
+	}
+
+Now let's see how to build registered URLs.
+
+Routes can be named. All routes that define a name can have their URLs built,
+or "reversed". We define a name calling Name() on a route. For example:
+
+	r := mux.NewRouter()
+	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+	  Name("article")
+
+To build a URL, get the route and call the URL() method, passing a sequence of
+key/value pairs for the route variables. For the previous route, we would do:
+
+	url, err := r.Get("article").URL("category", "technology", "id", "42")
+
+...and the result will be a url.URL with the following path:
+
+	"/articles/technology/42"
+
+This also works for host variables:
+
+	r := mux.NewRouter()
+	r.Host("{subdomain}.domain.com").
+	  Path("/articles/{category}/{id:[0-9]+}").
+	  HandlerFunc(ArticleHandler).
+	  Name("article")
+
+	// url.String() will be "http://news.domain.com/articles/technology/42"
+	url, err := r.Get("article").URL("subdomain", "news",
+	                                 "category", "technology",
+	                                 "id", "42")
+
+All variables defined in the route are required, and their values must
+conform to the corresponding patterns. These requirements guarantee that a
+generated URL will always match a registered route -- the only exception is
+for explicitly defined "build-only" routes which never match.
+
+Regex support also exists for matching Headers within a route. For example, we could do:
+
+	r.HeadersRegexp("Content-Type", "application/(text|json)")
+
+...and the route will match both requests with a Content-Type of `application/json` as well as
+`application/text`
+
+There's also a way to build only the URL host or path for a route:
+use the methods URLHost() or URLPath() instead. For the previous route,
+we would do:
+
+	// "http://news.domain.com/"
+	host, err := r.Get("article").URLHost("subdomain", "news")
+
+	// "/articles/technology/42"
+	path, err := r.Get("article").URLPath("category", "technology", "id", "42")
+
+And if you use subrouters, host and path defined separately can be built
+as well:
+
+	r := mux.NewRouter()
+	s := r.Host("{subdomain}.domain.com").Subrouter()
+	s.Path("/articles/{category}/{id:[0-9]+}").
+	  HandlerFunc(ArticleHandler).
+	  Name("article")
+
+	// "http://news.domain.com/articles/technology/42"
+	url, err := r.Get("article").URL("subdomain", "news",
+	                                 "category", "technology",
+	                                 "id", "42")
+*/
+package mux
diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go
new file mode 100644
index 0000000000..d66ec38415
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/mux.go
@@ -0,0 +1,542 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"path"
+	"regexp"
+	"strings"
+)
+
+// NewRouter returns a new router instance.
+func NewRouter() *Router {
+	return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
+}
+
+// Router registers routes to be matched and dispatches a handler.
+//
+// It implements the http.Handler interface, so it can be registered to serve
+// requests:
+//
+//     var router = mux.NewRouter()
+//
+//     func main() {
+//         http.Handle("/", router)
+//     }
+//
+// Or, for Google App Engine, register it in a init() function:
+//
+//     func init() {
+//         http.Handle("/", router)
+//     }
+//
+// This will send all incoming requests to the router.
+type Router struct {
+	// Configurable Handler to be used when no route matches.
+	NotFoundHandler http.Handler
+	// Parent route, if this is a subrouter.
+	parent parentRoute
+	// Routes to be matched, in order.
+	routes []*Route
+	// Routes by name for URL building.
+	namedRoutes map[string]*Route
+	// See Router.StrictSlash(). This defines the flag for new routes.
+	strictSlash bool
+	// See Router.SkipClean(). This defines the flag for new routes.
+	skipClean bool
+	// If true, do not clear the request context after handling the request.
+	// This has no effect when go1.7+ is used, since the context is stored
+	// on the request itself.
+	KeepContext bool
+	// see Router.UseEncodedPath(). This defines a flag for all routes.
+	useEncodedPath bool
+}
+
+// Match matches registered routes against the request.
+func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
+	for _, route := range r.routes {
+		if route.Match(req, match) {
+			return true
+		}
+	}
+
+	// Closest match for a router (includes sub-routers)
+	if r.NotFoundHandler != nil {
+		match.Handler = r.NotFoundHandler
+		return true
+	}
+	return false
+}
+
+// ServeHTTP dispatches the handler registered in the matched route.
+//
+// When there is a match, the route variables can be retrieved calling
+// mux.Vars(request).
+func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	if !r.skipClean {
+		path := req.URL.Path
+		if r.useEncodedPath {
+			path = getPath(req)
+		}
+		// Clean path to canonical form and redirect.
+		if p := cleanPath(path); p != path {
+
+			// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
+			// This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:
+			// http://code.google.com/p/go/issues/detail?id=5252
+			url := *req.URL
+			url.Path = p
+			p = url.String()
+
+			w.Header().Set("Location", p)
+			w.WriteHeader(http.StatusMovedPermanently)
+			return
+		}
+	}
+	var match RouteMatch
+	var handler http.Handler
+	if r.Match(req, &match) {
+		handler = match.Handler
+		req = setVars(req, match.Vars)
+		req = setCurrentRoute(req, match.Route)
+	}
+	if handler == nil {
+		handler = http.NotFoundHandler()
+	}
+	if !r.KeepContext {
+		defer contextClear(req)
+	}
+	handler.ServeHTTP(w, req)
+}
+
+// Get returns a route registered with the given name.
+func (r *Router) Get(name string) *Route {
+	return r.getNamedRoutes()[name]
+}
+
+// GetRoute returns a route registered with the given name. This method
+// was renamed to Get() and remains here for backwards compatibility.
+func (r *Router) GetRoute(name string) *Route {
+	return r.getNamedRoutes()[name]
+}
+
+// StrictSlash defines the trailing slash behavior for new routes. The initial
+// value is false.
+//
+// When true, if the route path is "/path/", accessing "/path" will redirect
+// to the former and vice versa. In other words, your application will always
+// see the path as specified in the route.
+//
+// When false, if the route path is "/path", accessing "/path/" will not match
+// this route and vice versa.
+//
+// Special case: when a route sets a path prefix using the PathPrefix() method,
+// strict slash is ignored for that route because the redirect behavior can't
+// be determined from a prefix alone. However, any subrouters created from that
+// route inherit the original StrictSlash setting.
+func (r *Router) StrictSlash(value bool) *Router {
+	r.strictSlash = value
+	return r
+}
+
+// SkipClean defines the path cleaning behaviour for new routes. The initial
+// value is false. Users should be careful about which routes are not cleaned
+//
+// When true, if the route path is "/path//to", it will remain with the double
+// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
+//
+// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
+// become /fetch/http/xkcd.com/534
+func (r *Router) SkipClean(value bool) *Router {
+	r.skipClean = value
+	return r
+}
+
+// UseEncodedPath tells the router to match the encoded original path
+// to the routes.
+// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
+// This behavior has the drawback of needing to match routes against
+// r.RequestURI instead of r.URL.Path. Any modifications (such as http.StripPrefix)
+// to r.URL.Path will not affect routing when this flag is on and thus may
+// induce unintended behavior.
+//
+// If not called, the router will match the unencoded path to the routes.
+// For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to"
+func (r *Router) UseEncodedPath() *Router {
+	r.useEncodedPath = true
+	return r
+}
+
+// ----------------------------------------------------------------------------
+// parentRoute
+// ----------------------------------------------------------------------------
+
+// getNamedRoutes returns the map where named routes are registered.
+func (r *Router) getNamedRoutes() map[string]*Route {
+	if r.namedRoutes == nil {
+		if r.parent != nil {
+			r.namedRoutes = r.parent.getNamedRoutes()
+		} else {
+			r.namedRoutes = make(map[string]*Route)
+		}
+	}
+	return r.namedRoutes
+}
+
+// getRegexpGroup returns regexp definitions from the parent route, if any.
+func (r *Router) getRegexpGroup() *routeRegexpGroup {
+	if r.parent != nil {
+		return r.parent.getRegexpGroup()
+	}
+	return nil
+}
+
+func (r *Router) buildVars(m map[string]string) map[string]string {
+	if r.parent != nil {
+		m = r.parent.buildVars(m)
+	}
+	return m
+}
+
+// ----------------------------------------------------------------------------
+// Route factories
+// ----------------------------------------------------------------------------
+
+// NewRoute registers an empty route.
+func (r *Router) NewRoute() *Route {
+	route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath}
+	r.routes = append(r.routes, route)
+	return route
+}
+
+// Handle registers a new route with a matcher for the URL path.
+// See Route.Path() and Route.Handler().
+func (r *Router) Handle(path string, handler http.Handler) *Route {
+	return r.NewRoute().Path(path).Handler(handler)
+}
+
+// HandleFunc registers a new route with a matcher for the URL path.
+// See Route.Path() and Route.HandlerFunc().
+func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
+	*http.Request)) *Route {
+	return r.NewRoute().Path(path).HandlerFunc(f)
+}
+
+// Headers registers a new route with a matcher for request header values.
+// See Route.Headers().
+func (r *Router) Headers(pairs ...string) *Route {
+	return r.NewRoute().Headers(pairs...)
+}
+
+// Host registers a new route with a matcher for the URL host.
+// See Route.Host().
+func (r *Router) Host(tpl string) *Route {
+	return r.NewRoute().Host(tpl)
+}
+
+// MatcherFunc registers a new route with a custom matcher function.
+// See Route.MatcherFunc().
+func (r *Router) MatcherFunc(f MatcherFunc) *Route {
+	return r.NewRoute().MatcherFunc(f)
+}
+
+// Methods registers a new route with a matcher for HTTP methods.
+// See Route.Methods().
+func (r *Router) Methods(methods ...string) *Route {
+	return r.NewRoute().Methods(methods...)
+}
+
+// Path registers a new route with a matcher for the URL path.
+// See Route.Path().
+func (r *Router) Path(tpl string) *Route {
+	return r.NewRoute().Path(tpl)
+}
+
+// PathPrefix registers a new route with a matcher for the URL path prefix.
+// See Route.PathPrefix().
+func (r *Router) PathPrefix(tpl string) *Route {
+	return r.NewRoute().PathPrefix(tpl)
+}
+
+// Queries registers a new route with a matcher for URL query values.
+// See Route.Queries().
+func (r *Router) Queries(pairs ...string) *Route {
+	return r.NewRoute().Queries(pairs...)
+}
+
+// Schemes registers a new route with a matcher for URL schemes.
+// See Route.Schemes().
+func (r *Router) Schemes(schemes ...string) *Route {
+	return r.NewRoute().Schemes(schemes...)
+}
+
+// BuildVarsFunc registers a new route with a custom function for modifying
+// route variables before building a URL.
+func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
+	return r.NewRoute().BuildVarsFunc(f)
+}
+
+// Walk walks the router and all its sub-routers, calling walkFn for each route
+// in the tree. The routes are walked in the order they were added. Sub-routers
+// are explored depth-first.
+func (r *Router) Walk(walkFn WalkFunc) error {
+	return r.walk(walkFn, []*Route{})
+}
+
+// SkipRouter is used as a return value from WalkFuncs to indicate that the
+// router that walk is about to descend down to should be skipped.
+var SkipRouter = errors.New("skip this router")
+
+// WalkFunc is the type of the function called for each route visited by Walk.
+// At every invocation, it is given the current route, and the current router,
+// and a list of ancestor routes that lead to the current route.
+type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
+
+func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
+	for _, t := range r.routes {
+		if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" {
+			continue
+		}
+
+		err := walkFn(t, r, ancestors)
+		if err == SkipRouter {
+			continue
+		}
+		if err != nil {
+			return err
+		}
+		for _, sr := range t.matchers {
+			if h, ok := sr.(*Router); ok {
+				err := h.walk(walkFn, ancestors)
+				if err != nil {
+					return err
+				}
+			}
+		}
+		if h, ok := t.handler.(*Router); ok {
+			ancestors = append(ancestors, t)
+			err := h.walk(walkFn, ancestors)
+			if err != nil {
+				return err
+			}
+			ancestors = ancestors[:len(ancestors)-1]
+		}
+	}
+	return nil
+}
+
+// ----------------------------------------------------------------------------
+// Context
+// ----------------------------------------------------------------------------
+
+// RouteMatch stores information about a matched route.
+type RouteMatch struct {
+	Route   *Route
+	Handler http.Handler
+	Vars    map[string]string
+}
+
+type contextKey int
+
+const (
+	varsKey contextKey = iota
+	routeKey
+)
+
+// Vars returns the route variables for the current request, if any.
+func Vars(r *http.Request) map[string]string {
+	if rv := contextGet(r, varsKey); rv != nil {
+		return rv.(map[string]string)
+	}
+	return nil
+}
+
+// CurrentRoute returns the matched route for the current request, if any.
+// This only works when called inside the handler of the matched route
+// because the matched route is stored in the request context which is cleared
+// after the handler returns, unless the KeepContext option is set on the
+// Router.
+func CurrentRoute(r *http.Request) *Route {
+	if rv := contextGet(r, routeKey); rv != nil {
+		return rv.(*Route)
+	}
+	return nil
+}
+
+func setVars(r *http.Request, val interface{}) *http.Request {
+	return contextSet(r, varsKey, val)
+}
+
+func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
+	return contextSet(r, routeKey, val)
+}
+
+// ----------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------
+
+// getPath returns the escaped path if possible; doing what URL.EscapedPath()
+// which was added in go1.5 does
+func getPath(req *http.Request) string {
+	if req.RequestURI != "" {
+		// Extract the path from RequestURI (which is escaped unlike URL.Path)
+		// as detailed here as detailed in https://golang.org/pkg/net/url/#URL
+		// for < 1.5 server side workaround
+		// http://localhost/path/here?v=1 -> /path/here
+		path := req.RequestURI
+		path = strings.TrimPrefix(path, req.URL.Scheme+`://`)
+		path = strings.TrimPrefix(path, req.URL.Host)
+		if i := strings.LastIndex(path, "?"); i > -1 {
+			path = path[:i]
+		}
+		if i := strings.LastIndex(path, "#"); i > -1 {
+			path = path[:i]
+		}
+		return path
+	}
+	return req.URL.Path
+}
+
+// cleanPath returns the canonical path for p, eliminating . and .. elements.
+// Borrowed from the net/http package.
+func cleanPath(p string) string {
+	if p == "" {
+		return "/"
+	}
+	if p[0] != '/' {
+		p = "/" + p
+	}
+	np := path.Clean(p)
+	// path.Clean removes trailing slash except for root;
+	// put the trailing slash back if necessary.
+	if p[len(p)-1] == '/' && np != "/" {
+		np += "/"
+	}
+
+	return np
+}
+
+// uniqueVars returns an error if two slices contain duplicated strings.
+func uniqueVars(s1, s2 []string) error {
+	for _, v1 := range s1 {
+		for _, v2 := range s2 {
+			if v1 == v2 {
+				return fmt.Errorf("mux: duplicated route variable %q", v2)
+			}
+		}
+	}
+	return nil
+}
+
+// checkPairs returns the count of strings passed in, and an error if
+// the count is not an even number.
+func checkPairs(pairs ...string) (int, error) {
+	length := len(pairs)
+	if length%2 != 0 {
+		return length, fmt.Errorf(
+			"mux: number of parameters must be multiple of 2, got %v", pairs)
+	}
+	return length, nil
+}
+
+// mapFromPairsToString converts variadic string parameters to a
+// string to string map.
+func mapFromPairsToString(pairs ...string) (map[string]string, error) {
+	length, err := checkPairs(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	m := make(map[string]string, length/2)
+	for i := 0; i < length; i += 2 {
+		m[pairs[i]] = pairs[i+1]
+	}
+	return m, nil
+}
+
+// mapFromPairsToRegex converts variadic string paramers to a
+// string to regex map.
+func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
+	length, err := checkPairs(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	m := make(map[string]*regexp.Regexp, length/2)
+	for i := 0; i < length; i += 2 {
+		regex, err := regexp.Compile(pairs[i+1])
+		if err != nil {
+			return nil, err
+		}
+		m[pairs[i]] = regex
+	}
+	return m, nil
+}
+
+// matchInArray returns true if the given string value is in the array.
+func matchInArray(arr []string, value string) bool {
+	for _, v := range arr {
+		if v == value {
+			return true
+		}
+	}
+	return false
+}
+
+// matchMapWithString returns true if the given key/value pairs exist in a given map.
+func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
+	for k, v := range toCheck {
+		// Check if key exists.
+		if canonicalKey {
+			k = http.CanonicalHeaderKey(k)
+		}
+		if values := toMatch[k]; values == nil {
+			return false
+		} else if v != "" {
+			// If value was defined as an empty string we only check that the
+			// key exists. Otherwise we also check for equality.
+			valueExists := false
+			for _, value := range values {
+				if v == value {
+					valueExists = true
+					break
+				}
+			}
+			if !valueExists {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
+// the given regex
+func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
+	for k, v := range toCheck {
+		// Check if key exists.
+		if canonicalKey {
+			k = http.CanonicalHeaderKey(k)
+		}
+		if values := toMatch[k]; values == nil {
+			return false
+		} else if v != nil {
+			// If value was defined as an empty string we only check that the
+			// key exists. Otherwise we also check for equality.
+			valueExists := false
+			for _, value := range values {
+				if v.MatchString(value) {
+					valueExists = true
+					break
+				}
+			}
+			if !valueExists {
+				return false
+			}
+		}
+	}
+	return true
+}
diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go
new file mode 100644
index 0000000000..fd8fe39560
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/regexp.go
@@ -0,0 +1,316 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"bytes"
+	"fmt"
+	"net/http"
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+// newRouteRegexp parses a route template and returns a routeRegexp,
+// used to match a host, a path or a query string.
+//
+// It will extract named variables, assemble a regexp to be matched, create
+// a "reverse" template to build URLs and compile regexps to validate variable
+// values used in URL building.
+//
+// Previously we accepted only Python-like identifiers for variable
+// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
+// name and pattern can't be empty, and names can't contain a colon.
+func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash, useEncodedPath bool) (*routeRegexp, error) {
+	// Check if it is well-formed.
+	idxs, errBraces := braceIndices(tpl)
+	if errBraces != nil {
+		return nil, errBraces
+	}
+	// Backup the original.
+	template := tpl
+	// Now let's parse it.
+	defaultPattern := "[^/]+"
+	if matchQuery {
+		defaultPattern = "[^?&]*"
+	} else if matchHost {
+		defaultPattern = "[^.]+"
+		matchPrefix = false
+	}
+	// Only match strict slash if not matching
+	if matchPrefix || matchHost || matchQuery {
+		strictSlash = false
+	}
+	// Set a flag for strictSlash.
+	endSlash := false
+	if strictSlash && strings.HasSuffix(tpl, "/") {
+		tpl = tpl[:len(tpl)-1]
+		endSlash = true
+	}
+	varsN := make([]string, len(idxs)/2)
+	varsR := make([]*regexp.Regexp, len(idxs)/2)
+	pattern := bytes.NewBufferString("")
+	pattern.WriteByte('^')
+	reverse := bytes.NewBufferString("")
+	var end int
+	var err error
+	for i := 0; i < len(idxs); i += 2 {
+		// Set all values we are interested in.
+		raw := tpl[end:idxs[i]]
+		end = idxs[i+1]
+		parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
+		name := parts[0]
+		patt := defaultPattern
+		if len(parts) == 2 {
+			patt = parts[1]
+		}
+		// Name or pattern can't be empty.
+		if name == "" || patt == "" {
+			return nil, fmt.Errorf("mux: missing name or pattern in %q",
+				tpl[idxs[i]:end])
+		}
+		// Build the regexp pattern.
+		fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt)
+
+		// Build the reverse template.
+		fmt.Fprintf(reverse, "%s%%s", raw)
+
+		// Append variable name and compiled pattern.
+		varsN[i/2] = name
+		varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
+		if err != nil {
+			return nil, err
+		}
+	}
+	// Add the remaining.
+	raw := tpl[end:]
+	pattern.WriteString(regexp.QuoteMeta(raw))
+	if strictSlash {
+		pattern.WriteString("[/]?")
+	}
+	if matchQuery {
+		// Add the default pattern if the query value is empty
+		if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
+			pattern.WriteString(defaultPattern)
+		}
+	}
+	if !matchPrefix {
+		pattern.WriteByte('$')
+	}
+	reverse.WriteString(raw)
+	if endSlash {
+		reverse.WriteByte('/')
+	}
+	// Compile full regexp.
+	reg, errCompile := regexp.Compile(pattern.String())
+	if errCompile != nil {
+		return nil, errCompile
+	}
+	// Done!
+	return &routeRegexp{
+		template:       template,
+		matchHost:      matchHost,
+		matchQuery:     matchQuery,
+		strictSlash:    strictSlash,
+		useEncodedPath: useEncodedPath,
+		regexp:         reg,
+		reverse:        reverse.String(),
+		varsN:          varsN,
+		varsR:          varsR,
+	}, nil
+}
+
+// routeRegexp stores a regexp to match a host or path and information to
+// collect and validate route variables.
+type routeRegexp struct {
+	// The unmodified template.
+	template string
+	// True for host match, false for path or query string match.
+	matchHost bool
+	// True for query string match, false for path and host match.
+	matchQuery bool
+	// The strictSlash value defined on the route, but disabled if PathPrefix was used.
+	strictSlash bool
+	// Determines whether to use encoded path from getPath function or unencoded
+	// req.URL.Path for path matching
+	useEncodedPath bool
+	// Expanded regexp.
+	regexp *regexp.Regexp
+	// Reverse template.
+	reverse string
+	// Variable names.
+	varsN []string
+	// Variable regexps (validators).
+	varsR []*regexp.Regexp
+}
+
+// Match matches the regexp against the URL host or path.
+func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
+	if !r.matchHost {
+		if r.matchQuery {
+			return r.matchQueryString(req)
+		}
+		path := req.URL.Path
+		if r.useEncodedPath {
+			path = getPath(req)
+		}
+		return r.regexp.MatchString(path)
+	}
+
+	return r.regexp.MatchString(getHost(req))
+}
+
+// url builds a URL part using the given values.
+func (r *routeRegexp) url(values map[string]string) (string, error) {
+	urlValues := make([]interface{}, len(r.varsN))
+	for k, v := range r.varsN {
+		value, ok := values[v]
+		if !ok {
+			return "", fmt.Errorf("mux: missing route variable %q", v)
+		}
+		urlValues[k] = value
+	}
+	rv := fmt.Sprintf(r.reverse, urlValues...)
+	if !r.regexp.MatchString(rv) {
+		// The URL is checked against the full regexp, instead of checking
+		// individual variables. This is faster but to provide a good error
+		// message, we check individual regexps if the URL doesn't match.
+		for k, v := range r.varsN {
+			if !r.varsR[k].MatchString(values[v]) {
+				return "", fmt.Errorf(
+					"mux: variable %q doesn't match, expected %q", values[v],
+					r.varsR[k].String())
+			}
+		}
+	}
+	return rv, nil
+}
+
+// getURLQuery returns a single query parameter from a request URL.
+// For a URL with foo=bar&baz=ding, we return only the relevant key
+// value pair for the routeRegexp.
+func (r *routeRegexp) getURLQuery(req *http.Request) string {
+	if !r.matchQuery {
+		return ""
+	}
+	templateKey := strings.SplitN(r.template, "=", 2)[0]
+	for key, vals := range req.URL.Query() {
+		if key == templateKey && len(vals) > 0 {
+			return key + "=" + vals[0]
+		}
+	}
+	return ""
+}
+
+func (r *routeRegexp) matchQueryString(req *http.Request) bool {
+	return r.regexp.MatchString(r.getURLQuery(req))
+}
+
+// braceIndices returns the first level curly brace indices from a string.
+// It returns an error in case of unbalanced braces.
+func braceIndices(s string) ([]int, error) {
+	var level, idx int
+	var idxs []int
+	for i := 0; i < len(s); i++ {
+		switch s[i] {
+		case '{':
+			if level++; level == 1 {
+				idx = i
+			}
+		case '}':
+			if level--; level == 0 {
+				idxs = append(idxs, idx, i+1)
+			} else if level < 0 {
+				return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
+			}
+		}
+	}
+	if level != 0 {
+		return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
+	}
+	return idxs, nil
+}
+
+// varGroupName builds a capturing group name for the indexed variable.
+func varGroupName(idx int) string {
+	return "v" + strconv.Itoa(idx)
+}
+
+// ----------------------------------------------------------------------------
+// routeRegexpGroup
+// ----------------------------------------------------------------------------
+
+// routeRegexpGroup groups the route matchers that carry variables.
+type routeRegexpGroup struct {
+	host    *routeRegexp
+	path    *routeRegexp
+	queries []*routeRegexp
+}
+
+// setMatch extracts the variables from the URL once a route matches.
+func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
+	// Store host variables.
+	if v.host != nil {
+		host := getHost(req)
+		matches := v.host.regexp.FindStringSubmatchIndex(host)
+		if len(matches) > 0 {
+			extractVars(host, matches, v.host.varsN, m.Vars)
+		}
+	}
+	path := req.URL.Path
+	if r.useEncodedPath {
+		path = getPath(req)
+	}
+	// Store path variables.
+	if v.path != nil {
+		matches := v.path.regexp.FindStringSubmatchIndex(path)
+		if len(matches) > 0 {
+			extractVars(path, matches, v.path.varsN, m.Vars)
+			// Check if we should redirect.
+			if v.path.strictSlash {
+				p1 := strings.HasSuffix(path, "/")
+				p2 := strings.HasSuffix(v.path.template, "/")
+				if p1 != p2 {
+					u, _ := url.Parse(req.URL.String())
+					if p1 {
+						u.Path = u.Path[:len(u.Path)-1]
+					} else {
+						u.Path += "/"
+					}
+					m.Handler = http.RedirectHandler(u.String(), 301)
+				}
+			}
+		}
+	}
+	// Store query string variables.
+	for _, q := range v.queries {
+		queryURL := q.getURLQuery(req)
+		matches := q.regexp.FindStringSubmatchIndex(queryURL)
+		if len(matches) > 0 {
+			extractVars(queryURL, matches, q.varsN, m.Vars)
+		}
+	}
+}
+
+// getHost tries its best to return the request host.
+func getHost(r *http.Request) string {
+	if r.URL.IsAbs() {
+		return r.URL.Host
+	}
+	host := r.Host
+	// Slice off any port information.
+	if i := strings.Index(host, ":"); i != -1 {
+		host = host[:i]
+	}
+	return host
+
+}
+
+func extractVars(input string, matches []int, names []string, output map[string]string) {
+	for i, name := range names {
+		output[name] = input[matches[2*i+2]:matches[2*i+3]]
+	}
+}
diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go
new file mode 100644
index 0000000000..293b6d4938
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/route.go
@@ -0,0 +1,636 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mux
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"net/url"
+	"regexp"
+	"strings"
+)
+
+// Route stores information to match a request and build URLs.
+type Route struct {
+	// Parent where the route was registered (a Router).
+	parent parentRoute
+	// Request handler for the route.
+	handler http.Handler
+	// List of matchers.
+	matchers []matcher
+	// Manager for the variables from host and path.
+	regexp *routeRegexpGroup
+	// If true, when the path pattern is "/path/", accessing "/path" will
+	// redirect to the former and vice versa.
+	strictSlash bool
+	// If true, when the path pattern is "/path//to", accessing "/path//to"
+	// will not redirect
+	skipClean bool
+	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
+	useEncodedPath bool
+	// If true, this route never matches: it is only used to build URLs.
+	buildOnly bool
+	// The name used to build URLs.
+	name string
+	// Error resulted from building a route.
+	err error
+
+	buildVarsFunc BuildVarsFunc
+}
+
+func (r *Route) SkipClean() bool {
+	return r.skipClean
+}
+
+// Match matches the route against the request.
+func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
+	if r.buildOnly || r.err != nil {
+		return false
+	}
+	// Match everything.
+	for _, m := range r.matchers {
+		if matched := m.Match(req, match); !matched {
+			return false
+		}
+	}
+	// Yay, we have a match. Let's collect some info about it.
+	if match.Route == nil {
+		match.Route = r
+	}
+	if match.Handler == nil {
+		match.Handler = r.handler
+	}
+	if match.Vars == nil {
+		match.Vars = make(map[string]string)
+	}
+	// Set variables.
+	if r.regexp != nil {
+		r.regexp.setMatch(req, match, r)
+	}
+	return true
+}
+
+// ----------------------------------------------------------------------------
+// Route attributes
+// ----------------------------------------------------------------------------
+
+// GetError returns an error resulted from building the route, if any.
+func (r *Route) GetError() error {
+	return r.err
+}
+
+// BuildOnly sets the route to never match: it is only used to build URLs.
+func (r *Route) BuildOnly() *Route {
+	r.buildOnly = true
+	return r
+}
+
+// Handler --------------------------------------------------------------------
+
+// Handler sets a handler for the route.
+func (r *Route) Handler(handler http.Handler) *Route {
+	if r.err == nil {
+		r.handler = handler
+	}
+	return r
+}
+
+// HandlerFunc sets a handler function for the route.
+func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
+	return r.Handler(http.HandlerFunc(f))
+}
+
+// GetHandler returns the handler for the route, if any.
+func (r *Route) GetHandler() http.Handler {
+	return r.handler
+}
+
+// Name -----------------------------------------------------------------------
+
+// Name sets the name for the route, used to build URLs.
+// If the name was registered already it will be overwritten.
+func (r *Route) Name(name string) *Route {
+	if r.name != "" {
+		r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
+			r.name, name)
+	}
+	if r.err == nil {
+		r.name = name
+		r.getNamedRoutes()[name] = r
+	}
+	return r
+}
+
+// GetName returns the name for the route, if any.
+func (r *Route) GetName() string {
+	return r.name
+}
+
+// ----------------------------------------------------------------------------
+// Matchers
+// ----------------------------------------------------------------------------
+
+// matcher types try to match a request.
+type matcher interface {
+	Match(*http.Request, *RouteMatch) bool
+}
+
+// addMatcher adds a matcher to the route.
+func (r *Route) addMatcher(m matcher) *Route {
+	if r.err == nil {
+		r.matchers = append(r.matchers, m)
+	}
+	return r
+}
+
+// addRegexpMatcher adds a host or path matcher and builder to a route.
+func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
+	if r.err != nil {
+		return r.err
+	}
+	r.regexp = r.getRegexpGroup()
+	if !matchHost && !matchQuery {
+		if len(tpl) == 0 || tpl[0] != '/' {
+			return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
+		}
+		if r.regexp.path != nil {
+			tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
+		}
+	}
+	rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash, r.useEncodedPath)
+	if err != nil {
+		return err
+	}
+	for _, q := range r.regexp.queries {
+		if err = uniqueVars(rr.varsN, q.varsN); err != nil {
+			return err
+		}
+	}
+	if matchHost {
+		if r.regexp.path != nil {
+			if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
+				return err
+			}
+		}
+		r.regexp.host = rr
+	} else {
+		if r.regexp.host != nil {
+			if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
+				return err
+			}
+		}
+		if matchQuery {
+			r.regexp.queries = append(r.regexp.queries, rr)
+		} else {
+			r.regexp.path = rr
+		}
+	}
+	r.addMatcher(rr)
+	return nil
+}
+
+// Headers --------------------------------------------------------------------
+
+// headerMatcher matches the request against header values.
+type headerMatcher map[string]string
+
+func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
+	return matchMapWithString(m, r.Header, true)
+}
+
+// Headers adds a matcher for request header values.
+// It accepts a sequence of key/value pairs to be matched. For example:
+//
+//     r := mux.NewRouter()
+//     r.Headers("Content-Type", "application/json",
+//               "X-Requested-With", "XMLHttpRequest")
+//
+// The above route will only match if both request header values match.
+// If the value is an empty string, it will match any value if the key is set.
+func (r *Route) Headers(pairs ...string) *Route {
+	if r.err == nil {
+		var headers map[string]string
+		headers, r.err = mapFromPairsToString(pairs...)
+		return r.addMatcher(headerMatcher(headers))
+	}
+	return r
+}
+
+// headerRegexMatcher matches the request against the route given a regex for the header
+type headerRegexMatcher map[string]*regexp.Regexp
+
+func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
+	return matchMapWithRegex(m, r.Header, true)
+}
+
+// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
+// support. For example:
+//
+//     r := mux.NewRouter()
+//     r.HeadersRegexp("Content-Type", "application/(text|json)",
+//               "X-Requested-With", "XMLHttpRequest")
+//
+// The above route will only match if both the request header matches both regular expressions.
+// It the value is an empty string, it will match any value if the key is set.
+func (r *Route) HeadersRegexp(pairs ...string) *Route {
+	if r.err == nil {
+		var headers map[string]*regexp.Regexp
+		headers, r.err = mapFromPairsToRegex(pairs...)
+		return r.addMatcher(headerRegexMatcher(headers))
+	}
+	return r
+}
+
+// Host -----------------------------------------------------------------------
+
+// Host adds a matcher for the URL host.
+// It accepts a template with zero or more URL variables enclosed by {}.
+// Variables can define an optional regexp pattern to be matched:
+//
+// - {name} matches anything until the next dot.
+//
+// - {name:pattern} matches the given regexp pattern.
+//
+// For example:
+//
+//     r := mux.NewRouter()
+//     r.Host("www.example.com")
+//     r.Host("{subdomain}.domain.com")
+//     r.Host("{subdomain:[a-z]+}.domain.com")
+//
+// Variable names must be unique in a given route. They can be retrieved
+// calling mux.Vars(request).
+func (r *Route) Host(tpl string) *Route {
+	r.err = r.addRegexpMatcher(tpl, true, false, false)
+	return r
+}
+
+// MatcherFunc ----------------------------------------------------------------
+
+// MatcherFunc is the function signature used by custom matchers.
+type MatcherFunc func(*http.Request, *RouteMatch) bool
+
+// Match returns the match for a given request.
+func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
+	return m(r, match)
+}
+
+// MatcherFunc adds a custom function to be used as request matcher.
+func (r *Route) MatcherFunc(f MatcherFunc) *Route {
+	return r.addMatcher(f)
+}
+
+// Methods --------------------------------------------------------------------
+
+// methodMatcher matches the request against HTTP methods.
+type methodMatcher []string
+
+func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
+	return matchInArray(m, r.Method)
+}
+
+// Methods adds a matcher for HTTP methods.
+// It accepts a sequence of one or more methods to be matched, e.g.:
+// "GET", "POST", "PUT".
+func (r *Route) Methods(methods ...string) *Route {
+	for k, v := range methods {
+		methods[k] = strings.ToUpper(v)
+	}
+	return r.addMatcher(methodMatcher(methods))
+}
+
+// Path -----------------------------------------------------------------------
+
+// Path adds a matcher for the URL path.
+// It accepts a template with zero or more URL variables enclosed by {}. The
+// template must start with a "/".
+// Variables can define an optional regexp pattern to be matched:
+//
+// - {name} matches anything until the next slash.
+//
+// - {name:pattern} matches the given regexp pattern.
+//
+// For example:
+//
+//     r := mux.NewRouter()
+//     r.Path("/products/").Handler(ProductsHandler)
+//     r.Path("/products/{key}").Handler(ProductsHandler)
+//     r.Path("/articles/{category}/{id:[0-9]+}").
+//       Handler(ArticleHandler)
+//
+// Variable names must be unique in a given route. They can be retrieved
+// calling mux.Vars(request).
+func (r *Route) Path(tpl string) *Route {
+	r.err = r.addRegexpMatcher(tpl, false, false, false)
+	return r
+}
+
+// PathPrefix -----------------------------------------------------------------
+
+// PathPrefix adds a matcher for the URL path prefix. This matches if the given
+// template is a prefix of the full URL path. See Route.Path() for details on
+// the tpl argument.
+//
+// Note that it does not treat slashes specially ("/foobar/" will be matched by
+// the prefix "/foo") so you may want to use a trailing slash here.
+//
+// Also note that the setting of Router.StrictSlash() has no effect on routes
+// with a PathPrefix matcher.
+func (r *Route) PathPrefix(tpl string) *Route {
+	r.err = r.addRegexpMatcher(tpl, false, true, false)
+	return r
+}
+
+// Query ----------------------------------------------------------------------
+
+// Queries adds a matcher for URL query values.
+// It accepts a sequence of key/value pairs. Values may define variables.
+// For example:
+//
+//     r := mux.NewRouter()
+//     r.Queries("foo", "bar", "id", "{id:[0-9]+}")
+//
+// The above route will only match if the URL contains the defined queries
+// values, e.g.: ?foo=bar&id=42.
+//
+// It the value is an empty string, it will match any value if the key is set.
+//
+// Variables can define an optional regexp pattern to be matched:
+//
+// - {name} matches anything until the next slash.
+//
+// - {name:pattern} matches the given regexp pattern.
+func (r *Route) Queries(pairs ...string) *Route {
+	length := len(pairs)
+	if length%2 != 0 {
+		r.err = fmt.Errorf(
+			"mux: number of parameters must be multiple of 2, got %v", pairs)
+		return nil
+	}
+	for i := 0; i < length; i += 2 {
+		if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
+			return r
+		}
+	}
+
+	return r
+}
+
+// Schemes --------------------------------------------------------------------
+
+// schemeMatcher matches the request against URL schemes.
+type schemeMatcher []string
+
+func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
+	return matchInArray(m, r.URL.Scheme)
+}
+
+// Schemes adds a matcher for URL schemes.
+// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
+func (r *Route) Schemes(schemes ...string) *Route {
+	for k, v := range schemes {
+		schemes[k] = strings.ToLower(v)
+	}
+	return r.addMatcher(schemeMatcher(schemes))
+}
+
+// BuildVarsFunc --------------------------------------------------------------
+
+// BuildVarsFunc is the function signature used by custom build variable
+// functions (which can modify route variables before a route's URL is built).
+type BuildVarsFunc func(map[string]string) map[string]string
+
+// BuildVarsFunc adds a custom function to be used to modify build variables
+// before a route's URL is built.
+func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
+	r.buildVarsFunc = f
+	return r
+}
+
+// Subrouter ------------------------------------------------------------------
+
+// Subrouter creates a subrouter for the route.
+//
+// It will test the inner routes only if the parent route matched. For example:
+//
+//     r := mux.NewRouter()
+//     s := r.Host("www.example.com").Subrouter()
+//     s.HandleFunc("/products/", ProductsHandler)
+//     s.HandleFunc("/products/{key}", ProductHandler)
+//     s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+//
+// Here, the routes registered in the subrouter won't be tested if the host
+// doesn't match.
+func (r *Route) Subrouter() *Router {
+	router := &Router{parent: r, strictSlash: r.strictSlash}
+	r.addMatcher(router)
+	return router
+}
+
+// ----------------------------------------------------------------------------
+// URL building
+// ----------------------------------------------------------------------------
+
+// URL builds a URL for the route.
+//
+// It accepts a sequence of key/value pairs for the route variables. For
+// example, given this route:
+//
+//     r := mux.NewRouter()
+//     r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+//       Name("article")
+//
+// ...a URL for it can be built using:
+//
+//     url, err := r.Get("article").URL("category", "technology", "id", "42")
+//
+// ...which will return an url.URL with the following path:
+//
+//     "/articles/technology/42"
+//
+// This also works for host variables:
+//
+//     r := mux.NewRouter()
+//     r.Host("{subdomain}.domain.com").
+//       HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+//       Name("article")
+//
+//     // url.String() will be "http://news.domain.com/articles/technology/42"
+//     url, err := r.Get("article").URL("subdomain", "news",
+//                                      "category", "technology",
+//                                      "id", "42")
+//
+// All variables defined in the route are required, and their values must
+// conform to the corresponding patterns.
+func (r *Route) URL(pairs ...string) (*url.URL, error) {
+	if r.err != nil {
+		return nil, r.err
+	}
+	if r.regexp == nil {
+		return nil, errors.New("mux: route doesn't have a host or path")
+	}
+	values, err := r.prepareVars(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	var scheme, host, path string
+	if r.regexp.host != nil {
+		// Set a default scheme.
+		scheme = "http"
+		if host, err = r.regexp.host.url(values); err != nil {
+			return nil, err
+		}
+	}
+	if r.regexp.path != nil {
+		if path, err = r.regexp.path.url(values); err != nil {
+			return nil, err
+		}
+	}
+	return &url.URL{
+		Scheme: scheme,
+		Host:   host,
+		Path:   path,
+	}, nil
+}
+
+// URLHost builds the host part of the URL for a route. See Route.URL().
+//
+// The route must have a host defined.
+func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
+	if r.err != nil {
+		return nil, r.err
+	}
+	if r.regexp == nil || r.regexp.host == nil {
+		return nil, errors.New("mux: route doesn't have a host")
+	}
+	values, err := r.prepareVars(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	host, err := r.regexp.host.url(values)
+	if err != nil {
+		return nil, err
+	}
+	return &url.URL{
+		Scheme: "http",
+		Host:   host,
+	}, nil
+}
+
+// URLPath builds the path part of the URL for a route. See Route.URL().
+//
+// The route must have a path defined.
+func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
+	if r.err != nil {
+		return nil, r.err
+	}
+	if r.regexp == nil || r.regexp.path == nil {
+		return nil, errors.New("mux: route doesn't have a path")
+	}
+	values, err := r.prepareVars(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	path, err := r.regexp.path.url(values)
+	if err != nil {
+		return nil, err
+	}
+	return &url.URL{
+		Path: path,
+	}, nil
+}
+
+// GetPathTemplate returns the template used to build the
+// route match.
+// This is useful for building simple REST API documentation and for instrumentation
+// against third-party services.
+// An error will be returned if the route does not define a path.
+func (r *Route) GetPathTemplate() (string, error) {
+	if r.err != nil {
+		return "", r.err
+	}
+	if r.regexp == nil || r.regexp.path == nil {
+		return "", errors.New("mux: route doesn't have a path")
+	}
+	return r.regexp.path.template, nil
+}
+
+// GetHostTemplate returns the template used to build the
+// route match.
+// This is useful for building simple REST API documentation and for instrumentation
+// against third-party services.
+// An error will be returned if the route does not define a host.
+func (r *Route) GetHostTemplate() (string, error) {
+	if r.err != nil {
+		return "", r.err
+	}
+	if r.regexp == nil || r.regexp.host == nil {
+		return "", errors.New("mux: route doesn't have a host")
+	}
+	return r.regexp.host.template, nil
+}
+
+// prepareVars converts the route variable pairs into a map. If the route has a
+// BuildVarsFunc, it is invoked.
+func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
+	m, err := mapFromPairsToString(pairs...)
+	if err != nil {
+		return nil, err
+	}
+	return r.buildVars(m), nil
+}
+
+func (r *Route) buildVars(m map[string]string) map[string]string {
+	if r.parent != nil {
+		m = r.parent.buildVars(m)
+	}
+	if r.buildVarsFunc != nil {
+		m = r.buildVarsFunc(m)
+	}
+	return m
+}
+
+// ----------------------------------------------------------------------------
+// parentRoute
+// ----------------------------------------------------------------------------
+
+// parentRoute allows routes to know about parent host and path definitions.
+type parentRoute interface {
+	getNamedRoutes() map[string]*Route
+	getRegexpGroup() *routeRegexpGroup
+	buildVars(map[string]string) map[string]string
+}
+
+// getNamedRoutes returns the map where named routes are registered.
+func (r *Route) getNamedRoutes() map[string]*Route {
+	if r.parent == nil {
+		// During tests router is not always set.
+		r.parent = NewRouter()
+	}
+	return r.parent.getNamedRoutes()
+}
+
+// getRegexpGroup returns regexp definitions from this route.
+func (r *Route) getRegexpGroup() *routeRegexpGroup {
+	if r.regexp == nil {
+		if r.parent == nil {
+			// During tests router is not always set.
+			r.parent = NewRouter()
+		}
+		regexp := r.parent.getRegexpGroup()
+		if regexp == nil {
+			r.regexp = new(routeRegexpGroup)
+		} else {
+			// Copy.
+			r.regexp = &routeRegexpGroup{
+				host:    regexp.host,
+				path:    regexp.path,
+				queries: regexp.queries,
+			}
+		}
+	}
+	return r.regexp
+}
diff --git a/vendor/github.com/gorilla/securecookie/LICENSE b/vendor/github.com/gorilla/securecookie/LICENSE
new file mode 100644
index 0000000000..0e5fb87280
--- /dev/null
+++ b/vendor/github.com/gorilla/securecookie/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+	 * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+	 * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+	 * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/securecookie/README.md b/vendor/github.com/gorilla/securecookie/README.md
new file mode 100644
index 0000000000..da112e4d08
--- /dev/null
+++ b/vendor/github.com/gorilla/securecookie/README.md
@@ -0,0 +1,78 @@
+securecookie
+============
+[![GoDoc](https://godoc.org/github.com/gorilla/securecookie?status.svg)](https://godoc.org/github.com/gorilla/securecookie) [![Build Status](https://travis-ci.org/gorilla/securecookie.png?branch=master)](https://travis-ci.org/gorilla/securecookie)
+
+securecookie encodes and decodes authenticated and optionally encrypted 
+cookie values.
+
+Secure cookies can't be forged, because their values are validated using HMAC.
+When encrypted, the content is also inaccessible to malicious eyes. It is still
+recommended that sensitive data not be stored in cookies, and that HTTPS be used
+to prevent cookie [replay attacks](https://en.wikipedia.org/wiki/Replay_attack).
+
+## Examples
+
+To use it, first create a new SecureCookie instance:
+
+```go
+// Hash keys should be at least 32 bytes long
+var hashKey = []byte("very-secret")
+// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
+// Shorter keys may weaken the encryption used.
+var blockKey = []byte("a-lot-secret")
+var s = securecookie.New(hashKey, blockKey)
+```
+
+The hashKey is required, used to authenticate the cookie value using HMAC.
+It is recommended to use a key with 32 or 64 bytes.
+
+The blockKey is optional, used to encrypt the cookie value -- set it to nil
+to not use encryption. If set, the length must correspond to the block size
+of the encryption algorithm. For AES, used by default, valid lengths are
+16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
+
+Strong keys can be created using the convenience function GenerateRandomKey().
+
+Once a SecureCookie instance is set, use it to encode a cookie value:
+
+```go
+func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
+	value := map[string]string{
+		"foo": "bar",
+	}
+	if encoded, err := s.Encode("cookie-name", value); err == nil {
+		cookie := &http.Cookie{
+			Name:  "cookie-name",
+			Value: encoded,
+			Path:  "/",
+			Secure: true,
+			HttpOnly: true,
+		}
+		http.SetCookie(w, cookie)
+	}
+}
+```
+
+Later, use the same SecureCookie instance to decode and validate a cookie
+value:
+
+```go
+func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
+	if cookie, err := r.Cookie("cookie-name"); err == nil {
+		value := make(map[string]string)
+		if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
+			fmt.Fprintf(w, "The value of foo is %q", value["foo"])
+		}
+	}
+}
+```
+
+We stored a map[string]string, but secure cookies can hold any value that
+can be encoded using `encoding/gob`. To store custom types, they must be
+registered first using gob.Register(). For basic types this is not needed;
+it works out of the box. An optional JSON encoder that uses `encoding/json` is
+available for types compatible with JSON.
+
+## License
+
+BSD licensed. See the LICENSE file for details.
diff --git a/vendor/github.com/gorilla/securecookie/doc.go b/vendor/github.com/gorilla/securecookie/doc.go
new file mode 100644
index 0000000000..ae89408d9d
--- /dev/null
+++ b/vendor/github.com/gorilla/securecookie/doc.go
@@ -0,0 +1,61 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package securecookie encodes and decodes authenticated and optionally
+encrypted cookie values.
+
+Secure cookies can't be forged, because their values are validated using HMAC.
+When encrypted, the content is also inaccessible to malicious eyes.
+
+To use it, first create a new SecureCookie instance:
+
+	var hashKey = []byte("very-secret")
+	var blockKey = []byte("a-lot-secret")
+	var s = securecookie.New(hashKey, blockKey)
+
+The hashKey is required, used to authenticate the cookie value using HMAC.
+It is recommended to use a key with 32 or 64 bytes.
+
+The blockKey is optional, used to encrypt the cookie value -- set it to nil
+to not use encryption. If set, the length must correspond to the block size
+of the encryption algorithm. For AES, used by default, valid lengths are
+16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
+
+Strong keys can be created using the convenience function GenerateRandomKey().
+
+Once a SecureCookie instance is set, use it to encode a cookie value:
+
+	func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
+		value := map[string]string{
+			"foo": "bar",
+		}
+		if encoded, err := s.Encode("cookie-name", value); err == nil {
+			cookie := &http.Cookie{
+				Name:  "cookie-name",
+				Value: encoded,
+				Path:  "/",
+			}
+			http.SetCookie(w, cookie)
+		}
+	}
+
+Later, use the same SecureCookie instance to decode and validate a cookie
+value:
+
+	func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
+		if cookie, err := r.Cookie("cookie-name"); err == nil {
+			value := make(map[string]string)
+			if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
+				fmt.Fprintf(w, "The value of foo is %q", value["foo"])
+			}
+		}
+	}
+
+We stored a map[string]string, but secure cookies can hold any value that
+can be encoded using encoding/gob. To store custom types, they must be
+registered first using gob.Register(). For basic types this is not needed;
+it works out of the box.
+*/
+package securecookie
diff --git a/vendor/github.com/gorilla/securecookie/fuzz.go b/vendor/github.com/gorilla/securecookie/fuzz.go
new file mode 100644
index 0000000000..e4d0534e41
--- /dev/null
+++ b/vendor/github.com/gorilla/securecookie/fuzz.go
@@ -0,0 +1,25 @@
+// +build gofuzz
+
+package securecookie
+
+var hashKey = []byte("very-secret12345")
+var blockKey = []byte("a-lot-secret1234")
+var s = New(hashKey, blockKey)
+
+type Cookie struct {
+	B bool
+	I int
+	S string
+}
+
+func Fuzz(data []byte) int {
+	datas := string(data)
+	var c Cookie
+	if err := s.Decode("fuzz", datas, &c); err != nil {
+		return 0
+	}
+	if _, err := s.Encode("fuzz", c); err != nil {
+		panic(err)
+	}
+	return 1
+}
diff --git a/vendor/github.com/gorilla/securecookie/securecookie.go b/vendor/github.com/gorilla/securecookie/securecookie.go
new file mode 100644
index 0000000000..cd4e0976d6
--- /dev/null
+++ b/vendor/github.com/gorilla/securecookie/securecookie.go
@@ -0,0 +1,646 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package securecookie
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/hmac"
+	"crypto/rand"
+	"crypto/sha256"
+	"crypto/subtle"
+	"encoding/base64"
+	"encoding/gob"
+	"encoding/json"
+	"fmt"
+	"hash"
+	"io"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// Error is the interface of all errors returned by functions in this library.
+type Error interface {
+	error
+
+	// IsUsage returns true for errors indicating the client code probably
+	// uses this library incorrectly.  For example, the client may have
+	// failed to provide a valid hash key, or may have failed to configure
+	// the Serializer adequately for encoding value.
+	IsUsage() bool
+
+	// IsDecode returns true for errors indicating that a cookie could not
+	// be decoded and validated.  Since cookies are usually untrusted
+	// user-provided input, errors of this type should be expected.
+	// Usually, the proper action is simply to reject the request.
+	IsDecode() bool
+
+	// IsInternal returns true for unexpected errors occurring in the
+	// securecookie implementation.
+	IsInternal() bool
+
+	// Cause, if it returns a non-nil value, indicates that this error was
+	// propagated from some underlying library.  If this method returns nil,
+	// this error was raised directly by this library.
+	//
+	// Cause is provided principally for debugging/logging purposes; it is
+	// rare that application logic should perform meaningfully different
+	// logic based on Cause.  See, for example, the caveats described on
+	// (MultiError).Cause().
+	Cause() error
+}
+
+// errorType is a bitmask giving the error type(s) of an cookieError value.
+type errorType int
+
+const (
+	usageError = errorType(1 << iota)
+	decodeError
+	internalError
+)
+
+type cookieError struct {
+	typ   errorType
+	msg   string
+	cause error
+}
+
+func (e cookieError) IsUsage() bool    { return (e.typ & usageError) != 0 }
+func (e cookieError) IsDecode() bool   { return (e.typ & decodeError) != 0 }
+func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 }
+
+func (e cookieError) Cause() error { return e.cause }
+
+func (e cookieError) Error() string {
+	parts := []string{"securecookie: "}
+	if e.msg == "" {
+		parts = append(parts, "error")
+	} else {
+		parts = append(parts, e.msg)
+	}
+	if c := e.Cause(); c != nil {
+		parts = append(parts, " - caused by: ", c.Error())
+	}
+	return strings.Join(parts, "")
+}
+
+var (
+	errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"}
+
+	errNoCodecs            = cookieError{typ: usageError, msg: "no codecs provided"}
+	errHashKeyNotSet       = cookieError{typ: usageError, msg: "hash key is not set"}
+	errBlockKeyNotSet      = cookieError{typ: usageError, msg: "block key is not set"}
+	errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"}
+
+	errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"}
+	errTimestampInvalid     = cookieError{typ: decodeError, msg: "invalid timestamp"}
+	errTimestampTooNew      = cookieError{typ: decodeError, msg: "timestamp is too new"}
+	errTimestampExpired     = cookieError{typ: decodeError, msg: "expired timestamp"}
+	errDecryptionFailed     = cookieError{typ: decodeError, msg: "the value could not be decrypted"}
+	errValueNotByte         = cookieError{typ: decodeError, msg: "value not a []byte."}
+	errValueNotBytePtr      = cookieError{typ: decodeError, msg: "value not a pointer to []byte."}
+
+	// ErrMacInvalid indicates that cookie decoding failed because the HMAC
+	// could not be extracted and verified.  Direct use of this error
+	// variable is deprecated; it is public only for legacy compatibility,
+	// and may be privatized in the future, as it is rarely useful to
+	// distinguish between this error and other Error implementations.
+	ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"}
+)
+
+// Codec defines an interface to encode and decode cookie values.
+type Codec interface {
+	Encode(name string, value interface{}) (string, error)
+	Decode(name, value string, dst interface{}) error
+}
+
+// New returns a new SecureCookie.
+//
+// hashKey is required, used to authenticate values using HMAC. Create it using
+// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
+//
+// blockKey is optional, used to encrypt values. Create it using
+// GenerateRandomKey(). The key length must correspond to the block size
+// of the encryption algorithm. For AES, used by default, valid lengths are
+// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
+// The default encoder used for cookie serialization is encoding/gob.
+//
+// Note that keys created using GenerateRandomKey() are not automatically
+// persisted. New keys will be created when the application is restarted, and
+// previously issued cookies will not be able to be decoded.
+func New(hashKey, blockKey []byte) *SecureCookie {
+	s := &SecureCookie{
+		hashKey:   hashKey,
+		blockKey:  blockKey,
+		hashFunc:  sha256.New,
+		maxAge:    86400 * 30,
+		maxLength: 4096,
+		sz:        GobEncoder{},
+	}
+	if hashKey == nil {
+		s.err = errHashKeyNotSet
+	}
+	if blockKey != nil {
+		s.BlockFunc(aes.NewCipher)
+	}
+	return s
+}
+
+// SecureCookie encodes and decodes authenticated and optionally encrypted
+// cookie values.
+type SecureCookie struct {
+	hashKey   []byte
+	hashFunc  func() hash.Hash
+	blockKey  []byte
+	block     cipher.Block
+	maxLength int
+	maxAge    int64
+	minAge    int64
+	err       error
+	sz        Serializer
+	// For testing purposes, the function that returns the current timestamp.
+	// If not set, it will use time.Now().UTC().Unix().
+	timeFunc func() int64
+}
+
+// Serializer provides an interface for providing custom serializers for cookie
+// values.
+type Serializer interface {
+	Serialize(src interface{}) ([]byte, error)
+	Deserialize(src []byte, dst interface{}) error
+}
+
+// GobEncoder encodes cookie values using encoding/gob. This is the simplest
+// encoder and can handle complex types via gob.Register.
+type GobEncoder struct{}
+
+// JSONEncoder encodes cookie values using encoding/json. Users who wish to
+// encode complex types need to satisfy the json.Marshaller and
+// json.Unmarshaller interfaces.
+type JSONEncoder struct{}
+
+// NopEncoder does not encode cookie values, and instead simply accepts a []byte
+// (as an interface{}) and returns a []byte. This is particularly useful when
+// you encoding an object upstream and do not wish to re-encode it.
+type NopEncoder struct{}
+
+// MaxLength restricts the maximum length, in bytes, for the cookie value.
+//
+// Default is 4096, which is the maximum value accepted by Internet Explorer.
+func (s *SecureCookie) MaxLength(value int) *SecureCookie {
+	s.maxLength = value
+	return s
+}
+
+// MaxAge restricts the maximum age, in seconds, for the cookie value.
+//
+// Default is 86400 * 30. Set it to 0 for no restriction.
+func (s *SecureCookie) MaxAge(value int) *SecureCookie {
+	s.maxAge = int64(value)
+	return s
+}
+
+// MinAge restricts the minimum age, in seconds, for the cookie value.
+//
+// Default is 0 (no restriction).
+func (s *SecureCookie) MinAge(value int) *SecureCookie {
+	s.minAge = int64(value)
+	return s
+}
+
+// HashFunc sets the hash function used to create HMAC.
+//
+// Default is crypto/sha256.New.
+func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
+	s.hashFunc = f
+	return s
+}
+
+// BlockFunc sets the encryption function used to create a cipher.Block.
+//
+// Default is crypto/aes.New.
+func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
+	if s.blockKey == nil {
+		s.err = errBlockKeyNotSet
+	} else if block, err := f(s.blockKey); err == nil {
+		s.block = block
+	} else {
+		s.err = cookieError{cause: err, typ: usageError}
+	}
+	return s
+}
+
+// Encoding sets the encoding/serialization method for cookies.
+//
+// Default is encoding/gob.  To encode special structures using encoding/gob,
+// they must be registered first using gob.Register().
+func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie {
+	s.sz = sz
+
+	return s
+}
+
+// Encode encodes a cookie value.
+//
+// It serializes, optionally encrypts, signs with a message authentication code,
+// and finally encodes the value.
+//
+// The name argument is the cookie name. It is stored with the encoded value.
+// The value argument is the value to be encoded. It can be any value that can
+// be encoded using the currently selected serializer; see SetSerializer().
+//
+// It is the client's responsibility to ensure that value, when encoded using
+// the current serialization/encryption settings on s and then base64-encoded,
+// is shorter than the maximum permissible length.
+func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
+	if s.err != nil {
+		return "", s.err
+	}
+	if s.hashKey == nil {
+		s.err = errHashKeyNotSet
+		return "", s.err
+	}
+	var err error
+	var b []byte
+	// 1. Serialize.
+	if b, err = s.sz.Serialize(value); err != nil {
+		return "", cookieError{cause: err, typ: usageError}
+	}
+	// 2. Encrypt (optional).
+	if s.block != nil {
+		if b, err = encrypt(s.block, b); err != nil {
+			return "", cookieError{cause: err, typ: usageError}
+		}
+	}
+	b = encode(b)
+	// 3. Create MAC for "name|date|value". Extra pipe to be used later.
+	b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
+	mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
+	// Append mac, remove name.
+	b = append(b, mac...)[len(name)+1:]
+	// 4. Encode to base64.
+	b = encode(b)
+	// 5. Check length.
+	if s.maxLength != 0 && len(b) > s.maxLength {
+		return "", errEncodedValueTooLong
+	}
+	// Done.
+	return string(b), nil
+}
+
+// Decode decodes a cookie value.
+//
+// It decodes, verifies a message authentication code, optionally decrypts and
+// finally deserializes the value.
+//
+// The name argument is the cookie name. It must be the same name used when
+// it was stored. The value argument is the encoded cookie value. The dst
+// argument is where the cookie will be decoded. It must be a pointer.
+func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
+	if s.err != nil {
+		return s.err
+	}
+	if s.hashKey == nil {
+		s.err = errHashKeyNotSet
+		return s.err
+	}
+	// 1. Check length.
+	if s.maxLength != 0 && len(value) > s.maxLength {
+		return errValueToDecodeTooLong
+	}
+	// 2. Decode from base64.
+	b, err := decode([]byte(value))
+	if err != nil {
+		return err
+	}
+	// 3. Verify MAC. Value is "date|value|mac".
+	parts := bytes.SplitN(b, []byte("|"), 3)
+	if len(parts) != 3 {
+		return ErrMacInvalid
+	}
+	h := hmac.New(s.hashFunc, s.hashKey)
+	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...)
+	if err = verifyMac(h, b, parts[2]); err != nil {
+		return err
+	}
+	// 4. Verify date ranges.
+	var t1 int64
+	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
+		return errTimestampInvalid
+	}
+	t2 := s.timestamp()
+	if s.minAge != 0 && t1 > t2-s.minAge {
+		return errTimestampTooNew
+	}
+	if s.maxAge != 0 && t1 < t2-s.maxAge {
+		return errTimestampExpired
+	}
+	// 5. Decrypt (optional).
+	b, err = decode(parts[1])
+	if err != nil {
+		return err
+	}
+	if s.block != nil {
+		if b, err = decrypt(s.block, b); err != nil {
+			return err
+		}
+	}
+	// 6. Deserialize.
+	if err = s.sz.Deserialize(b, dst); err != nil {
+		return cookieError{cause: err, typ: decodeError}
+	}
+	// Done.
+	return nil
+}
+
+// timestamp returns the current timestamp, in seconds.
+//
+// For testing purposes, the function that generates the timestamp can be
+// overridden. If not set, it will return time.Now().UTC().Unix().
+func (s *SecureCookie) timestamp() int64 {
+	if s.timeFunc == nil {
+		return time.Now().UTC().Unix()
+	}
+	return s.timeFunc()
+}
+
+// Authentication -------------------------------------------------------------
+
+// createMac creates a message authentication code (MAC).
+func createMac(h hash.Hash, value []byte) []byte {
+	h.Write(value)
+	return h.Sum(nil)
+}
+
+// verifyMac verifies that a message authentication code (MAC) is valid.
+func verifyMac(h hash.Hash, value []byte, mac []byte) error {
+	mac2 := createMac(h, value)
+	// Check that both MACs are of equal length, as subtle.ConstantTimeCompare
+	// does not do this prior to Go 1.4.
+	if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
+		return nil
+	}
+	return ErrMacInvalid
+}
+
+// Encryption -----------------------------------------------------------------
+
+// encrypt encrypts a value using the given block in counter mode.
+//
+// A random initialization vector (http://goo.gl/zF67k) with the length of the
+// block size is prepended to the resulting ciphertext.
+func encrypt(block cipher.Block, value []byte) ([]byte, error) {
+	iv := GenerateRandomKey(block.BlockSize())
+	if iv == nil {
+		return nil, errGeneratingIV
+	}
+	// Encrypt it.
+	stream := cipher.NewCTR(block, iv)
+	stream.XORKeyStream(value, value)
+	// Return iv + ciphertext.
+	return append(iv, value...), nil
+}
+
+// decrypt decrypts a value using the given block in counter mode.
+//
+// The value to be decrypted must be prepended by a initialization vector
+// (http://goo.gl/zF67k) with the length of the block size.
+func decrypt(block cipher.Block, value []byte) ([]byte, error) {
+	size := block.BlockSize()
+	if len(value) > size {
+		// Extract iv.
+		iv := value[:size]
+		// Extract ciphertext.
+		value = value[size:]
+		// Decrypt it.
+		stream := cipher.NewCTR(block, iv)
+		stream.XORKeyStream(value, value)
+		return value, nil
+	}
+	return nil, errDecryptionFailed
+}
+
+// Serialization --------------------------------------------------------------
+
+// Serialize encodes a value using gob.
+func (e GobEncoder) Serialize(src interface{}) ([]byte, error) {
+	buf := new(bytes.Buffer)
+	enc := gob.NewEncoder(buf)
+	if err := enc.Encode(src); err != nil {
+		return nil, cookieError{cause: err, typ: usageError}
+	}
+	return buf.Bytes(), nil
+}
+
+// Deserialize decodes a value using gob.
+func (e GobEncoder) Deserialize(src []byte, dst interface{}) error {
+	dec := gob.NewDecoder(bytes.NewBuffer(src))
+	if err := dec.Decode(dst); err != nil {
+		return cookieError{cause: err, typ: decodeError}
+	}
+	return nil
+}
+
+// Serialize encodes a value using encoding/json.
+func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) {
+	buf := new(bytes.Buffer)
+	enc := json.NewEncoder(buf)
+	if err := enc.Encode(src); err != nil {
+		return nil, cookieError{cause: err, typ: usageError}
+	}
+	return buf.Bytes(), nil
+}
+
+// Deserialize decodes a value using encoding/json.
+func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error {
+	dec := json.NewDecoder(bytes.NewReader(src))
+	if err := dec.Decode(dst); err != nil {
+		return cookieError{cause: err, typ: decodeError}
+	}
+	return nil
+}
+
+// Serialize passes a []byte through as-is.
+func (e NopEncoder) Serialize(src interface{}) ([]byte, error) {
+	if b, ok := src.([]byte); ok {
+		return b, nil
+	}
+
+	return nil, errValueNotByte
+}
+
+// Deserialize passes a []byte through as-is.
+func (e NopEncoder) Deserialize(src []byte, dst interface{}) error {
+	if dat, ok := dst.(*[]byte); ok {
+		*dat = src
+		return nil
+	}
+	return errValueNotBytePtr
+}
+
+// Encoding -------------------------------------------------------------------
+
+// encode encodes a value using base64.
+func encode(value []byte) []byte {
+	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
+	base64.URLEncoding.Encode(encoded, value)
+	return encoded
+}
+
+// decode decodes a cookie using base64.
+func decode(value []byte) ([]byte, error) {
+	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
+	b, err := base64.URLEncoding.Decode(decoded, value)
+	if err != nil {
+		return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"}
+	}
+	return decoded[:b], nil
+}
+
+// Helpers --------------------------------------------------------------------
+
+// GenerateRandomKey creates a random key with the given length in bytes.
+// On failure, returns nil.
+//
+// Callers should explicitly check for the possibility of a nil return, treat
+// it as a failure of the system random number generator, and not continue.
+func GenerateRandomKey(length int) []byte {
+	k := make([]byte, length)
+	if _, err := io.ReadFull(rand.Reader, k); err != nil {
+		return nil
+	}
+	return k
+}
+
+// CodecsFromPairs returns a slice of SecureCookie instances.
+//
+// It is a convenience function to create a list of codecs for key rotation. Note
+// that the generated Codecs will have the default options applied: callers
+// should iterate over each Codec and type-assert the underlying *SecureCookie to
+// change these.
+//
+// Example:
+//
+//      codecs := securecookie.CodecsFromPairs(
+//           []byte("new-hash-key"),
+//           []byte("new-block-key"),
+//           []byte("old-hash-key"),
+//           []byte("old-block-key"),
+//       )
+//
+//      // Modify each instance.
+//      for _, s := range codecs {
+//             if cookie, ok := s.(*securecookie.SecureCookie); ok {
+//                 cookie.MaxAge(86400 * 7)
+//                 cookie.SetSerializer(securecookie.JSONEncoder{})
+//                 cookie.HashFunc(sha512.New512_256)
+//             }
+//         }
+//
+func CodecsFromPairs(keyPairs ...[]byte) []Codec {
+	codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2)
+	for i := 0; i < len(keyPairs); i += 2 {
+		var blockKey []byte
+		if i+1 < len(keyPairs) {
+			blockKey = keyPairs[i+1]
+		}
+		codecs[i/2] = New(keyPairs[i], blockKey)
+	}
+	return codecs
+}
+
+// EncodeMulti encodes a cookie value using a group of codecs.
+//
+// The codecs are tried in order. Multiple codecs are accepted to allow
+// key rotation.
+//
+// On error, may return a MultiError.
+func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) {
+	if len(codecs) == 0 {
+		return "", errNoCodecs
+	}
+
+	var errors MultiError
+	for _, codec := range codecs {
+		encoded, err := codec.Encode(name, value)
+		if err == nil {
+			return encoded, nil
+		}
+		errors = append(errors, err)
+	}
+	return "", errors
+}
+
+// DecodeMulti decodes a cookie value using a group of codecs.
+//
+// The codecs are tried in order. Multiple codecs are accepted to allow
+// key rotation.
+//
+// On error, may return a MultiError.
+func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error {
+	if len(codecs) == 0 {
+		return errNoCodecs
+	}
+
+	var errors MultiError
+	for _, codec := range codecs {
+		err := codec.Decode(name, value, dst)
+		if err == nil {
+			return nil
+		}
+		errors = append(errors, err)
+	}
+	return errors
+}
+
+// MultiError groups multiple errors.
+type MultiError []error
+
+func (m MultiError) IsUsage() bool    { return m.any(func(e Error) bool { return e.IsUsage() }) }
+func (m MultiError) IsDecode() bool   { return m.any(func(e Error) bool { return e.IsDecode() }) }
+func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) }
+
+// Cause returns nil for MultiError; there is no unique underlying cause in the
+// general case.
+//
+// Note: we could conceivably return a non-nil Cause only when there is exactly
+// one child error with a Cause.  However, it would be brittle for client code
+// to rely on the arity of causes inside a MultiError, so we have opted not to
+// provide this functionality.  Clients which really wish to access the Causes
+// of the underlying errors are free to iterate through the errors themselves.
+func (m MultiError) Cause() error { return nil }
+
+func (m MultiError) Error() string {
+	s, n := "", 0
+	for _, e := range m {
+		if e != nil {
+			if n == 0 {
+				s = e.Error()
+			}
+			n++
+		}
+	}
+	switch n {
+	case 0:
+		return "(0 errors)"
+	case 1:
+		return s
+	case 2:
+		return s + " (and 1 other error)"
+	}
+	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
+}
+
+// any returns true if any element of m is an Error for which pred returns true.
+func (m MultiError) any(pred func(Error) bool) bool {
+	for _, e := range m {
+		if ourErr, ok := e.(Error); ok && pred(ourErr) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/vendor/github.com/gorilla/sessions/LICENSE b/vendor/github.com/gorilla/sessions/LICENSE
new file mode 100644
index 0000000000..0e5fb87280
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+	 * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+	 * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+	 * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/sessions/README.md b/vendor/github.com/gorilla/sessions/README.md
new file mode 100644
index 0000000000..5bb3107041
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/README.md
@@ -0,0 +1,81 @@
+sessions
+========
+[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.png?branch=master)](https://travis-ci.org/gorilla/sessions)
+
+gorilla/sessions provides cookie and filesystem sessions and infrastructure for
+custom session backends.
+
+The key features are:
+
+* Simple API: use it as an easy way to set signed (and optionally
+  encrypted) cookies.
+* Built-in backends to store sessions in cookies or the filesystem.
+* Flash messages: session values that last until read.
+* Convenient way to switch session persistency (aka "remember me") and set
+  other attributes.
+* Mechanism to rotate authentication and encryption keys.
+* Multiple sessions per request, even using different backends.
+* Interfaces and infrastructure for custom session backends: sessions from
+  different stores can be retrieved and batch-saved using a common API.
+
+Let's start with an example that shows the sessions API in a nutshell:
+
+```go
+	import (
+		"net/http"
+		"github.com/gorilla/sessions"
+	)
+
+	var store = sessions.NewCookieStore([]byte("something-very-secret"))
+
+	func MyHandler(w http.ResponseWriter, r *http.Request) {
+		// Get a session. We're ignoring the error resulted from decoding an
+		// existing session: Get() always returns a session, even if empty.
+		session, _ := store.Get(r, "session-name")
+		// Set some session values.
+		session.Values["foo"] = "bar"
+		session.Values[42] = 43
+		// Save it before we write to the response/return from the handler.
+		session.Save(r, w)
+	}
+```
+
+First we initialize a session store calling `NewCookieStore()` and passing a
+secret key used to authenticate the session. Inside the handler, we call
+`store.Get()` to retrieve an existing session or a new one. Then we set some
+session values in session.Values, which is a `map[interface{}]interface{}`.
+And finally we call `session.Save()` to save the session in the response.
+
+Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
+with
+[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
+as or else you will leak memory! An easy way to do this is to wrap the top-level
+mux when calling http.ListenAndServe:
+
+More examples are available [on the Gorilla
+website](http://www.gorillatoolkit.org/pkg/sessions).
+
+## Store Implementations
+
+Other implementations of the `sessions.Store` interface:
+
+* [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB
+* [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt
+* [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase
+* [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS
+* [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache
+* [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine
+* [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB
+* [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL
+* [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster
+* [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL
+* [github.com/boj/redistore](https://github.com/boj/redistore) - Redis
+* [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB
+* [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak
+* [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite
+* [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite)
+* [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql
+
+## License
+
+BSD licensed. See the LICENSE file for details.
diff --git a/vendor/github.com/gorilla/sessions/doc.go b/vendor/github.com/gorilla/sessions/doc.go
new file mode 100644
index 0000000000..668e05e455
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/doc.go
@@ -0,0 +1,199 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package sessions provides cookie and filesystem sessions and
+infrastructure for custom session backends.
+
+The key features are:
+
+	* Simple API: use it as an easy way to set signed (and optionally
+	  encrypted) cookies.
+	* Built-in backends to store sessions in cookies or the filesystem.
+	* Flash messages: session values that last until read.
+	* Convenient way to switch session persistency (aka "remember me") and set
+	  other attributes.
+	* Mechanism to rotate authentication and encryption keys.
+	* Multiple sessions per request, even using different backends.
+	* Interfaces and infrastructure for custom session backends: sessions from
+	  different stores can be retrieved and batch-saved using a common API.
+
+Let's start with an example that shows the sessions API in a nutshell:
+
+	import (
+		"net/http"
+		"github.com/gorilla/sessions"
+	)
+
+	var store = sessions.NewCookieStore([]byte("something-very-secret"))
+
+	func MyHandler(w http.ResponseWriter, r *http.Request) {
+		// Get a session. We're ignoring the error resulted from decoding an
+		// existing session: Get() always returns a session, even if empty.
+		session, err := store.Get(r, "session-name")
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		// Set some session values.
+		session.Values["foo"] = "bar"
+		session.Values[42] = 43
+		// Save it before we write to the response/return from the handler.
+		session.Save(r, w)
+	}
+
+First we initialize a session store calling NewCookieStore() and passing a
+secret key used to authenticate the session. Inside the handler, we call
+store.Get() to retrieve an existing session or a new one. Then we set some
+session values in session.Values, which is a map[interface{}]interface{}.
+And finally we call session.Save() to save the session in the response.
+
+Note that in production code, we should check for errors when calling
+session.Save(r, w), and either display an error message or otherwise handle it.
+
+Save must be called before writing to the response, otherwise the session
+cookie will not be sent to the client.
+
+Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
+with context.ClearHandler as or else you will leak memory! An easy way to do this
+is to wrap the top-level mux when calling http.ListenAndServe:
+
+    http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
+
+The ClearHandler function is provided by the gorilla/context package.
+
+That's all you need to know for the basic usage. Let's take a look at other
+options, starting with flash messages.
+
+Flash messages are session values that last until read. The term appeared with
+Ruby On Rails a few years back. When we request a flash message, it is removed
+from the session. To add a flash, call session.AddFlash(), and to get all
+flashes, call session.Flashes(). Here is an example:
+
+	func MyHandler(w http.ResponseWriter, r *http.Request) {
+		// Get a session.
+		session, err := store.Get(r, "session-name")
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		// Get the previously flashes, if any.
+		if flashes := session.Flashes(); len(flashes) > 0 {
+			// Use the flash values.
+		} else {
+			// Set a new flash.
+			session.AddFlash("Hello, flash messages world!")
+		}
+		session.Save(r, w)
+	}
+
+Flash messages are useful to set information to be read after a redirection,
+like after form submissions.
+
+There may also be cases where you want to store a complex datatype within a
+session, such as a struct. Sessions are serialised using the encoding/gob package,
+so it is easy to register new datatypes for storage in sessions:
+
+	import(
+		"encoding/gob"
+		"github.com/gorilla/sessions"
+	)
+
+	type Person struct {
+		FirstName	string
+		LastName 	string
+		Email		string
+		Age			int
+	}
+
+	type M map[string]interface{}
+
+	func init() {
+
+		gob.Register(&Person{})
+		gob.Register(&M{})
+	}
+
+As it's not possible to pass a raw type as a parameter to a function, gob.Register()
+relies on us passing it a value of the desired type. In the example above we've passed
+it a pointer to a struct and a pointer to a custom type representing a
+map[string]interface. (We could have passed non-pointer values if we wished.) This will
+then allow us to serialise/deserialise values of those types to and from our sessions.
+
+Note that because session values are stored in a map[string]interface{}, there's
+a need to type-assert data when retrieving it. We'll use the Person struct we registered above:
+
+	func MyHandler(w http.ResponseWriter, r *http.Request) {
+		session, err := store.Get(r, "session-name")
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		// Retrieve our struct and type-assert it
+		val := session.Values["person"]
+		var person = &Person{}
+		if person, ok := val.(*Person); !ok {
+			// Handle the case that it's not an expected type
+		}
+
+		// Now we can use our person object
+	}
+
+By default, session cookies last for a month. This is probably too long for
+some cases, but it is easy to change this and other attributes during
+runtime. Sessions can be configured individually or the store can be
+configured and then all sessions saved using it will use that configuration.
+We access session.Options or store.Options to set a new configuration. The
+fields are basically a subset of http.Cookie fields. Let's change the
+maximum age of a session to one week:
+
+	session.Options = &sessions.Options{
+		Path:     "/",
+		MaxAge:   86400 * 7,
+		HttpOnly: true,
+	}
+
+Sometimes we may want to change authentication and/or encryption keys without
+breaking existing sessions. The CookieStore supports key rotation, and to use
+it you just need to set multiple authentication and encryption keys, in pairs,
+to be tested in order:
+
+	var store = sessions.NewCookieStore(
+		[]byte("new-authentication-key"),
+		[]byte("new-encryption-key"),
+		[]byte("old-authentication-key"),
+		[]byte("old-encryption-key"),
+	)
+
+New sessions will be saved using the first pair. Old sessions can still be
+read because the first pair will fail, and the second will be tested. This
+makes it easy to "rotate" secret keys and still be able to validate existing
+sessions. Note: for all pairs the encryption key is optional; set it to nil
+or omit it and and encryption won't be used.
+
+Multiple sessions can be used in the same request, even with different
+session backends. When this happens, calling Save() on each session
+individually would be cumbersome, so we have a way to save all sessions
+at once: it's sessions.Save(). Here's an example:
+
+	var store = sessions.NewCookieStore([]byte("something-very-secret"))
+
+	func MyHandler(w http.ResponseWriter, r *http.Request) {
+		// Get a session and set a value.
+		session1, _ := store.Get(r, "session-one")
+		session1.Values["foo"] = "bar"
+		// Get another session and set another value.
+		session2, _ := store.Get(r, "session-two")
+		session2.Values[42] = 43
+		// Save all sessions.
+		sessions.Save(r, w)
+	}
+
+This is possible because when we call Get() from a session store, it adds the
+session to a common registry. Save() uses it to save all registered sessions.
+*/
+package sessions
diff --git a/vendor/github.com/gorilla/sessions/lex.go b/vendor/github.com/gorilla/sessions/lex.go
new file mode 100644
index 0000000000..4bbbe1096d
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/lex.go
@@ -0,0 +1,102 @@
+// This file contains code adapted from the Go standard library
+// https://github.com/golang/go/blob/39ad0fd0789872f9469167be7fe9578625ff246e/src/net/http/lex.go
+
+package sessions
+
+import "strings"
+
+var isTokenTable = [127]bool{
+	'!':  true,
+	'#':  true,
+	'$':  true,
+	'%':  true,
+	'&':  true,
+	'\'': true,
+	'*':  true,
+	'+':  true,
+	'-':  true,
+	'.':  true,
+	'0':  true,
+	'1':  true,
+	'2':  true,
+	'3':  true,
+	'4':  true,
+	'5':  true,
+	'6':  true,
+	'7':  true,
+	'8':  true,
+	'9':  true,
+	'A':  true,
+	'B':  true,
+	'C':  true,
+	'D':  true,
+	'E':  true,
+	'F':  true,
+	'G':  true,
+	'H':  true,
+	'I':  true,
+	'J':  true,
+	'K':  true,
+	'L':  true,
+	'M':  true,
+	'N':  true,
+	'O':  true,
+	'P':  true,
+	'Q':  true,
+	'R':  true,
+	'S':  true,
+	'T':  true,
+	'U':  true,
+	'W':  true,
+	'V':  true,
+	'X':  true,
+	'Y':  true,
+	'Z':  true,
+	'^':  true,
+	'_':  true,
+	'`':  true,
+	'a':  true,
+	'b':  true,
+	'c':  true,
+	'd':  true,
+	'e':  true,
+	'f':  true,
+	'g':  true,
+	'h':  true,
+	'i':  true,
+	'j':  true,
+	'k':  true,
+	'l':  true,
+	'm':  true,
+	'n':  true,
+	'o':  true,
+	'p':  true,
+	'q':  true,
+	'r':  true,
+	's':  true,
+	't':  true,
+	'u':  true,
+	'v':  true,
+	'w':  true,
+	'x':  true,
+	'y':  true,
+	'z':  true,
+	'|':  true,
+	'~':  true,
+}
+
+func isToken(r rune) bool {
+	i := int(r)
+	return i < len(isTokenTable) && isTokenTable[i]
+}
+
+func isNotToken(r rune) bool {
+	return !isToken(r)
+}
+
+func isCookieNameValid(raw string) bool {
+	if raw == "" {
+		return false
+	}
+	return strings.IndexFunc(raw, isNotToken) < 0
+}
diff --git a/vendor/github.com/gorilla/sessions/sessions.go b/vendor/github.com/gorilla/sessions/sessions.go
new file mode 100644
index 0000000000..fe0d2bc8fa
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/sessions.go
@@ -0,0 +1,241 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sessions
+
+import (
+	"encoding/gob"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/gorilla/context"
+)
+
+// Default flashes key.
+const flashesKey = "_flash"
+
+// Options --------------------------------------------------------------------
+
+// Options stores configuration for a session or session store.
+//
+// Fields are a subset of http.Cookie fields.
+type Options struct {
+	Path   string
+	Domain string
+	// MaxAge=0 means no 'Max-Age' attribute specified.
+	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
+	// MaxAge>0 means Max-Age attribute present and given in seconds.
+	MaxAge   int
+	Secure   bool
+	HttpOnly bool
+}
+
+// Session --------------------------------------------------------------------
+
+// NewSession is called by session stores to create a new session instance.
+func NewSession(store Store, name string) *Session {
+	return &Session{
+		Values: make(map[interface{}]interface{}),
+		store:  store,
+		name:   name,
+	}
+}
+
+// Session stores the values and optional configuration for a session.
+type Session struct {
+	// The ID of the session, generated by stores. It should not be used for
+	// user data.
+	ID string
+	// Values contains the user-data for the session.
+	Values  map[interface{}]interface{}
+	Options *Options
+	IsNew   bool
+	store   Store
+	name    string
+}
+
+// Flashes returns a slice of flash messages from the session.
+//
+// A single variadic argument is accepted, and it is optional: it defines
+// the flash key. If not defined "_flash" is used by default.
+func (s *Session) Flashes(vars ...string) []interface{} {
+	var flashes []interface{}
+	key := flashesKey
+	if len(vars) > 0 {
+		key = vars[0]
+	}
+	if v, ok := s.Values[key]; ok {
+		// Drop the flashes and return it.
+		delete(s.Values, key)
+		flashes = v.([]interface{})
+	}
+	return flashes
+}
+
+// AddFlash adds a flash message to the session.
+//
+// A single variadic argument is accepted, and it is optional: it defines
+// the flash key. If not defined "_flash" is used by default.
+func (s *Session) AddFlash(value interface{}, vars ...string) {
+	key := flashesKey
+	if len(vars) > 0 {
+		key = vars[0]
+	}
+	var flashes []interface{}
+	if v, ok := s.Values[key]; ok {
+		flashes = v.([]interface{})
+	}
+	s.Values[key] = append(flashes, value)
+}
+
+// Save is a convenience method to save this session. It is the same as calling
+// store.Save(request, response, session). You should call Save before writing to
+// the response or returning from the handler.
+func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
+	return s.store.Save(r, w, s)
+}
+
+// Name returns the name used to register the session.
+func (s *Session) Name() string {
+	return s.name
+}
+
+// Store returns the session store used to register the session.
+func (s *Session) Store() Store {
+	return s.store
+}
+
+// Registry -------------------------------------------------------------------
+
+// sessionInfo stores a session tracked by the registry.
+type sessionInfo struct {
+	s *Session
+	e error
+}
+
+// contextKey is the type used to store the registry in the context.
+type contextKey int
+
+// registryKey is the key used to store the registry in the context.
+const registryKey contextKey = 0
+
+// GetRegistry returns a registry instance for the current request.
+func GetRegistry(r *http.Request) *Registry {
+	registry := context.Get(r, registryKey)
+	if registry != nil {
+		return registry.(*Registry)
+	}
+	newRegistry := &Registry{
+		request:  r,
+		sessions: make(map[string]sessionInfo),
+	}
+	context.Set(r, registryKey, newRegistry)
+	return newRegistry
+}
+
+// Registry stores sessions used during a request.
+type Registry struct {
+	request  *http.Request
+	sessions map[string]sessionInfo
+}
+
+// Get registers and returns a session for the given name and session store.
+//
+// It returns a new session if there are no sessions registered for the name.
+func (s *Registry) Get(store Store, name string) (session *Session, err error) {
+	if !isCookieNameValid(name) {
+		return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
+	}
+	if info, ok := s.sessions[name]; ok {
+		session, err = info.s, info.e
+	} else {
+		session, err = store.New(s.request, name)
+		session.name = name
+		s.sessions[name] = sessionInfo{s: session, e: err}
+	}
+	session.store = store
+	return
+}
+
+// Save saves all sessions registered for the current request.
+func (s *Registry) Save(w http.ResponseWriter) error {
+	var errMulti MultiError
+	for name, info := range s.sessions {
+		session := info.s
+		if session.store == nil {
+			errMulti = append(errMulti, fmt.Errorf(
+				"sessions: missing store for session %q", name))
+		} else if err := session.store.Save(s.request, w, session); err != nil {
+			errMulti = append(errMulti, fmt.Errorf(
+				"sessions: error saving session %q -- %v", name, err))
+		}
+	}
+	if errMulti != nil {
+		return errMulti
+	}
+	return nil
+}
+
+// Helpers --------------------------------------------------------------------
+
+func init() {
+	gob.Register([]interface{}{})
+}
+
+// Save saves all sessions used during the current request.
+func Save(r *http.Request, w http.ResponseWriter) error {
+	return GetRegistry(r).Save(w)
+}
+
+// NewCookie returns an http.Cookie with the options set. It also sets
+// the Expires field calculated based on the MaxAge value, for Internet
+// Explorer compatibility.
+func NewCookie(name, value string, options *Options) *http.Cookie {
+	cookie := &http.Cookie{
+		Name:     name,
+		Value:    value,
+		Path:     options.Path,
+		Domain:   options.Domain,
+		MaxAge:   options.MaxAge,
+		Secure:   options.Secure,
+		HttpOnly: options.HttpOnly,
+	}
+	if options.MaxAge > 0 {
+		d := time.Duration(options.MaxAge) * time.Second
+		cookie.Expires = time.Now().Add(d)
+	} else if options.MaxAge < 0 {
+		// Set it to the past to expire now.
+		cookie.Expires = time.Unix(1, 0)
+	}
+	return cookie
+}
+
+// Error ----------------------------------------------------------------------
+
+// MultiError stores multiple errors.
+//
+// Borrowed from the App Engine SDK.
+type MultiError []error
+
+func (m MultiError) Error() string {
+	s, n := "", 0
+	for _, e := range m {
+		if e != nil {
+			if n == 0 {
+				s = e.Error()
+			}
+			n++
+		}
+	}
+	switch n {
+	case 0:
+		return "(0 errors)"
+	case 1:
+		return s
+	case 2:
+		return s + " (and 1 other error)"
+	}
+	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
+}
diff --git a/vendor/github.com/gorilla/sessions/store.go b/vendor/github.com/gorilla/sessions/store.go
new file mode 100644
index 0000000000..4ff6b6c322
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/store.go
@@ -0,0 +1,295 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sessions
+
+import (
+	"encoding/base32"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+
+	"github.com/gorilla/securecookie"
+)
+
+// Store is an interface for custom session stores.
+//
+// See CookieStore and FilesystemStore for examples.
+type Store interface {
+	// Get should return a cached session.
+	Get(r *http.Request, name string) (*Session, error)
+
+	// New should create and return a new session.
+	//
+	// Note that New should never return a nil session, even in the case of
+	// an error if using the Registry infrastructure to cache the session.
+	New(r *http.Request, name string) (*Session, error)
+
+	// Save should persist session to the underlying store implementation.
+	Save(r *http.Request, w http.ResponseWriter, s *Session) error
+}
+
+// CookieStore ----------------------------------------------------------------
+
+// NewCookieStore returns a new CookieStore.
+//
+// Keys are defined in pairs to allow key rotation, but the common case is
+// to set a single authentication key and optionally an encryption key.
+//
+// The first key in a pair is used for authentication and the second for
+// encryption. The encryption key can be set to nil or omitted in the last
+// pair, but the authentication key is required in all pairs.
+//
+// It is recommended to use an authentication key with 32 or 64 bytes.
+// The encryption key, if set, must be either 16, 24, or 32 bytes to select
+// AES-128, AES-192, or AES-256 modes.
+//
+// Use the convenience function securecookie.GenerateRandomKey() to create
+// strong keys.
+func NewCookieStore(keyPairs ...[]byte) *CookieStore {
+	cs := &CookieStore{
+		Codecs: securecookie.CodecsFromPairs(keyPairs...),
+		Options: &Options{
+			Path:   "/",
+			MaxAge: 86400 * 30,
+		},
+	}
+
+	cs.MaxAge(cs.Options.MaxAge)
+	return cs
+}
+
+// CookieStore stores sessions using secure cookies.
+type CookieStore struct {
+	Codecs  []securecookie.Codec
+	Options *Options // default configuration
+}
+
+// Get returns a session for the given name after adding it to the registry.
+//
+// It returns a new session if the sessions doesn't exist. Access IsNew on
+// the session to check if it is an existing session or a new one.
+//
+// It returns a new session and an error if the session exists but could
+// not be decoded.
+func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) {
+	return GetRegistry(r).Get(s, name)
+}
+
+// New returns a session for the given name without adding it to the registry.
+//
+// The difference between New() and Get() is that calling New() twice will
+// decode the session data twice, while Get() registers and reuses the same
+// decoded session after the first call.
+func (s *CookieStore) New(r *http.Request, name string) (*Session, error) {
+	session := NewSession(s, name)
+	opts := *s.Options
+	session.Options = &opts
+	session.IsNew = true
+	var err error
+	if c, errCookie := r.Cookie(name); errCookie == nil {
+		err = securecookie.DecodeMulti(name, c.Value, &session.Values,
+			s.Codecs...)
+		if err == nil {
+			session.IsNew = false
+		}
+	}
+	return session, err
+}
+
+// Save adds a single session to the response.
+func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter,
+	session *Session) error {
+	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
+		s.Codecs...)
+	if err != nil {
+		return err
+	}
+	http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
+	return nil
+}
+
+// MaxAge sets the maximum age for the store and the underlying cookie
+// implementation. Individual sessions can be deleted by setting Options.MaxAge
+// = -1 for that session.
+func (s *CookieStore) MaxAge(age int) {
+	s.Options.MaxAge = age
+
+	// Set the maxAge for each securecookie instance.
+	for _, codec := range s.Codecs {
+		if sc, ok := codec.(*securecookie.SecureCookie); ok {
+			sc.MaxAge(age)
+		}
+	}
+}
+
+// FilesystemStore ------------------------------------------------------------
+
+var fileMutex sync.RWMutex
+
+// NewFilesystemStore returns a new FilesystemStore.
+//
+// The path argument is the directory where sessions will be saved. If empty
+// it will use os.TempDir().
+//
+// See NewCookieStore() for a description of the other parameters.
+func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore {
+	if path == "" {
+		path = os.TempDir()
+	}
+	fs := &FilesystemStore{
+		Codecs: securecookie.CodecsFromPairs(keyPairs...),
+		Options: &Options{
+			Path:   "/",
+			MaxAge: 86400 * 30,
+		},
+		path: path,
+	}
+
+	fs.MaxAge(fs.Options.MaxAge)
+	return fs
+}
+
+// FilesystemStore stores sessions in the filesystem.
+//
+// It also serves as a reference for custom stores.
+//
+// This store is still experimental and not well tested. Feedback is welcome.
+type FilesystemStore struct {
+	Codecs  []securecookie.Codec
+	Options *Options // default configuration
+	path    string
+}
+
+// MaxLength restricts the maximum length of new sessions to l.
+// If l is 0 there is no limit to the size of a session, use with caution.
+// The default for a new FilesystemStore is 4096.
+func (s *FilesystemStore) MaxLength(l int) {
+	for _, c := range s.Codecs {
+		if codec, ok := c.(*securecookie.SecureCookie); ok {
+			codec.MaxLength(l)
+		}
+	}
+}
+
+// Get returns a session for the given name after adding it to the registry.
+//
+// See CookieStore.Get().
+func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) {
+	return GetRegistry(r).Get(s, name)
+}
+
+// New returns a session for the given name without adding it to the registry.
+//
+// See CookieStore.New().
+func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) {
+	session := NewSession(s, name)
+	opts := *s.Options
+	session.Options = &opts
+	session.IsNew = true
+	var err error
+	if c, errCookie := r.Cookie(name); errCookie == nil {
+		err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
+		if err == nil {
+			err = s.load(session)
+			if err == nil {
+				session.IsNew = false
+			}
+		}
+	}
+	return session, err
+}
+
+// Save adds a single session to the response.
+//
+// If the Options.MaxAge of the session is <= 0 then the session file will be
+// deleted from the store path. With this process it enforces the properly
+// session cookie handling so no need to trust in the cookie management in the
+// web browser.
+func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter,
+	session *Session) error {
+	// Delete if max-age is <= 0
+	if session.Options.MaxAge <= 0 {
+		if err := s.erase(session); err != nil {
+			return err
+		}
+		http.SetCookie(w, NewCookie(session.Name(), "", session.Options))
+		return nil
+	}
+
+	if session.ID == "" {
+		// Because the ID is used in the filename, encode it to
+		// use alphanumeric characters only.
+		session.ID = strings.TrimRight(
+			base32.StdEncoding.EncodeToString(
+				securecookie.GenerateRandomKey(32)), "=")
+	}
+	if err := s.save(session); err != nil {
+		return err
+	}
+	encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
+		s.Codecs...)
+	if err != nil {
+		return err
+	}
+	http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
+	return nil
+}
+
+// MaxAge sets the maximum age for the store and the underlying cookie
+// implementation. Individual sessions can be deleted by setting Options.MaxAge
+// = -1 for that session.
+func (s *FilesystemStore) MaxAge(age int) {
+	s.Options.MaxAge = age
+
+	// Set the maxAge for each securecookie instance.
+	for _, codec := range s.Codecs {
+		if sc, ok := codec.(*securecookie.SecureCookie); ok {
+			sc.MaxAge(age)
+		}
+	}
+}
+
+// save writes encoded session.Values to a file.
+func (s *FilesystemStore) save(session *Session) error {
+	encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
+		s.Codecs...)
+	if err != nil {
+		return err
+	}
+	filename := filepath.Join(s.path, "session_"+session.ID)
+	fileMutex.Lock()
+	defer fileMutex.Unlock()
+	return ioutil.WriteFile(filename, []byte(encoded), 0600)
+}
+
+// load reads a file and decodes its content into session.Values.
+func (s *FilesystemStore) load(session *Session) error {
+	filename := filepath.Join(s.path, "session_"+session.ID)
+	fileMutex.RLock()
+	defer fileMutex.RUnlock()
+	fdata, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return err
+	}
+	if err = securecookie.DecodeMulti(session.Name(), string(fdata),
+		&session.Values, s.Codecs...); err != nil {
+		return err
+	}
+	return nil
+}
+
+// delete session file
+func (s *FilesystemStore) erase(session *Session) error {
+	filename := filepath.Join(s.path, "session_"+session.ID)
+
+	fileMutex.RLock()
+	defer fileMutex.RUnlock()
+
+	err := os.Remove(filename)
+	return err
+}
diff --git a/vendor/github.com/markbates/goth/LICENSE.txt b/vendor/github.com/markbates/goth/LICENSE.txt
new file mode 100644
index 0000000000..f8e6d5b27f
--- /dev/null
+++ b/vendor/github.com/markbates/goth/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Mark Bates
+
+MIT License
+
+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/vendor/github.com/markbates/goth/README.md b/vendor/github.com/markbates/goth/README.md
new file mode 100644
index 0000000000..b481683bcc
--- /dev/null
+++ b/vendor/github.com/markbates/goth/README.md
@@ -0,0 +1,143 @@
+# Goth: Multi-Provider Authentication for Go [![GoDoc](https://godoc.org/github.com/markbates/goth?status.svg)](https://godoc.org/github.com/markbates/goth) [![Build Status](https://travis-ci.org/markbates/goth.svg)](https://travis-ci.org/markbates/goth)
+
+Package goth provides a simple, clean, and idiomatic way to write authentication
+packages for Go web applications.
+
+Unlike other similar packages, Goth, lets you write OAuth, OAuth2, or any other
+protocol providers, as long as they implement the `Provider` and `Session` interfaces.
+
+This package was inspired by [https://github.com/intridea/omniauth](https://github.com/intridea/omniauth).
+
+## Installation
+
+```text
+$ go get github.com/markbates/goth
+```
+
+## Supported Providers
+
+* Amazon
+* Auth0
+* Bitbucket
+* Box
+* Cloud Foundry
+* Dailymotion
+* Deezer
+* Digital Ocean
+* Discord
+* Dropbox
+* Facebook
+* Fitbit
+* GitHub
+* Gitlab
+* Google+
+* Heroku
+* InfluxCloud
+* Instagram
+* Intercom
+* Lastfm
+* Linkedin
+* Meetup
+* OneDrive
+* OpenID Connect (auto discovery)
+* Paypal
+* SalesForce
+* Slack
+* Soundcloud
+* Spotify
+* Steam
+* Stripe
+* Twitch
+* Twitter
+* Uber
+* Wepay
+* Yahoo
+* Yammer
+
+## Examples
+
+See the [examples](examples) folder for a working application that lets users authenticate
+through Twitter, Facebook, Google Plus etc.
+
+To run the example either clone the source from GitHub
+
+```text
+$ git clone git@github.com:markbates/goth.git
+```
+or use
+```text
+$ go get github.com/markbates/goth
+```
+```text
+$ cd goth/examples
+$ go get -v
+$ go build 
+$ ./examples
+```
+
+Now open up your browser and go to [http://localhost:3000](http://localhost:3000) to see the example.
+
+To actually use the different providers, please make sure you configure them given the system environments as defined in the examples/main.go file
+
+## Issues
+
+Issues always stand a significantly better chance of getting fixed if the are accompanied by a
+pull request.
+
+## Contributing
+
+Would I love to see more providers? Certainly! Would you love to contribute one? Hopefully, yes!
+
+1. Fork it
+2. Create your feature branch (git checkout -b my-new-feature)
+3. Write Tests!
+4. Commit your changes (git commit -am 'Add some feature')
+5. Push to the branch (git push origin my-new-feature)
+6. Create new Pull Request
+
+## Contributors
+
+* Mark Bates
+* Tyler Bunnell
+* Corey McGrillis
+* willemvd
+* Rakesh Goyal
+* Andy Grunwald
+* Glenn Walker
+* Kevin Fitzpatrick
+* Ben Tranter
+* Sharad Ganapathy
+* Andrew Chilton
+* sharadgana
+* Aurorae
+* Craig P Jolicoeur
+* Zac Bergquist
+* Geoff Franks
+* Raphael Geronimi
+* Noah Shibley
+* lumost
+* oov
+* Felix Lamouroux
+* Rafael Quintela
+* Tyler
+* DenSm
+* Samy KACIMI
+* dante gray
+* Noah
+* Jacob Walker
+* Marin Martinic
+* Roy
+* Omni Adams
+* Sasa Brankovic
+* dkhamsing
+* Dante Swift
+* Attila Domokos
+* Albin Gilles
+* Syed Zubairuddin
+* Johnny Boursiquot
+* Jerome Touffe-Blin
+* bryanl
+* Masanobu YOSHIOKA
+* Jonathan Hall
+* HaiMing.Yin
+* Sairam Kunala
diff --git a/vendor/github.com/markbates/goth/doc.go b/vendor/github.com/markbates/goth/doc.go
new file mode 100644
index 0000000000..d0bec281c1
--- /dev/null
+++ b/vendor/github.com/markbates/goth/doc.go
@@ -0,0 +1,10 @@
+/*
+Package goth provides a simple, clean, and idiomatic way to write authentication
+packages for Go web applications.
+
+This package was inspired by https://github.com/intridea/omniauth.
+
+See the examples folder for a working application that lets users authenticate
+through Twitter or Facebook.
+*/
+package goth
diff --git a/vendor/github.com/markbates/goth/gothic/gothic.go b/vendor/github.com/markbates/goth/gothic/gothic.go
new file mode 100644
index 0000000000..f6aaf2d117
--- /dev/null
+++ b/vendor/github.com/markbates/goth/gothic/gothic.go
@@ -0,0 +1,219 @@
+/*
+Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
+and running with Goth. Of course, if you want complete control over how things flow, in regards
+to the authentication process, feel free and use Goth directly.
+
+See https://github.com/markbates/goth/examples/main.go to see this in action.
+*/
+package gothic
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"os"
+
+	"github.com/gorilla/mux"
+	"github.com/gorilla/sessions"
+	"github.com/markbates/goth"
+)
+
+// SessionName is the key used to access the session store.
+const SessionName = "_gothic_session"
+
+// Store can/should be set by applications using gothic. The default is a cookie store.
+var Store sessions.Store
+var defaultStore sessions.Store
+
+var keySet = false
+
+func init() {
+	key := []byte(os.Getenv("SESSION_SECRET"))
+	keySet = len(key) != 0
+	Store = sessions.NewCookieStore([]byte(key))
+	defaultStore = Store
+}
+
+/*
+BeginAuthHandler is a convienence handler for starting the authentication process.
+It expects to be able to get the name of the provider from the query parameters
+as either "provider" or ":provider".
+
+BeginAuthHandler will redirect the user to the appropriate authentication end-point
+for the requested provider.
+
+See https://github.com/markbates/goth/examples/main.go to see this in action.
+*/
+func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
+	url, err := GetAuthURL(res, req)
+	if err != nil {
+		res.WriteHeader(http.StatusBadRequest)
+		fmt.Fprintln(res, err)
+		return
+	}
+
+	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
+}
+
+// SetState sets the state string associated with the given request.
+// If no state string is associated with the request, one will be generated.
+// This state is sent to the provider and can be retrieved during the
+// callback.
+var SetState = func(req *http.Request) string {
+	state := req.URL.Query().Get("state")
+	if len(state) > 0 {
+		return state
+	}
+
+	return "state"
+
+}
+
+// GetState gets the state returned by the provider during the callback.
+// This is used to prevent CSRF attacks, see
+// http://tools.ietf.org/html/rfc6749#section-10.12
+var GetState = func(req *http.Request) string {
+	return req.URL.Query().Get("state")
+}
+
+/*
+GetAuthURL starts the authentication process with the requested provided.
+It will return a URL that should be used to send users to.
+
+It expects to be able to get the name of the provider from the query parameters
+as either "provider" or ":provider".
+
+I would recommend using the BeginAuthHandler instead of doing all of these steps
+yourself, but that's entirely up to you.
+*/
+func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
+
+	if !keySet && defaultStore == Store {
+		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
+	}
+
+	providerName, err := GetProviderName(req)
+	if err != nil {
+		return "", err
+	}
+
+	provider, err := goth.GetProvider(providerName)
+	if err != nil {
+		return "", err
+	}
+	sess, err := provider.BeginAuth(SetState(req))
+	if err != nil {
+		return "", err
+	}
+
+	url, err := sess.GetAuthURL()
+	if err != nil {
+		return "", err
+	}
+
+	err = storeInSession(providerName, sess.Marshal(), req, res)
+
+	if err != nil {
+		return "", err
+	}
+
+	return url, err
+}
+
+/*
+CompleteUserAuth does what it says on the tin. It completes the authentication
+process and fetches all of the basic information about the user from the provider.
+
+It expects to be able to get the name of the provider from the query parameters
+as either "provider" or ":provider".
+
+See https://github.com/markbates/goth/examples/main.go to see this in action.
+*/
+var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
+
+	if !keySet && defaultStore == Store {
+		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
+	}
+
+	providerName, err := GetProviderName(req)
+	if err != nil {
+		return goth.User{}, err
+	}
+
+	provider, err := goth.GetProvider(providerName)
+	if err != nil {
+		return goth.User{}, err
+	}
+
+	value, err := getFromSession(providerName, req)
+	if err != nil {
+		return goth.User{}, err
+	}
+
+	sess, err := provider.UnmarshalSession(value)
+	if err != nil {
+		return goth.User{}, err
+	}
+
+	user, err := provider.FetchUser(sess)
+	if err == nil {
+		// user can be found with existing session data
+		return user, err
+	}
+
+	// get new token and retry fetch
+	_, err = sess.Authorize(provider, req.URL.Query())
+	if err != nil {
+		return goth.User{}, err
+	}
+
+	err = storeInSession(providerName, sess.Marshal(), req, res)
+
+	if err != nil {
+		return goth.User{}, err
+	}
+
+	return provider.FetchUser(sess)
+}
+
+// GetProviderName is a function used to get the name of a provider
+// for a given request. By default, this provider is fetched from
+// the URL query string. If you provide it in a different way,
+// assign your own function to this variable that returns the provider
+// name for your request.
+var GetProviderName = getProviderName
+
+func getProviderName(req *http.Request) (string, error) {
+	provider := req.URL.Query().Get("provider")
+	if provider == "" {
+		if p, ok := mux.Vars(req)["provider"]; ok {
+			return p, nil
+		}
+	}
+	if provider == "" {
+		provider = req.URL.Query().Get(":provider")
+	}
+	if provider == "" {
+		return provider, errors.New("you must select a provider")
+	}
+	return provider, nil
+}
+
+func storeInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
+	session, _ := Store.Get(req, key + SessionName)
+
+	session.Values[key] = value
+
+	return session.Save(req, res)
+}
+
+func getFromSession(key string, req *http.Request) (string, error) {
+	session, _ := Store.Get(req, key + SessionName)
+
+	value := session.Values[key]
+	if value == nil {
+		return "", errors.New("could not find a matching session for this request")
+	}
+
+	return value.(string), nil
+}
\ No newline at end of file
diff --git a/vendor/github.com/markbates/goth/provider.go b/vendor/github.com/markbates/goth/provider.go
new file mode 100644
index 0000000000..58d0d60bbf
--- /dev/null
+++ b/vendor/github.com/markbates/goth/provider.go
@@ -0,0 +1,75 @@
+package goth
+
+import (
+	"fmt"
+	"net/http"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+)
+
+// Provider needs to be implemented for each 3rd party authentication provider
+// e.g. Facebook, Twitter, etc...
+type Provider interface {
+	Name() string
+	SetName(name string)
+	BeginAuth(state string) (Session, error)
+	UnmarshalSession(string) (Session, error)
+	FetchUser(Session) (User, error)
+	Debug(bool)
+	RefreshToken(refreshToken string) (*oauth2.Token, error) //Get new access token based on the refresh token
+	RefreshTokenAvailable() bool                             //Refresh token is provided by auth provider or not
+}
+
+const NoAuthUrlErrorMessage = "an AuthURL has not been set"
+
+// Providers is list of known/available providers.
+type Providers map[string]Provider
+
+var providers = Providers{}
+
+// UseProviders adds a list of available providers for use with Goth.
+// Can be called multiple times. If you pass the same provider more
+// than once, the last will be used.
+func UseProviders(viders ...Provider) {
+	for _, provider := range viders {
+		providers[provider.Name()] = provider
+	}
+}
+
+// GetProviders returns a list of all the providers currently in use.
+func GetProviders() Providers {
+	return providers
+}
+
+// GetProvider returns a previously created provider. If Goth has not
+// been told to use the named provider it will return an error.
+func GetProvider(name string) (Provider, error) {
+	provider := providers[name]
+	if provider == nil {
+		return nil, fmt.Errorf("no provider for %s exists", name)
+	}
+	return provider, nil
+}
+
+// ClearProviders will remove all providers currently in use.
+// This is useful, mostly, for testing purposes.
+func ClearProviders() {
+	providers = Providers{}
+}
+
+// ContextForClient provides a context for use with oauth2.
+func ContextForClient(h *http.Client) context.Context {
+	if h == nil {
+		return oauth2.NoContext
+	}
+	return context.WithValue(oauth2.NoContext, oauth2.HTTPClient, h)
+}
+
+// HTTPClientWithFallBack to be used in all fetch operations.
+func HTTPClientWithFallBack(h *http.Client) *http.Client {
+	if h != nil {
+		return h
+	}
+	return http.DefaultClient
+}
diff --git a/vendor/github.com/markbates/goth/providers/github/github.go b/vendor/github.com/markbates/goth/providers/github/github.go
new file mode 100644
index 0000000000..866150e63a
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/github/github.go
@@ -0,0 +1,224 @@
+// Package github implements the OAuth2 protocol for authenticating users through Github.
+// This package can be used as a reference implementation of an OAuth2 provider for Goth.
+package github
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+
+	"github.com/markbates/goth"
+	"golang.org/x/oauth2"
+)
+
+// These vars define the Authentication, Token, and API URLS for GitHub. If
+// using GitHub enterprise you should change these values before calling New.
+//
+// Examples:
+//	github.AuthURL = "https://github.acme.com/login/oauth/authorize
+//	github.TokenURL = "https://github.acme.com/login/oauth/access_token
+//	github.ProfileURL = "https://github.acme.com/api/v3/user
+//	github.EmailURL = "https://github.acme.com/api/v3/user/emails
+var (
+	AuthURL    = "https://github.com/login/oauth/authorize"
+	TokenURL   = "https://github.com/login/oauth/access_token"
+	ProfileURL = "https://api.github.com/user"
+	EmailURL   = "https://api.github.com/user/emails"
+)
+
+// New creates a new Github provider, and sets up important connection details.
+// You should always call `github.New` to get a new Provider. Never try to create
+// one manually.
+func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
+	p := &Provider{
+		ClientKey:           clientKey,
+		Secret:              secret,
+		CallbackURL:         callbackURL,
+		providerName:        "github",
+	}
+	p.config = newConfig(p, scopes)
+	return p
+}
+
+// Provider is the implementation of `goth.Provider` for accessing Github.
+type Provider struct {
+	ClientKey    string
+	Secret       string
+	CallbackURL  string
+	HTTPClient   *http.Client
+	config       *oauth2.Config
+	providerName string
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+	return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+	p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+	return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug is a no-op for the github package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks Github for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+	url := p.config.AuthCodeURL(state)
+	session := &Session{
+		AuthURL: url,
+	}
+	return session, nil
+}
+
+// FetchUser will go to Github and access basic information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+	sess := session.(*Session)
+	user := goth.User{
+		AccessToken: sess.AccessToken,
+		Provider:    p.Name(),
+	}
+
+	if user.AccessToken == "" {
+		// data is not yet retrieved since accessToken is still empty
+		return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
+	}
+
+	response, err := p.Client().Get(ProfileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
+	if err != nil {
+		return user, err
+	}
+	defer response.Body.Close()
+
+	if response.StatusCode != http.StatusOK {
+		return user, fmt.Errorf("GitHub API responded with a %d trying to fetch user information", response.StatusCode)
+	}
+
+	bits, err := ioutil.ReadAll(response.Body)
+	if err != nil {
+		return user, err
+	}
+
+	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
+	if err != nil {
+		return user, err
+	}
+
+	err = userFromReader(bytes.NewReader(bits), &user)
+	if err != nil {
+		return user, err
+	}
+
+	if user.Email == "" {
+		for _, scope := range p.config.Scopes {
+			if strings.TrimSpace(scope) == "user" || strings.TrimSpace(scope) == "user:email" {
+				user.Email, err = getPrivateMail(p, sess)
+				if err != nil {
+					return user, err
+				}
+				break
+			}
+		}
+	}
+	return user, err
+}
+
+func userFromReader(reader io.Reader, user *goth.User) error {
+	u := struct {
+		ID       int    `json:"id"`
+		Email    string `json:"email"`
+		Bio      string `json:"bio"`
+		Name     string `json:"name"`
+		Login    string `json:"login"`
+		Picture  string `json:"avatar_url"`
+		Location string `json:"location"`
+	}{}
+
+	err := json.NewDecoder(reader).Decode(&u)
+	if err != nil {
+		return err
+	}
+
+	user.Name = u.Name
+	user.NickName = u.Login
+	user.Email = u.Email
+	user.Description = u.Bio
+	user.AvatarURL = u.Picture
+	user.UserID = strconv.Itoa(u.ID)
+	user.Location = u.Location
+
+	return err
+}
+
+func getPrivateMail(p *Provider, sess *Session) (email string, err error) {
+	response, err := p.Client().Get(EmailURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
+	if err != nil {
+		if response != nil {
+			response.Body.Close()
+		}
+		return email, err
+	}
+	defer response.Body.Close()
+
+	if response.StatusCode != http.StatusOK {
+		return email, fmt.Errorf("GitHub API responded with a %d trying to fetch user email", response.StatusCode)
+	}
+
+	var mailList = []struct {
+		Email    string `json:"email"`
+		Primary  bool   `json:"primary"`
+		Verified bool   `json:"verified"`
+	}{}
+	err = json.NewDecoder(response.Body).Decode(&mailList)
+	if err != nil {
+		return email, err
+	}
+	for _, v := range mailList {
+		if v.Primary && v.Verified {
+			return v.Email, nil
+		}
+	}
+	// can't get primary email - shouldn't be possible
+	return
+}
+
+func newConfig(provider *Provider, scopes []string) *oauth2.Config {
+	c := &oauth2.Config{
+		ClientID:     provider.ClientKey,
+		ClientSecret: provider.Secret,
+		RedirectURL:  provider.CallbackURL,
+		Endpoint: oauth2.Endpoint{
+			AuthURL:  AuthURL,
+			TokenURL: TokenURL,
+		},
+		Scopes: []string{},
+	}
+
+	for _, scope := range scopes {
+		c.Scopes = append(c.Scopes, scope)
+	}
+
+	return c
+}
+
+//RefreshToken refresh token is not provided by github
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+	return nil, errors.New("Refresh token is not provided by github")
+}
+
+//RefreshTokenAvailable refresh token is not provided by github
+func (p *Provider) RefreshTokenAvailable() bool {
+	return false
+}
diff --git a/vendor/github.com/markbates/goth/providers/github/session.go b/vendor/github.com/markbates/goth/providers/github/session.go
new file mode 100644
index 0000000000..f473a526e5
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/github/session.go
@@ -0,0 +1,56 @@
+package github
+
+import (
+	"encoding/json"
+	"errors"
+	"strings"
+
+	"github.com/markbates/goth"
+)
+
+// Session stores data during the auth process with Github.
+type Session struct {
+	AuthURL     string
+	AccessToken string
+}
+
+// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Github provider.
+func (s Session) GetAuthURL() (string, error) {
+	if s.AuthURL == "" {
+		return "", errors.New(goth.NoAuthUrlErrorMessage)
+	}
+	return s.AuthURL, nil
+}
+
+// Authorize the session with Github and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+	p := provider.(*Provider)
+	token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
+	if err != nil {
+		return "", err
+	}
+
+	if !token.Valid() {
+		return "", errors.New("Invalid token received from provider")
+	}
+
+	s.AccessToken = token.AccessToken
+	return token.AccessToken, err
+}
+
+// Marshal the session into a string
+func (s Session) Marshal() string {
+	b, _ := json.Marshal(s)
+	return string(b)
+}
+
+func (s Session) String() string {
+	return s.Marshal()
+}
+
+// UnmarshalSession will unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+	sess := &Session{}
+	err := json.NewDecoder(strings.NewReader(data)).Decode(sess)
+	return sess, err
+}
diff --git a/vendor/github.com/markbates/goth/session.go b/vendor/github.com/markbates/goth/session.go
new file mode 100644
index 0000000000..2d40b50bb4
--- /dev/null
+++ b/vendor/github.com/markbates/goth/session.go
@@ -0,0 +1,21 @@
+package goth
+
+// Params is used to pass data to sessions for authorization. An existing
+// implementation, and the one most likely to be used, is `url.Values`.
+type Params interface {
+	Get(string) string
+}
+
+// Session needs to be implemented as part of the provider package.
+// It will be marshaled and persisted between requests to "tie"
+// the start and the end of the authorization process with a
+// 3rd party provider.
+type Session interface {
+	// GetAuthURL returns the URL for the authentication end-point for the provider.
+	GetAuthURL() (string, error)
+	// Marshal generates a string representation of the Session for storing between requests.
+	Marshal() string
+	// Authorize should validate the data from the provider and return an access token
+	// that can be stored for later access to the provider.
+	Authorize(Provider, Params) (string, error)
+}
diff --git a/vendor/github.com/markbates/goth/user.go b/vendor/github.com/markbates/goth/user.go
new file mode 100644
index 0000000000..1d6a419632
--- /dev/null
+++ b/vendor/github.com/markbates/goth/user.go
@@ -0,0 +1,30 @@
+package goth
+
+import (
+	"encoding/gob"
+	"time"
+)
+
+func init() {
+	gob.Register(User{})
+}
+
+// User contains the information common amongst most OAuth and OAuth2 providers.
+// All of the "raw" datafrom the provider can be found in the `RawData` field.
+type User struct {
+	RawData           map[string]interface{}
+	Provider          string
+	Email             string
+	Name              string
+	FirstName         string
+	LastName          string
+	NickName          string
+	Description       string
+	UserID            string
+	AvatarURL         string
+	Location          string
+	AccessToken       string
+	AccessTokenSecret string
+	RefreshToken      string
+	ExpiresAt         time.Time
+}
diff --git a/vendor/golang.org/x/oauth2/AUTHORS b/vendor/golang.org/x/oauth2/AUTHORS
new file mode 100644
index 0000000000..15167cd746
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTING.md b/vendor/golang.org/x/oauth2/CONTRIBUTING.md
new file mode 100644
index 0000000000..46aa2b12dd
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Contributing to Go
+
+Go is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+
+## Filing issues
+
+When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
+The gophers there will answer or ask you to file an issue if you've tripped over a bug.
+
+## Contributing code
+
+Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
+before sending patches.
+
+**We do not accept GitHub pull requests**
+(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
+
+Unless otherwise noted, the Go source files are distributed under
+the BSD-style license found in the LICENSE file.
+
diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTORS b/vendor/golang.org/x/oauth2/CONTRIBUTORS
new file mode 100644
index 0000000000..1c4577e968
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/oauth2/LICENSE b/vendor/golang.org/x/oauth2/LICENSE
new file mode 100644
index 0000000000..d02f24fd52
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The oauth2 Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/oauth2/README.md b/vendor/golang.org/x/oauth2/README.md
new file mode 100644
index 0000000000..1643c08ef8
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/README.md
@@ -0,0 +1,65 @@
+# OAuth2 for Go
+
+[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2)
+[![GoDoc](https://godoc.org/golang.org/x/oauth2?status.svg)](https://godoc.org/golang.org/x/oauth2)
+
+oauth2 package contains a client implementation for OAuth 2.0 spec.
+
+## Installation
+
+~~~~
+go get golang.org/x/oauth2
+~~~~
+
+See godoc for further documentation and examples.
+
+* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
+* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
+
+
+## App Engine
+
+In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor
+of the [`context.Context`](https://golang.org/x/net/context#Context) type from
+the `golang.org/x/net/context` package
+
+This means its no longer possible to use the "Classic App Engine"
+`appengine.Context` type with the `oauth2` package. (You're using
+Classic App Engine if you import the package `"appengine"`.)
+
+To work around this, you may use the new `"google.golang.org/appengine"`
+package. This package has almost the same API as the `"appengine"` package,
+but it can be fetched with `go get` and used on "Managed VMs" and well as
+Classic App Engine.
+
+See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
+for information on updating your app.
+
+If you don't want to update your entire app to use the new App Engine packages,
+you may use both sets of packages in parallel, using only the new packages
+with the `oauth2` package.
+
+	import (
+		"golang.org/x/net/context"
+		"golang.org/x/oauth2"
+		"golang.org/x/oauth2/google"
+		newappengine "google.golang.org/appengine"
+		newurlfetch "google.golang.org/appengine/urlfetch"
+
+		"appengine"
+	)
+
+	func handler(w http.ResponseWriter, r *http.Request) {
+		var c appengine.Context = appengine.NewContext(r)
+		c.Infof("Logging a message with the old package")
+
+		var ctx context.Context = newappengine.NewContext(r)
+		client := &http.Client{
+			Transport: &oauth2.Transport{
+				Source: google.AppEngineTokenSource(ctx, "scope"),
+				Base:   &newurlfetch.Transport{Context: ctx},
+			},
+		}
+		client.Get("...")
+	}
+
diff --git a/vendor/golang.org/x/oauth2/client_appengine.go b/vendor/golang.org/x/oauth2/client_appengine.go
new file mode 100644
index 0000000000..8962c49d1d
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/client_appengine.go
@@ -0,0 +1,25 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+// App Engine hooks.
+
+package oauth2
+
+import (
+	"net/http"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2/internal"
+	"google.golang.org/appengine/urlfetch"
+)
+
+func init() {
+	internal.RegisterContextClientFunc(contextClientAppEngine)
+}
+
+func contextClientAppEngine(ctx context.Context) (*http.Client, error) {
+	return urlfetch.Client(ctx), nil
+}
diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go
new file mode 100644
index 0000000000..fbe1028d64
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/oauth2.go
@@ -0,0 +1,76 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package internal contains support packages for oauth2 package.
+package internal
+
+import (
+	"bufio"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+	"fmt"
+	"io"
+	"strings"
+)
+
+// ParseKey converts the binary contents of a private key file
+// to an *rsa.PrivateKey. It detects whether the private key is in a
+// PEM container or not. If so, it extracts the the private key
+// from PEM container before conversion. It only supports PEM
+// containers with no passphrase.
+func ParseKey(key []byte) (*rsa.PrivateKey, error) {
+	block, _ := pem.Decode(key)
+	if block != nil {
+		key = block.Bytes
+	}
+	parsedKey, err := x509.ParsePKCS8PrivateKey(key)
+	if err != nil {
+		parsedKey, err = x509.ParsePKCS1PrivateKey(key)
+		if err != nil {
+			return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
+		}
+	}
+	parsed, ok := parsedKey.(*rsa.PrivateKey)
+	if !ok {
+		return nil, errors.New("private key is invalid")
+	}
+	return parsed, nil
+}
+
+func ParseINI(ini io.Reader) (map[string]map[string]string, error) {
+	result := map[string]map[string]string{
+		"": map[string]string{}, // root section
+	}
+	scanner := bufio.NewScanner(ini)
+	currentSection := ""
+	for scanner.Scan() {
+		line := strings.TrimSpace(scanner.Text())
+		if strings.HasPrefix(line, ";") {
+			// comment.
+			continue
+		}
+		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
+			currentSection = strings.TrimSpace(line[1 : len(line)-1])
+			result[currentSection] = map[string]string{}
+			continue
+		}
+		parts := strings.SplitN(line, "=", 2)
+		if len(parts) == 2 && parts[0] != "" {
+			result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
+		}
+	}
+	if err := scanner.Err(); err != nil {
+		return nil, fmt.Errorf("error scanning ini: %v", err)
+	}
+	return result, nil
+}
+
+func CondVal(v string) []string {
+	if v == "" {
+		return nil
+	}
+	return []string{v}
+}
diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go
new file mode 100644
index 0000000000..1c0ec76d04
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/token.go
@@ -0,0 +1,227 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package internal contains support packages for oauth2 package.
+package internal
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"mime"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"golang.org/x/net/context"
+)
+
+// Token represents the crendentials used to authorize
+// the requests to access protected resources on the OAuth 2.0
+// provider's backend.
+//
+// This type is a mirror of oauth2.Token and exists to break
+// an otherwise-circular dependency. Other internal packages
+// should convert this Token into an oauth2.Token before use.
+type Token struct {
+	// AccessToken is the token that authorizes and authenticates
+	// the requests.
+	AccessToken string
+
+	// TokenType is the type of token.
+	// The Type method returns either this or "Bearer", the default.
+	TokenType string
+
+	// RefreshToken is a token that's used by the application
+	// (as opposed to the user) to refresh the access token
+	// if it expires.
+	RefreshToken string
+
+	// Expiry is the optional expiration time of the access token.
+	//
+	// If zero, TokenSource implementations will reuse the same
+	// token forever and RefreshToken or equivalent
+	// mechanisms for that TokenSource will not be used.
+	Expiry time.Time
+
+	// Raw optionally contains extra metadata from the server
+	// when updating a token.
+	Raw interface{}
+}
+
+// tokenJSON is the struct representing the HTTP response from OAuth2
+// providers returning a token in JSON form.
+type tokenJSON struct {
+	AccessToken  string         `json:"access_token"`
+	TokenType    string         `json:"token_type"`
+	RefreshToken string         `json:"refresh_token"`
+	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
+	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
+}
+
+func (e *tokenJSON) expiry() (t time.Time) {
+	if v := e.ExpiresIn; v != 0 {
+		return time.Now().Add(time.Duration(v) * time.Second)
+	}
+	if v := e.Expires; v != 0 {
+		return time.Now().Add(time.Duration(v) * time.Second)
+	}
+	return
+}
+
+type expirationTime int32
+
+func (e *expirationTime) UnmarshalJSON(b []byte) error {
+	var n json.Number
+	err := json.Unmarshal(b, &n)
+	if err != nil {
+		return err
+	}
+	i, err := n.Int64()
+	if err != nil {
+		return err
+	}
+	*e = expirationTime(i)
+	return nil
+}
+
+var brokenAuthHeaderProviders = []string{
+	"https://accounts.google.com/",
+	"https://api.dropbox.com/",
+	"https://api.dropboxapi.com/",
+	"https://api.instagram.com/",
+	"https://api.netatmo.net/",
+	"https://api.odnoklassniki.ru/",
+	"https://api.pushbullet.com/",
+	"https://api.soundcloud.com/",
+	"https://api.twitch.tv/",
+	"https://app.box.com/",
+	"https://connect.stripe.com/",
+	"https://login.microsoftonline.com/",
+	"https://login.salesforce.com/",
+	"https://oauth.sandbox.trainingpeaks.com/",
+	"https://oauth.trainingpeaks.com/",
+	"https://oauth.vk.com/",
+	"https://openapi.baidu.com/",
+	"https://slack.com/",
+	"https://test-sandbox.auth.corp.google.com",
+	"https://test.salesforce.com/",
+	"https://user.gini.net/",
+	"https://www.douban.com/",
+	"https://www.googleapis.com/",
+	"https://www.linkedin.com/",
+	"https://www.strava.com/oauth/",
+	"https://www.wunderlist.com/oauth/",
+	"https://api.patreon.com/",
+	"https://sandbox.codeswholesale.com/oauth/token",
+	"https://api.codeswholesale.com/oauth/token",
+}
+
+func RegisterBrokenAuthHeaderProvider(tokenURL string) {
+	brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
+}
+
+// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
+// implements the OAuth2 spec correctly
+// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
+// In summary:
+// - Reddit only accepts client secret in the Authorization header
+// - Dropbox accepts either it in URL param or Auth header, but not both.
+// - Google only accepts URL param (not spec compliant?), not Auth header
+// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
+func providerAuthHeaderWorks(tokenURL string) bool {
+	for _, s := range brokenAuthHeaderProviders {
+		if strings.HasPrefix(tokenURL, s) {
+			// Some sites fail to implement the OAuth2 spec fully.
+			return false
+		}
+	}
+
+	// Assume the provider implements the spec properly
+	// otherwise. We can add more exceptions as they're
+	// discovered. We will _not_ be adding configurable hooks
+	// to this package to let users select server bugs.
+	return true
+}
+
+func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
+	hc, err := ContextClient(ctx)
+	if err != nil {
+		return nil, err
+	}
+	v.Set("client_id", clientID)
+	bustedAuth := !providerAuthHeaderWorks(tokenURL)
+	if bustedAuth && clientSecret != "" {
+		v.Set("client_secret", clientSecret)
+	}
+	req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	if !bustedAuth {
+		req.SetBasicAuth(clientID, clientSecret)
+	}
+	r, err := hc.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer r.Body.Close()
+	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
+	if err != nil {
+		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+	}
+	if code := r.StatusCode; code < 200 || code > 299 {
+		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
+	}
+
+	var token *Token
+	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
+	switch content {
+	case "application/x-www-form-urlencoded", "text/plain":
+		vals, err := url.ParseQuery(string(body))
+		if err != nil {
+			return nil, err
+		}
+		token = &Token{
+			AccessToken:  vals.Get("access_token"),
+			TokenType:    vals.Get("token_type"),
+			RefreshToken: vals.Get("refresh_token"),
+			Raw:          vals,
+		}
+		e := vals.Get("expires_in")
+		if e == "" {
+			// TODO(jbd): Facebook's OAuth2 implementation is broken and
+			// returns expires_in field in expires. Remove the fallback to expires,
+			// when Facebook fixes their implementation.
+			e = vals.Get("expires")
+		}
+		expires, _ := strconv.Atoi(e)
+		if expires != 0 {
+			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
+		}
+	default:
+		var tj tokenJSON
+		if err = json.Unmarshal(body, &tj); err != nil {
+			return nil, err
+		}
+		token = &Token{
+			AccessToken:  tj.AccessToken,
+			TokenType:    tj.TokenType,
+			RefreshToken: tj.RefreshToken,
+			Expiry:       tj.expiry(),
+			Raw:          make(map[string]interface{}),
+		}
+		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
+	}
+	// Don't overwrite `RefreshToken` with an empty value
+	// if this was a token refreshing request.
+	if token.RefreshToken == "" {
+		token.RefreshToken = v.Get("refresh_token")
+	}
+	return token, nil
+}
diff --git a/vendor/golang.org/x/oauth2/internal/transport.go b/vendor/golang.org/x/oauth2/internal/transport.go
new file mode 100644
index 0000000000..f1f173e345
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/transport.go
@@ -0,0 +1,69 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package internal contains support packages for oauth2 package.
+package internal
+
+import (
+	"net/http"
+
+	"golang.org/x/net/context"
+)
+
+// HTTPClient is the context key to use with golang.org/x/net/context's
+// WithValue function to associate an *http.Client value with a context.
+var HTTPClient ContextKey
+
+// ContextKey is just an empty struct. It exists so HTTPClient can be
+// an immutable public variable with a unique type. It's immutable
+// because nobody else can create a ContextKey, being unexported.
+type ContextKey struct{}
+
+// ContextClientFunc is a func which tries to return an *http.Client
+// given a Context value. If it returns an error, the search stops
+// with that error.  If it returns (nil, nil), the search continues
+// down the list of registered funcs.
+type ContextClientFunc func(context.Context) (*http.Client, error)
+
+var contextClientFuncs []ContextClientFunc
+
+func RegisterContextClientFunc(fn ContextClientFunc) {
+	contextClientFuncs = append(contextClientFuncs, fn)
+}
+
+func ContextClient(ctx context.Context) (*http.Client, error) {
+	if ctx != nil {
+		if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
+			return hc, nil
+		}
+	}
+	for _, fn := range contextClientFuncs {
+		c, err := fn(ctx)
+		if err != nil {
+			return nil, err
+		}
+		if c != nil {
+			return c, nil
+		}
+	}
+	return http.DefaultClient, nil
+}
+
+func ContextTransport(ctx context.Context) http.RoundTripper {
+	hc, err := ContextClient(ctx)
+	// This is a rare error case (somebody using nil on App Engine).
+	if err != nil {
+		return ErrorTransport{err}
+	}
+	return hc.Transport
+}
+
+// ErrorTransport returns the specified error on RoundTrip.
+// This RoundTripper should be used in rare error cases where
+// error handling can be postponed to response handling time.
+type ErrorTransport struct{ Err error }
+
+func (t ErrorTransport) RoundTrip(*http.Request) (*http.Response, error) {
+	return nil, t.Err
+}
diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go
new file mode 100644
index 0000000000..7b06bfe1ef
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/oauth2.go
@@ -0,0 +1,341 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package oauth2 provides support for making
+// OAuth2 authorized and authenticated HTTP requests.
+// It can additionally grant authorization with Bearer JWT.
+package oauth2 // import "golang.org/x/oauth2"
+
+import (
+	"bytes"
+	"errors"
+	"net/http"
+	"net/url"
+	"strings"
+	"sync"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2/internal"
+)
+
+// NoContext is the default context you should supply if not using
+// your own context.Context (see https://golang.org/x/net/context).
+//
+// Deprecated: Use context.Background() or context.TODO() instead.
+var NoContext = context.TODO()
+
+// RegisterBrokenAuthHeaderProvider registers an OAuth2 server
+// identified by the tokenURL prefix as an OAuth2 implementation
+// which doesn't support the HTTP Basic authentication
+// scheme to authenticate with the authorization server.
+// Once a server is registered, credentials (client_id and client_secret)
+// will be passed as query parameters rather than being present
+// in the Authorization header.
+// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
+func RegisterBrokenAuthHeaderProvider(tokenURL string) {
+	internal.RegisterBrokenAuthHeaderProvider(tokenURL)
+}
+
+// Config describes a typical 3-legged OAuth2 flow, with both the
+// client application information and the server's endpoint URLs.
+// For the client credentials 2-legged OAuth2 flow, see the clientcredentials
+// package (https://golang.org/x/oauth2/clientcredentials).
+type Config struct {
+	// ClientID is the application's ID.
+	ClientID string
+
+	// ClientSecret is the application's secret.
+	ClientSecret string
+
+	// Endpoint contains the resource server's token endpoint
+	// URLs. These are constants specific to each server and are
+	// often available via site-specific packages, such as
+	// google.Endpoint or github.Endpoint.
+	Endpoint Endpoint
+
+	// RedirectURL is the URL to redirect users going through
+	// the OAuth flow, after the resource owner's URLs.
+	RedirectURL string
+
+	// Scope specifies optional requested permissions.
+	Scopes []string
+}
+
+// A TokenSource is anything that can return a token.
+type TokenSource interface {
+	// Token returns a token or an error.
+	// Token must be safe for concurrent use by multiple goroutines.
+	// The returned Token must not be modified.
+	Token() (*Token, error)
+}
+
+// Endpoint contains the OAuth 2.0 provider's authorization and token
+// endpoint URLs.
+type Endpoint struct {
+	AuthURL  string
+	TokenURL string
+}
+
+var (
+	// AccessTypeOnline and AccessTypeOffline are options passed
+	// to the Options.AuthCodeURL method. They modify the
+	// "access_type" field that gets sent in the URL returned by
+	// AuthCodeURL.
+	//
+	// Online is the default if neither is specified. If your
+	// application needs to refresh access tokens when the user
+	// is not present at the browser, then use offline. This will
+	// result in your application obtaining a refresh token the
+	// first time your application exchanges an authorization
+	// code for a user.
+	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online")
+	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")
+
+	// ApprovalForce forces the users to view the consent dialog
+	// and confirm the permissions request at the URL returned
+	// from AuthCodeURL, even if they've already done so.
+	ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force")
+)
+
+// An AuthCodeOption is passed to Config.AuthCodeURL.
+type AuthCodeOption interface {
+	setValue(url.Values)
+}
+
+type setParam struct{ k, v string }
+
+func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
+
+// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
+// to a provider's authorization endpoint.
+func SetAuthURLParam(key, value string) AuthCodeOption {
+	return setParam{key, value}
+}
+
+// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
+// that asks for permissions for the required scopes explicitly.
+//
+// State is a token to protect the user from CSRF attacks. You must
+// always provide a non-zero string and validate that it matches the
+// the state query parameter on your redirect callback.
+// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
+//
+// Opts may include AccessTypeOnline or AccessTypeOffline, as well
+// as ApprovalForce.
+func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
+	var buf bytes.Buffer
+	buf.WriteString(c.Endpoint.AuthURL)
+	v := url.Values{
+		"response_type": {"code"},
+		"client_id":     {c.ClientID},
+		"redirect_uri":  internal.CondVal(c.RedirectURL),
+		"scope":         internal.CondVal(strings.Join(c.Scopes, " ")),
+		"state":         internal.CondVal(state),
+	}
+	for _, opt := range opts {
+		opt.setValue(v)
+	}
+	if strings.Contains(c.Endpoint.AuthURL, "?") {
+		buf.WriteByte('&')
+	} else {
+		buf.WriteByte('?')
+	}
+	buf.WriteString(v.Encode())
+	return buf.String()
+}
+
+// PasswordCredentialsToken converts a resource owner username and password
+// pair into a token.
+//
+// Per the RFC, this grant type should only be used "when there is a high
+// degree of trust between the resource owner and the client (e.g., the client
+// is part of the device operating system or a highly privileged application),
+// and when other authorization grant types are not available."
+// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
+//
+// The HTTP client to use is derived from the context.
+// If nil, http.DefaultClient is used.
+func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
+	return retrieveToken(ctx, c, url.Values{
+		"grant_type": {"password"},
+		"username":   {username},
+		"password":   {password},
+		"scope":      internal.CondVal(strings.Join(c.Scopes, " ")),
+	})
+}
+
+// Exchange converts an authorization code into a token.
+//
+// It is used after a resource provider redirects the user back
+// to the Redirect URI (the URL obtained from AuthCodeURL).
+//
+// The HTTP client to use is derived from the context.
+// If a client is not provided via the context, http.DefaultClient is used.
+//
+// The code will be in the *http.Request.FormValue("code"). Before
+// calling Exchange, be sure to validate FormValue("state").
+func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
+	return retrieveToken(ctx, c, url.Values{
+		"grant_type":   {"authorization_code"},
+		"code":         {code},
+		"redirect_uri": internal.CondVal(c.RedirectURL),
+		"scope":        internal.CondVal(strings.Join(c.Scopes, " ")),
+	})
+}
+
+// Client returns an HTTP client using the provided token.
+// The token will auto-refresh as necessary. The underlying
+// HTTP transport will be obtained using the provided context.
+// The returned client and its Transport should not be modified.
+func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
+	return NewClient(ctx, c.TokenSource(ctx, t))
+}
+
+// TokenSource returns a TokenSource that returns t until t expires,
+// automatically refreshing it as necessary using the provided context.
+//
+// Most users will use Config.Client instead.
+func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
+	tkr := &tokenRefresher{
+		ctx:  ctx,
+		conf: c,
+	}
+	if t != nil {
+		tkr.refreshToken = t.RefreshToken
+	}
+	return &reuseTokenSource{
+		t:   t,
+		new: tkr,
+	}
+}
+
+// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
+// HTTP requests to renew a token using a RefreshToken.
+type tokenRefresher struct {
+	ctx          context.Context // used to get HTTP requests
+	conf         *Config
+	refreshToken string
+}
+
+// WARNING: Token is not safe for concurrent access, as it
+// updates the tokenRefresher's refreshToken field.
+// Within this package, it is used by reuseTokenSource which
+// synchronizes calls to this method with its own mutex.
+func (tf *tokenRefresher) Token() (*Token, error) {
+	if tf.refreshToken == "" {
+		return nil, errors.New("oauth2: token expired and refresh token is not set")
+	}
+
+	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
+		"grant_type":    {"refresh_token"},
+		"refresh_token": {tf.refreshToken},
+	})
+
+	if err != nil {
+		return nil, err
+	}
+	if tf.refreshToken != tk.RefreshToken {
+		tf.refreshToken = tk.RefreshToken
+	}
+	return tk, err
+}
+
+// reuseTokenSource is a TokenSource that holds a single token in memory
+// and validates its expiry before each call to retrieve it with
+// Token. If it's expired, it will be auto-refreshed using the
+// new TokenSource.
+type reuseTokenSource struct {
+	new TokenSource // called when t is expired.
+
+	mu sync.Mutex // guards t
+	t  *Token
+}
+
+// Token returns the current token if it's still valid, else will
+// refresh the current token (using r.Context for HTTP client
+// information) and return the new one.
+func (s *reuseTokenSource) Token() (*Token, error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.t.Valid() {
+		return s.t, nil
+	}
+	t, err := s.new.Token()
+	if err != nil {
+		return nil, err
+	}
+	s.t = t
+	return t, nil
+}
+
+// StaticTokenSource returns a TokenSource that always returns the same token.
+// Because the provided token t is never refreshed, StaticTokenSource is only
+// useful for tokens that never expire.
+func StaticTokenSource(t *Token) TokenSource {
+	return staticTokenSource{t}
+}
+
+// staticTokenSource is a TokenSource that always returns the same Token.
+type staticTokenSource struct {
+	t *Token
+}
+
+func (s staticTokenSource) Token() (*Token, error) {
+	return s.t, nil
+}
+
+// HTTPClient is the context key to use with golang.org/x/net/context's
+// WithValue function to associate an *http.Client value with a context.
+var HTTPClient internal.ContextKey
+
+// NewClient creates an *http.Client from a Context and TokenSource.
+// The returned client is not valid beyond the lifetime of the context.
+//
+// As a special case, if src is nil, a non-OAuth2 client is returned
+// using the provided context. This exists to support related OAuth2
+// packages.
+func NewClient(ctx context.Context, src TokenSource) *http.Client {
+	if src == nil {
+		c, err := internal.ContextClient(ctx)
+		if err != nil {
+			return &http.Client{Transport: internal.ErrorTransport{Err: err}}
+		}
+		return c
+	}
+	return &http.Client{
+		Transport: &Transport{
+			Base:   internal.ContextTransport(ctx),
+			Source: ReuseTokenSource(nil, src),
+		},
+	}
+}
+
+// ReuseTokenSource returns a TokenSource which repeatedly returns the
+// same token as long as it's valid, starting with t.
+// When its cached token is invalid, a new token is obtained from src.
+//
+// ReuseTokenSource is typically used to reuse tokens from a cache
+// (such as a file on disk) between runs of a program, rather than
+// obtaining new tokens unnecessarily.
+//
+// The initial token t may be nil, in which case the TokenSource is
+// wrapped in a caching version if it isn't one already. This also
+// means it's always safe to wrap ReuseTokenSource around any other
+// TokenSource without adverse effects.
+func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
+	// Don't wrap a reuseTokenSource in itself. That would work,
+	// but cause an unnecessary number of mutex operations.
+	// Just build the equivalent one.
+	if rt, ok := src.(*reuseTokenSource); ok {
+		if t == nil {
+			// Just use it directly.
+			return rt
+		}
+		src = rt.new
+	}
+	return &reuseTokenSource{
+		t:   t,
+		new: src,
+	}
+}
diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go
new file mode 100644
index 0000000000..7a3167f15b
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/token.go
@@ -0,0 +1,158 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2/internal"
+)
+
+// expiryDelta determines how earlier a token should be considered
+// expired than its actual expiration time. It is used to avoid late
+// expirations due to client-server time mismatches.
+const expiryDelta = 10 * time.Second
+
+// Token represents the crendentials used to authorize
+// the requests to access protected resources on the OAuth 2.0
+// provider's backend.
+//
+// Most users of this package should not access fields of Token
+// directly. They're exported mostly for use by related packages
+// implementing derivative OAuth2 flows.
+type Token struct {
+	// AccessToken is the token that authorizes and authenticates
+	// the requests.
+	AccessToken string `json:"access_token"`
+
+	// TokenType is the type of token.
+	// The Type method returns either this or "Bearer", the default.
+	TokenType string `json:"token_type,omitempty"`
+
+	// RefreshToken is a token that's used by the application
+	// (as opposed to the user) to refresh the access token
+	// if it expires.
+	RefreshToken string `json:"refresh_token,omitempty"`
+
+	// Expiry is the optional expiration time of the access token.
+	//
+	// If zero, TokenSource implementations will reuse the same
+	// token forever and RefreshToken or equivalent
+	// mechanisms for that TokenSource will not be used.
+	Expiry time.Time `json:"expiry,omitempty"`
+
+	// raw optionally contains extra metadata from the server
+	// when updating a token.
+	raw interface{}
+}
+
+// Type returns t.TokenType if non-empty, else "Bearer".
+func (t *Token) Type() string {
+	if strings.EqualFold(t.TokenType, "bearer") {
+		return "Bearer"
+	}
+	if strings.EqualFold(t.TokenType, "mac") {
+		return "MAC"
+	}
+	if strings.EqualFold(t.TokenType, "basic") {
+		return "Basic"
+	}
+	if t.TokenType != "" {
+		return t.TokenType
+	}
+	return "Bearer"
+}
+
+// SetAuthHeader sets the Authorization header to r using the access
+// token in t.
+//
+// This method is unnecessary when using Transport or an HTTP Client
+// returned by this package.
+func (t *Token) SetAuthHeader(r *http.Request) {
+	r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
+}
+
+// WithExtra returns a new Token that's a clone of t, but using the
+// provided raw extra map. This is only intended for use by packages
+// implementing derivative OAuth2 flows.
+func (t *Token) WithExtra(extra interface{}) *Token {
+	t2 := new(Token)
+	*t2 = *t
+	t2.raw = extra
+	return t2
+}
+
+// Extra returns an extra field.
+// Extra fields are key-value pairs returned by the server as a
+// part of the token retrieval response.
+func (t *Token) Extra(key string) interface{} {
+	if raw, ok := t.raw.(map[string]interface{}); ok {
+		return raw[key]
+	}
+
+	vals, ok := t.raw.(url.Values)
+	if !ok {
+		return nil
+	}
+
+	v := vals.Get(key)
+	switch s := strings.TrimSpace(v); strings.Count(s, ".") {
+	case 0: // Contains no "."; try to parse as int
+		if i, err := strconv.ParseInt(s, 10, 64); err == nil {
+			return i
+		}
+	case 1: // Contains a single "."; try to parse as float
+		if f, err := strconv.ParseFloat(s, 64); err == nil {
+			return f
+		}
+	}
+
+	return v
+}
+
+// expired reports whether the token is expired.
+// t must be non-nil.
+func (t *Token) expired() bool {
+	if t.Expiry.IsZero() {
+		return false
+	}
+	return t.Expiry.Add(-expiryDelta).Before(time.Now())
+}
+
+// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
+func (t *Token) Valid() bool {
+	return t != nil && t.AccessToken != "" && !t.expired()
+}
+
+// tokenFromInternal maps an *internal.Token struct into
+// a *Token struct.
+func tokenFromInternal(t *internal.Token) *Token {
+	if t == nil {
+		return nil
+	}
+	return &Token{
+		AccessToken:  t.AccessToken,
+		TokenType:    t.TokenType,
+		RefreshToken: t.RefreshToken,
+		Expiry:       t.Expiry,
+		raw:          t.Raw,
+	}
+}
+
+// retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
+// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
+// with an error..
+func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
+	tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
+	if err != nil {
+		return nil, err
+	}
+	return tokenFromInternal(tk), nil
+}
diff --git a/vendor/golang.org/x/oauth2/transport.go b/vendor/golang.org/x/oauth2/transport.go
new file mode 100644
index 0000000000..92ac7e2531
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/transport.go
@@ -0,0 +1,132 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+	"errors"
+	"io"
+	"net/http"
+	"sync"
+)
+
+// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
+// wrapping a base RoundTripper and adding an Authorization header
+// with a token from the supplied Sources.
+//
+// Transport is a low-level mechanism. Most code will use the
+// higher-level Config.Client method instead.
+type Transport struct {
+	// Source supplies the token to add to outgoing requests'
+	// Authorization headers.
+	Source TokenSource
+
+	// Base is the base RoundTripper used to make HTTP requests.
+	// If nil, http.DefaultTransport is used.
+	Base http.RoundTripper
+
+	mu     sync.Mutex                      // guards modReq
+	modReq map[*http.Request]*http.Request // original -> modified
+}
+
+// RoundTrip authorizes and authenticates the request with an
+// access token. If no token exists or token is expired,
+// tries to refresh/fetch a new token.
+func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
+	if t.Source == nil {
+		return nil, errors.New("oauth2: Transport's Source is nil")
+	}
+	token, err := t.Source.Token()
+	if err != nil {
+		return nil, err
+	}
+
+	req2 := cloneRequest(req) // per RoundTripper contract
+	token.SetAuthHeader(req2)
+	t.setModReq(req, req2)
+	res, err := t.base().RoundTrip(req2)
+	if err != nil {
+		t.setModReq(req, nil)
+		return nil, err
+	}
+	res.Body = &onEOFReader{
+		rc: res.Body,
+		fn: func() { t.setModReq(req, nil) },
+	}
+	return res, nil
+}
+
+// CancelRequest cancels an in-flight request by closing its connection.
+func (t *Transport) CancelRequest(req *http.Request) {
+	type canceler interface {
+		CancelRequest(*http.Request)
+	}
+	if cr, ok := t.base().(canceler); ok {
+		t.mu.Lock()
+		modReq := t.modReq[req]
+		delete(t.modReq, req)
+		t.mu.Unlock()
+		cr.CancelRequest(modReq)
+	}
+}
+
+func (t *Transport) base() http.RoundTripper {
+	if t.Base != nil {
+		return t.Base
+	}
+	return http.DefaultTransport
+}
+
+func (t *Transport) setModReq(orig, mod *http.Request) {
+	t.mu.Lock()
+	defer t.mu.Unlock()
+	if t.modReq == nil {
+		t.modReq = make(map[*http.Request]*http.Request)
+	}
+	if mod == nil {
+		delete(t.modReq, orig)
+	} else {
+		t.modReq[orig] = mod
+	}
+}
+
+// cloneRequest returns a clone of the provided *http.Request.
+// The clone is a shallow copy of the struct and its Header map.
+func cloneRequest(r *http.Request) *http.Request {
+	// shallow copy of the struct
+	r2 := new(http.Request)
+	*r2 = *r
+	// deep copy of the Header
+	r2.Header = make(http.Header, len(r.Header))
+	for k, s := range r.Header {
+		r2.Header[k] = append([]string(nil), s...)
+	}
+	return r2
+}
+
+type onEOFReader struct {
+	rc io.ReadCloser
+	fn func()
+}
+
+func (r *onEOFReader) Read(p []byte) (n int, err error) {
+	n, err = r.rc.Read(p)
+	if err == io.EOF {
+		r.runFunc()
+	}
+	return
+}
+
+func (r *onEOFReader) Close() error {
+	err := r.rc.Close()
+	r.runFunc()
+	return err
+}
+
+func (r *onEOFReader) runFunc() {
+	if fn := r.fn; fn != nil {
+		fn()
+		r.fn = nil
+	}
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 12e62b02dd..49dcec8c45 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -550,6 +550,24 @@
 			"revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b",
 			"revisionTime": "2016-11-03T02:43:54Z"
 		},
+		{
+			"checksumSHA1": "O3KUfEXQPfdQ+tCMpP2RAIRJJqY=",
+			"path": "github.com/markbates/goth",
+			"revision": "450379d2950a65070b23cc93c53436553add4484",
+			"revisionTime": "2017-02-06T19:46:32Z"
+		},
+		{
+			"checksumSHA1": "MkFKwLV3icyUo4oP0BgEs+7+R1Y=",
+			"path": "github.com/markbates/goth/gothic",
+			"revision": "450379d2950a65070b23cc93c53436553add4484",
+			"revisionTime": "2017-02-06T19:46:32Z"
+		},
+		{
+			"checksumSHA1": "ZFqznX3/ZW65I4QeepiHQdE69nA=",
+			"path": "github.com/markbates/goth/providers/github",
+			"revision": "450379d2950a65070b23cc93c53436553add4484",
+			"revisionTime": "2017-02-06T19:46:32Z"
+		},
 		{
 			"checksumSHA1": "9FJUwn3EIgASVki+p8IHgWVC5vQ=",
 			"path": "github.com/mattn/go-sqlite3",