diff --git a/authorizer/src/asapo_authorizer/cli/cli.go b/authorizer/src/asapo_authorizer/cli/cli.go
index 3bfde762cae35679406182a4b8abdc15adf5073b..e39bbd65638b2cfc93c02ec9023870f1d4acd5b9 100644
--- a/authorizer/src/asapo_authorizer/cli/cli.go
+++ b/authorizer/src/asapo_authorizer/cli/cli.go
@@ -3,8 +3,7 @@
 package cli
 
 import (
-	"asapo_authorizer/database"
-	"asapo_authorizer/server"
+	"asapo_authorizer/token_store"
 	"errors"
 	"flag"
 	"fmt"
@@ -18,7 +17,7 @@ var flHelp bool
 
 var outBuf io.Writer = os.Stdout
 
-var db database.Agent
+var store token_store.Store
 
 func printHelp(f *flag.FlagSet) bool {
 	if flHelp {
@@ -43,10 +42,9 @@ func DoCommand(name string, args []string) error {
 
 	method := methodVal.Interface().(func() error)
 
-	server.CreateDiscoveryService()
-	db = new(database.Mongodb)
-	server.InitDB(db)
-	defer db.Close()
+	store = new(token_store.TokenStore)
+	store.Init(nil)
+	defer store.Close()
 
 	return method()
 }
diff --git a/authorizer/src/asapo_authorizer/cli/command_test.go b/authorizer/src/asapo_authorizer/cli/command_test.go
index dd457455f141710adbc4ffed143ad0bdc8d045a6..6736fc763650de6f3f1f6dd25075af7a93de67b9 100644
--- a/authorizer/src/asapo_authorizer/cli/command_test.go
+++ b/authorizer/src/asapo_authorizer/cli/command_test.go
@@ -5,30 +5,31 @@ import (
 	"asapo_authorizer/server"
 	"asapo_common/utils"
 	"bytes"
-	"testing"
 	"github.com/stretchr/testify/assert"
+	"testing"
 )
 
 var CommandTests = []struct {
-	cmd    command
-	ok bool
-	msg string
+	cmd   command
+	error string
+	msg   string
 }{
-	{command{"create-token", []string{"-type", "user-token", "-beamtime","123","-access-types","read","-duration-days","1"}}, true,"ok"},
-	{command{"dummy", []string{"description"}}, false,"wrong command"},
+	{command{"create-token", []string{"-type", "user-token", "-beamtime", "123", "-access-types", "read", "-duration-days", "1"}}, "database", "ok"},
+	{command{"list-tokens", []string{}}, "database", "ok"},
+	{command{"revoke-token", []string{"-token","123"}}, "database", "ok"},
+	{command{"revoke-token", []string{"-token-id","123"}}, "database", "ok"},
+	{command{"dummy", []string{"description"}}, "wrong", "wrong command"},
 }
 
 func TestCommand(t *testing.T) {
 	outBuf = new(bytes.Buffer)
-	server.Auth = authorization.NewAuth(utils.NewJWTAuth("secret"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret"))
 
+	server.Auth = authorization.NewAuth(utils.NewJWTAuth("secret"), utils.NewJWTAuth("secret_admin"), utils.NewJWTAuth("secret"))
 	for _, test := range CommandTests {
 		outBuf.(*bytes.Buffer).Reset()
 		err := DoCommand(test.cmd.name, test.cmd.args)
-		if !test.ok {
-			assert.NotNil(t, err, "Should be error",test.msg)
-		} else {
-			assert.Nil(t, err, "Should be ok",test.msg)
+		if err != nil {
+			assert.Contains(t, err.Error(), test.error)
 		}
 	}
 
diff --git a/authorizer/src/asapo_authorizer/cli/create_token.go b/authorizer/src/asapo_authorizer/cli/create_token.go
index 5ddd51c7e2d8253b799c7e4f2c2dd472c0323600..4d267a204f3f2fe4caed814e5e406fac8b33cfe8 100644
--- a/authorizer/src/asapo_authorizer/cli/create_token.go
+++ b/authorizer/src/asapo_authorizer/cli/create_token.go
@@ -2,13 +2,14 @@ package cli
 
 import (
 	"asapo_authorizer/authorization"
-	"asapo_authorizer/database"
 	"asapo_authorizer/server"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"errors"
 	"fmt"
 	"os"
 	"strings"
+	"time"
 )
 
 type tokenFlags struct {
@@ -87,13 +88,10 @@ func (cmd *command) CommandCreate_token() (err error) {
 	if err != nil {
 		return err
 	}
-
-	record := database.TokenRecord{claims.Id, claims, token}
-	_, err = db.ProcessRequest(database.Request{
-		DbName:     database.KAdminDb,
-		Collection: database.KTokens,
-		Op:         "create_record",
-	}, &record)
+	claims.StandardClaims.Issuer = "asapo_cli"
+	claims.StandardClaims.IssuedAt = time.Now().Unix()
+	record := token_store.TokenRecord{claims.Id, claims, token, false}
+	err = store.AddToken(record)
 	if err != nil {
 		return err
 	}
diff --git a/authorizer/src/asapo_authorizer/cli/create_token_test.go b/authorizer/src/asapo_authorizer/cli/create_token_test.go
index a3e33caad6d76ed455dbab2ccd668f0d880a306a..bedd7476bfdbcecfcc1dda25ad6436bc46a7eb44 100644
--- a/authorizer/src/asapo_authorizer/cli/create_token_test.go
+++ b/authorizer/src/asapo_authorizer/cli/create_token_test.go
@@ -2,8 +2,8 @@ package cli
 
 import (
 	"asapo_authorizer/authorization"
-	"asapo_authorizer/database"
 	"asapo_authorizer/server"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"encoding/json"
@@ -43,20 +43,14 @@ var tokenTests = []struct {
 
 func TestGenerateToken(t *testing.T) {
 	server.Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret"))
-	mock_db := new(database.MockedDatabase)
-	db = mock_db
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
 
 	for _, test := range tokenTests {
 		outBuf = new(bytes.Buffer)
 
 		if test.ok {
-			req := database.Request{
-				DbName:     "asapo_admin",
-				Collection: "tokens",
-				Op:         "create_record",
-			}
-
-			mock_db.On("ProcessRequest", req, mock.Anything).Return([]byte(""), nil)
+			mock_store.On("AddToken", mock.Anything).Return(nil)
 		}
 
 		err := test.cmd.CommandCreate_token()
@@ -81,8 +75,8 @@ func TestGenerateToken(t *testing.T) {
 			assert.Empty(t, token.Expires, test.msg)
 		}
 
-		mock_db.AssertExpectations(t)
-		mock_db.ExpectedCalls = nil
-		mock_db.Calls = nil
+		mock_store.AssertExpectations(t)
+		mock_store.ExpectedCalls = nil
+		mock_store.Calls = nil
 	}
 }
diff --git a/authorizer/src/asapo_authorizer/cli/list_tokens.go b/authorizer/src/asapo_authorizer/cli/list_tokens.go
index 0c8397ebac41ef930795c95ba2052e8822a7f26d..923cc8f8b622b38023b1c256f190bce8faf478c9 100644
--- a/authorizer/src/asapo_authorizer/cli/list_tokens.go
+++ b/authorizer/src/asapo_authorizer/cli/list_tokens.go
@@ -1,55 +1,23 @@
 package cli
 
 import (
-	"asapo_authorizer/authorization"
-	"asapo_authorizer/database"
-	"asapo_authorizer/server"
+	"encoding/json"
 	"fmt"
-	"os"
 )
 
-type listTokenFlags struct {
-	Beamtime   string
-	Beamline   string
-}
-
 func (cmd *command) CommandList_tokens() (err error) {
 	message_string := "List tokens"
 	if cmd.description(message_string) {
 		return nil
 	}
 
-	_, err = cmd.parseTokenFlags(message_string)
-	if err != nil {
-		return err
-	}
-
-	var res map[string]interface{}
-	_, err = db.ProcessRequest(database.Request{
-		DbName:     database.KAdminDb,
-		Collection: database.KTokens,
-		Op:         "list_records",
-	}, &res)
+	tokens,err := store.GetTokenList()
 	if err != nil {
 		return err
 	}
 
-	answer := authorization.UserTokenResponce(request, token)
+	answer,_ := json.Marshal(tokens)
 	fmt.Fprintf(outBuf, "%s\n", string(answer))
 	return nil
 }
 
-func (cmd *command) parseListTokenFlags(message_string string) (tokenFlags, error) {
-
-	var flags tokenFlags
-	flagset := cmd.createDefaultFlagset(message_string, "")
-	flagset.StringVar(&flags.Beamtime, "beamtime", "", "beamtime for user token")
-	flagset.StringVar(&flags.Beamline, "beamline", "", "beamline for user token")
-
-	flagset.Parse(cmd.args)
-
-	if printHelp(flagset) {
-		os.Exit(0)
-	}
-	return flags, nil
-}
diff --git a/authorizer/src/asapo_authorizer/cli/list_tokens_test.go b/authorizer/src/asapo_authorizer/cli/list_tokens_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..dd4b74db6ad9a4ea70c38673a6fa89e7691ae4cb
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/cli/list_tokens_test.go
@@ -0,0 +1,18 @@
+package cli
+
+import (
+	"asapo_authorizer/token_store"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"testing"
+)
+
+func TestListTokens(t *testing.T) {
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
+
+	mock_store.On("GetTokenList", mock.Anything).Return([]token_store.TokenRecord{}, nil)
+	c := command{"list-tokens", []string{}}
+	err := c.CommandList_tokens()
+	assert.Nil(t, err)
+}
\ No newline at end of file
diff --git a/authorizer/src/asapo_authorizer/cli/revoke_token.go b/authorizer/src/asapo_authorizer/cli/revoke_token.go
new file mode 100644
index 0000000000000000000000000000000000000000..83712600298c8d423b34b790bf1e0ab023b7f02c
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/cli/revoke_token.go
@@ -0,0 +1,57 @@
+package cli
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os"
+)
+
+type revokeTokenFlags struct {
+	Token   string
+	TokenId string
+}
+
+func (cmd *command) CommandRevoke_token() (err error) {
+	message_string := "Revoke token"
+	if cmd.description(message_string) {
+		return nil
+	}
+
+	flags, err := cmd.parseRevokeTokenFlags(message_string)
+	if err != nil {
+		return err
+	}
+	token,err := store.RevokeToken(flags.Token, flags.TokenId)
+	if err != nil {
+		return err
+	}
+
+	out, _ := json.Marshal(token)
+	fmt.Fprintln(outBuf, string(out))
+	return nil
+}
+
+func (cmd *command) parseRevokeTokenFlags(message_string string) (revokeTokenFlags, error) {
+
+	var flags revokeTokenFlags
+	flagset := cmd.createDefaultFlagset(message_string, "")
+	flagset.StringVar(&flags.Token, "token", "", "token to revoke")
+	flagset.StringVar(&flags.TokenId, "token-id", "", "token id to revoke")
+	flagset.Parse(cmd.args)
+
+	if printHelp(flagset) {
+		os.Exit(0)
+	}
+
+	if flags.Token == "" && flags.TokenId == "" {
+		return flags, errors.New("set token or token id to revoke")
+	}
+
+	if flags.Token != "" && flags.TokenId != "" {
+		return flags, errors.New("cannot use both token and token id")
+	}
+
+	return flags, nil
+
+}
diff --git a/authorizer/src/asapo_authorizer/cli/revoke_token_test.go b/authorizer/src/asapo_authorizer/cli/revoke_token_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..8be7a957b7118e5ac10816d65e12b124597790ed
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/cli/revoke_token_test.go
@@ -0,0 +1,27 @@
+package cli
+
+import (
+	"asapo_authorizer/token_store"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestRevokeTokenToken(t *testing.T) {
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
+
+	mock_store.On("RevokeToken", "123","").Return(token_store.TokenRecord{}, nil)
+	c := command{"revoke-token", []string{"-token","123"}}
+	err := c.CommandRevoke_token()
+	assert.Nil(t, err)
+}
+
+func TestRevokeTokenTokenId(t *testing.T) {
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
+
+	mock_store.On("RevokeToken", "","123").Return(token_store.TokenRecord{}, nil)
+	c := command{"revoke-token", []string{"-token-id","123"}}
+	err := c.CommandRevoke_token()
+	assert.Nil(t, err)
+}
\ No newline at end of file
diff --git a/authorizer/src/asapo_authorizer/common/settings.go b/authorizer/src/asapo_authorizer/common/settings.go
new file mode 100644
index 0000000000000000000000000000000000000000..48dc90fedccb98c087a36bb5ab61e62f9c68d7ba
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/common/settings.go
@@ -0,0 +1,23 @@
+package common
+
+var Settings authorizerSettings
+
+type authorizerSettings struct {
+	Port                   int
+	LogLevel               string
+	RootBeamtimesFolder    string
+	CurrentBeamlinesFolder string
+	AlwaysAllowedBeamtimes []BeamtimeMeta
+	UserSecretFile         string
+	AdminSecretFile        string
+	FolderTokenDurationMin int
+	Ldap                   struct {
+		Uri string
+		BaseDn string
+		FilterTemplate string
+	}
+	DiscoveryServer string
+	DatabaseServer string
+	UpdateRevokedTokensIntervalSec int
+	UpdateTokenCacheIntervalSec int
+}
diff --git a/authorizer/src/asapo_authorizer/common/structs.go b/authorizer/src/asapo_authorizer/common/structs.go
index 805d0c79aadd885ddb6ed76b1e678c10a63bbe45..ca2a6de00c0f7c82371580343293eae2a097ca80 100644
--- a/authorizer/src/asapo_authorizer/common/structs.go
+++ b/authorizer/src/asapo_authorizer/common/structs.go
@@ -1 +1,17 @@
 package common
+
+type  BeamtimeMeta struct {
+	BeamtimeId string  `json:"beamtimeId"`
+	Beamline string     `json:"beamline"`
+	DataSource string       `json:"dataSource"`
+	OfflinePath string `json:"corePath"`
+	OnlinePath string `json:"beamline-path"`
+	Type string `json:"source-type"`
+	AccessTypes []string `json:"access-types"`
+}
+
+type  CommissioningMeta struct {
+	Id string  `json:"id"`
+	Beamline string     `json:"beamline"`
+	OfflinePath string `json:"corePath"`
+}
diff --git a/authorizer/src/asapo_authorizer/server/authorize.go b/authorizer/src/asapo_authorizer/server/authorize.go
index aaaade1b34aca6afa722a4798afd785ef68a4f38..e8faa9caaef9e2fa14a35f58561579e23fe3157d 100644
--- a/authorizer/src/asapo_authorizer/server/authorize.go
+++ b/authorizer/src/asapo_authorizer/server/authorize.go
@@ -60,21 +60,21 @@ func splitHost(hostPort string) string {
 	return s[0]
 }
 
-func beamtimeMetaFromJson(fname string) (beamtimeMeta, error) {
-	var meta beamtimeMeta
+func beamtimeMetaFromJson(fname string) (common.BeamtimeMeta, error) {
+	var meta common.BeamtimeMeta
 	err := utils.ReadJsonFromFile(fname, &meta)
 	if err != nil {
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 	return meta, nil
 }
 
-func commissioningMetaFromJson(fname string) (beamtimeMeta, error) {
-	var meta beamtimeMeta
-	var comMeta commissioningMeta
+func commissioningMetaFromJson(fname string) (common.BeamtimeMeta, error) {
+	var meta common.BeamtimeMeta
+	var comMeta common.CommissioningMeta
 	err := utils.ReadJsonFromFile(fname, &comMeta)
 	if err != nil {
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 	meta.BeamtimeId = comMeta.Id
 	meta.Beamline = strings.ToLower(comMeta.Beamline)
@@ -82,32 +82,32 @@ func commissioningMetaFromJson(fname string) (beamtimeMeta, error) {
 	return meta, nil
 }
 
-func beamtimeMetaFromMatch(match string) (beamtimeMeta, error) {
-	match = strings.TrimPrefix(match, settings.RootBeamtimesFolder)
+func beamtimeMetaFromMatch(match string) (common.BeamtimeMeta, error) {
+	match = strings.TrimPrefix(match, common.Settings.RootBeamtimesFolder)
 	match = strings.TrimPrefix(match, string(filepath.Separator))
 	vars := strings.Split(match, string(filepath.Separator))
 	if len(vars) != 6 {
-		return beamtimeMeta{}, errors.New("bad pattern")
+		return common.BeamtimeMeta{}, errors.New("bad pattern")
 	}
-	var bt beamtimeMeta
+	var bt common.BeamtimeMeta
 	ignoredFoldersAfterGpfs := []string{"common", "BeamtimeUsers", "state", "support"}
 	if utils.StringInSlice(vars[2], ignoredFoldersAfterGpfs) {
-		return beamtimeMeta{}, errors.New("skipped fodler")
+		return common.BeamtimeMeta{}, errors.New("skipped fodler")
 	}
 
-	bt.OfflinePath = settings.RootBeamtimesFolder+string(filepath.Separator)+match
+	bt.OfflinePath = common.Settings.RootBeamtimesFolder+string(filepath.Separator)+match
 	bt.Beamline, bt.BeamtimeId = vars[2], vars[5]
 
 	return bt, nil
 }
 
-func findBeamtimeInfoFromId(beamtime_id string) (beamtimeMeta, error) {
+func findBeamtimeInfoFromId(beamtime_id string) (common.BeamtimeMeta, error) {
 	sep := string(filepath.Separator)
 	pattern := sep + "*" + sep + "gpfs" + sep + "*" + sep + "*" + sep + "*" + sep
-	matches, err := filepath.Glob(settings.RootBeamtimesFolder + pattern + beamtime_id)
+	matches, err := filepath.Glob(common.Settings.RootBeamtimesFolder + pattern + beamtime_id)
 
 	if err != nil || len(matches) == 0 {
-		return beamtimeMeta{}, errors.New("Cannot find beamline for "+beamtime_id)
+		return common.BeamtimeMeta{}, errors.New("Cannot find beamline for "+beamtime_id)
 	}
 
 	for _, match := range (matches) {
@@ -119,7 +119,7 @@ func findBeamtimeInfoFromId(beamtime_id string) (beamtimeMeta, error) {
 			return btInfo, nil
 		}
 	}
-	return beamtimeMeta{}, errors.New("Cannot find beamline for "+beamtime_id)
+	return common.BeamtimeMeta{}, errors.New("Cannot find beamline for "+beamtime_id)
 }
 
 func findMetaFileInFolder(beamline string,iscommissioning bool) (string, string, error){
@@ -132,7 +132,7 @@ func findMetaFileInFolder(beamline string,iscommissioning bool) (string, string,
 		pattern = "commissioning-metadata-*.json"
 		folder = "commissioning"
 	}
-	online_path := settings.CurrentBeamlinesFolder + sep + beamline + sep + folder
+	online_path := common.Settings.CurrentBeamlinesFolder + sep + beamline + sep + folder
 	matches, err := filepath.Glob(online_path + sep + pattern)
 	if err != nil {
 		return "","", err
@@ -144,10 +144,10 @@ func findMetaFileInFolder(beamline string,iscommissioning bool) (string, string,
 
 }
 
-func findBeamtimeMetaFromBeamline(beamline string,iscommissioning bool) (meta beamtimeMeta, err error) {
+func findBeamtimeMetaFromBeamline(beamline string,iscommissioning bool) (meta common.BeamtimeMeta, err error) {
 	fName,online_path, err := findMetaFileInFolder(beamline,iscommissioning)
 	if (err != nil) {
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 
 	if iscommissioning {
@@ -156,19 +156,19 @@ func findBeamtimeMetaFromBeamline(beamline string,iscommissioning bool) (meta be
 		meta, err = beamtimeMetaFromJson(fName)
 	}
 	if (err != nil) {
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 
 	if meta.BeamtimeId == "" || meta.OfflinePath=="" || meta.Beamline == ""{
-		return beamtimeMeta{}, errors.New("cannot set meta fields from beamtime file")
+		return common.BeamtimeMeta{}, errors.New("cannot set meta fields from beamtime file")
 	}
 
 	meta.OnlinePath = online_path
 	return meta, nil
 }
 
-func alwaysAllowed(creds SourceCredentials) (beamtimeMeta, bool) {
-	for _, pair := range settings.AlwaysAllowedBeamtimes {
+func alwaysAllowed(creds SourceCredentials) (common.BeamtimeMeta, bool) {
+	for _, pair := range common.Settings.AlwaysAllowedBeamtimes {
 		if pair.BeamtimeId == creds.BeamtimeId {
 			pair.DataSource = creds.DataSource
 			pair.Type = creds.Type
@@ -176,12 +176,12 @@ func alwaysAllowed(creds SourceCredentials) (beamtimeMeta, bool) {
 			return pair, true
 		}
 	}
-	return beamtimeMeta{}, false
+	return common.BeamtimeMeta{}, false
 }
 
 func authorizeByHost(host_ip, beamline string) (error) {
-	filter := strings.Replace(settings.Ldap.FilterTemplate,"__BEAMLINE__",beamline,1)
-	allowed_ips, err := ldapClient.GetAllowedIpsForBeamline(settings.Ldap.Uri,settings.Ldap.BaseDn, filter)
+	filter := strings.Replace(common.Settings.Ldap.FilterTemplate,"__BEAMLINE__",beamline,1)
+	allowed_ips, err := ldapClient.GetAllowedIpsForBeamline(common.Settings.Ldap.Uri,common.Settings.Ldap.BaseDn, filter)
 	if err != nil {
 		log.Error("cannot get list of allowed hosts from LDAP: " + err.Error())
 		return err
@@ -230,9 +230,9 @@ func iscommissioning(beamtime string) bool {
 	return len(beamtime)>0 && beamtime[0]=='c'
 }
 
-func findMeta(creds SourceCredentials) (beamtimeMeta, error) {
+func findMeta(creds SourceCredentials) (common.BeamtimeMeta, error) {
 	var err error
-	var meta beamtimeMeta
+	var meta common.BeamtimeMeta
 	if (creds.BeamtimeId != "auto") {
 		meta, err = findBeamtimeInfoFromId(creds.BeamtimeId)
 		if (err == nil ) {
@@ -251,7 +251,7 @@ func findMeta(creds SourceCredentials) (beamtimeMeta, error) {
 
 	if (err != nil) {
 		log.Error(err.Error())
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 
 	meta.DataSource = creds.DataSource
@@ -260,7 +260,7 @@ func findMeta(creds SourceCredentials) (beamtimeMeta, error) {
 	return meta, nil
 }
 
-func authorizeMeta(meta beamtimeMeta, request authorizationRequest, creds SourceCredentials) (accessTypes []string, err error) {
+func authorizeMeta(meta common.BeamtimeMeta, request authorizationRequest, creds SourceCredentials) (accessTypes []string, err error) {
 	accessTypes = nil
 	if creds.Type=="raw" && meta.OnlinePath=="" {
 		err_string := "beamtime "+meta.BeamtimeId+" is not online"
@@ -290,19 +290,19 @@ func authorizeMeta(meta beamtimeMeta, request authorizationRequest, creds Source
 	return accessTypes,err
 }
 
-func authorize(request authorizationRequest, creds SourceCredentials) (beamtimeMeta, error) {
+func authorize(request authorizationRequest, creds SourceCredentials) (common.BeamtimeMeta, error) {
 	if meta, ok := alwaysAllowed(creds); ok {
 		return meta, nil
 	}
 
 	meta, err := findMeta(creds)
 	if err != nil {
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 
 	var accessTypes []string
 	if accessTypes, err = authorizeMeta(meta, request, creds); err != nil {
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 
 	meta.AccessTypes = accessTypes
diff --git a/authorizer/src/asapo_authorizer/server/authorize_test.go b/authorizer/src/asapo_authorizer/server/authorize_test.go
index 3c5f2838824cf44f5dca55009645f4f9e06cab9b..7272ea101c0c1fed437ca450580779fbd016333d 100644
--- a/authorizer/src/asapo_authorizer/server/authorize_test.go
+++ b/authorizer/src/asapo_authorizer/server/authorize_test.go
@@ -50,8 +50,8 @@ type request struct {
 	message string
 }
 
-func allowBeamlines(beamlines []beamtimeMeta) {
-	settings.AlwaysAllowedBeamtimes=beamlines
+func allowBeamlines(beamlines []common.BeamtimeMeta) {
+	common.Settings.AlwaysAllowedBeamtimes=beamlines
 }
 
 
@@ -111,7 +111,7 @@ func TestSplitCreds(t *testing.T) {
 }
 
 func TestAuthorizeDefaultOK(t *testing.T) {
-	allowBeamlines([]beamtimeMeta{{"asapo_test","beamline","","2019","tf","",nil}})
+	allowBeamlines([]common.BeamtimeMeta{{"asapo_test","beamline","","2019","tf","",nil}})
 	request :=  makeRequest(authorizationRequest{"processed%asapo_test%%%","host"})
 	w := doPostRequest("/authorize",request,"")
 
@@ -244,16 +244,16 @@ var authTests = [] struct {
 
 func TestAuthorize(t *testing.T) {
 	ldapClient = mockClient
-	allowBeamlines([]beamtimeMeta{})
+	allowBeamlines([]common.BeamtimeMeta{})
 	Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret"))
 	expected_uri := "expected_uri"
 	expected_base := "expected_base"
 	allowed_ips := []string{"127.0.0.1"}
-	settings.RootBeamtimesFolder ="."
-	settings.CurrentBeamlinesFolder="."
-	settings.Ldap.FilterTemplate="a3__BEAMLINE__-hosts"
-	settings.Ldap.Uri = expected_uri
-	settings.Ldap.BaseDn = expected_base
+	common.Settings.RootBeamtimesFolder ="."
+	common.Settings.CurrentBeamlinesFolder="."
+	common.Settings.Ldap.FilterTemplate="a3__BEAMLINE__-hosts"
+	common.Settings.Ldap.Uri = expected_uri
+	common.Settings.Ldap.BaseDn = expected_base
 
 	os.MkdirAll(filepath.Clean("tf/gpfs/bl1/2019/data/test"), os.ModePerm)
 	os.MkdirAll(filepath.Clean("tf/gpfs/bl1/2019/data/test_online"), os.ModePerm)
@@ -323,7 +323,7 @@ func TestAuthorizeWrongPath(t *testing.T) {
 }
 
 func TestDoNotAuthorizeIfNotInAllowed(t *testing.T) {
-	allowBeamlines([]beamtimeMeta{{"test","beamline","","2019","tf","",nil}})
+	allowBeamlines([]common.BeamtimeMeta{{"test","beamline","","2019","tf","",nil}})
 
 	request :=  authorizationRequest{"asapo_test%%","host"}
 	creds,_ := getSourceCredentials(request)
@@ -360,7 +360,7 @@ var extractBtinfoTests = [] struct {
 }
 func TestGetBeamtimeInfo(t *testing.T) {
 	for _, test := range extractBtinfoTests {
-		settings.RootBeamtimesFolder=test.root
+		common.Settings.RootBeamtimesFolder=test.root
 		bt,err:= beamtimeMetaFromMatch(test.root+string(filepath.Separator)+test.fname)
 		if test.ok {
 			assert.Equal(t,bt.OfflinePath,test.root+string(filepath.Separator)+test.fname)
diff --git a/authorizer/src/asapo_authorizer/server/db_request.go b/authorizer/src/asapo_authorizer/server/db_request.go
deleted file mode 100644
index 135aca5a24e4108a07294e863f77cde42b7a34d3..0000000000000000000000000000000000000000
--- a/authorizer/src/asapo_authorizer/server/db_request.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package server
-
-import (
-	"asapo_authorizer/database"
-	log "asapo_common/logger"
-	"asapo_common/utils"
-)
-
-func reconnectIfNeeded(db_error error) {
-	code := database.GetStatusCodeFromError(db_error)
-	if code != utils.StatusServiceUnavailable {
-		return
-	}
-
-	if err := ReconnectDb(); err != nil {
-		log.Error("cannot reconnect to database at : " + settings.GetDatabaseServer() + " " + err.Error())
-	} else {
-		log.Debug("reconnected to database" + settings.GetDatabaseServer())
-	}
-}
-
-func ProcessRequestInDb(request database.Request) ([]byte,error) {
-	answer, err := db.ProcessRequest(request)
-	if err != nil {
-		go reconnectIfNeeded(err)
-		return nil,err
-	}
-	return answer,nil
-}
diff --git a/authorizer/src/asapo_authorizer/server/db_request_test.go b/authorizer/src/asapo_authorizer/server/db_request_test.go
deleted file mode 100644
index 4cd03bfb7c7cc537de725b4d85569d6018fdfae5..0000000000000000000000000000000000000000
--- a/authorizer/src/asapo_authorizer/server/db_request_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package server
-
-import (
-	"asapo_authorizer/database"
-	"asapo_common/logger"
-	"asapo_common/utils"
-	"github.com/stretchr/testify/mock"
-	"github.com/stretchr/testify/suite"
-	"testing"
-	"time"
-)
-
-
-const expectedSource = "datasource"
-const expectedStream = "stream"
-
-
-func ExpectReconnect(mock_db *database.MockedDatabase) {
-	mock_db.On("Close").Return()
-	mock_db.On("Connect", mock.AnythingOfType("string")).Return(nil)
-}
-
-type ProcessRequestTestSuite struct {
-	suite.Suite
-	mock_db *database.MockedDatabase
-}
-
-func (suite *ProcessRequestTestSuite) SetupTest() {
-	suite.mock_db = new(database.MockedDatabase)
-	db = suite.mock_db
-	logger.SetMockLog()
-}
-
-func (suite *ProcessRequestTestSuite) TearDownTest() {
-	assertExpectations(suite.T(), suite.mock_db)
-	logger.UnsetMockLog()
-	db = nil
-}
-
-func TestProcessRequestTestSuite(t *testing.T) {
-	suite.Run(t, new(ProcessRequestTestSuite))
-}
-
-
-func (suite *ProcessRequestTestSuite) TestProcessRequestWithConnectionError() {
-	req := database.Request{}
-
-	suite.mock_db.On("ProcessRequest", req,mock.Anything).Return([]byte(""),
-		&database.DBError{utils.StatusServiceUnavailable, ""})
-
-	ExpectReconnect(suite.mock_db)
-	logger.MockLog.On("Debug", mock.MatchedBy(containsMatcher("reconnected")))
-
-	_,err := ProcessRequestInDb(req)
-	time.Sleep(time.Second)
-	suite.Error(err, "need reconnect")
-}
-
-
-func (suite *ProcessRequestTestSuite) TestProcessRequestAddTokenToDb() {
-	req := database.Request{
-		DbName:     "test",
-		Collection: "test",
-		Op:         "test",
-	}
-
-	suite.mock_db.On("ProcessRequest", req,mock.Anything).Return([]byte("Hello"), nil)
-
-
-	_,err := ProcessRequestInDb(req)
-	suite.Equal(err, nil, "ok")
-
-}
diff --git a/authorizer/src/asapo_authorizer/server/folder_token.go b/authorizer/src/asapo_authorizer/server/folder_token.go
index dcdfbe38ba74631c5c31ad96dacef18a474dd6ca..5d10ed86a844902a0c33101d0fd7862f5628e854 100644
--- a/authorizer/src/asapo_authorizer/server/folder_token.go
+++ b/authorizer/src/asapo_authorizer/server/folder_token.go
@@ -1,14 +1,15 @@
 package server
 
 import (
+	"asapo_authorizer/common"
+	log "asapo_common/logger"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"asapo_common/version"
-	"net/http"
-	"time"
-	log "asapo_common/logger"
 	"errors"
+	"net/http"
 	"path/filepath"
+	"time"
 )
 
 type folderTokenRequest struct {
@@ -27,7 +28,7 @@ type folderToken struct {
 }
 
 /*func routeFolderToken(w http.ResponseWriter, r *http.Request) {
-	utils.ProcessJWTAuth(processFolderTokenRequest,settings.secret)(w,r)
+	utils.ProcessJWTAuth(processFolderTokenRequest,common.Settings.secret)(w,r)
 }*/
 
 func prepareJWTToken(folders tokenFolders) (string, error) {
@@ -38,7 +39,7 @@ func prepareJWTToken(folders tokenFolders) (string, error) {
 	extraClaim.SecondFolder = folders.SecondFolder
 
 	claims.ExtraClaims = &extraClaim
-	claims.SetExpiration(time.Duration(settings.FolderTokenDurationMin) * time.Minute)
+	claims.SetExpiration(time.Duration(common.Settings.FolderTokenDurationMin) * time.Minute)
 	return Auth.JWTAuth().GenerateToken(&claims)
 
 }
diff --git a/authorizer/src/asapo_authorizer/server/folder_token_test.go b/authorizer/src/asapo_authorizer/server/folder_token_test.go
index f90bb77eb7c4ee3c5edc1771192a7e628e22f452..88c02cba5f960ab017e1508157ff30ce3d6b355f 100644
--- a/authorizer/src/asapo_authorizer/server/folder_token_test.go
+++ b/authorizer/src/asapo_authorizer/server/folder_token_test.go
@@ -2,16 +2,17 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
+	"asapo_authorizer/common"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"asapo_common/version"
+	"fmt"
 	"github.com/stretchr/testify/assert"
 	"io/ioutil"
 	"net/http"
-	"testing"
 	"os"
 	"path/filepath"
-	"fmt"
+	"testing"
 )
 
 var  fodlerTokenTests = [] struct {
@@ -37,9 +38,9 @@ var  fodlerTokenTests = [] struct {
 }
 
 func TestFolderToken(t *testing.T) {
-	allowBeamlines([]beamtimeMeta{})
-	settings.RootBeamtimesFolder ="."
-	settings.CurrentBeamlinesFolder="."
+	allowBeamlines([]common.BeamtimeMeta{})
+	common.Settings.RootBeamtimesFolder ="."
+	common.Settings.CurrentBeamlinesFolder="."
 	Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret_folder"))
 
 	os.MkdirAll(filepath.Clean("tf/gpfs/bl1/2019/data/test"), os.ModePerm)
@@ -52,10 +53,10 @@ func TestFolderToken(t *testing.T) {
 	defer 	os.RemoveAll("bl1")
 
 	for _, test := range fodlerTokenTests {
-		abs_path:=settings.RootBeamtimesFolder + string(filepath.Separator)+test.root_folder
+		abs_path:=common.Settings.RootBeamtimesFolder + string(filepath.Separator)+test.root_folder
 		abs_path_second :=""
 		if test.second_folder!="" {
-			abs_path_second =settings.RootBeamtimesFolder + string(filepath.Separator)+test.second_folder
+			abs_path_second =common.Settings.RootBeamtimesFolder + string(filepath.Separator)+test.second_folder
 		}
 		path_in_token:=abs_path
 		if test.auto {
diff --git a/authorizer/src/asapo_authorizer/server/server.go b/authorizer/src/asapo_authorizer/server/server.go
index 8fec39728a532987473239776c7ce6ec6db9e00a..ccb57bab4facbdfde0e6589fe94f9bdbe34f82d2 100644
--- a/authorizer/src/asapo_authorizer/server/server.go
+++ b/authorizer/src/asapo_authorizer/server/server.go
@@ -2,89 +2,10 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
-	"asapo_authorizer/database"
 	"asapo_authorizer/ldap_client"
-	"asapo_common/discovery"
-	log "asapo_common/logger"
-	"errors"
+	"asapo_authorizer/token_store"
 )
 
-type  beamtimeMeta struct {
-	BeamtimeId string  `json:"beamtimeId"`
-	Beamline string     `json:"beamline"`
-	DataSource string       `json:"dataSource"`
-	OfflinePath string `json:"corePath"`
-	OnlinePath string `json:"beamline-path"`
-	Type string `json:"source-type"`
-	AccessTypes []string `json:"access-types"`
-}
-
-type  commissioningMeta struct {
-	Id string  `json:"id"`
-	Beamline string     `json:"beamline"`
-	OfflinePath string `json:"corePath"`
-}
-
-
-type serverSettings struct {
-	Port                   int
-	LogLevel               string
-	RootBeamtimesFolder    string
-	CurrentBeamlinesFolder string
-	AlwaysAllowedBeamtimes []beamtimeMeta
-	UserSecretFile         string
-	AdminSecretFile        string
-	FolderTokenDurationMin int
-	Ldap                   struct {
-		Uri string
-		BaseDn string
-		FilterTemplate string
-	}
-	DiscoveryServer string
-	DatabaseServer string
-	discoveredDbAddress string
-}
-
-func (s *serverSettings) GetDatabaseServer() string {
-	if s.DatabaseServer == "auto" {
-		return s.discoveredDbAddress
-	} else {
-		return s.DatabaseServer
-	}
-}
-
-var settings serverSettings
 var ldapClient ldap_client.LdapClient
 var Auth *authorization.Auth
-var discoveryService discovery.DiscoveryAPI
-var db database.Agent
-
-func ReconnectDb() (err error) {
-	if db == nil {
-		return errors.New("database not initialized")
-	}
-	db.Close()
-	return InitDB(db)
-}
-
-func InitDB(dbAgent database.Agent) (err error) {
-	db = dbAgent
-	if settings.DatabaseServer == "auto" {
-		settings.discoveredDbAddress, err = discoveryService.GetMongoDbAddress()
-		if err != nil {
-			return err
-		}
-		if settings.discoveredDbAddress == "" {
-			return errors.New("no database servers found")
-		}
-		log.Debug("Got mongodb server: " + settings.discoveredDbAddress)
-	}
-
-	return db.Connect(settings.GetDatabaseServer())
-}
-
-func CleanupDB() {
-	if db != nil {
-		db.Close()
-	}
-}
+var store token_store.Store
diff --git a/authorizer/src/asapo_authorizer/server/server_nottested.go b/authorizer/src/asapo_authorizer/server/server_nottested.go
index a4dec96733a06581603bd1d0caa3dbc13997ffe9..4661af0a6db1f502c854f491b51a183050cd8c9a 100644
--- a/authorizer/src/asapo_authorizer/server/server_nottested.go
+++ b/authorizer/src/asapo_authorizer/server/server_nottested.go
@@ -4,9 +4,9 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
-	"asapo_authorizer/database"
+	"asapo_authorizer/common"
 	"asapo_authorizer/ldap_client"
-	"asapo_common/discovery"
+	"asapo_authorizer/token_store"
 	log "asapo_common/logger"
 	"asapo_common/utils"
 	"asapo_common/version"
@@ -15,32 +15,29 @@ import (
 	"strconv"
 )
 
-func CreateDiscoveryService() {
-	discoveryService = discovery.CreateDiscoveryService(&http.Client{},"http://" + settings.DiscoveryServer)
-}
-
 func Start() {
 	mux := utils.NewRouter(listRoutes)
 	ldapClient = new(ldap_client.OpenLdapClient)
 
 	log.Info("Starting ASAPO Authorizer, version " + version.GetVersion())
-	CreateDiscoveryService()
-	err := InitDB(new(database.Mongodb))
+
+	store = new(token_store.TokenStore)
+	err := 	store.Init(nil)
 	if err != nil {
 		log.Error(err.Error())
 	}
-	defer CleanupDB()
+	defer store.Close()
 
-	log.Info("Listening on port: " + strconv.Itoa(settings.Port))
-	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP)))
+	log.Info("Listening on port: " + strconv.Itoa(common.Settings.Port))
+	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(common.Settings.Port), http.HandlerFunc(mux.ServeHTTP)))
 }
 
 func createAuth() (*authorization.Auth,error) {
-	secret, err := utils.ReadFirstStringFromFile(settings.UserSecretFile)
+	secret, err := utils.ReadFirstStringFromFile(common.Settings.UserSecretFile)
 	if err != nil {
 		return nil, err
 	}
-	adminSecret, err := utils.ReadFirstStringFromFile(settings.AdminSecretFile)
+	adminSecret, err := utils.ReadFirstStringFromFile(common.Settings.AdminSecretFile)
 	if err != nil {
 		return nil, err
 	}
@@ -48,27 +45,27 @@ func createAuth() (*authorization.Auth,error) {
 }
 
 func ReadConfig(fname string) (log.Level, error) {
-	if err := utils.ReadJsonFromFile(fname, &settings); err != nil {
+	if err := utils.ReadJsonFromFile(fname, &common.Settings); err != nil {
 		return log.FatalLevel, err
 	}
 
-	if settings.Port == 0 {
+	if common.Settings.Port == 0 {
 		return log.FatalLevel, errors.New("Server port not set")
 	}
 
-	if settings.UserSecretFile == "" {
+	if common.Settings.UserSecretFile == "" {
 		return log.FatalLevel, errors.New("User secret file not set")
 	}
 
-	if settings.AdminSecretFile == "" {
+	if common.Settings.AdminSecretFile == "" {
 		return log.FatalLevel, errors.New("Admin secret file not set")
 	}
 
-	if settings.DatabaseServer == "" {
+	if common.Settings.DatabaseServer == "" {
 		return log.FatalLevel, errors.New("Database server not set")
 	}
 
-	if settings.DatabaseServer == "auto" && settings.DiscoveryServer=="" {
+	if common.Settings.DatabaseServer == "auto" && common.Settings.DiscoveryServer=="" {
 		return log.FatalLevel, errors.New("Discovery server not set")
 	}
 
@@ -78,7 +75,7 @@ func ReadConfig(fname string) (log.Level, error) {
 		return log.FatalLevel, err
 	}
 
-	level, err := log.LevelFromString(settings.LogLevel)
+	level, err := log.LevelFromString(common.Settings.LogLevel)
 
 	return level, err
 }
diff --git a/authorizer/src/asapo_authorizer/server/server_test.go b/authorizer/src/asapo_authorizer/server/server_test.go
index 46f045ddfc12553ad377e8e7fd3525c147ff99a1..f18a0f738e4b4ed9397f8449a679f8650b7f6928 100644
--- a/authorizer/src/asapo_authorizer/server/server_test.go
+++ b/authorizer/src/asapo_authorizer/server/server_test.go
@@ -1,5 +1,7 @@
 package server
 
+/*
+
 import (
 	"asapo_authorizer/database"
 	"asapo_common/discovery"
@@ -138,3 +140,4 @@ func TestCleanupDBInit(t *testing.T) {
 
 	assertExpectations(t, mock_db)
 }
+*/
\ No newline at end of file
diff --git a/authorizer/src/asapo_authorizer/database/database.go b/authorizer/src/asapo_authorizer/token_store/database.go
similarity index 84%
rename from authorizer/src/asapo_authorizer/database/database.go
rename to authorizer/src/asapo_authorizer/token_store/database.go
index 187adeaa8d055ca9f40ff6fa418aa271f9801c33..2afabbe9ffe0fc058e3da879bb751d466d268042 100644
--- a/authorizer/src/asapo_authorizer/database/database.go
+++ b/authorizer/src/asapo_authorizer/token_store/database.go
@@ -1,10 +1,11 @@
-package database
+package token_store
 
 import "asapo_common/utils"
 
 
 const KAdminDb = "asapo_admin"
 const KTokens = "tokens"
+const KRevokedTokens = "revoked_tokens"
 
 type Request struct {
 	DbName         string
@@ -16,6 +17,11 @@ type TokenRecord struct {
 	Id string `bson:"_id"`
 	*utils.CustomClaims
 	Token string
+	Revoked bool
+}
+
+type IdRecord struct {
+	Id string `bson:"_id"`
 }
 
 type Agent interface {
diff --git a/authorizer/src/asapo_authorizer/database/mock_database.go b/authorizer/src/asapo_authorizer/token_store/mock_database.go
similarity index 61%
rename from authorizer/src/asapo_authorizer/database/mock_database.go
rename to authorizer/src/asapo_authorizer/token_store/mock_database.go
index e44cacaa7ef9fe59b3c31f5345cc5dd093a415c9..7878da8d43baecbb694a9d880a5c7db972444a2b 100644
--- a/authorizer/src/asapo_authorizer/database/mock_database.go
+++ b/authorizer/src/asapo_authorizer/token_store/mock_database.go
@@ -1,6 +1,6 @@
 // +build !release
 
-package database
+package token_store
 
 import (
 	"github.com/stretchr/testify/mock"
@@ -28,3 +28,23 @@ func (db *MockedDatabase) ProcessRequest(request Request, extraParams ...interfa
 	args := db.Called(request,extraParams)
 	return args.Get(0).([]byte), args.Error(1)
 }
+
+
+type FakeDatabase struct {
+}
+
+func (db *FakeDatabase) Connect(address string) error {
+	return nil
+}
+
+func (db *FakeDatabase) Close() {
+	return
+}
+
+func (db *FakeDatabase) Ping() error {
+	return nil
+}
+
+func (db *FakeDatabase) ProcessRequest(request Request, extraParams ...interface{}) (answer []byte, err error) {
+	return nil,nil
+}
diff --git a/authorizer/src/asapo_authorizer/token_store/mock_store.go b/authorizer/src/asapo_authorizer/token_store/mock_store.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2db86bcbbf8bf0363310e0c75487631a2ff76d4
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/mock_store.go
@@ -0,0 +1,43 @@
+// +build !release
+
+package token_store
+
+import (
+	"github.com/stretchr/testify/mock"
+)
+
+type MockedStore struct {
+	mock.Mock
+}
+
+func (store *MockedStore) Init(db Agent) error {
+	args := store.Called(db)
+	return args.Error(0)
+}
+
+func (store *MockedStore)  AddToken(token TokenRecord) error {
+	args := store.Called(token)
+	return args.Error(0)
+}
+
+func (store *MockedStore)  RevokeToken(token string,id string)  (TokenRecord, error) {
+	args := store.Called(token,id)
+	return args.Get(0).(TokenRecord), args.Error(1)
+}
+
+func (store *MockedStore)  GetTokenList() ([]TokenRecord,error) {
+	args := store.Called()
+	return args.Get(0).([]TokenRecord), args.Error(1)
+}
+
+func (store *MockedStore) GetRevokedTokenIds() ([]string,error) {
+	args := store.Called()
+	return args.Get(0).([]string), args.Error(1)
+}
+
+func (store *MockedStore) Close() {
+	store.Called()
+	return
+}
+
+
diff --git a/authorizer/src/asapo_authorizer/database/mongodb.go b/authorizer/src/asapo_authorizer/token_store/mongodb.go
similarity index 62%
rename from authorizer/src/asapo_authorizer/database/mongodb.go
rename to authorizer/src/asapo_authorizer/token_store/mongodb.go
index 51d69165d4adc2fce64e251c7b97513ca811a893..fbd1359125d9ad023f7074a909280b3e5a1b946b 100644
--- a/authorizer/src/asapo_authorizer/database/mongodb.go
+++ b/authorizer/src/asapo_authorizer/token_store/mongodb.go
@@ -1,11 +1,12 @@
 //+build !test
 
-package database
+package token_store
 
 import (
 	"asapo_common/utils"
 	"context"
 	"errors"
+	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson/primitive"
 	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo/options"
@@ -95,7 +96,6 @@ func duplicateError(err error) bool {
 	return command_error.Name == "DuplicateKey"
 }
 
-
 func (db *Mongodb) checkDatabaseOperationPrerequisites(request Request) error {
 	if db.client == nil {
 		return &DBError{utils.StatusServiceUnavailable, no_session_msg}
@@ -125,8 +125,69 @@ func (db *Mongodb) createRecord(request Request, extra_params ...interface{}) ([
 	return nil, nil
 }
 
+func (db *Mongodb) readRecords(request Request, extraParams ...interface{}) ([]byte, error) {
+	c := db.client.Database(request.DbName).Collection(request.Collection)
+
+	if len(extraParams) != 1 {
+		return nil, errors.New("wrong number of parameters")
+	}
+	res := extraParams[0]
+
+	opts := options.Find()
+
+	cursor, err := c.Find(context.TODO(), bson.M{}, opts)
+	if err != nil {
+		return nil, err
+	}
+
+	err = cursor.All(context.TODO(), res)
+	if err != nil {
+		return nil, err
+	}
+	return nil, nil
+}
+
+func (db *Mongodb) readRecord(request Request, extraParams ...interface{}) ([]byte, error) {
+	c := db.client.Database(request.DbName).Collection(request.Collection)
+
+	if len(extraParams) != 2 {
+		return nil, errors.New("wrong number of parameters")
+	}
+	q := extraParams[0]
+	res := extraParams[1]
+	err := c.FindOne(context.TODO(), q, options.FindOne()).Decode(res)
+	if err != nil {
+		return nil, err
+	}
+	return nil, nil
+}
+
+func (db *Mongodb) updateRecord(request Request, extra_params ...interface{}) ([]byte, error) {
+	if len(extra_params) != 4 {
+		return nil, errors.New("wrong number of parameters")
+	}
+	id, ok := extra_params[0].(string)
+	if !ok {
+		return nil, errors.New("id must be string")
+	}
+	input := extra_params[1]
+	upsert, ok := extra_params[2].(bool)
+	if !ok {
+		return nil, errors.New("upsert must be string")
+	}
+	output := extra_params[3]
+
+	opts := options.FindOneAndUpdate().SetUpsert(upsert).SetReturnDocument(options.After)
+	filter := bson.D{{"_id", id}}
+
+	update := bson.D{{"$set", input}}
+	c := db.client.Database(request.DbName).Collection(request.Collection)
+	err := c.FindOneAndUpdate(context.TODO(), filter, update, opts).Decode(output)
+	return nil, err
+}
+
 
-func (db *Mongodb) ProcessRequest(request Request,extraParams ...interface{}) (answer []byte, err error) {
+func (db *Mongodb) ProcessRequest(request Request, extraParams ...interface{}) (answer []byte, err error) {
 	dbClientLock.RLock()
 	defer dbClientLock.RUnlock()
 
@@ -137,6 +198,12 @@ func (db *Mongodb) ProcessRequest(request Request,extraParams ...interface{}) (a
 	switch request.Op {
 	case "create_record":
 		return db.createRecord(request, extraParams...)
+	case "read_records":
+		return db.readRecords(request, extraParams...)
+	case "read_record":
+		return db.readRecord(request, extraParams...)
+	case "update_record":
+		return db.updateRecord(request, extraParams...)
 	}
 
 	return nil, errors.New("Wrong db operation: " + request.Op)
diff --git a/authorizer/src/asapo_authorizer/token_store/token_store.go b/authorizer/src/asapo_authorizer/token_store/token_store.go
new file mode 100644
index 0000000000000000000000000000000000000000..0de0d1c45c61a240079f478673ff3b829ce25774
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/token_store.go
@@ -0,0 +1,243 @@
+package token_store
+
+import (
+	"asapo_authorizer/common"
+	"asapo_common/discovery"
+	log "asapo_common/logger"
+	"asapo_common/utils"
+	"errors"
+	"net/http"
+	"sync"
+	"time"
+)
+
+type Store interface {
+	Init(db Agent) error
+	AddToken(token TokenRecord) error
+	RevokeToken(token string, id string) (TokenRecord, error)
+	GetTokenList() ([]TokenRecord, error)
+	GetRevokedTokenIds() ([]string, error)
+	Close()
+}
+
+type TokenStore struct {
+	db               Agent
+	revokedTokenList struct {
+		tokens     []string
+		lock       sync.RWMutex
+		lastUpdate time.Time
+		lastError  error
+	}
+	stopchan chan bool
+}
+
+var discoveryService discovery.DiscoveryAPI
+
+func (store *TokenStore) reconnectDb() (dbaddress string, err error) {
+	if store.db == nil {
+		return "", errors.New("token_store not initialized")
+	}
+	store.db.Close()
+	return store.initDB()
+}
+
+func (store *TokenStore) initDB() (dbaddress string, err error) {
+	dbaddress = common.Settings.DatabaseServer
+	if common.Settings.DatabaseServer == "auto" {
+		dbaddress, err = discoveryService.GetMongoDbAddress()
+		if err != nil {
+			return "", err
+		}
+		if dbaddress == "" {
+			return "", errors.New("no token_store servers found")
+		}
+		log.Debug("Got mongodb server: " + dbaddress)
+	}
+	return dbaddress, store.db.Connect(dbaddress)
+
+}
+
+func (store *TokenStore) reconnectIfNeeded(db_error error) {
+	code := GetStatusCodeFromError(db_error)
+	if code != utils.StatusServiceUnavailable {
+		return
+	}
+
+	if dbaddress, err := store.reconnectDb(); err != nil {
+		log.Error("cannot reconnect to database: " + err.Error())
+	} else {
+		log.Debug("reconnected to database at" + dbaddress)
+	}
+}
+
+func createDiscoveryService() {
+	discoveryService = discovery.CreateDiscoveryService(&http.Client{}, "http://"+common.Settings.DiscoveryServer)
+}
+
+func (store *TokenStore) Init(db Agent) error {
+	if db == nil {
+		store.db = new(Mongodb)
+	} else {
+		store.db = db
+	}
+	createDiscoveryService()
+	_, err := store.initDB()
+	store.stopchan = make(chan bool)
+	if common.Settings.UpdateRevokedTokensIntervalSec > 0 {
+		go store.loopGetRevokedTokens()
+	}
+	return err
+}
+
+func (store *TokenStore) processError(err error) error {
+	if err != nil {
+		go store.reconnectIfNeeded(err)
+	}
+	return err
+}
+
+func (store *TokenStore) AddToken(token TokenRecord) error {
+	_, err := store.db.ProcessRequest(Request{
+		DbName:     KAdminDb,
+		Collection: KTokens,
+		Op:         "create_record",
+	}, &token)
+	return store.processError(err)
+}
+
+func (store *TokenStore) findToken(token string, id string) (TokenRecord, error) {
+	q := map[string]interface{}{}
+	if token != "" {
+		q["token"] = token
+	} else {
+		q["_id"] = id
+	}
+	var record TokenRecord
+	_, err := store.db.ProcessRequest(Request{
+		DbName:     KAdminDb,
+		Collection: KTokens,
+		Op:         "read_record",
+	}, q, &record)
+	if err != nil {
+		return TokenRecord{}, err
+	}
+	if record.Revoked {
+		return TokenRecord{}, errors.New("token already revoked")
+	}
+	return record, nil
+}
+
+func (store *TokenStore) updateTokenStatus(token *TokenRecord) error {
+	_, err := store.db.ProcessRequest(Request{
+		DbName:     KAdminDb,
+		Collection: KTokens,
+		Op:         "update_record",
+	}, token.Id, map[string]interface{}{"revoked": true}, false, token)
+	if err != nil {
+		return err
+	}
+
+	idRec := IdRecord{token.Id}
+	_, err = store.db.ProcessRequest(Request{
+		DbName:     KAdminDb,
+		Collection: KRevokedTokens,
+		Op:         "create_record",
+	}, &idRec)
+	return err
+}
+
+func (store *TokenStore) updateRevokedTokensList(tokenId string) error {
+	idRec := IdRecord{tokenId}
+	_, err := store.db.ProcessRequest(Request{
+		DbName:     KAdminDb,
+		Collection: KRevokedTokens,
+		Op:         "create_record",
+	}, &idRec)
+	return err
+}
+
+func (store *TokenStore) RevokeToken(token string, id string) (TokenRecord, error) {
+	tokenRecord, err := store.findToken(token, id)
+	if err != nil {
+		return TokenRecord{}, store.processError(err)
+	}
+
+	err = store.updateTokenStatus(&tokenRecord)
+	if err != nil {
+		return TokenRecord{}, store.processError(err)
+	}
+
+	err = store.updateRevokedTokensList(tokenRecord.Id)
+	if err != nil {
+		return TokenRecord{}, store.processError(err)
+	}
+
+	return tokenRecord, nil
+}
+
+func (store *TokenStore) GetTokenList() ([]TokenRecord, error) {
+	var res []TokenRecord
+	_, err := store.db.ProcessRequest(Request{
+		DbName:     KAdminDb,
+		Collection: KTokens,
+		Op:         "read_records",
+	}, &res)
+	return res, store.processError(err)
+}
+
+func (store *TokenStore) loopGetRevokedTokens() {
+	next_update := 0
+	for true {
+		var res []IdRecord
+		_, err := store.db.ProcessRequest(Request{
+			DbName:     KAdminDb,
+			Collection: KRevokedTokens,
+			Op:         "read_records",
+		}, &res)
+		if err != nil {
+			store.revokedTokenList.lock.Lock()
+			store.revokedTokenList.lastError = err
+			store.processError(err)
+			store.revokedTokenList.lock.Unlock()
+			next_update = 1
+			log.Error("cannot get revoked tokens: " + err.Error())
+		} else {
+			log.Debug("received revoked tokens list")
+			next_update = common.Settings.UpdateRevokedTokensIntervalSec
+			tokens := make([]string, len(res))
+			for i, token := range res {
+				tokens[i] = token.Id
+			}
+			store.revokedTokenList.lock.Lock()
+			store.revokedTokenList.tokens = tokens
+			store.revokedTokenList.lock.Unlock()
+		}
+		select {
+		case <-store.stopchan:
+			return
+		case <-time.After(time.Duration(next_update) * time.Second):
+			break
+		}
+	}
+}
+
+func (store *TokenStore) GetRevokedTokenIds() ([]string, error) {
+	store.revokedTokenList.lock.RLock()
+	defer store.revokedTokenList.lock.RUnlock()
+	if store.revokedTokenList.lastError != nil {
+		return []string{}, store.revokedTokenList.lastError
+	}
+//	res := make([]string, len(store.revokedTokenList.tokens))
+//	copy(res, store.revokedTokenList.tokens)
+//	return res,nil
+	return store.revokedTokenList.tokens, nil
+}
+
+func (store *TokenStore) Close() {
+	if common.Settings.UpdateRevokedTokensIntervalSec > 0 {
+		store.stopchan <- true
+	}
+	if store.db != nil {
+		store.db.Close()
+	}
+}
diff --git a/authorizer/src/asapo_authorizer/token_store/token_store_test.go b/authorizer/src/asapo_authorizer/token_store/token_store_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..550784d6364b95e3369a5a2de3f75661e3059e71
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/token_store_test.go
@@ -0,0 +1,146 @@
+package token_store
+
+import (
+	"asapo_authorizer/common"
+	"asapo_common/logger"
+	"asapo_common/utils"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/suite"
+	"strings"
+	"testing"
+	"time"
+)
+
+const expectedSource = "datasource"
+const expectedStream = "stream"
+
+func ExpectReconnect(mock_db *MockedDatabase) {
+	mock_db.On("Close").Return()
+	mock_db.On("Connect", mock.Anything).Return(nil)
+}
+
+func assertExpectations(t *testing.T, mock_db *MockedDatabase) {
+	mock_db.AssertExpectations(t)
+	mock_db.ExpectedCalls = nil
+	logger.MockLog.AssertExpectations(t)
+	logger.MockLog.ExpectedCalls = nil
+}
+
+
+type TokenStoreTestSuite struct {
+	suite.Suite
+	mock_db *MockedDatabase
+	store Store
+}
+
+func (suite *TokenStoreTestSuite) SetupTest() {
+	common.Settings.UpdateRevokedTokensIntervalSec = 0
+	suite.mock_db = new(MockedDatabase)
+	suite.store = new(TokenStore)
+	suite.mock_db.On("Connect", mock.Anything).Return( nil)
+	suite.store.Init(suite.mock_db)
+	logger.SetMockLog()
+}
+
+func (suite *TokenStoreTestSuite) TearDownTest() {
+	assertExpectations(suite.T(), suite.mock_db)
+	logger.UnsetMockLog()
+	suite.store = nil
+}
+
+func TestTokenStoreTestSuite(t *testing.T) {
+	suite.Run(t, new(TokenStoreTestSuite))
+}
+
+func containsMatcher(substr string) func(str string) bool {
+	return func(str string) bool { return strings.Contains(str, substr) }
+}
+
+func (suite *TokenStoreTestSuite) TestProcessRequestWithConnectionError() {
+	ExpectReconnect(suite.mock_db)
+	suite.mock_db.On("ProcessRequest", mock.Anything, mock.Anything).Return([]byte(""),
+		&DBError{utils.StatusServiceUnavailable, ""})
+	logger.MockLog.On("Debug", mock.MatchedBy(containsMatcher("reconnected")))
+
+	err := suite.store.AddToken(TokenRecord{})
+	time.Sleep(time.Second)
+	suite.Error(err, "need reconnect")
+}
+
+
+func (suite *TokenStoreTestSuite) TestProcessRequestAddToken() {
+	req := Request{
+		DbName:     "asapo_admin",
+		Collection: "tokens",
+		Op:         "create_record",
+	}
+	suite.mock_db.On("ProcessRequest", req, mock.Anything).Return([]byte(""), nil)
+	err := suite.store.AddToken(TokenRecord{})
+	suite.Equal(err, nil, "ok")
+}
+
+
+func (suite *TokenStoreTestSuite) TestProcessRequestGetTokenList() {
+	req := Request{
+		DbName:     "asapo_admin",
+		Collection: "tokens",
+		Op:         "read_records",
+	}
+	suite.mock_db.On("ProcessRequest", req, mock.Anything).Return([]byte(""), nil)
+	_,err := suite.store.GetTokenList()
+	suite.Equal(err, nil, "ok")
+}
+
+
+func (suite *TokenStoreTestSuite) TestProcessRequestRevokeToken() {
+	req := Request{
+		DbName:     "asapo_admin",
+		Collection: "tokens",
+		Op:         "read_record",
+	}
+	expectedToken := TokenRecord{Id :"1234", Token:"token",Revoked: false}
+	expectedRevokedToken := expectedToken
+	expectedRevokedToken.Revoked = true
+	suite.mock_db.On("ProcessRequest", req,mock.Anything).Return([]byte(""), nil).Run(func(args mock.Arguments) {
+		rec := args.Get(1).([]interface{})[1].(*TokenRecord)
+		*rec = expectedToken
+	})
+
+	req = Request{
+		DbName:     KAdminDb,
+		Collection: KTokens,
+		Op:         "update_record"}
+	suite.mock_db.On("ProcessRequest", req,mock.Anything).Return([]byte(""), nil).Run(func(args mock.Arguments) {
+		rec := args.Get(1).([]interface{})[3].(*TokenRecord)
+		*rec = expectedRevokedToken
+	})
+
+	req = Request{
+		DbName:     "asapo_admin",
+		Collection: "revoked_tokens",
+		Op:         "create_record",
+	}
+	suite.mock_db.On("ProcessRequest", req, mock.Anything).Return([]byte(""), nil)
+
+	token,err := suite.store.RevokeToken(expectedToken.Token,"")
+	suite.Equal(err, nil, "ok")
+	suite.Equal(token, expectedRevokedToken, "ok")
+}
+
+func (suite *TokenStoreTestSuite) TestProcessRequestGetRevokedTokens() {
+	suite.mock_db.On("Close")
+	suite.store.Close()
+	common.Settings.UpdateRevokedTokensIntervalSec = 5
+	suite.store.Init(suite.mock_db)
+	req := Request{
+		DbName:     "asapo_admin",
+		Collection: "revoked_tokens",
+		Op:         "read_records",
+	}
+	suite.mock_db.On("ProcessRequest", req, mock.Anything).Return([]byte(""), nil)
+
+	logger.MockLog.On("Debug", mock.MatchedBy(containsMatcher("list")))
+	time.Sleep(time.Second*1)
+	_,err := suite.store.GetRevokedTokenIds()
+	suite.Equal(err, nil, "ok")
+}
\ No newline at end of file
diff --git a/deploy/build_env/centos/build.sh b/deploy/build_env/centos/build.sh
index 0f000741de74a14b217b875804e5059d0316de35..103ae9e77c95a0761b89a43d3e2114d1beb604fd 100755
--- a/deploy/build_env/centos/build.sh
+++ b/deploy/build_env/centos/build.sh
@@ -24,6 +24,7 @@ cmake \
     -DBUILD_CLIENTS_ONLY=ON \
     -DNUMPY_VERSION=0   \
     -DBUILD_PYTHON=ON   \
+    -DPACKAGE_RELEASE_SUFFIX=1.$OS \
     -DBUILD_PYTHON_PACKAGES="source;rpm"   \
     -DBUILD_PYTHON_DOCS=$BUILD_PYTHON_DOCS \
     ..
diff --git a/deploy/build_env/debians/build.sh b/deploy/build_env/debians/build.sh
index e78771d5ca95635f17a6dac13619390a4128fec0..9e27fc1ee4cdad925988a7b758854c4ab6804f04 100755
--- a/deploy/build_env/debians/build.sh
+++ b/deploy/build_env/debians/build.sh
@@ -34,6 +34,7 @@ cmake \
     -DBUILD_CLIENTS_ONLY=ON \
     -DNUMPY_VERSION=0   \
     -DBUILD_PYTHON=ON   \
+    -DPACKAGE_RELEASE_SUFFIX=$OS \
     -DBUILD_PYTHON_PACKAGES="source;deb"   \
     -DBUILD_PYTHON_DOCS=$BUILD_PYTHON_DOCS \
     ..