Repository: Nerzal/gocloak
Pull request #49 (2019-05-14 19:26:55)

Refactoring of the Login functions
Add GetToken function to obtain a token.
Add TokenOptions structure to provide an ability to use response types, scopes and other token options.
Add IDToken paramater in JWT structure.
Log the Resty requests and responses in unit tests.


codecov[bot]: # [Codecov](https://codecov.io/gh/Nerzal/gocloak/pull/49?src=pr&el=h1) Report
> Merging [#49](https://codecov.io/gh/Nerzal/gocloak/pull/49?src=pr&el=desc) into [master](https://codecov.io/gh/Nerzal/gocloak/commit/ca871bd50af7b922474f9bf440dd36edb66a8663?src=pr&el=desc) will **increase** coverage by `2.33%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/Nerzal/gocloak/pull/49/graphs/tree.svg?width=650&token=gB52LSVNIV&height=150&src=pr)](https://codecov.io/gh/Nerzal/gocloak/pull/49?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #49      +/-   ##
==========================================
+ Coverage   68.19%   70.53%   +2.33%     
==========================================
  Files           3        3              
  Lines         544      526      -18     
==========================================
  Hits          371      371              
+ Misses        128      119       -9     
+ Partials       45       36       -9
```


| [Impacted Files](https://codecov.io/gh/Nerzal/gocloak/pull/49?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [client.go](https://codecov.io/gh/Nerzal/gocloak/pull/49/diff?src=pr&el=tree#diff-Y2xpZW50Lmdv) | `70.71% <100%> (+1.79%)` | :arrow_up: |
| [models.go](https://codecov.io/gh/Nerzal/gocloak/pull/49/diff?src=pr&el=tree#diff-bW9kZWxzLmdv) | `72.72% <100%> (+27.27%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Nerzal/gocloak/pull/49?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Nerzal/gocloak/pull/49?src=pr&el=footer). Last update [ca871bd...258ae3a](https://codecov.io/gh/Nerzal/gocloak/pull/49?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

SVilgelm: I removed the commit with returning arrays, will send a separate PR

Patch:
diff --git a/client.go b/client.go
index d63afcb..ef82349 100644
--- a/client.go
+++ b/client.go
@@ -3,7 +3,6 @@ package gocloak
 import (
 	"encoding/base64"
 	"errors"
-	"log"
 	"strings"
 	"time"
 
@@ -21,13 +20,6 @@ type gocloak struct {
 	}
 }
 
-type loginData struct {
-	ClientID  string `json:"client_id"`
-	UserName  string `json:"username"`
-	Password  string `json:"password"`
-	GrantType string `json:"grant_type"`
-}
-
 const (
 	adminClientID string = "admin-cli"
 	urlSeparator  string = "/"
@@ -69,7 +61,6 @@ func checkForError(resp *resty.Response, err error) error {
 		if resp.StatusCode() == 409 {
 			return &ObjectAlreadyExists{}
 		}
-		log.Printf("Error: Request returned a response with status '%s' and body: %s", resp.Status(), string(resp.Body()))
 		return errors.New(resp.Status())
 	}
 	return nil
@@ -161,7 +152,7 @@ func (client *gocloak) GetCerts(realm string) (*CertResponse, error) {
 	return cert, nil
 }
 
-// GetIssuer gets the isser of the given realm
+// GetIssuer gets the issuer of the given realm
 func (client *gocloak) GetIssuer(realm string) (*IssuerResponse, error) {
 	var result IssuerResponse
 	resp, err := client.restyClient.R().
@@ -207,13 +198,13 @@ func (client *gocloak) DecodeAccessToken(accessToken string, realm string) (*jwt
 
 	usedKey := findUsedKey(decodedHeader.Kid, certResult.Keys)
 	if usedKey == nil {
-		return nil, nil, errors.New("Cannot find a key to decode the token")
+		return nil, nil, errors.New("cannot find a key to decode the token")
 	}
 
 	return jwx.DecodeAccessToken(accessToken, usedKey.E, usedKey.N)
 }
 
-// DecodeAccesTokenCustomClaims decodes the accessToken and writes claims into the given claims
+// DecodeAccessTokenCustomClaims decodes the accessToken and writes claims into the given claims
 func (client *gocloak) DecodeAccessTokenCustomClaims(accessToken string, realm string, claims jwt.Claims) (*jwt.Token, error) {
 	decodedHeader, err := jwx.DecodeAccessTokenHeader(accessToken)
 	if err != nil {
@@ -229,102 +220,67 @@ func (client *gocloak) DecodeAccessTokenCustomClaims(accessToken string, realm s
 	return token, err
 }
 
-// RefreshToken refrehes the given token
-func (client *gocloak) RefreshToken(refreshToken string, clientID, clientSecret, realm string) (*JWT, error) {
-	var result JWT
-	resp, err := client.restyClient.R().
-		SetFormData(map[string]string{
-			"client_id":     clientID,
-			"client_secret": clientSecret,
-			"grant_type":    "refresh_token",
-			"refresh_token": refreshToken,
-		}).
-		SetResult(&result).
+func (client *gocloak) GetToken(realm string, options TokenOptions) (*JWT, error) {
+	var token JWT
+	var req *resty.Request
+	if len(options.ClientSecret) > 0 {
+		req = client.getRequestWithBasicAuth(options.ClientID, options.ClientSecret)
+	} else {
+		req = client.restyClient.R()
+	}
+	resp, err := req.SetFormData(options.FormData()).
+		SetResult(&token).
 		Post(client.getRealmURL(realm, tokenEndpoint))
 
 	if err := checkForError(resp, err); err != nil {
 		return nil, err
 	}
 
-	if result.AccessToken == "" {
-		return nil, errors.New("Authentication Failed")
-	}
+	return &token, nil
+}
 
-	return &result, nil
+// RefreshToken refreshes the given token
+func (client *gocloak) RefreshToken(refreshToken, clientID, clientSecret, realm string) (*JWT, error) {
+	return client.GetToken(realm, TokenOptions{
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		GrantType:    "refresh_token",
+		RefreshToken: refreshToken,
+	})
 }
 
-// LoginAdmin performs a login
+// LoginAdmin performs a login with Admin client
 func (client *gocloak) LoginAdmin(username, password, realm string) (*JWT, error) {
-	var result JWT
-	resp, err := client.restyClient.R().
-		SetFormData(map[string]string{
-			"client_id":  adminClientID,
-			"grant_type": "password",
-			"username":   username,
-			"password":   password,
-		}).
-		SetResult(&result).
-		Post(client.getRealmURL(realm, tokenEndpoint))
-
-	if err := checkForError(resp, err); err != nil {
-		return nil, err
-	}
-
-	if result.AccessToken == "" {
-		return nil, errors.New("Authentication Failed")
-	}
-
-	return &result, nil
+	return client.GetToken(realm, TokenOptions{
+		ClientID:  adminClientID,
+		GrantType: "password",
+		Username:  username,
+		Password:  password,
+	})
 }
 
-// Login performs a login
+// Login performs a login with client credentials
 func (client *gocloak) LoginClient(clientID, clientSecret, realm string) (*JWT, error) {
-	var result JWT
-	resp, err := client.getRequestWithBasicAuth(clientID, clientSecret).
-		SetFormData(map[string]string{
-			"client_id":     clientID,
-			"client_secret": clientSecret,
-			"grant_type":    "client_credentials",
-		}).
-		SetResult(&result).
-		Post(client.getRealmURL(realm, tokenEndpoint))
-
-	if err := checkForError(resp, err); err != nil {
-		return nil, err
-	}
-
-	if result.AccessToken == "" {
-		return nil, errors.New("Authentication Failed")
-	}
-
-	return &result, nil
+	return client.GetToken(realm, TokenOptions{
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		GrantType:    "client_credentials",
+	})
 }
 
-// Login like login, but with basic auth
-func (client *gocloak) Login(clientID string, clientSecret string, realm string, username string, password string) (*JWT, error) {
-	var result JWT
-	resp, err := client.getRequestWithBasicAuth(clientID, clientSecret).
-		SetFormData(map[string]string{
-			"grant_type": "password",
-			"username":   username,
-			"password":   password,
-		}).
-		SetResult(&result).
-		Post(client.getRealmURL(realm, tokenEndpoint))
-
-	if err := checkForError(resp, err); err != nil {
-		return nil, err
-	}
-
-	if result.AccessToken == "" {
-		return nil, errors.New("Authentication Failed")
-	}
-
-	return &result, nil
+// Login performs a login with user credentials and a client
+func (client *gocloak) Login(clientID, clientSecret, realm, username, password string) (*JWT, error) {
+	return client.GetToken(realm, TokenOptions{
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		GrantType:    "password",
+		Username:     username,
+		Password:     password,
+	})
 }
 
 // Logout logs out users with refresh token
-func (client *gocloak) Logout(clientID string, clientSecret string, realm string, refreshToken string) error {
+func (client *gocloak) Logout(clientID, clientSecret, realm, refreshToken string) error {
 	resp, err := client.getRequestWithBasicAuth(clientID, clientSecret).
 		SetFormData(map[string]string{
 			"client_id":     clientID,
@@ -335,28 +291,16 @@ func (client *gocloak) Logout(clientID string, clientSecret string, realm string
 	return checkForError(resp, err)
 }
 
-// RequestPermission l
-func (client *gocloak) RequestPermission(clientID string, clientSecret string, realm string, username string, password string, permission string) (*JWT, error) {
-	var result JWT
-	resp, err := client.getRequestWithBasicAuth(clientID, clientSecret).
-		SetFormData(map[string]string{
-			"grant_type": "password",
-			"username":   username,
-			"password":   password,
-			"permission": permission,
-		}).
-		SetResult(&result).
-		Post(client.getRealmURL(realm, tokenEndpoint))
-
-	if err := checkForError(resp, err); err != nil {
-		return nil, err
-	}
-
-	if result.AccessToken == "" {
-		return nil, errors.New("Authentication Failed")
-	}
-
-	return &result, nil
+// RequestPermission request a permission
+func (client *gocloak) RequestPermission(clientID, clientSecret, realm, username, password string, permission string) (*JWT, error) {
+	return client.GetToken(realm, TokenOptions{
+		ClientID:     clientID,
+		ClientSecret: clientSecret,
+		GrantType:    "password",
+		Username:     username,
+		Password:     password,
+		Permission:   permission,
+	})
 }
 
 // ExecuteActionsEmail executes an actions email
@@ -537,7 +481,7 @@ func (client *gocloak) GetKeyStoreConfig(token string, realm string) (*KeyStoreC
 	return &result, nil
 }
 
-// GetComponents get all cimponents in realm
+// GetComponents get all components in realm
 func (client *gocloak) GetComponents(token string, realm string) (*[]Component, error) {
 	var result []Component
 	resp, err := client.getRequestWithBearerAuth(token).
@@ -852,7 +796,7 @@ func (client *gocloak) DeleteUser(token string, realm string, userID string) err
 // GetUserByID fetches a user from the given realm with the given userID
 func (client *gocloak) GetUserByID(accessToken string, realm string, userID string) (*User, error) {
 	if userID == "" {
-		return nil, errors.New("UserID shall not be empty")
+		return nil, errors.New("userID shall not be empty")
 	}
 
 	var result User
diff --git a/gocloak.go b/gocloak.go
index 9ac247b..db99dcc 100644
--- a/gocloak.go
+++ b/gocloak.go
@@ -10,16 +10,18 @@ type GoCloak interface {
 	// RestyClient returns a resty client that gocloak uses
 	RestyClient() *resty.Client
 
+	// GetToken returns a token
+	GetToken(realm string, options TokenOptions) (*JWT, error)
 	// Login sends a request to the token endpoint using user and client credentials
-	Login(clientID string, clientSecret string, realm string, username string, password string) (*JWT, error)
+	Login(clientID, clientSecret, realm, username, password string) (*JWT, error)
 	// Logout sends a request to the logout endpoint using refresh token
 	Logout(clientID, clientSecret, realm, refreshToken string) error
 	// LoginClient sends a request to the token endpoint using client credentials
 	LoginClient(clientID, clientSecret, realm string) (*JWT, error)
 	// LoginAdmin login as admin
 	LoginAdmin(username, password, realm string) (*JWT, error)
-	// RequestPermisssion sends a request to the token endpoint with permission parameter
-	RequestPermission(clientID string, clientSecret string, realm string, username string, password string, permission string) (*JWT, error)
+	// RequestPermission sends a request to the token endpoint with permission parameter
+	RequestPermission(clientID, clientSecret, realm, username, password, permission string) (*JWT, error)
 	// RefreshToken used to refresh the token
 	RefreshToken(refreshToken string, clientID, clientSecret, realm string) (*JWT, error)
 	// DecodeAccessToken decodes the accessToken
diff --git a/models.go b/models.go
index 0235662..4f518f6 100644
--- a/models.go
+++ b/models.go
@@ -2,6 +2,7 @@ package gocloak
 
 import (
 	"encoding/json"
+	"strings"
 )
 
 // GetQueryParams converts the struct to map[string]string
@@ -515,3 +516,35 @@ type CredentialRepresentation struct {
 	Type              string             `json:"type,omitempty"`
 	Value             string             `json:"value,omitempty"`
 }
+
+// TokenOptions represents the options to obtain a token
+type TokenOptions struct {
+	ClientID      string   `json:"client_id"`
+	ClientSecret  string   `json:"-"`
+	GrantType     string   `json:"grant_type"`
+	RefreshToken  string   `json:"refresh_token,omitempty"`
+	Scopes        []string `json:"-"`
+	Scope         string   `json:"scope,omitempty"`
+	ResponseTypes []string `json:"-"`
+	ResponseType  string   `json:"response_type,omitempty"`
+	Permission    string   `json:"permission,omitempty"`
+	Username      string   `json:"username,omitempty"`
+	Password      string   `json:"password,omitempty"`
+}
+
+// FormData returns a map of options to be used in SetFormData function
+func (t *TokenOptions) FormData() map[string]string {
+	if len(t.Scopes) > 0 {
+		t.Scope = strings.Join(t.Scopes, " ")
+	}
+	if len(t.ResponseTypes) > 0 {
+		t.ResponseType = strings.Join(t.ResponseTypes, " ")
+	}
+	if len(t.ResponseType) == 0 {
+		t.ResponseType = "token"
+	}
+	m, _ := json.Marshal(t)
+	var res map[string]string
+	_ = json.Unmarshal(m, &res)
+	return res
+}
diff --git a/token.go b/token.go
index 121a94b..4735102 100644
--- a/token.go
+++ b/token.go
@@ -3,6 +3,7 @@ package gocloak
 // JWT is a JWT
 type JWT struct {
 	AccessToken      string `json:"access_token"`
+	IDToken          string `json:"id_token"`
 	ExpiresIn        int    `json:"expires_in"`
 	RefreshExpiresIn int    `json:"refresh_expires_in"`
 	RefreshToken     string `json:"refresh_token"`


Test patch:
diff --git a/client_test.go b/client_test.go
index e1cfd54..825b6c9 100644
--- a/client_test.go
+++ b/client_test.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"github.com/dgrijalva/jwt-go"
 	"gopkg.in/resty.v1"
+	"io"
 	"io/ioutil"
 	"math/rand"
 	"net/http"
@@ -81,7 +82,10 @@ func GetConfig(t *testing.T) *Config {
 		}
 		configFile, err := os.Open(configFileName)
 		FailIfErr(t, err, "cannot open config.json")
-		defer configFile.Close()
+		defer func() {
+			err := configFile.Close()
+			FailIfErr(t, err, "cannot close config file")
+		}()
 		data, err := ioutil.ReadAll(configFile)
 		FailIfErr(t, err, "cannot read config.json")
 		config = &Config{}
@@ -235,10 +239,33 @@ func SetUpTestUser(t *testing.T, client GoCloak) {
 			cfg.GoCloak.Realm,
 			cfg.GoCloak.Password,
 			false)
-		FailIfErr(t, err, "SetPassword	 failed")
+		//client.GetRealmRole(token.AccessToken, cfg.GoCloak.Realm, "")
+		FailIfErr(t, err, "SetPassword failed")
 	})
 }
 
+type RestyLogWriter struct {
+	io.Writer
+	t *testing.T
+}
+
+func (w *RestyLogWriter) Write(p []byte) (n int, err error) {
+	w.t.Log(string(p))
+	return len(p), nil
+}
+
+func NewClientWithDebug(t *testing.T) GoCloak {
+	cfg := GetConfig(t)
+	client := NewClient(cfg.HostName)
+	restyClient := client.RestyClient()
+	restyClient.SetDebug(true)
+	restyClient.SetLogger(&RestyLogWriter{
+		t: t,
+	})
+
+	return client
+}
+
 func TestGetQueryParams(t *testing.T) {
 	t.Parallel()
 
@@ -270,8 +297,7 @@ func TestGetQueryParams(t *testing.T) {
 
 func TestGocloak_RestyClient(t *testing.T) {
 	t.Parallel()
-	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	restyClient := client.RestyClient()
 	FailIf(
 		t,
@@ -287,7 +313,7 @@ func TestGocloak_RestyClient(t *testing.T) {
 func TestGocloak_GetUserInfo(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetClientToken(t, client)
 
 	userInfo, err := client.GetUserInfo(
@@ -300,7 +326,7 @@ func TestGocloak_GetUserInfo(t *testing.T) {
 func TestGocloak_RequestPermission(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	SetUpTestUser(t, client)
 	token, err := client.RequestPermission(
 		cfg.GoCloak.ClientID,
@@ -324,7 +350,7 @@ func TestGocloak_RequestPermission(t *testing.T) {
 func TestGocloak_GetCerts(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	certs, err := client.GetCerts(cfg.GoCloak.Realm)
 	t.Log(certs)
 	FailIfErr(t, err, "get certs")
@@ -333,7 +359,7 @@ func TestGocloak_GetCerts(t *testing.T) {
 func TestGocloak_LoginClient_UnknownRealm(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	_, err := client.LoginClient(
 		cfg.GoCloak.ClientID,
 		cfg.GoCloak.ClientSecret,
@@ -347,7 +373,7 @@ func TestGocloak_LoginClient_UnknownRealm(t *testing.T) {
 func TestGocloak_GetIssuer(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	issuer, err := client.GetIssuer(cfg.GoCloak.Realm)
 	t.Log(issuer)
 	FailIfErr(t, err, "get issuer")
@@ -356,7 +382,7 @@ func TestGocloak_GetIssuer(t *testing.T) {
 func TestGocloak_RetrospectToken_InactiveToken(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 
 	rptResult, err := client.RetrospectToken(
 		"foobar",
@@ -372,7 +398,7 @@ func TestGocloak_RetrospectToken_InactiveToken(t *testing.T) {
 func TestGocloak_RetrospectToken(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetClientToken(t, client)
 
 	rptResult, err := client.RetrospectToken(
@@ -388,7 +414,7 @@ func TestGocloak_RetrospectToken(t *testing.T) {
 func TestGocloak_DecodeAccessToken(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetClientToken(t, client)
 
 	resultToken, claims, err := client.DecodeAccessToken(
@@ -402,7 +428,7 @@ func TestGocloak_DecodeAccessToken(t *testing.T) {
 func TestGocloak_DecodeAccessTokenCustomClaims(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetClientToken(t, client)
 
 	claims := jwt.MapClaims{}
@@ -418,7 +444,7 @@ func TestGocloak_DecodeAccessTokenCustomClaims(t *testing.T) {
 func TestGocloak_RefreshToken(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetClientToken(t, client)
 
 	token, err := client.RefreshToken(
@@ -437,8 +463,7 @@ func TestGocloak_UserAttributeContains(t *testing.T) {
 	attributes["foo"] = []string{"bar", "alice", "bob", "roflcopter"}
 	attributes["bar"] = []string{"baz"}
 
-	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	ok := client.UserAttributeContains(attributes, "foo", "alice")
 	FailIf(t, !ok, "UserAttributeContains")
 }
@@ -446,7 +471,7 @@ func TestGocloak_UserAttributeContains(t *testing.T) {
 func TestGocloak_GetKeyStoreConfig(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	config, err := client.GetKeyStoreConfig(
@@ -459,7 +484,7 @@ func TestGocloak_GetKeyStoreConfig(t *testing.T) {
 func TestGocloak_Login(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	SetUpTestUser(t, client)
 	_, err := client.Login(
 		cfg.GoCloak.ClientID,
@@ -470,10 +495,34 @@ func TestGocloak_Login(t *testing.T) {
 	FailIfErr(t, err, "Login failed")
 }
 
+func TestGocloak_GetToken(t *testing.T) {
+	t.Parallel()
+	t.Failed()
+	cfg := GetConfig(t)
+	client := NewClientWithDebug(t)
+	SetUpTestUser(t, client)
+	newToken, err := client.GetToken(
+		cfg.GoCloak.Realm,
+		TokenOptions{
+			ClientID:      cfg.GoCloak.ClientID,
+			ClientSecret:  cfg.GoCloak.ClientSecret,
+			Username:      cfg.GoCloak.UserName,
+			Password:      cfg.GoCloak.Password,
+			GrantType:     "password",
+			ResponseTypes: []string{"token", "id_token"},
+			Scopes:        []string{"openid", "offline_access"},
+		},
+	)
+	FailIfErr(t, err, "Login failed")
+	t.Logf("New token: %+v", *newToken)
+	FailIf(t, newToken.RefreshExpiresIn > 0, "Got a refresh token instead of offline")
+	FailIf(t, len(newToken.IDToken) == 0, "Got an empty if token")
+}
+
 func TestGocloak_LoginClient(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	_, err := client.LoginClient(
 		cfg.GoCloak.ClientID,
 		cfg.GoCloak.ClientSecret,
@@ -484,7 +533,7 @@ func TestGocloak_LoginClient(t *testing.T) {
 func TestGocloak_LoginAdmin(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	_, err := client.LoginAdmin(
 		cfg.Admin.UserName,
 		cfg.Admin.Password,
@@ -495,7 +544,7 @@ func TestGocloak_LoginAdmin(t *testing.T) {
 func TestGocloak_SetPassword(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, userID := CreateUser(t, client)
@@ -512,8 +561,7 @@ func TestGocloak_SetPassword(t *testing.T) {
 
 func TestGocloak_CreateGroup(t *testing.T) {
 	t.Parallel()
-	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 
 	tearDown, _ := CreateGroup(t, client)
 	defer tearDown()
@@ -522,7 +570,7 @@ func TestGocloak_CreateGroup(t *testing.T) {
 func TestGocloak_CreateClientRole(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	role := Role{
@@ -536,17 +584,19 @@ func TestGocloak_CreateClientRole(t *testing.T) {
 		testClient.ID,
 		role)
 	FailIfErr(t, err, "CreateClientRole failed")
-	defer client.DeleteClientRole(
+	err = client.DeleteClientRole(
 		token.AccessToken,
 		cfg.GoCloak.Realm,
 		testClient.ID,
-		role.Name)
+		role.Name,
+	)
+	FailIfErr(t, err, "DeleteClientRole failed")
 }
 
 func TestGocloak_CreateClient(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	newClient := Client{
@@ -558,16 +608,17 @@ func TestGocloak_CreateClient(t *testing.T) {
 		cfg.GoCloak.Realm,
 		newClient)
 	FailIfErr(t, err, "CreateClient failed")
-	defer client.DeleteClient(
+	err = client.DeleteClient(
 		token.AccessToken,
 		cfg.GoCloak.Realm,
 		newClient.ID)
+	FailIfErr(t, err, "DeleteClient failed")
 }
 
 func TestGocloak_GetGroups(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	_, err := client.GetGroups(
@@ -580,7 +631,7 @@ func TestGocloak_GetGroups(t *testing.T) {
 func TestGocloak_GetClients(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	_, err := client.GetClients(
@@ -595,7 +646,7 @@ func TestGocloak_GetClients(t *testing.T) {
 func TestGocloak_GetClientRoles(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	testClient := GetClientByClientID(t, client, cfg.GoCloak.ClientID)
@@ -610,7 +661,7 @@ func TestGocloak_GetClientRoles(t *testing.T) {
 func TestGocloak_GetRoleMappingByGroupID(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, groupID := CreateGroup(t, client)
@@ -626,7 +677,7 @@ func TestGocloak_GetRoleMappingByGroupID(t *testing.T) {
 func TestGocloak_GetRoleMappingByUserID(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, userID := CreateUser(t, client)
@@ -642,7 +693,7 @@ func TestGocloak_GetRoleMappingByUserID(t *testing.T) {
 func TestGocloak_ExecuteActionsEmail_UpdatePassword(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, userID := CreateUser(t, client)
@@ -670,7 +721,7 @@ func TestGocloak_ExecuteActionsEmail_UpdatePassword(t *testing.T) {
 func TestGocloak_Logout(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetUserToken(t, client)
 
 	err := client.Logout(
@@ -684,7 +735,7 @@ func TestGocloak_Logout(t *testing.T) {
 func TestGocloak_GetRealm(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	r, err := client.GetRealm(
@@ -720,8 +771,7 @@ func CreateRealm(t *testing.T, client GoCloak) (func(), string) {
 
 func TestGocloak_CreateRealm(t *testing.T) {
 	t.Parallel()
-	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	tearDown, _ := CreateRealm(t, client)
 	defer tearDown()
 }
@@ -756,8 +806,7 @@ func CreateRealmRole(t *testing.T, client GoCloak) (func(), string) {
 
 func TestGocloak_CreateRealmRole(t *testing.T) {
 	t.Parallel()
-	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	tearDown, _ := CreateRealmRole(t, client)
 	defer tearDown()
 }
@@ -765,7 +814,7 @@ func TestGocloak_CreateRealmRole(t *testing.T) {
 func TestGocloak_GetRealmRole(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, roleName := CreateRealmRole(t, client)
@@ -775,8 +824,8 @@ func TestGocloak_GetRealmRole(t *testing.T) {
 		token.AccessToken,
 		cfg.GoCloak.Realm,
 		roleName)
-	t.Logf("Role: %+v", *role)
 	FailIfErr(t, err, "GetRealmRole failed")
+	t.Logf("Role: %+v", *role)
 	FailIf(
 		t,
 		role.Name != roleName,
@@ -787,7 +836,7 @@ func TestGocloak_GetRealmRole(t *testing.T) {
 func TestGocloak_GetRealmRoles(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, _ := CreateRealmRole(t, client)
@@ -796,14 +845,14 @@ func TestGocloak_GetRealmRoles(t *testing.T) {
 	roles, err := client.GetRealmRoles(
 		token.AccessToken,
 		cfg.GoCloak.Realm)
-	t.Logf("Roles: %+v", *roles)
 	FailIfErr(t, err, "GetRealmRoles failed")
+	t.Logf("Roles: %+v", *roles)
 }
 
 func TestGocloak_UpdateRealmRole(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	newRoleName := GetRandomName("Role")
@@ -836,7 +885,7 @@ func TestGocloak_UpdateRealmRole(t *testing.T) {
 func TestGocloak_DeleteRealmRole(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	_, roleName := CreateRealmRole(t, client)
@@ -851,7 +900,7 @@ func TestGocloak_DeleteRealmRole(t *testing.T) {
 func TestGocloak_AddRealmRoleToUser(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDownUser, userID := CreateUser(t, client)
@@ -877,7 +926,7 @@ func TestGocloak_AddRealmRoleToUser(t *testing.T) {
 func TestGocloak_GetRealmRolesByUserID(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDownUser, userID := CreateUser(t, client)
@@ -903,8 +952,8 @@ func TestGocloak_GetRealmRolesByUserID(t *testing.T) {
 		token.AccessToken,
 		cfg.GoCloak.Realm,
 		userID)
-	t.Logf("User roles: %+v", *roles)
 	FailIfErr(t, err, "GetRealmRolesByUserID failed")
+	t.Logf("User roles: %+v", *roles)
 	for _, r := range *roles {
 		if r.Name == role.Name {
 			return
@@ -916,7 +965,7 @@ func TestGocloak_GetRealmRolesByUserID(t *testing.T) {
 func TestGocloak_GetRealmRolesByGroupID(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, groupID := CreateGroup(t, client)
@@ -932,7 +981,7 @@ func TestGocloak_GetRealmRolesByGroupID(t *testing.T) {
 func TestGocloak_DeleteRealmRoleFromUser(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDownUser, userID := CreateUser(t, client)
@@ -978,7 +1027,7 @@ func TestGocloak_DeleteRealmRoleFromUser(t *testing.T) {
 func TestGocloak_AddRealmRoleComposite(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, compositeRole := CreateRealmRole(t, client)
@@ -998,7 +1047,7 @@ func TestGocloak_AddRealmRoleComposite(t *testing.T) {
 func TestGocloak_DeleteRealmRoleComposite(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, compositeRole := CreateRealmRole(t, client)
@@ -1043,8 +1092,8 @@ func CreateUser(t *testing.T, client GoCloak) (func(), string) {
 		token.AccessToken,
 		cfg.GoCloak.Realm,
 		user)
-	user.ID = *userID
 	FailIfErr(t, err, "CreateUser failed")
+	user.ID = *userID
 	t.Logf("Created User: %+v", user)
 	tearDown := func() {
 		err := client.DeleteUser(
@@ -1059,8 +1108,7 @@ func CreateUser(t *testing.T, client GoCloak) (func(), string) {
 
 func TestGocloak_CreateUser(t *testing.T) {
 	t.Parallel()
-	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 
 	tearDown, _ := CreateUser(t, client)
 	defer tearDown()
@@ -1069,7 +1117,7 @@ func TestGocloak_CreateUser(t *testing.T) {
 func TestGocloak_CreateUserCustomAttributes(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, userID := CreateUser(t, client)
@@ -1089,7 +1137,7 @@ func TestGocloak_CreateUserCustomAttributes(t *testing.T) {
 func TestGocloak_GetUserByID(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, userID := CreateUser(t, client)
@@ -1106,7 +1154,7 @@ func TestGocloak_GetUserByID(t *testing.T) {
 func TestGocloak_GetUsers(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	users, err := client.GetUsers(
@@ -1122,7 +1170,7 @@ func TestGocloak_GetUsers(t *testing.T) {
 func TestGocloak_GetUserCount(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	count, err := client.GetUserCount(
@@ -1135,7 +1183,7 @@ func TestGocloak_GetUserCount(t *testing.T) {
 func TestGocloak_AddUserToGroup(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 	tearDownUser, userID := CreateUser(t, client)
 	defer tearDownUser()
@@ -1155,7 +1203,7 @@ func TestGocloak_AddUserToGroup(t *testing.T) {
 func TestGocloak_DeleteUserFromGroup(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 	tearDownUser, userID := CreateUser(t, client)
 	defer tearDownUser()
@@ -1181,7 +1229,7 @@ func TestGocloak_DeleteUserFromGroup(t *testing.T) {
 func TestGocloak_GetUserGroups(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDownUser, userID := CreateUser(t, client)
@@ -1214,8 +1262,7 @@ func TestGocloak_GetUserGroups(t *testing.T) {
 
 func TestGocloak_DeleteUser(t *testing.T) {
 	t.Parallel()
-	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 
 	tearDown, _ := CreateUser(t, client)
 	defer tearDown()
@@ -1224,7 +1271,7 @@ func TestGocloak_DeleteUser(t *testing.T) {
 func TestGocloak_UpdateUser(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDown, userID := CreateUser(t, client)
@@ -1245,7 +1292,7 @@ func TestGocloak_UpdateUser(t *testing.T) {
 func TestGocloak_GetUsersByRoleName(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
-	client := NewClient(cfg.HostName)
+	client := NewClientWithDebug(t)
 	token := GetAdminToken(t, client)
 
 	tearDownUser, userID := CreateUser(t, client)