diff --git a/.gitignore b/.gitignore
index 881fb9e8192042a5289a784d09fcac4421242eb4..a2cb4e04b9b8b2dc71fa54aba1a5cc820f51c1e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
 bin/
 release/
 tests/
+cover.html
+cover.out
 
 *.iml
 config.yml
diff --git a/Makefile b/Makefile
index d86dd2ebe7dfdcef8e79c6e4ad3b495d7d0829b5..f11cee3764828e943d5451690f6fe9c75a595ad2 100644
--- a/Makefile
+++ b/Makefile
@@ -79,6 +79,11 @@ format:
 	@echo "--> Running go fmt"
 	@go fmt $(PACKAGES)
 
+coverage:
+	@echo "--> Running go coverage"
+	@go test -coverprofile cover.out
+	@go tool cover -html=cover.out -o cover.html
+
 cover:
 	@echo "--> Running go cover"
 	@go test --cover
diff --git a/build.go b/build.go
index 92e253dcfae016cbe836c95e2fb0d5576d18634b..4f83b87c77e6d7690d053af96a6132e8a46c46ce 100644
--- a/build.go
+++ b/build.go
@@ -1,3 +1,3 @@
 package main
 
-const buildID = "v0.0.5, git+sha: 4c3ee98"
+const buildID = "v0.0.5, git+sha: 81b7def"
diff --git a/config_test.go b/config_test.go
index 3f5bbcfda60c30a156622b71b3bb02d2c7a6c5ca..ec787823150407c488845806dfc7d7d48586b2a2 100644
--- a/config_test.go
+++ b/config_test.go
@@ -21,6 +21,12 @@ import (
 	"testing"
 )
 
+func TestNewDefaultConfig(t *testing.T) {
+	if config := newDefaultConfig(); config == nil {
+		t.Errorf("we should have recieved a config")
+	}
+}
+
 func TestReadConfiguration(t *testing.T) {
 	testCases := []struct {
 		Content string
@@ -60,6 +66,97 @@ redirection_url: http://127.0.0.1:3000
 	}
 }
 
+func TestIsConfig(t *testing.T) {
+	tests := []struct {
+		Config *Config
+		Ok     bool
+	}{
+		{
+			Config: &Config{},
+		},
+		{
+			Config: &Config{
+				DiscoveryURL: "http://127.0.0.1:8080",
+			},
+		},
+		{
+			Config: &Config{
+				DiscoveryURL: "http://127.0.0.1:8080",
+				ClientID:     "client",
+				Secret:       "client",
+			},
+		},
+		{
+			Config: &Config{
+				Listen:         ":8080",
+				DiscoveryURL:   "http://127.0.0.1:8080",
+				ClientID:       "client",
+				Secret:         "client",
+				RedirectionURL: "http://120.0.0.1",
+			},
+		},
+		{
+			Config: &Config{
+				Listen:         ":8080",
+				DiscoveryURL:   "http://127.0.0.1:8080",
+				ClientID:       "client",
+				Secret:         "client",
+				RedirectionURL: "http://120.0.0.1",
+				Upstream:       "http://120.0.0.1",
+			},
+			Ok: true,
+		},
+		{
+			Config: &Config{
+				Listen:                ":8080",
+				SkipTokenVerification: true,
+				Upstream:              "http://120.0.0.1",
+			},
+			Ok: true,
+		},
+		{
+			Config: &Config{
+				DiscoveryURL:   "http://127.0.0.1:8080",
+				ClientID:       "client",
+				Secret:         "client",
+				RedirectionURL: "http://120.0.0.1",
+				Upstream:       "http://120.0.0.1",
+			},
+		},
+		{
+			Config: &Config{
+				Listen:         ":8080",
+				DiscoveryURL:   "http://127.0.0.1:8080",
+				ClientID:       "client",
+				Secret:         "client",
+				RedirectionURL: "http://120.0.0.1",
+			},
+		},
+		{
+			Config: &Config{
+				Listen:         ":8080",
+				DiscoveryURL:   "http://127.0.0.1:8080",
+				ClientID:       "client",
+				Secret:         "client",
+				RedirectionURL: "http://120.0.0.1",
+				Upstream:       "this should fail",
+			},
+		},
+	}
+
+	for i, c := range tests {
+		if err := c.Config.isValid(); err != nil && c.Ok {
+			t.Errorf("test case %d, the config should not have errored, error: %s", i, err)
+		}
+	}
+}
+
+func TestGetOptions(t *testing.T) {
+	if flags := getOptions(); flags == nil {
+		t.Errorf("we should have received some flags options")
+	}
+}
+
 func writeFakeConfigFile(t *testing.T, content string) *os.File {
 	f, err := ioutil.TempFile("", "node_label_file")
 	if err != nil {
diff --git a/cover_test.go b/cover_test.go
index 4ca5a5ce1585fed56ca2f88f50177bd143b36258..d087cb87a8f5c1cfc99ffae674f7098462e6438b 100644
--- a/cover_test.go
+++ b/cover_test.go
@@ -1 +1,16 @@
-package keycloak_proxy
+/*
+Copyright 2015 All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
diff --git a/doc.go b/doc.go
index 50bd787249bef30fbb4ddc94fa05fd2e3260a1f8..e2878d8b92b8bd373200fb899f4f4c8e75449dc5 100644
--- a/doc.go
+++ b/doc.go
@@ -27,7 +27,7 @@ import (
 
 const (
 	prog        = "keycloak-proxy"
-	version     = "v0.0.5"
+	version     = "v0.0.6"
 	author      = "Rohith"
 	email       = "gambol99@gmail.com"
 	description = "is a proxy using the keycloak service for auth and authorization"
@@ -72,8 +72,6 @@ type Resource struct {
 
 // Config is the configuration for the proxy
 type Config struct {
-	// Verbose switches on debug logging
-	Verbose bool `json:"verbose" yaml:"verbose"`
 	// LogRequests indicates if we should log all the requests
 	LogRequests bool `json:"log_requests" yaml:"log_requests"`
 	// LogFormat is the logging format
@@ -116,6 +114,10 @@ type Config struct {
 	ForbiddenPage string `json:"forbidden_page" yaml:"forbidden_page"`
 	// SkipTokenVerification tells the service to skipp verifying the access token - for testing purposes
 	SkipTokenVerification bool
+	// Verbose switches on debug logging
+	Verbose bool `json:"verbose" yaml:"verbose"`
+	// Hostname is a list of hostnames the service should response to
+	Hostnames []string `json:"hostnames" yaml:"hostname"`
 }
 
 // KeycloakProxy is the server component
diff --git a/handlers_test.go b/handlers_test.go
index 4ca5a5ce1585fed56ca2f88f50177bd143b36258..b619853339359759cdeb3b855c891162be742cc2 100644
--- a/handlers_test.go
+++ b/handlers_test.go
@@ -1 +1,149 @@
-package keycloak_proxy
+/*
+Copyright 2015 All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+	"net/http"
+	"testing"
+
+	"github.com/gambol99/go-oidc/jose"
+	"github.com/gin-gonic/gin"
+)
+
+func TestEntrypointHandler(t *testing.T) {
+	proxy := newFakeKeycloakProxy(t)
+	handler := proxy.entrypointHandler()
+
+	tests := []struct {
+		Context *gin.Context
+		Secure  bool
+	}{
+		{
+			Context: newFakeGinContext("GET", fakeAdminRoleURL), Secure: true,
+		},
+		{
+			Context: newFakeGinContext("GET", fakeAdminRoleURL+"/sso"), Secure: true,
+		},
+		{
+			Context: newFakeGinContext("GET", fakeAdminRoleURL+"/../sso"), Secure: true,
+		},
+		{
+			Context: newFakeGinContext("GET", "/not_secure"),
+		},
+		{
+			Context: newFakeGinContext("GET", oauthURL),
+		},
+	}
+
+	for i, c := range tests {
+		handler(c.Context)
+		if _, found := c.Context.Get(cxEnforce); c.Secure && !found {
+			t.Errorf("test case %d should have been set secure", i)
+		}
+	}
+}
+
+func TestAdmissionHandler(t *testing.T) {
+	proxy := newFakeKeycloakProxy(t)
+	handler := proxy.admissionHandler()
+	tests := []struct {
+		Context     *gin.Context
+		Resource    *Resource
+		UserContext *userContext
+		HTTPCode    int
+	}{
+		{
+			Context:  newFakeGinContext("GET", ""),
+			HTTPCode: http.StatusOK,
+		},
+		{
+			Context:  newFakeGinContext("GET", "/admin"),
+			HTTPCode: http.StatusForbidden,
+			Resource: &Resource{
+				URL:          fakeAdminRoleURL,
+				Methods:      []string{"GET"},
+				RolesAllowed: []string{fakeAdminRole},
+			},
+			UserContext: &userContext{
+				roles: []string{},
+			},
+		},
+		{
+			Context:  newFakeGinContext("GET", fakeAdminRoleURL),
+			HTTPCode: http.StatusOK,
+			Resource: &Resource{
+				URL:          fakeAdminRoleURL,
+				Methods:      []string{"GET"},
+				RolesAllowed: []string{fakeAdminRole},
+			},
+			UserContext: &userContext{
+				roles:  []string{fakeAdminRole},
+				claims: jose.Claims{"aud": fakeClientID},
+			},
+		},
+		{
+			Context:  newFakeGinContext("GET", fakeAdminRoleURL+"/sso"),
+			HTTPCode: http.StatusOK,
+			Resource: &Resource{
+				URL:          fakeAdminRoleURL,
+				Methods:      []string{"GET"},
+				RolesAllowed: []string{fakeAdminRole},
+			},
+			UserContext: &userContext{
+				roles:  []string{fakeTestRole, fakeAdminRole},
+				claims: jose.Claims{"aud": fakeClientID},
+			},
+		},
+		{
+			Context:  newFakeGinContext("GET", fakeTestRoleURL),
+			HTTPCode: http.StatusForbidden,
+			Resource: &Resource{
+				URL:          fakeAdminRoleURL,
+				Methods:      []string{"GET"},
+				RolesAllowed: []string{fakeTestRole, "test"},
+			},
+			UserContext: &userContext{
+				roles:  []string{fakeTestRole, fakeAdminRole},
+				claims: jose.Claims{"aud": fakeClientID},
+			},
+		},
+		{
+			Context:  newFakeGinContext("GET", fakeAdminRoleURL),
+			HTTPCode: http.StatusForbidden,
+			Resource: &Resource{
+				URL:          fakeAdminRoleURL,
+				Methods:      []string{"GET"},
+				RolesAllowed: []string{fakeTestRole, "test"},
+			},
+			UserContext: &userContext{
+				roles: []string{fakeTestRole, fakeAdminRole},
+			},
+		},
+	}
+
+	for i, c := range tests {
+		if c.Resource != nil {
+			c.Context.Set(cxEnforce, c.Resource)
+		}
+		if c.UserContext != nil {
+			c.Context.Set(userContextName, c.UserContext)
+		}
+		handler(c.Context)
+		if c.Context.Writer.Status() != c.HTTPCode {
+			t.Errorf("test case %d should have recieved code: %d, got %d", i, c.HTTPCode, c.Context.Writer.Status())
+		}
+	}
+}
diff --git a/resource_test.go b/resource_test.go
index 86b674925a5939719ea34499902994913d2af7a5..641890f52ea8cc95f24efa1cd947475e343c067e 100644
--- a/resource_test.go
+++ b/resource_test.go
@@ -51,6 +51,15 @@ func TestIsValid(t *testing.T) {
 	}
 }
 
+func TestResourceString(t *testing.T) {
+	resource := &Resource{
+		RolesAllowed: []string{"1", "2", "3"},
+	}
+	if s := resource.String(); s == "" {
+		t.Errorf("we should have recieved a string")
+	}
+}
+
 func TestGetRoles(t *testing.T) {
 	resource := &Resource{
 		RolesAllowed: []string{"1", "2", "3"},
diff --git a/server_test.go b/server_test.go
index 3d5ec9bfa037d99227ad3b96d9e9c239a043cbb6..c5a7e049232aa20fb6518616e282adb00e1cbcb3 100644
--- a/server_test.go
+++ b/server_test.go
@@ -16,25 +16,145 @@ limitations under the License.
 package main
 
 import (
+	"bufio"
+	"io/ioutil"
+	"net"
+	"net/http"
 	"testing"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/gin-gonic/gin"
+	"net/url"
+)
+
+const (
+	fakeClientID = "test"
+	fakeSecret   = fakeClientID
+
+	fakeAdminRoleURL      = "/admin"
+	fakeTestRoleURL       = "/test_role"
+	fakeTestAdminRolesURL = "/test_admin_roles"
+
+	fakeAdminRole = "role:admin"
+	fakeTestRole  = "role:test"
 )
 
 func newFakeKeycloakProxy(t *testing.T) *KeycloakProxy {
+	log.SetOutput(ioutil.Discard)
 	return &KeycloakProxy{
 		config: &Config{
-			DiscoveryURL:   "127.0.0.1:",
-			ClientID:       "test_client",
-			Secret:         "test_secret",
-			EncryptionKey:  "AgXa7xRcoClDEU0ZDSH4X0XhL5Qy2Z2j",
-			Scopes:         []string{},
-			RefreshSession: false,
+			DiscoveryURL:          "127.0.0.1:",
+			ClientID:              fakeClientID,
+			Secret:                fakeSecret,
+			EncryptionKey:         "AgXa7xRcoClDEU0ZDSH4X0XhL5Qy2Z2j",
+			SkipTokenVerification: true,
+			Scopes:                []string{},
+			RefreshSession:        false,
+			ClaimsMatch: map[string]string{
+				"aud": fakeClientID,
+			},
 			Resources: []*Resource{
 				&Resource{
-					URL:          "/protect",
+					URL:          fakeAdminRoleURL,
 					Methods:      []string{"GET"},
-					RolesAllowed: []string{"test_role"},
+					RolesAllowed: []string{fakeAdminRole},
+				},
+				&Resource{
+					URL:          fakeTestRoleURL,
+					Methods:      []string{"GET"},
+					RolesAllowed: []string{fakeTestRole},
+				},
+				&Resource{
+					URL:          fakeTestAdminRolesURL,
+					Methods:      []string{"GET"},
+					RolesAllowed: []string{fakeAdminRole, fakeTestRole},
 				},
 			},
 		},
 	}
 }
+
+func TestRedirectToAuthorization(t *testing.T) {
+	context := newFakeGinContext("GET", "/admin")
+	proxy := newFakeKeycloakProxy(t)
+
+	proxy.config.SkipTokenVerification = false
+	proxy.redirectToAuthorization(context)
+	if context.Writer.Status() != http.StatusTemporaryRedirect {
+		t.Errorf("we should have been given a temporary redirect")
+	}
+
+	proxy.config.SkipTokenVerification = true
+	proxy.redirectToAuthorization(context)
+	if context.Writer.Status() != http.StatusForbidden {
+		t.Errorf("we should have been given a forbidden code")
+	}
+}
+
+func TestRedirectURL(t *testing.T) {
+	context := newFakeGinContext("GET", "/admin")
+	proxy := newFakeKeycloakProxy(t)
+
+	if proxy.redirectToURL("http://127.0.0.1", context); context.Writer.Status() != http.StatusTemporaryRedirect {
+		t.Errorf("we should have recieved a redirect")
+	}
+
+	if !context.IsAborted() {
+		t.Errorf("the context should have been aborted")
+	}
+}
+
+func TestAccessForbidden(t *testing.T) {
+	context := newFakeGinContext("GET", "/admin")
+	proxy := newFakeKeycloakProxy(t)
+
+	proxy.config.SkipTokenVerification = false
+	if proxy.accessForbidden(context); context.Writer.Status() != http.StatusForbidden {
+		t.Errorf("we should have recieved a forbidden access")
+	}
+
+	proxy.config.SkipTokenVerification = true
+	if proxy.accessForbidden(context); context.Writer.Status() != http.StatusForbidden {
+		t.Errorf("we should have recieved a forbidden access")
+	}
+}
+
+func newFakeResponse() *fakeResponse {
+	return &fakeResponse{
+		status:  http.StatusOK,
+		headers: make(http.Header, 0),
+	}
+}
+
+func newFakeGinContext(method, uri string) *gin.Context {
+	return &gin.Context{
+		Request: &http.Request{
+			Method:     method,
+			RequestURI: uri,
+			URL: &url.URL{
+				Scheme: "http",
+				Host:   "127.0.0.1",
+				Path:   "uri",
+			},
+		},
+		Writer: newFakeResponse(),
+	}
+}
+
+type fakeResponse struct {
+	size    int
+	status  int
+	headers http.Header
+}
+
+func (r *fakeResponse) Flush()                                       {}
+func (r *fakeResponse) Written() bool                                { return false }
+func (r *fakeResponse) WriteHeaderNow()                              {}
+func (r *fakeResponse) Size() int                                    { return r.size }
+func (r *fakeResponse) Status() int                                  { return r.status }
+func (r *fakeResponse) Header() http.Header                          { return r.headers }
+func (r *fakeResponse) WriteHeader(code int)                         { r.status = code }
+func (r *fakeResponse) Write(content []byte) (int, error)            { return len(content), nil }
+func (r *fakeResponse) WriteString(s string) (int, error)            { return len(s), nil }
+func (r *fakeResponse) Hijack() (net.Conn, *bufio.ReadWriter, error) { return nil, nil, nil }
+func (r *fakeResponse) CloseNotify() <-chan bool                     { return make(chan bool, 0) }
diff --git a/user_context_test.go b/user_context_test.go
index ae944739bdc1b0150fd09538b3e41ed4f0a97f87..11f909fec655db2a53c419c7c86dbaca95ff6c18 100644
--- a/user_context_test.go
+++ b/user_context_test.go
@@ -32,7 +32,7 @@ func TestIsAudience(t *testing.T) {
 	}
 }
 
-func TestGetROles(t *testing.T) {
+func TestGetUserRoles(t *testing.T) {
 	user := &userContext{
 		roles: []string{"1", "2", "3"},
 	}
@@ -52,3 +52,12 @@ func TestIsExpired(t *testing.T) {
 		t.Errorf("we should have been false")
 	}
 }
+
+func TestIsBearerToken(t *testing.T) {
+	user := &userContext{
+		bearerToken: true,
+	}
+	if !user.isBearerToken() {
+		t.Errorf("the bearer token should have been true")
+	}
+}