diff --git a/Makefile b/Makefile
index e4f51d9..d1f338b 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ format:
 	go fix ./...
 
 test:
-	go test -v $(GONODE_PLUGINS) ./test/api ./core ./core/config ./commands/server
+	go test $(GONODE_PLUGINS) ./test/api ./core ./core/config ./commands/server
 	go vet ./...
 	#cd explorer && npm test
 
diff --git a/README.md b/README.md
index 797d7b1..27f6637 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,9 @@ A prototype to store dynamic node inside a PostgreSQL database with the JSONb st
 
 Documentation
 -------------
-
- - [Contributing](docs/contributing.md)
- - [Node](docs/node.md)
- - [Vault](docs/vault.md)
\ No newline at end of file
+ 
+ * [Node](docs/node.md)
+ * [Plugins](docs/plugins)
+    * [Vault](docs/plugins/vault.md)
+    * [Guard](docs/plugins/guard.md)
+ * [Contributing](docs/contributing.md)
\ No newline at end of file
diff --git a/commands/server/cli.go b/commands/server/cli.go
index f8992eb..29e81c4 100644
--- a/commands/server/cli.go
+++ b/commands/server/cli.go
@@ -14,6 +14,7 @@ import (
 
 	"github.com/rande/gonode/core/config"
 	"github.com/rande/gonode/plugins/api"
+	"github.com/rande/gonode/plugins/guard"
 	"github.com/rande/gonode/plugins/setup"
 	"github.com/zenazn/goji/bind"
 	"github.com/zenazn/goji/graceful"
@@ -54,9 +55,11 @@ func (c *ServerCommand) Run(args []string) int {
 	l := goapp.NewLifecycle()
 
 	ConfigureServer(l, conf)
+
 	// add plugins
 	setup.ConfigureServer(l, conf)
 	api.ConfigureServer(l, conf)
+	guard.ConfigureServer(l, conf)
 
 	l.Run(func(app *goapp.App, state *goapp.GoroutineState) error {
 		mux := app.Get("goji.mux").(*web.Mux)
diff --git a/core/config/config_server.go b/core/config/config_server.go
index 8922629..604b47c 100644
--- a/core/config/config_server.go
+++ b/core/config/config_server.go
@@ -5,8 +5,17 @@
 
 package config
 
-type ServerAuth struct {
+type ServerGuard struct {
 	Key string `toml:"key"`
+	Jwt struct {
+		Validity int64 `toml:"validity"`
+		Login    struct {
+			Path string `toml:"path"`
+		} `toml:"login"`
+		Token struct {
+			Path string `toml:"path"`
+		} `toml:"token"`
+	} `toml:"jwt"`
 }
 
 type ServerDatabase struct {
@@ -33,7 +42,7 @@ type ServerConfig struct {
 	Filesystem ServerFilesystem           `toml:"filesystem"`
 	Test       bool                       `toml:"test"`
 	Bind       string                     `toml:"bind"`
-	Auth       ServerAuth                 `toml:"auth"`
+	Guard      ServerGuard                `toml:"guard"`
 }
 
 func NewServerConfig() *ServerConfig {
diff --git a/core/config/config_test.go b/core/config/config_test.go
index 557e03e..bf5e37d 100644
--- a/core/config/config_test.go
+++ b/core/config/config_test.go
@@ -6,6 +6,8 @@
 package config
 
 import (
+	"bytes"
+	"github.com/BurntSushi/toml"
 	"github.com/stretchr/testify/assert"
 	"os"
 	"testing"
@@ -33,4 +35,15 @@ func Test_Server_LoadConfiguration(t *testing.T) {
 	assert.Equal(t, config.Databases["master"].Prefix, "test")
 	assert.Equal(t, config.Filesystem.Type, "") // not used for now
 	assert.Equal(t, config.Filesystem.Path, "/tmp/gnode")
+
+	assert.Equal(t, config.Guard.Jwt.Login.Path, "/login")
+	assert.Equal(t, config.Guard.Jwt.Token.Path, `^\/nodes\/(.*)$`)
+
+	config.Guard.Jwt.Login.Path = `^\/nodes\/(.*)$`
+
+	w := bytes.NewBufferString("")
+	e := toml.NewEncoder(w)
+
+	e.Encode(config)
+
 }
diff --git a/docs/plugins/guard.md b/docs/plugins/guard.md
new file mode 100644
index 0000000..69321c6
--- /dev/null
+++ b/docs/plugins/guard.md
@@ -0,0 +1,91 @@
+Guard
+=====
+
+Introduction
+------------
+
+Guard plugin handle request authentification. The plugins comes with a dedicated middleware and request authenticators.
+For now there is only 2 authenticators implemented:
+ - ``JwtLoginGuardAuthenticator``: create a valid Json Web Token from the posted ``username`` and ``password``.
+ - ``JwtTokenGuardAuthenticator``: validate a Json Web Token. 
+
+Configuration
+-------------
+
+
+    ```toml
+    [guard]
+    key = "ZeSecretKey0oo"
+    
+        [guard.jwt]
+            [guard.jwt.login]
+            path = "/login"
+    
+            [guard.jwt.token]
+            path = "^\\/nodes\\/(.*)$"
+
+
+- ``key`` is private and it is used to sign the JWT with a symetric algorythm.
+- ``guard.jwt.login.path`` is used to configure the login entry point, ie where the ``JwtLoginGuardAuthenticator`` will accept the request.
+- ``guard.jwt.token.path`` is used to configure paths requiring to have authentification handled by the ``JwtTokenGuardAuthenticator`` service.
+
+
+Authenticators
+--------------
+
+### JwtLoginGuardAuthenticator
+
+
+The service will use the ``core.user`` node type to find the user by her/his username. The query looks like: ``type = 'core.user' AND data->>'username' = ?``
+
+The authentification request should be a POST
+
+```HTTP
+POST /login HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+
+username=admin&password=secret
+```
+
+If the response is valid, the response will be:
+
+```HTTP
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+    "status":  "OK",
+    "message": "Request is authenticated",
+    "token":   "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NTA0Nzg1NzQsInJscyI6bnVsbCwidXNyIjoicmFuZGUifQ.E_BMRg2UWO7jVw1CGgn7WhhwbATCHjYYcausZZ7LSZA",
+}
+
+```
+
+If the response is not valid, the response will be
+
+```HTTP
+HTTP/1.1 403 Forbidden
+Content-Type: application/json
+
+{
+    "status":  "KO",
+    "message": "Unable to authenticate request"
+}
+```
+   
+### JwtTokenGuardAuthenticator
+
+The service will use the ``core.user`` node type to find the user by her/his username. The query looks like: ``type = 'core.user' AND data->>'username' = ?``
+
+The authentification request should be on any http method, either using the ``Authorization`` header or the ``access_token`` parameter.
+
+```HTTP
+GET /nodes HTTP/1.1
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NTA0Nzg1NzQsInJscyI6bnVsbCwidXNyIjoicmFuZGUifQ.E_BMRg2UWO7jVw1CGgn7WhhwbATCHjYYcausZZ7LSZA
+```
+
+or
+
+```HTTP
+GET /nodes?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NTA0Nzg1NzQsInJscyI6bnVsbCwidXNyIjoicmFuZGUifQ.E_BMRg2UWO7jVw1CGgn7WhhwbATCHjYYcausZZ7LSZA HTTP/1.1
+```
diff --git a/docs/vault.md b/docs/plugins/vault.md
similarity index 100%
rename from docs/vault.md
rename to docs/plugins/vault.md
diff --git a/plugins/api/api_http.go b/plugins/api/api_http.go
index e8d537f..a90ccce 100644
--- a/plugins/api/api_http.go
+++ b/plugins/api/api_http.go
@@ -186,7 +186,7 @@ func ConfigureServer(l *goapp.Lifecycle, conf *config.ServerConfig) {
 				// Set some claims
 				token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
 				// Sign and get the complete encoded token as a string
-				tokenString, err := token.SignedString([]byte(conf.Auth.Key))
+				tokenString, err := token.SignedString([]byte(conf.Guard.Key))
 
 				if err != nil {
 					helper.SendWithHttpCode(res, http.StatusInternalServerError, "Unable to sign the token")
diff --git a/plugins/guard/guard.go b/plugins/guard/guard.go
new file mode 100644
index 0000000..24bdf57
--- /dev/null
+++ b/plugins/guard/guard.go
@@ -0,0 +1,96 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"errors"
+	"net/http"
+)
+
+var (
+	InvalidCredentialsFormat        = errors.New("Invalid credentials format")
+	InvalidCredentials              = errors.New("Invalid credentials")
+	UnableRetrieveUser              = errors.New("Unable to retrieve the user")
+	CredentialMismatch              = errors.New("Credential mismatch")
+	AuthenticatedTokenCreationError = errors.New("Unable to create authentication token")
+)
+
+// Bare interface with the default requirement to check username and password
+type GuardUser interface {
+	GetUsername() string
+	GetPassword() string
+	GetRoles() []string
+}
+
+type DefaultGuardUser struct {
+	Username string
+	Password string
+	Roles    []string
+}
+
+func (u *DefaultGuardUser) GetUsername() string {
+	return u.Username
+}
+
+func (u *DefaultGuardUser) GetPassword() string {
+	return u.Password
+}
+
+func (u *DefaultGuardUser) GetRoles() []string {
+	return u.Roles
+}
+
+// Bare interface to used inside a request lifecycle
+type GuardToken interface {
+	// return the current username for the current token
+	GetUsername() string
+
+	// return the related roles linked to the current token
+	GetRoles() []string
+}
+
+// Default implementation to the GuardToken
+type DefaultGuardToken struct {
+	Username string
+	Roles    []string
+}
+
+func (t *DefaultGuardToken) GetUsername() string {
+	return t.Username
+}
+
+func (t *DefaultGuardToken) GetRoles() []string {
+	return t.Roles
+}
+
+type GuardAuthenticator interface {
+	// This method is call on each request.
+	// If the method return nil as interface{} value, it means the authenticator
+	// cannot handle the request
+	getCredentials(req *http.Request) (interface{}, error)
+
+	// Return the user from the credentials
+	getUser(credentials interface{}) (GuardUser, error)
+
+	// Check if the provided credentials are valid for the current user
+	checkCredentials(credentials interface{}, user GuardUser) error
+
+	// Return a security token related to the user
+	createAuthenticatedToken(u GuardUser) (GuardToken, error)
+
+	// Action when the authentication fail.
+	// On a default form login, it can be used to redirect the user to login page
+	// return true if the workflows must be stopped (ie, the authenticator was written
+	// bytes on the response. false if not.
+	onAuthenticationFailure(req *http.Request, res http.ResponseWriter, err error) bool
+
+	// Action when the authentication success
+	// On a default form login, it can be used to redirect the user to protected page
+	// or the homepage
+	// return true if the workflows must be stopped (ie, the authenticator was written
+	// bytes on the response. false if not.
+	onAuthenticationSuccess(req *http.Request, res http.ResponseWriter, token GuardToken) bool
+}
diff --git a/plugins/guard/guard_app.go b/plugins/guard/guard_app.go
new file mode 100644
index 0000000..79e426e
--- /dev/null
+++ b/plugins/guard/guard_app.go
@@ -0,0 +1,42 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"github.com/rande/goapp"
+	"github.com/rande/gonode/core"
+	"github.com/rande/gonode/core/config"
+	"github.com/zenazn/goji/web"
+	"regexp"
+)
+
+func ConfigureServer(l *goapp.Lifecycle, conf *config.ServerConfig) {
+
+	l.Prepare(func(app *goapp.App) error {
+		mux := app.Get("goji.mux").(*web.Mux)
+		conf := app.Get("gonode.configuration").(*config.ServerConfig)
+		manager := app.Get("gonode.manager").(*core.PgNodeManager)
+
+		auths := []GuardAuthenticator{
+			&JwtTokenGuardAuthenticator{
+				Path:        regexp.MustCompile(conf.Guard.Jwt.Token.Path),
+				Key:         []byte(conf.Guard.Key),
+				Validity:    conf.Guard.Jwt.Validity,
+				NodeManager: manager,
+			},
+			&JwtLoginGuardAuthenticator{
+				LoginPath:   conf.Guard.Jwt.Login.Path,
+				Key:         []byte(conf.Guard.Key),
+				Validity:    conf.Guard.Jwt.Validity,
+				NodeManager: manager,
+			},
+		}
+
+		mux.Use(GetGuardMiddleware(auths))
+
+		return nil
+	})
+}
diff --git a/plugins/guard/guard_jwt_login.go b/plugins/guard/guard_jwt_login.go
new file mode 100644
index 0000000..365bb05
--- /dev/null
+++ b/plugins/guard/guard_jwt_login.go
@@ -0,0 +1,122 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"encoding/json"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/gorilla/schema"
+	"github.com/rande/gonode/core"
+	"github.com/rande/gonode/plugins/user"
+	"golang.org/x/crypto/bcrypt"
+	"net/http"
+	"time"
+)
+
+// this authenticator will create a JWT Token from a standard form
+type JwtLoginGuardAuthenticator struct {
+	LoginPath   string
+	NodeManager core.NodeManager
+	Validity    int64
+	Key         []byte
+}
+
+func (a *JwtLoginGuardAuthenticator) getCredentials(req *http.Request) (interface{}, error) {
+	if !(req.Method == "POST" && req.URL.Path == a.LoginPath) {
+		return nil, nil
+	}
+
+	req.ParseForm()
+
+	loginForm := &struct {
+		Username string `schema:"username"`
+		Password string `schema:"password"`
+	}{}
+
+	decoder := schema.NewDecoder()
+	if err := decoder.Decode(loginForm, req.Form); err != nil {
+		return nil, err
+	}
+
+	return &struct{ Username, Password string }{loginForm.Username, loginForm.Password}, nil
+}
+
+func (a *JwtLoginGuardAuthenticator) getUser(credentials interface{}) (GuardUser, error) {
+	c := credentials.(*struct{ Username, Password string })
+
+	query := a.NodeManager.
+		SelectBuilder().
+		Where("type = 'core.user' AND data->>'username' = ?", c.Username)
+
+	if node := a.NodeManager.FindOneBy(query); node != nil {
+		return node.Data.(*user.User), nil
+	}
+
+	return nil, UnableRetrieveUser
+}
+
+func (a *JwtLoginGuardAuthenticator) checkCredentials(credentials interface{}, user GuardUser) error {
+	c := credentials.(*struct{ Username, Password string })
+
+	if err := bcrypt.CompareHashAndPassword([]byte(user.GetPassword()), []byte(c.Password)); err != nil { // equal
+		return InvalidCredentials
+	}
+
+	return nil
+}
+
+func (a *JwtLoginGuardAuthenticator) createAuthenticatedToken(user GuardUser) (GuardToken, error) {
+	return &DefaultGuardToken{
+		Username: user.GetUsername(),
+		Roles:    user.GetRoles(),
+	}, nil
+}
+
+func (a *JwtLoginGuardAuthenticator) onAuthenticationFailure(req *http.Request, res http.ResponseWriter, err error) bool {
+	// nothing to do
+	res.Header().Set("Content-Type", "application/json")
+
+	res.WriteHeader(http.StatusForbidden)
+
+	data, _ := json.Marshal(map[string]string{
+		"status":  "KO",
+		"message": "Unable to authenticate request",
+	})
+
+	res.Write(data)
+
+	return true
+}
+
+func (a *JwtLoginGuardAuthenticator) onAuthenticationSuccess(req *http.Request, res http.ResponseWriter, token GuardToken) bool {
+	jwtToken := jwt.New(jwt.SigningMethodHS256)
+
+	// @todo: add support for referenced token on database
+	// token.Header["kid"] = "the sha1"
+
+	// Set reserved claims
+	jwtToken.Claims["exp"] = time.Now().Add(time.Minute * 30).Unix()
+
+	// Set shared claims
+	jwtToken.Claims["rls"] = token.GetRoles()
+	jwtToken.Claims["usr"] = token.GetUsername()
+
+	// Sign and get the complete encoded token as a string
+	tokenString, _ := jwtToken.SignedString([]byte(a.Key))
+
+	res.Header().Set("Content-Type", "application/json")
+	res.Header().Set("X-Token", tokenString)
+
+	data, _ := json.Marshal(map[string]string{
+		"status":  "OK",
+		"message": "Request is authenticated",
+		"token":   tokenString,
+	})
+
+	res.Write(data)
+
+	return true
+}
diff --git a/plugins/guard/guard_jwt_login_test.go b/plugins/guard/guard_jwt_login_test.go
new file mode 100644
index 0000000..7fbfcdd
--- /dev/null
+++ b/plugins/guard/guard_jwt_login_test.go
@@ -0,0 +1,166 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/rande/gonode/core"
+	"github.com/stretchr/testify/assert"
+	"golang.org/x/crypto/bcrypt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"strings"
+	"testing"
+)
+
+func Test_JwtLoginGuardAuthenticator_getCredentials_Valid_Request(t *testing.T) {
+	a := &JwtLoginGuardAuthenticator{
+		LoginPath:   "/login",
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	v := url.Values{
+		"username": {"thomas"},
+		"password": {"ZePassword"},
+	}
+
+	req, _ := http.NewRequest("POST", "/login", strings.NewReader(v.Encode()))
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+	c, err := a.getCredentials(req)
+
+	assert.NotNil(t, c)
+	assert.Nil(t, err)
+
+	cs := c.(*struct{ Username, Password string })
+
+	assert.Equal(t, cs.Username, "thomas")
+	assert.Equal(t, cs.Password, "ZePassword")
+}
+
+func Test_JwtLoginGuardAuthenticator_checkCredentials_Valid_Password(t *testing.T) {
+	a := &JwtLoginGuardAuthenticator{
+		LoginPath:   "/login",
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	password, _ := bcrypt.GenerateFromPassword([]byte("ZePassword"), 1)
+
+	c := &struct{ Username, Password string }{Username: "thomas", Password: "ZePassword"}
+	u := &DefaultGuardUser{Username: "thomas", Password: string(password[:])}
+
+	err := a.checkCredentials(c, u)
+
+	assert.Nil(t, err)
+}
+
+func Test_JwtLoginGuardAuthenticator_createAuthenticatedToken(t *testing.T) {
+	a := &JwtLoginGuardAuthenticator{
+		LoginPath:   "/login",
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	u := &DefaultGuardUser{
+		Username: "Thomas",
+		Password: "EncryptedPassword",
+		Roles:    []string{"ADMIN"},
+	}
+
+	token, err := a.createAuthenticatedToken(u)
+
+	assert.NotNil(t, token)
+	assert.Nil(t, err)
+	assert.Equal(t, token.GetUsername(), "Thomas")
+	assert.Equal(t, token.GetRoles(), []string{"ADMIN"})
+}
+
+func Test_JwtLoginGuardAuthenticator_onAuthenticationSuccess(t *testing.T) {
+	a := &JwtLoginGuardAuthenticator{
+		LoginPath:   "/login",
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	req, _ := http.NewRequest("POST", "/login", nil)
+	res := httptest.NewRecorder()
+	token := &DefaultGuardToken{
+		Username: "thomas",
+		Roles:    []string{"ADMIN"},
+	}
+
+	a.onAuthenticationSuccess(req, res, token)
+
+	b := bytes.NewBuffer([]byte(""))
+	io.Copy(b, res.Body)
+
+	v := &struct {
+		Status  string `json:"status"`
+		Message string `json:"message"`
+		Token   string `json:"token"`
+	}{}
+
+	json.Unmarshal(b.Bytes(), v)
+
+	jwtToken, err := jwt.Parse(v.Token, func(token *jwt.Token) (interface{}, error) {
+		// Don't forget to validate the alg is what you expect:
+		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
+		}
+
+		return a.Key, nil
+	})
+
+	assert.Nil(t, err)
+	assert.Equal(t, token.Username, jwtToken.Claims["usr"])
+	assert.Equal(t, "application/json", res.Header().Get("Content-Type"))
+	assert.Equal(t, v.Token, res.Header().Get("X-Token"))
+	assert.Equal(t, "Request is authenticated", v.Message)
+	assert.Equal(t, "OK", v.Status)
+
+	// @todo: I fail on basic golang conversion here ... from []interface{} to []string
+	//assert.Equal(t, token.Roles, jwtToken.Claims["rls"].([]string))
+}
+
+func Test_JwtLoginGuardAuthenticator_onAuthenticationFailure(t *testing.T) {
+	a := &JwtLoginGuardAuthenticator{
+		LoginPath:   "/login",
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	req, _ := http.NewRequest("POST", "/login", nil)
+	res := httptest.NewRecorder()
+
+	err := InvalidCredentials
+
+	a.onAuthenticationFailure(req, res, err)
+	b := bytes.NewBuffer([]byte(""))
+	io.Copy(b, res.Body)
+
+	v := &struct {
+		Status  string `json:"status"`
+		Message string `json:"message"`
+	}{}
+
+	json.Unmarshal(b.Bytes(), v)
+
+	assert.Equal(t, "KO", v.Status)
+	assert.Equal(t, "Unable to authenticate request", v.Message)
+	assert.Equal(t, "application/json", res.Header().Get("Content-Type"))
+}
diff --git a/plugins/guard/guard_jwt_token.go b/plugins/guard/guard_jwt_token.go
new file mode 100644
index 0000000..d4fc532
--- /dev/null
+++ b/plugins/guard/guard_jwt_token.go
@@ -0,0 +1,92 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/rande/gonode/core"
+	"github.com/rande/gonode/plugins/user"
+	"net/http"
+	"regexp"
+)
+
+// this authenticator will create a JWT Token from a standard form
+type JwtTokenGuardAuthenticator struct {
+	Path        *regexp.Regexp
+	NodeManager core.NodeManager
+	Validity    int64
+	Key         []byte
+}
+
+func (a *JwtTokenGuardAuthenticator) getCredentials(req *http.Request) (interface{}, error) {
+	if !a.Path.Match([]byte(req.RequestURI)) {
+		return nil, nil
+	}
+
+	if credentials, err := jwt.ParseFromRequest(req, func(token *jwt.Token) (interface{}, error) {
+		// Don't forget to validate the alg is what you expect:
+		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
+		}
+
+		return []byte(a.Key), nil
+	}); err != nil {
+		return nil, InvalidCredentialsFormat
+	} else {
+		return credentials, nil
+	}
+}
+
+func (a *JwtTokenGuardAuthenticator) getUser(credentials interface{}) (GuardUser, error) {
+	jwtToken := credentials.(*jwt.Token)
+
+	query := a.NodeManager.
+		SelectBuilder().
+		Where("type = 'core.user' AND data->>'username' = ?", jwtToken.Claims["usr"].(string))
+
+	if node := a.NodeManager.FindOneBy(query); node != nil {
+		return node.Data.(*user.User), nil
+	}
+
+	return nil, UnableRetrieveUser
+}
+
+func (a *JwtTokenGuardAuthenticator) checkCredentials(credentials interface{}, user GuardUser) error {
+	// nothing to do ...
+
+	return nil
+}
+
+func (a *JwtTokenGuardAuthenticator) createAuthenticatedToken(user GuardUser) (GuardToken, error) {
+	return &DefaultGuardToken{
+		Username: user.GetUsername(),
+		Roles:    user.GetRoles(),
+	}, nil
+}
+
+func (a *JwtTokenGuardAuthenticator) onAuthenticationFailure(req *http.Request, res http.ResponseWriter, err error) bool {
+	// nothing to do
+	res.Header().Set("Content-Type", "application/json")
+
+	res.WriteHeader(http.StatusForbidden)
+
+	data, _ := json.Marshal(map[string]string{
+		"status":  "KO",
+		"message": "Unable to validate token",
+	})
+
+	res.Write(data)
+
+	return true
+}
+
+func (a *JwtTokenGuardAuthenticator) onAuthenticationSuccess(req *http.Request, res http.ResponseWriter, token GuardToken) bool {
+	// nothing to do
+
+	return false
+}
diff --git a/plugins/guard/guard_jwt_token_test.go b/plugins/guard/guard_jwt_token_test.go
new file mode 100644
index 0000000..af2989c
--- /dev/null
+++ b/plugins/guard/guard_jwt_token_test.go
@@ -0,0 +1,217 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/rande/gonode/core"
+	"github.com/stretchr/testify/assert"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"regexp"
+	"testing"
+	"time"
+)
+
+func GetToken() *jwt.Token {
+	jwtToken := jwt.New(jwt.SigningMethodHS256)
+
+	// @todo: add support for referenced token on database
+	// token.Header["kid"] = "the sha1"
+
+	// Set reserved claims
+	jwtToken.Claims["exp"] = time.Now().Add(time.Minute * 30).Unix()
+
+	// Set shared claims
+	jwtToken.Claims["rls"] = []string{"ADMIN"}
+	jwtToken.Claims["usr"] = "thomas"
+
+	return jwtToken
+}
+
+func Test_JwtTokenGuardAuthenticator_getCredentials_NoMatch(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/api/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	req, _ := http.NewRequest("GET", "/ressource", nil)
+
+	c, err := a.getCredentials(req)
+
+	assert.Nil(t, c)
+	assert.Nil(t, err)
+}
+
+func Test_JwtTokenGuardAuthenticator_getCredentials_NoHeader_Request(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	req, _ := http.NewRequest("GET", "/ressource", nil)
+
+	c, err := a.getCredentials(req)
+
+	assert.Nil(t, c)
+	assert.NotNil(t, err)
+}
+
+func Test_JwtTokenGuardAuthenticator_getCredentials_Invalid_Token(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	req, _ := http.NewRequest("GET", "/ressource", nil)
+	req.Header.Set("Authorization", "Bearer XXXX")
+
+	c, err := a.getCredentials(req)
+
+	assert.Nil(t, c)
+	assert.NotNil(t, err)
+}
+
+func Test_JwtTokenGuardAuthenticator_getCredentials_Valid_Token_Header(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	jwtToken := GetToken()
+	tokenString, _ := jwtToken.SignedString(a.Key)
+
+	req, _ := http.NewRequest("GET", "/ressource", nil)
+	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tokenString))
+
+	c, err := a.getCredentials(req)
+
+	assert.NotNil(t, c)
+	assert.Nil(t, err)
+	assert.Equal(t, "thomas", c.(*jwt.Token).Claims["usr"].(string))
+}
+
+func Test_JwtTokenGuardAuthenticator_getCredentials_Valid_Token_QueryString(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	jwtToken := GetToken()
+	tokenString, _ := jwtToken.SignedString(a.Key)
+
+	req, _ := http.NewRequest("GET", "/ressource?access_token="+tokenString, nil)
+
+	c, err := a.getCredentials(req)
+
+	assert.NotNil(t, c)
+	assert.Nil(t, err)
+	assert.Equal(t, "thomas", c.(*jwt.Token).Claims["usr"].(string))
+}
+
+func Test_JwtTokenGuardAuthenticator_checkCredentials(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	// not used as the getCredentials check token validity
+	c := GetToken()
+	u := &DefaultGuardUser{Username: "thomas", Password: "dontcareaboutpassword"}
+
+	err := a.checkCredentials(c, u)
+
+	assert.Nil(t, err)
+}
+
+func Test_JwtTokenGuardAuthenticator_createAuthenticatedToken(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	u := &DefaultGuardUser{
+		Username: "Thomas",
+		Roles:    []string{"ADMIN"},
+	}
+
+	token, err := a.createAuthenticatedToken(u)
+
+	assert.NotNil(t, token)
+	assert.Nil(t, err)
+	assert.Equal(t, token.GetUsername(), "Thomas")
+	assert.Equal(t, token.GetRoles(), []string{"ADMIN"})
+}
+
+func Test_JwtTokenGuardAuthenticator_onAuthenticationSuccess(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	req, _ := http.NewRequest("GET", "/ressource", nil)
+	res := httptest.NewRecorder()
+	token := &DefaultGuardToken{
+		Username: "thomas",
+		Roles:    []string{"ADMIN"},
+	}
+
+	a.onAuthenticationSuccess(req, res, token)
+
+	b := bytes.NewBuffer([]byte(""))
+	io.Copy(b, res.Body)
+
+	assert.Equal(t, b.Len(), 0)
+}
+
+func Test_JwtTokenGuardAuthenticator_onAuthenticationFailure(t *testing.T) {
+	a := &JwtTokenGuardAuthenticator{
+		Path:        regexp.MustCompile("/*"),
+		NodeManager: &core.MockedManager{},
+		Validity:    12,
+		Key:         []byte("ZeKey"),
+	}
+
+	req, _ := http.NewRequest("GET", "/ressource", nil)
+	res := httptest.NewRecorder()
+
+	err := InvalidCredentials
+
+	a.onAuthenticationFailure(req, res, err)
+
+	b := bytes.NewBuffer([]byte(""))
+	io.Copy(b, res.Body)
+
+	v := &struct {
+		Status  string `json:"status"`
+		Message string `json:"message"`
+	}{}
+
+	json.Unmarshal(b.Bytes(), v)
+
+	assert.Equal(t, "KO", v.Status)
+	assert.Equal(t, "Unable to validate token", v.Message)
+}
diff --git a/plugins/guard/guard_middleware.go b/plugins/guard/guard_middleware.go
new file mode 100644
index 0000000..4c82489
--- /dev/null
+++ b/plugins/guard/guard_middleware.go
@@ -0,0 +1,78 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"github.com/zenazn/goji/web"
+	"net/http"
+)
+
+func GetGuardMiddleware(auths []GuardAuthenticator) func(c *web.C, h http.Handler) http.Handler {
+	return func(c *web.C, h http.Handler) http.Handler {
+		fn := func(w http.ResponseWriter, r *http.Request) {
+
+			// handle security here
+			for _, authenticator := range auths {
+				performed, output := performAuthentication(c, authenticator, w, r)
+
+				if performed && output {
+					return
+				} else if performed {
+					break
+				}
+			}
+
+			h.ServeHTTP(w, r)
+		}
+
+		return http.HandlerFunc(fn)
+	}
+}
+
+// false means, no authentification has been done
+func performAuthentication(c *web.C, a GuardAuthenticator, w http.ResponseWriter, r *http.Request) (bool, bool) {
+	var o bool
+
+	// get credentials from request
+	credentials, err := a.getCredentials(r)
+
+	if err == InvalidCredentialsFormat {
+		o = a.onAuthenticationFailure(r, w, err)
+
+		return true, o
+	}
+
+	// no credentials, return
+	if credentials == nil { // nothing to do, next one
+		return false, false
+	}
+
+	// ok get the current user for the current credentials
+	user, err := a.getUser(credentials)
+
+	if err != nil || user == nil {
+		o = a.onAuthenticationFailure(r, w, err)
+
+		return true, o
+	}
+
+	// check if the request's credentials match user credentials
+	if err = a.checkCredentials(credentials, user); err != nil {
+		o = a.onAuthenticationFailure(r, w, err)
+
+		return true, o
+	}
+
+	// create a valid security token for the user
+	token, err := a.createAuthenticatedToken(user)
+
+	c.Env["guard_token"] = token
+
+	// complete the process
+	o = a.onAuthenticationSuccess(r, w, token)
+
+	return true, o
+}
diff --git a/plugins/guard/guard_middleware_test.go b/plugins/guard/guard_middleware_test.go
new file mode 100644
index 0000000..b158e91
--- /dev/null
+++ b/plugins/guard/guard_middleware_test.go
@@ -0,0 +1,114 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"github.com/stretchr/testify/assert"
+	"github.com/zenazn/goji/web"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func Test_Perform_Authentification_With_Not_Found_Credentials(t *testing.T) {
+
+	r, _ := http.NewRequest("GET", "/foobar", nil)
+	w := httptest.NewRecorder()
+
+	a := &MockedAuthenticator{}
+	a.On("getCredentials", r).Return(nil, nil)
+
+	cw := &web.C{Env: make(map[interface{}]interface{})}
+
+	performed, output := performAuthentication(cw, a, w, r)
+
+	assert.False(t, performed)
+	assert.False(t, output)
+}
+
+func Test_Perform_Authentification_With_Not_User_Found(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/foobar", nil)
+	w := httptest.NewRecorder()
+
+	c := map[string]string{
+		"login":    "thomas",
+		"password": "password",
+	}
+
+	a := &MockedAuthenticator{}
+	a.On("getCredentials", r).Return(c, nil)
+	a.On("getUser", c).Return(nil, nil)
+	a.On("onAuthenticationFailure", r, w, nil).Return(true)
+
+	cw := &web.C{Env: make(map[interface{}]interface{})}
+
+	performed, output := performAuthentication(cw, a, w, r)
+
+	assert.True(t, performed)
+	assert.True(t, output)
+}
+
+func Test_Perform_Authentification_With_Invalid_Credentials(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/foobar", nil)
+	w := httptest.NewRecorder()
+
+	c := map[string]string{
+		"login":    "thomas",
+		"password": "password",
+	}
+
+	u := &DefaultGuardUser{
+		Username: "thomas",
+		Password: "password",
+	}
+
+	a := &MockedAuthenticator{}
+	a.On("getCredentials", r).Return(c, nil)
+	a.On("getUser", c).Return(u, nil)
+	a.On("checkCredentials", c, u).Return(InvalidCredentials)
+	a.On("onAuthenticationFailure", r, w, InvalidCredentials).Return(true)
+
+	cw := &web.C{Env: make(map[interface{}]interface{})}
+
+	performed, output := performAuthentication(cw, a, w, r)
+
+	assert.True(t, performed)
+	assert.True(t, output)
+}
+
+func Test_Perform_Authentification_With_Valid_User(t *testing.T) {
+	r, _ := http.NewRequest("GET", "/foobar", nil)
+	w := httptest.NewRecorder()
+
+	c := map[string]string{
+		"login":    "thomas",
+		"password": "password",
+	}
+
+	u := &DefaultGuardUser{
+		Username: "thomas",
+		Password: "password",
+	}
+
+	token := &DefaultGuardToken{
+		Username: "thomas",
+	}
+
+	a := &MockedAuthenticator{}
+	a.On("getCredentials", r).Return(c, nil)
+	a.On("getUser", c).Return(u, nil)
+	a.On("checkCredentials", c, u).Return(nil)
+	a.On("createAuthenticatedToken", u).Return(token, nil)
+	a.On("onAuthenticationSuccess", r, w, token).Return(true)
+
+	cw := &web.C{Env: make(map[interface{}]interface{})}
+
+	performed, output := performAuthentication(cw, a, w, r)
+
+	assert.True(t, performed)
+	assert.True(t, output)
+	assert.Equal(t, token, cw.Env["guard_token"])
+}
diff --git a/plugins/guard/guard_mock.go b/plugins/guard/guard_mock.go
new file mode 100644
index 0000000..d818f7c
--- /dev/null
+++ b/plugins/guard/guard_mock.go
@@ -0,0 +1,59 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"github.com/stretchr/testify/mock"
+	"net/http"
+)
+
+type MockedAuthenticator struct {
+	mock.Mock
+}
+
+func (m *MockedAuthenticator) getCredentials(req *http.Request) (interface{}, error) {
+	args := m.Mock.Called(req)
+
+	if args.Get(0) == nil {
+		return nil, args.Error(1)
+	}
+
+	return args.Get(0).(interface{}), args.Error(1)
+}
+func (m *MockedAuthenticator) getUser(credentials interface{}) (GuardUser, error) {
+	args := m.Mock.Called(credentials)
+
+	if args.Get(0) == nil {
+		return nil, args.Error(1)
+	}
+
+	return args.Get(0).(GuardUser), args.Error(1)
+}
+func (m *MockedAuthenticator) checkCredentials(credentials interface{}, user GuardUser) error {
+	args := m.Mock.Called(credentials, user)
+
+	return args.Error(0)
+}
+func (m *MockedAuthenticator) createAuthenticatedToken(u GuardUser) (GuardToken, error) {
+	args := m.Mock.Called(u)
+
+	if args.Get(0) == nil {
+		return nil, args.Error(1)
+	}
+
+	return args.Get(0).(GuardToken), args.Error(1)
+}
+func (m *MockedAuthenticator) onAuthenticationFailure(req *http.Request, res http.ResponseWriter, err error) bool {
+	args := m.Mock.Called(req, res, err)
+
+	return args.Bool(0)
+}
+
+func (m *MockedAuthenticator) onAuthenticationSuccess(req *http.Request, res http.ResponseWriter, token GuardToken) bool {
+	args := m.Mock.Called(req, res, token)
+
+	return args.Bool(0)
+}
diff --git a/plugins/guard/jwt_token.go b/plugins/guard/jwt_token.go
new file mode 100644
index 0000000..44e49c7
--- /dev/null
+++ b/plugins/guard/jwt_token.go
@@ -0,0 +1,61 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package guard
+
+import (
+	"github.com/rande/gonode/core"
+	"io"
+	"time"
+)
+
+type JwtTokenMeta struct {
+	Expiration time.Time `json:"expiration"`
+}
+
+type JwtToken struct {
+	User  *core.Reference `json:"user"`
+	Key   []byte          `json:"key"`
+	Roles []string        `json:"roles"`
+}
+
+type JwtTokentHandler struct {
+}
+
+func (h *JwtTokentHandler) GetStruct() (core.NodeData, core.NodeMeta) {
+	return &JwtToken{}, &JwtTokenMeta{}
+}
+
+func (h *JwtTokentHandler) PreInsert(node *core.Node, m core.NodeManager) error {
+	return nil
+}
+
+func (h *JwtTokentHandler) PreUpdate(node *core.Node, m core.NodeManager) error {
+	return nil
+}
+
+func (h *JwtTokentHandler) PostInsert(node *core.Node, m core.NodeManager) error {
+	return nil
+}
+
+func (h *JwtTokentHandler) PostUpdate(node *core.Node, m core.NodeManager) error {
+	return nil
+}
+
+func (h *JwtTokentHandler) Validate(node *core.Node, m core.NodeManager, errors core.Errors) {
+
+}
+
+func (h *JwtTokentHandler) GetDownloadData(node *core.Node) *core.DownloadData {
+	return core.GetDownloadData()
+}
+
+func (h *JwtTokentHandler) Load(data []byte, meta []byte, node *core.Node) error {
+	return core.HandlerLoad(h, data, meta, node)
+}
+
+func (h *JwtTokentHandler) StoreStream(node *core.Node, r io.Reader) (int64, error) {
+	return core.DefaultHandlerStoreStream(node, r)
+}
diff --git a/plugins/setup/setup_app.go b/plugins/setup/setup_app.go
index 331ec8d..d588f10 100644
--- a/plugins/setup/setup_app.go
+++ b/plugins/setup/setup_app.go
@@ -14,12 +14,16 @@ import (
 func ConfigureServer(l *goapp.Lifecycle, conf *config.ServerConfig) {
 
 	l.Prepare(func(app *goapp.App) error {
+		if !conf.Test {
+			return nil
+		}
+
 		mux := app.Get("goji.mux").(*web.Mux)
 		manager := app.Get("gonode.manager").(*core.PgNodeManager)
 
 		prefix := ""
 
-		mux.Put(prefix+"/uninstall", func(res http.ResponseWriter, req *http.Request) {
+		mux.Put(prefix+"/setup/uninstall", func(res http.ResponseWriter, req *http.Request) {
 			res.Header().Set("Content-Type", "application/json")
 			res.Header().Set("Access-Control-Allow-Origin", "*")
 
@@ -35,7 +39,7 @@ func ConfigureServer(l *goapp.Lifecycle, conf *config.ServerConfig) {
 			helper.SendWithHttpCode(res, http.StatusOK, "Successfully delete tables!")
 		})
 
-		mux.Put(prefix+"/install", func(res http.ResponseWriter, req *http.Request) {
+		mux.Put(prefix+"/setup/install", func(res http.ResponseWriter, req *http.Request) {
 			res.Header().Set("Content-Type", "application/json")
 			res.Header().Set("Access-Control-Allow-Origin", "*")
 
@@ -112,19 +116,7 @@ func ConfigureServer(l *goapp.Lifecycle, conf *config.ServerConfig) {
 			}
 		})
 
-		return nil
-	})
-
-	l.Prepare(func(app *goapp.App) error {
-		if !conf.Test {
-			return nil
-		}
-
-		mux := app.Get("goji.mux").(*web.Mux)
-
-		prefix := ""
-
-		mux.Put(prefix+"/data/purge", func(res http.ResponseWriter, req *http.Request) {
+		mux.Put(prefix+"/setup/data/purge", func(res http.ResponseWriter, req *http.Request) {
 
 			manager := app.Get("gonode.manager").(*core.PgNodeManager)
 
@@ -142,7 +134,7 @@ func ConfigureServer(l *goapp.Lifecycle, conf *config.ServerConfig) {
 			}
 		})
 
-		mux.Put(prefix+"/data/load", func(res http.ResponseWriter, req *http.Request) {
+		mux.Put(prefix+"/setup/data/load", func(res http.ResponseWriter, req *http.Request) {
 			manager := app.Get("gonode.manager").(*core.PgNodeManager)
 			nodes := manager.FindBy(manager.SelectBuilder(), 0, 10)
 
diff --git a/server.toml.dist b/server.toml.dist
index 04d0215..454fb8a 100644
--- a/server.toml.dist
+++ b/server.toml.dist
@@ -1,5 +1,6 @@
 name= "GoNode - poc"
 bind= ":2405"
+test= true
 
 [databases.master]
 type    = "master"
@@ -9,3 +10,13 @@ prefix  = "prod"
 
 [filesystem]
 path = "/tmp/gnode"
+
+[guard]
+key = "ZeSecretKey0oo"
+
+    [guard.jwt]
+        [guard.jwt.login]
+        path = "/login"
+
+        [guard.jwt.token]
+        path = "^\\/nodes\\/(.*)$"
diff --git a/test/api/create_test.go b/test/api/create_test.go
index 664b033..0da2c38 100644
--- a/test/api/create_test.go
+++ b/test/api/create_test.go
@@ -20,9 +20,11 @@ import (
 
 func Test_Create_User(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *App) {
+		auth := test.GetAuthHeader(t, ts)
+
 		// WITH
 		file, _ := os.Open("../fixtures/new_user.json")
-		res, _ := test.RunRequest("POST", ts.URL+"/nodes", file)
+		res, _ := test.RunRequest("POST", ts.URL+"/nodes", file, auth)
 
 		assert.Equal(t, 201, res.StatusCode)
 
@@ -43,9 +45,11 @@ func Test_Create_User(t *testing.T) {
 
 func Test_Create_Media_With_Binary_Upload(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *App) {
+		auth := test.GetAuthHeader(t, ts)
+
 		// WITH
 		file, _ := os.Open("../fixtures/new_image.json")
-		res, _ := test.RunRequest("POST", ts.URL+"/nodes", file)
+		res, _ := test.RunRequest("POST", ts.URL+"/nodes", file, auth)
 
 		assert.Equal(t, 201, res.StatusCode)
 
@@ -55,15 +59,15 @@ func Test_Create_Media_With_Binary_Upload(t *testing.T) {
 
 		var message = "The content of the file, yep it is not an image"
 
-		res, _ = test.RunRequest("PUT", ts.URL+"/nodes/"+node.Uuid.CleanString()+"?raw", strings.NewReader(message))
+		res, _ = test.RunRequest("PUT", ts.URL+"/nodes/"+node.Uuid.CleanString()+"?raw", strings.NewReader(message), auth)
 
 		assert.Equal(t, 200, res.StatusCode)
 
-		res, _ = test.RunRequest("GET", ts.URL+"/nodes/"+node.Uuid.CleanString()+"?raw", nil)
+		res, _ = test.RunRequest("GET", ts.URL+"/nodes/"+node.Uuid.CleanString()+"?raw", nil, auth)
 
 		assert.Equal(t, message, string(res.GetBody()[:]))
 
-		res, _ = test.RunRequest("GET", ts.URL+"/nodes/"+node.Uuid.CleanString(), nil)
+		res, _ = test.RunRequest("GET", ts.URL+"/nodes/"+node.Uuid.CleanString(), nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
 		node = core.NewNode()
diff --git a/test/api/delete_test.go b/test/api/delete_test.go
index 7876a90..6c5d6fc 100644
--- a/test/api/delete_test.go
+++ b/test/api/delete_test.go
@@ -17,7 +17,9 @@ import (
 
 func Test_Delete_Non_Existant_Node(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *App) {
-		res, _ := test.RunRequest("DELETE", ts.URL+"/nodes/d703a3ab-8374-4c30-a8a4-2c22aa67763b", nil)
+		auth := test.GetAuthHeader(t, ts)
+
+		res, _ := test.RunRequest("DELETE", ts.URL+"/nodes/d703a3ab-8374-4c30-a8a4-2c22aa67763b", nil, auth)
 
 		assert.Equal(t, 404, res.StatusCode, "Delete non existant node")
 	})
@@ -25,8 +27,10 @@ func Test_Delete_Non_Existant_Node(t *testing.T) {
 
 func Test_Delete_Existant_Node(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *App) {
+		auth := test.GetAuthHeader(t, ts)
+
 		file, _ := os.Open("../fixtures/new_user.json")
-		res, _ := test.RunRequest("POST", ts.URL+"/nodes", file)
+		res, _ := test.RunRequest("POST", ts.URL+"/nodes", file, auth)
 
 		assert.Equal(t, 201, res.StatusCode, "Node created")
 
@@ -37,7 +41,7 @@ func Test_Delete_Existant_Node(t *testing.T) {
 
 		assert.Equal(t, "core.user", node.Type)
 
-		res, _ = test.RunRequest("DELETE", ts.URL+"/nodes/"+node.Uuid.CleanString(), nil)
+		res, _ = test.RunRequest("DELETE", ts.URL+"/nodes/"+node.Uuid.CleanString(), nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
 		serializer.Deserialize(res.Body, node)
@@ -45,28 +49,29 @@ func Test_Delete_Existant_Node(t *testing.T) {
 		assert.Equal(t, node.Deleted, true)
 
 		// test if we can delete a deleted node ...
-		res, _ = test.RunRequest("DELETE", ts.URL+"/nodes/"+node.Uuid.CleanString(), nil)
+		res, _ = test.RunRequest("DELETE", ts.URL+"/nodes/"+node.Uuid.CleanString(), nil, auth)
 		assert.Equal(t, 410, res.StatusCode)
 	})
 }
 
 func Test_Delete_Find_Filter(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *App) {
+		auth := test.GetAuthHeader(t, ts)
 		nodes := InitSearchFixture(app)
 
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes", nil, auth)
 		p := GetPager(app, res)
 
-		assert.Equal(t, 3, len(p.Elements))
+		assert.Equal(t, 4, len(p.Elements))
 
-		res, _ = test.RunRequest("DELETE", ts.URL+"/nodes/"+nodes[0].Uuid.CleanString(), nil)
+		res, _ = test.RunRequest("DELETE", ts.URL+"/nodes/"+nodes[0].Uuid.CleanString(), nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
-		res, _ = test.RunRequest("GET", ts.URL+"/nodes", nil)
+		res, _ = test.RunRequest("GET", ts.URL+"/nodes", nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
 		p = GetPager(app, res)
 
-		assert.Equal(t, 2, len(p.Elements))
+		assert.Equal(t, 3, len(p.Elements))
 	})
 }
diff --git a/test/api/find_test.go b/test/api/find_test.go
index 1c19a87..1a64f4e 100644
--- a/test/api/find_test.go
+++ b/test/api/find_test.go
@@ -15,7 +15,9 @@ import (
 
 func Test_Find_Non_Existant(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *App) {
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes/d703a3ab-8374-4c30-a8a4-2c22aa67763b", nil)
+		auth := test.GetAuthHeader(t, ts)
+
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes/d703a3ab-8374-4c30-a8a4-2c22aa67763b", nil, auth)
 
 		assert.Equal(t, 404, res.StatusCode, "Delete non existant node")
 	})
diff --git a/test/api/guard_app_test.go b/test/api/guard_app_test.go
new file mode 100644
index 0000000..ade0f4d
--- /dev/null
+++ b/test/api/guard_app_test.go
@@ -0,0 +1,18 @@
+package api
+
+import (
+	"fmt"
+	"github.com/rande/goapp"
+	"github.com/rande/gonode/test"
+	"github.com/stretchr/testify/assert"
+	"net/http/httptest"
+	"testing"
+)
+
+func Test_Guard_Error(t *testing.T) {
+	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		res, _ := test.RunRequest("GET", fmt.Sprintf("%s/nodes/protected", ts.URL), nil)
+
+		assert.Equal(t, 403, res.StatusCode)
+	})
+}
diff --git a/test/api/login_jwt_test.go b/test/api/login_jwt_test.go
new file mode 100644
index 0000000..2d1e32f
--- /dev/null
+++ b/test/api/login_jwt_test.go
@@ -0,0 +1,82 @@
+// Copyright © 2014-2015 Thomas Rabaix <thomas.rabaix@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package api
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"github.com/dgrijalva/jwt-go"
+	"github.com/rande/goapp"
+	"github.com/rande/gonode/core"
+	"github.com/rande/gonode/core/config"
+	"github.com/rande/gonode/plugins/user"
+	"github.com/rande/gonode/test"
+	"github.com/stretchr/testify/assert"
+	"io"
+	"net/http/httptest"
+	"net/url"
+	"testing"
+)
+
+func Test_Create_Username(t *testing.T) {
+	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+
+		configuration := app.Get("gonode.configuration").(*config.ServerConfig)
+
+		// WITH
+		// create a valid user into the database ...
+		manager := app.Get("gonode.manager").(*core.PgNodeManager)
+
+		u := app.Get("gonode.handler_collection").(core.HandlerCollection).NewNode("core.user")
+		data := u.Data.(*user.User)
+		data.Email = "test@example.org"
+		data.Enabled = true
+		data.FirstName = "Thomas"
+		data.LastName = "Rxxxx"
+		data.NewPassword = "ZePassword"
+		data.Username = "rande"
+
+		meta := u.Meta.(*user.UserMeta)
+		meta.PasswordCost = 1 // save test time
+
+		manager.Save(u, false)
+
+		res, _ := test.RunRequest("POST", fmt.Sprintf("%s/login", ts.URL), url.Values{
+			"username": {data.Username},
+			"password": {"ZePassword"},
+		})
+
+		assert.Equal(t, 200, res.StatusCode)
+
+		b := bytes.NewBuffer([]byte(""))
+		io.Copy(b, res.Body)
+
+		v := &struct {
+			Status  string `json:"status"`
+			Message string `json:"message"`
+			Token   string `json:"token"`
+		}{}
+
+		json.Unmarshal(b.Bytes(), v)
+
+		token, err := jwt.Parse(v.Token, func(token *jwt.Token) (interface{}, error) {
+			// Don't forget to validate the alg is what you expect:
+			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+				return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
+			}
+
+			return []byte(configuration.Guard.Key), nil
+		})
+
+		assert.NotNil(t, configuration.Guard.Key)
+		assert.True(t, len(configuration.Guard.Key) > 0)
+		assert.Nil(t, err)
+		assert.True(t, token.Valid)
+
+		fmt.Printf("%v", token)
+	})
+}
diff --git a/test/api/parent_test.go b/test/api/parent_test.go
index 2884650..6efc588 100644
--- a/test/api/parent_test.go
+++ b/test/api/parent_test.go
@@ -67,6 +67,8 @@ func Test_Create_Parents_With_Manager(t *testing.T) {
 func Test_Create_Parents_With_Api(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
 		// WITH
+		auth := test.GetAuthHeader(t, ts)
+
 		handlers := app.Get("gonode.handler_collection").(core.HandlerCollection)
 		manager := app.Get("gonode.manager").(*core.PgNodeManager)
 
@@ -82,16 +84,16 @@ func Test_Create_Parents_With_Api(t *testing.T) {
 		node4 := handlers.NewNode("default")
 		manager.Save(node4, false)
 
-		res, _ := test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node2.Uuid, node1.Uuid), nil)
+		res, _ := test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node2.Uuid, node1.Uuid), nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
-		res, _ = test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node3.Uuid, node2.Uuid), nil)
+		res, _ = test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node3.Uuid, node2.Uuid), nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
-		res, _ = test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node4.Uuid, node3.Uuid), nil)
+		res, _ = test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node4.Uuid, node3.Uuid), nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
-		res, _ = test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node1.Uuid, node4.Uuid), nil)
+		res, _ = test.RunRequest("PUT", fmt.Sprintf("%s/nodes/move/%s/%s", ts.URL, node1.Uuid, node4.Uuid), nil, auth)
 		assert.Equal(t, 200, res.StatusCode)
 
 		serializer := app.Get("gonode.node.serializer").(*core.Serializer)
diff --git a/test/api/search_test.go b/test/api/search_test.go
index 63fa329..1ba51b0 100644
--- a/test/api/search_test.go
+++ b/test/api/search_test.go
@@ -25,29 +25,33 @@ func CheckNoResults(t *testing.T, p *api.ApiPager) {
 }
 
 func Test_Search_Basic(t *testing.T) {
-	urls := []string{
-		"/nodes",
-		"/nodes?type=core.user",
-		"/nodes?type=core.user&data.username=user12",
-		"/nodes?type=core.user&data.username=user12&data.username=user13",
-		"/nodes?&page=-1&page=1", // the last occurrence erase first values
+	values := []struct {
+		Url string
+		Len int
+	}{
+		{"/nodes", 2},
+		{"/nodes?type=core.user", 2},
+		{"/nodes?type=core.user&data.username=user12", 1},
+		{"/nodes?type=core.user&data.username=user12&data.username=user13", 1},
+		{"/nodes?&page=-1&page=1", 2}, // the last occurrence erase first values
 	}
 
-	for _, url := range urls {
+	for _, v := range values {
 		test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
 			// WITH
+			auth := test.GetAuthHeader(t, ts)
 			file, _ := os.Open("../fixtures/new_user.json")
-			test.RunRequest("POST", ts.URL+"/nodes", file)
+			test.RunRequest("POST", ts.URL+"/nodes", file, auth)
 
 			// WHEN
-			res, _ := test.RunRequest("GET", ts.URL+url, nil)
+			res, _ := test.RunRequest("GET", ts.URL+v.Url, nil, auth)
 
 			p := GetPager(app, res)
 
 			// THEN
 			assert.Equal(t, uint64(32), p.PerPage)
 			assert.Equal(t, uint64(1), p.Page)
-			assert.Equal(t, 1, len(p.Elements))
+			assert.Equal(t, v.Len, len(p.Elements))
 			assert.Equal(t, uint64(0), p.Next)
 			assert.Equal(t, uint64(0), p.Previous)
 
@@ -62,11 +66,12 @@ func Test_Search_Basic(t *testing.T) {
 func Test_Search_NoResult(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
 		// WITH
+		auth := test.GetAuthHeader(t, ts)
 		file, _ := os.Open("../fixtures/new_user.json")
 		test.RunRequest("POST", ts.URL+"/nodes", file)
 
 		// WHEN
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?type=other", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?type=other", nil, auth)
 
 		p := GetPager(app, res)
 
@@ -87,11 +92,13 @@ func Test_Search_Invalid_Pagination(t *testing.T) {
 
 	for _, url := range urls {
 		test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+			// WITH
+			auth := test.GetAuthHeader(t, ts)
 			file, _ := os.Open("../fixtures/new_user.json")
-			test.RunRequest("POST", ts.URL+"/nodes", file)
+			test.RunRequest("POST", ts.URL+"/nodes", file, auth)
 
 			// WHEN
-			res, _ := test.RunRequest("GET", ts.URL+url, nil)
+			res, _ := test.RunRequest("GET", ts.URL+url, nil, auth)
 
 			assert.Equal(t, 412, res.StatusCode, "url: "+url)
 		})
@@ -100,27 +107,30 @@ func Test_Search_Invalid_Pagination(t *testing.T) {
 
 func Test_Search_Invalid_OrderBy(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
+
 		// seems goji or golang block this request
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=\"1 = 1\"; DELETE * FROM node,ASC", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=\"1 = 1\"; DELETE * FROM node,ASC", nil, auth)
 		assert.Equal(t, 400, res.StatusCode, "url: /nodes?order_by=\"1 = 1\"; DELETE * FROM node,ASC")
 
 		// seems goji or golang block this request
-		res, _ = test.RunRequest("GET", ts.URL+"/nodes?order_by=DELETE%20*%20FROM%20node,ASC", nil)
+		res, _ = test.RunRequest("GET", ts.URL+"/nodes?order_by=DELETE%20*%20FROM%20node,ASC", nil, auth)
 		assert.Equal(t, 412, res.StatusCode, "url: /nodes?order_by=DELETE%20*%20FROM%20node,ASC")
 	})
 }
 
 func Test_Search_OrderBy_Name_ASC(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
 		InitSearchFixture(app)
 
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=name,ASC", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=name,ASC", nil, auth)
 
 		assert.Equal(t, 200, res.StatusCode, "url: /nodes?order_by=name,ASC")
 
 		p := GetPager(app, res)
 
-		assert.Equal(t, 3, len(p.Elements))
+		assert.Equal(t, 4, len(p.Elements))
 		assert.Equal(t, "User A", p.Elements[0].(*core.Node).Name)
 		assert.Equal(t, "User AA", p.Elements[1].(*core.Node).Name)
 		assert.Equal(t, "User B", p.Elements[2].(*core.Node).Name)
@@ -129,33 +139,35 @@ func Test_Search_OrderBy_Name_ASC(t *testing.T) {
 
 func Test_Search_OrderBy_Name_DESC(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
 		InitSearchFixture(app)
 
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=name,DESC", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=name,DESC", nil, auth)
 
 		assert.Equal(t, 200, res.StatusCode, "url: /nodes?order_by=name,DESC")
 
 		p := GetPager(app, res)
 
-		assert.Equal(t, 3, len(p.Elements))
-		assert.Equal(t, "User B", p.Elements[0].(*core.Node).Name)
-		assert.Equal(t, "User AA", p.Elements[1].(*core.Node).Name)
-		assert.Equal(t, "User A", p.Elements[2].(*core.Node).Name)
+		assert.Equal(t, 4, len(p.Elements))
+		assert.Equal(t, "User ZZ", p.Elements[0].(*core.Node).Name)
+		assert.Equal(t, "User B", p.Elements[1].(*core.Node).Name)
+		assert.Equal(t, "User AA", p.Elements[2].(*core.Node).Name)
 	})
 }
 
 func Test_Search_OrderBy_Weight_DESC_Name_ASC(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
 		InitSearchFixture(app)
 
 		// TESTING WITH 2 ORDERING OPTION
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=weight,DESC&order_by=name,ASC", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=weight,DESC&order_by=name,ASC", nil, auth)
 
 		assert.Equal(t, 200, res.StatusCode, "url: /nodes?order_by=weight,DESC&order_by=name,ASC")
 
 		p := GetPager(app, res)
 
-		assert.Equal(t, 3, len(p.Elements))
+		assert.Equal(t, 4, len(p.Elements))
 		assert.Equal(t, "User AA", p.Elements[0].(*core.Node).Name)
 		assert.Equal(t, "User A", p.Elements[1].(*core.Node).Name)
 		assert.Equal(t, "User B", p.Elements[2].(*core.Node).Name)
@@ -164,27 +176,29 @@ func Test_Search_OrderBy_Weight_DESC_Name_ASC(t *testing.T) {
 
 func Test_Search_OrderBy_Meta_Username(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
 		InitSearchFixture(app)
 
 		// TESTING WITH 2 ORDERING OPTION
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=meta.username,DESC", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?order_by=meta.username,DESC", nil, auth)
 
 		assert.Equal(t, 200, res.StatusCode, "url: /nodes?order_by=meta.username")
 
 		p := GetPager(app, res)
 
-		assert.Equal(t, 3, len(p.Elements))
-		assert.Equal(t, "User A", p.Elements[0].(*core.Node).Name)
-		assert.Equal(t, "User AA", p.Elements[1].(*core.Node).Name)
-		assert.Equal(t, "User B", p.Elements[2].(*core.Node).Name)
+		assert.Equal(t, 4, len(p.Elements))
+		assert.Equal(t, "User ZZ", p.Elements[0].(*core.Node).Name)
+		assert.Equal(t, "User A", p.Elements[1].(*core.Node).Name)
+		assert.Equal(t, "User AA", p.Elements[2].(*core.Node).Name)
 	})
 }
 
 func Test_Search_OrderBy_Meta_Non_Existant_Meta(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
 		InitSearchFixture(app)
 
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?meta.username.fake=foo&order_by=meta.username.fake,DESC", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?meta.username.fake=foo&order_by=meta.username.fake,DESC", nil, auth)
 
 		assert.Equal(t, 200, res.StatusCode, "url: /nodes?order_by=meta.username.fake")
 
@@ -196,9 +210,10 @@ func Test_Search_OrderBy_Meta_Non_Existant_Meta(t *testing.T) {
 
 func Test_Search_Meta(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
 		InitSearchFixture(app)
 
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?data.username=user-a", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?data.username=user-a", nil, auth)
 
 		assert.Equal(t, 200, res.StatusCode, "url: /nodes?data.username=user-a")
 
@@ -210,9 +225,10 @@ func Test_Search_Meta(t *testing.T) {
 
 func Test_Search_Slug(t *testing.T) {
 	test.RunHttpTest(t, func(t *testing.T, ts *httptest.Server, app *goapp.App) {
+		auth := test.GetAuthHeader(t, ts)
 		InitSearchFixture(app)
 
-		res, _ := test.RunRequest("GET", ts.URL+"/nodes?slug=user-a", nil)
+		res, _ := test.RunRequest("GET", ts.URL+"/nodes?slug=user-a", nil, auth)
 
 		assert.Equal(t, 200, res.StatusCode, "url: /nodes?slug=user-a")
 
diff --git a/test/config_codeship.toml b/test/config_codeship.toml
index 7169efa..711f138 100644
--- a/test/config_codeship.toml
+++ b/test/config_codeship.toml
@@ -10,3 +10,13 @@ prefix  = "test"
 
 [filesystem]
 path = "/tmp/gnode"
+
+[guard]
+key = "ZeSecretKey0oo"
+
+    [guard.jwt]
+        [guard.jwt.login]
+        path = "/login"
+
+        [guard.jwt.token]
+        path = "^\\/nodes\\/(.*)$"
diff --git a/test/config_test.toml b/test/config_test.toml
index 660acc4..dcbda87 100644
--- a/test/config_test.toml
+++ b/test/config_test.toml
@@ -10,3 +10,13 @@ prefix  = "test"
 
 [filesystem]
 path = "/tmp/gnode"
+
+[guard]
+key = "ZeSecretKey0oo"
+
+    [guard.jwt]
+        [guard.jwt.login]
+        path = "/login"
+
+        [guard.jwt.token]
+        path = "^\\/nodes\\/(.*)$"
diff --git a/test/config_travis.toml b/test/config_travis.toml
index 7f9afd5..ab637c4 100644
--- a/test/config_travis.toml
+++ b/test/config_travis.toml
@@ -10,3 +10,13 @@ prefix  = "test"
 
 [filesystem]
 path = "/tmp/gnode"
+
+[guard]
+key = "ZeSecretKey0oo"
+
+    [guard.jwt]
+        [guard.jwt.login]
+        path = "/login"
+
+        [guard.jwt.token]
+        path = "^\\/nodes\\/(.*)$"
diff --git a/test/test.go b/test/test.go
index 95045db..856be69 100644
--- a/test/test.go
+++ b/test/test.go
@@ -6,13 +6,17 @@
 package test
 
 import (
+	"bytes"
+	"encoding/json"
 	"fmt"
 	"github.com/rande/goapp"
 	"github.com/rande/gonode/commands/server"
 	"github.com/rande/gonode/core"
 	"github.com/rande/gonode/core/config"
 	"github.com/rande/gonode/plugins/api"
+	"github.com/rande/gonode/plugins/guard"
 	"github.com/rande/gonode/plugins/setup"
+	"github.com/rande/gonode/plugins/user"
 	"github.com/stretchr/testify/assert"
 	"github.com/zenazn/goji/web"
 	"github.com/zenazn/goji/web/middleware"
@@ -54,10 +58,8 @@ func GetLifecycle(file string) *goapp.Lifecycle {
 		app.Set("goji.mux", func(app *goapp.App) interface{} {
 			mux := web.New()
 
-			//		mux.Use(middleware.RequestID)
 			mux.Use(middleware.Logger)
 			mux.Use(middleware.Recoverer)
-			//		mux.Use(middleware.AutomaticOptions)
 
 			return mux
 		})
@@ -66,8 +68,11 @@ func GetLifecycle(file string) *goapp.Lifecycle {
 	})
 
 	server.ConfigureServer(l, conf)
+
+	// configure plugin
 	api.ConfigureServer(l, conf)
 	setup.ConfigureServer(l, conf)
+	guard.ConfigureServer(l, conf)
 
 	return l
 }
@@ -94,7 +99,46 @@ func (r Response) GetBody() []byte {
 	return r.RawBody
 }
 
-func RunRequest(method string, path string, body interface{}) (*Response, error) {
+func GetAuthHeader(t *testing.T, ts *httptest.Server) map[string]string {
+	return map[string]string{
+		"Authorization": fmt.Sprintf("Bearer %s", GetAuthToken(t, ts)),
+	}
+}
+
+func GetAuthToken(t *testing.T, ts *httptest.Server) string {
+	res, _ := RunRequest("POST", fmt.Sprintf("%s/login", ts.URL), url.Values{
+		"username": {"test-admin"},
+		"password": {"admin"},
+	})
+
+	assert.Equal(t, 200, res.StatusCode)
+
+	b := bytes.NewBuffer([]byte(""))
+	io.Copy(b, res.Body)
+
+	v := &struct {
+		Status  string `json:"status"`
+		Message string `json:"message"`
+		Token   string `json:"token"`
+	}{}
+
+	json.Unmarshal(b.Bytes(), v)
+
+	return v.Token
+}
+
+func RunRequest(method string, path string, options ...interface{}) (*Response, error) {
+	var body interface{}
+	var headers map[string]string
+
+	if len(options) > 0 {
+		body = options[0]
+	}
+
+	if len(options) > 1 {
+		headers = options[1].(map[string]string)
+	}
+
 	client := &http.Client{}
 	var req *http.Request
 	var err error
@@ -115,6 +159,12 @@ func RunRequest(method string, path string, body interface{}) (*Response, error)
 		panic(fmt.Sprintf("please add a new test case for %T", body))
 	}
 
+	if headers != nil {
+		for name, value := range headers {
+			req.Header.Set(name, value)
+		}
+	}
+
 	core.PanicOnError(err)
 
 	resp, err := client.Do(req)
@@ -142,14 +192,31 @@ func RunHttpTest(t *testing.T, f func(t *testing.T, ts *httptest.Server, app *go
 			}
 		}()
 
-		res, err = RunRequest("PUT", ts.URL+"/uninstall", nil)
+		res, err = RunRequest("PUT", ts.URL+"/setup/uninstall", nil)
 		core.PanicIf(res.StatusCode != http.StatusOK, fmt.Sprintf("Expected code 200, get %d\n%s", res.StatusCode, string(res.GetBody()[:])))
 		core.PanicOnError(err)
 
-		res, err = RunRequest("PUT", ts.URL+"/install", nil)
+		res, err = RunRequest("PUT", ts.URL+"/setup/install", nil)
 		core.PanicIf(res.StatusCode != http.StatusOK, fmt.Sprintf("Expected code 200, get %d\n%s", res.StatusCode, string(res.GetBody()[:])))
 		core.PanicOnError(err)
 
+		// create a valid user
+		manager := app.Get("gonode.manager").(*core.PgNodeManager)
+
+		u := app.Get("gonode.handler_collection").(core.HandlerCollection).NewNode("core.user")
+		u.Name = "User ZZ"
+		data := u.Data.(*user.User)
+		data.Email = "test-admin@example.org"
+		data.Enabled = true
+		data.NewPassword = "admin"
+		data.Username = "test-admin"
+		data.Roles = []string{"ADMIN"}
+
+		meta := u.Meta.(*user.UserMeta)
+		meta.PasswordCost = 1 // save test time
+
+		manager.Save(u, false)
+
 		f(t, ts, app)
 
 		state.Out <- 1
