diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbbc7b6bc39bf254cde220402470f92b18dc8a28..bb02fdd1493bbbb1c3a376c7761db01fa4a605aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,19 @@
 ## 21.09.0 (in progress)
 
 FEATURES
-* Producer API: C client 
+* Producer API: C client
+* Introduce a token to send data in "raw" mode without LDAP authorization  
 
 IMPROVEMENTS
 * Allow using ASAPO for commissioning beamtimes
+* Implement token revocation
+
+BUG FIXES
+* Consumer/Producer API: fixed bug with "_" in stream name 
+
+INTERNAL
+* Improved authoriation service caching
+* Added profiling for Go services
 
 ## 21.06.0
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fcdc271fbc47de17adc767cf2939b496155a054a..36302965720bf9265d21426f3afa9bb1aba779f4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,12 +9,12 @@ set (ASAPO_EXAMPLES_DIR .)
 
 #protocol version changes if one of the microservice API's change
 set (ASAPO_CONSUMER_PROTOCOL "v0.4")
-set (ASAPO_PRODUCER_PROTOCOL "v0.3")
+set (ASAPO_PRODUCER_PROTOCOL "v0.4")
 set (ASAPO_DISCOVERY_API_VER "v0.1")
 set (ASAPO_AUTHORIZER_API_VER "v0.2")
 set (ASAPO_BROKER_API_VER "v0.4")
 set (ASAPO_FILE_TRANSFER_SERVICE_API_VER "v0.2")
-set (ASAPO_RECEIVER_API_VER "v0.3")
+set (ASAPO_RECEIVER_API_VER "v0.4")
 set (ASAPO_RDS_API_VER "v0.1")
 set (DB_SCHEMA_VER "v0.1")
 
diff --git a/CMakeModules/testing_cpp.cmake b/CMakeModules/testing_cpp.cmake
index 23a4457ac3af624949eeba0f4b3c9a55900820c3..63dae68e4cf2c706b2a245cbbbd8ef8de9419126 100644
--- a/CMakeModules/testing_cpp.cmake
+++ b/CMakeModules/testing_cpp.cmake
@@ -4,6 +4,7 @@ endif ()
 
 set (TOKENS "ASAPO_TEST_RW_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyMXJqaXB0MzVja3MzYTEwZyIsInN1YiI6ImJ0X2FzYXBvX3Rlc3QiLCJFeHRyYUNsYWltcyI6eyJBY2Nlc3NUeXBlcyI6WyJyZWFkIiwid3JpdGUiXX19.3PFdG0f48yKrOyJwPErYcewpcbZgnd8rBmBphw_kdJ0")
 set (TOKENS "${TOKENS};ASAPO_CREATE_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyYzMzaXB0Mzdkb3IzYmZjZyIsInN1YiI6ImFkbWluIiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsiY3JlYXRlIl19fQ.AI41cZ7dZL0g-rrdKIQgd7ijjzuyH1Fm0xojCXwLNBo")
+set (TOKENS "${TOKENS};ASAPO_REVOKE_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjNWFxOHVyaXB0MzV0aG9raDFwMCIsInN1YiI6ImFkbWluIiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsicmV2b2tlIl19fQ.GNje7w6biX0-ynltRr81p5SBSWwmKdDwGfs-adb094Q")
 set (TOKENS "${TOKENS};C20180508_000_COM20181_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyaDRiaXB0Mzd1cGo1aDdlMCIsInN1YiI6ImJ0X2MyMDE4MDUwOC0wMDAtQ09NMjAxODEiLCJFeHRyYUNsYWltcyI6eyJBY2Nlc3NUeXBlcyI6WyJyZWFkIiwid3JpdGUiXX19.yONpjW2ybZMc9E9Eu4Hmn1roVR-mxf2OQQyXfnel5C8")
 set (TOKENS "${TOKENS};BT11000015_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyajZqaXB0MzA3aHU1amwxZyIsInN1YiI6ImJ0XzExMDAwMDE1IiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsicmVhZCJdfX0.kVs669HAS4sj9VAZk8pWTLrYNQp46mOnH4id4-_qd9g")
 set (TOKENS "${TOKENS};BT11000016_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMTkyajQzaXB0MzA3OWxwc3Z2ZyIsInN1YiI6ImJ0XzExMDAwMDE2IiwiRXh0cmFDbGFpbXMiOnsiQWNjZXNzVHlwZXMiOlsicmVhZCJdfX0.mpTVGtcdR0l4NaeHFTf16iWrfMYaLzh2pAjN5muil6Q")
diff --git a/PROTOCOL-VERSIONS.md b/PROTOCOL-VERSIONS.md
index e72696805562d6e3d50eb4b6f713be8b7831b33b..3b87af27833e8b8bfeab57e857ca531cac18a631 100644
--- a/PROTOCOL-VERSIONS.md
+++ b/PROTOCOL-VERSIONS.md
@@ -1,7 +1,8 @@
 ### Producer Protocol
 | Release      | used by client      | Supported by server  | Status           |
 | ------------ | ------------------- | -------------------- | ---------------- |
-| v0.3         | 21.06.0 - 21.06.0   | 21.06.0  - 21.09.0   | Current version  |
+| v0.4         | 21.09.0 - 21.09.0   | 21.09.0  - 21.09.0   | Current version  |
+| v0.3         | 21.06.0 - 21.06.0   | 21.06.0  - 21.09.0   | Deprecates from 01.09.2022  |
 | v0.2         | 21.03.2 - 21.03.2   | 21.03.2  - 21.09.0   | Deprecates from 01.07.2022  |
 | v0.1         | 21.03.0 - 21.03.1   | 21.03.0  - 21.09.0   | Deprecates from 01.06.2022  |
 
@@ -9,7 +10,7 @@
 ### Consumer Protocol
 | Release      | used by client      | Supported by server  | Status           |
 | ------------ | ------------------- | -------------------- | ---------------- |
-| v0.4         | 21.06.0 - 21.06.0   | 21.06.0  - 21.09.0   | Current version  |
+| v0.4         | 21.06.0 - 21.09.0   | 21.06.0  - 21.09.0   | Current version  |
 | v0.3         | 21.03.3 - 21.03.3   | 21.03.3  - 21.09.0   | Deprecates from 01.07.2022  |
 | v0.2         | 21.03.2 - 21.03.2   | 21.03.2  - 21.09.0   | Deprecates from 01.06.2022  |
 | v0.1         | 21.03.0 - 21.03.1   | 21.03.0  - 21.09.0   | Deprecates from 01.06.2022  |
diff --git a/VERSIONS.md b/VERSIONS.md
index a52e3d2571f0d5faf4a6646ff2693fc75bfcbf2f..05d9509333bb681295b0a3f1a6c4f852e62c49d7 100644
--- a/VERSIONS.md
+++ b/VERSIONS.md
@@ -2,8 +2,8 @@
 
 | Release      | API changed\*\* |  Protocol | Supported by server from/to | Status              |Comment|
 | ------------ | ----------- | -------- | ------------------------- | --------------------- | ------- |
-| 21.09.0      | No          |  v0.3     | 21.06.0/21.09.0           | current version              ||
-| 21.06.0      | Yes         |  v0.3     | 21.06.0/21.09.0           |                              |arbitrary characters|
+| 21.09.0      | No         |  v0.4     | 21.09.0/21.09.0           | current version              |beamline token for raw |
+| 21.06.0      | Yes         |  v0.3     | 21.06.0/21.09.0           | deprecates 01.09.2022         |arbitrary characters|
 | 21.03.3      | No          |  v0.2     | 21.03.2/21.09.0           | deprecates 01.07.2022        |bugfix in server|
 | 21.03.2      | Yes         |  v0.2     | 21.03.2/21.09.0           | deprecates 01.07.2022        |bugfixes, add delete_stream|
 | 21.03.1      | No          |  v0.1     | 21.03.0/21.09.0           | deprecates 01.06.2022   |bugfix in server|
diff --git a/authorizer/src/asapo_authorizer/authorization/authorization.go b/authorizer/src/asapo_authorizer/authorization/authorization.go
index 1a0e85b25a6041b26e69a87705f36732f96a5838..59b613c6ff9e6ed3b7c227d307c39672c89ccaca 100644
--- a/authorizer/src/asapo_authorizer/authorization/authorization.go
+++ b/authorizer/src/asapo_authorizer/authorization/authorization.go
@@ -14,8 +14,8 @@ type Auth struct {
 	authJWT   utils.Auth
 }
 
-func NewAuth(authUser,authAdmin,authJWT utils.Auth) *Auth {
-	return &Auth{authUser,authAdmin,authJWT}
+func NewAuth(authUser, authAdmin, authJWT utils.Auth) *Auth {
+	return &Auth{authUser, authAdmin, authJWT}
 }
 
 func (auth *Auth) AdminAuth() utils.Auth {
@@ -31,7 +31,7 @@ func (auth *Auth) JWTAuth() utils.Auth {
 }
 
 func subjectFromRequest(request structs.IssueTokenRequest) string {
-	for key,value := range request.Subject {
+	for key, value := range request.Subject {
 		switch key {
 		case "beamline":
 			return utils.SubjectFromBeamline(value)
@@ -44,10 +44,10 @@ func subjectFromRequest(request structs.IssueTokenRequest) string {
 	return ""
 }
 
-func (auth *Auth) PrepareAccessToken(request structs.IssueTokenRequest, userToken bool) (string, error) {
-	var claims utils.CustomClaims
+func (auth *Auth) PrepareAccessToken(request structs.IssueTokenRequest, userToken bool) (token string, claims *utils.CustomClaims, err error) {
 	var extraClaim structs.AccessTokenExtraClaim
 
+	claims = new(utils.CustomClaims)
 	claims.Subject = subjectFromRequest(request)
 
 	extraClaim.AccessTypes = request.AccessTypes
@@ -57,15 +57,19 @@ func (auth *Auth) PrepareAccessToken(request structs.IssueTokenRequest, userToke
 	claims.Id = uid.String()
 
 	if userToken {
-		return auth.UserAuth().GenerateToken(&claims)
+		token, err = auth.UserAuth().GenerateToken(claims)
 	} else {
-		return auth.AdminAuth().GenerateToken(&claims)
+		token, err = auth.AdminAuth().GenerateToken(claims)
 	}
+	if err != nil {
+		return "", nil, err
+	}
+	return
 }
 
 func UserTokenResponce(request structs.IssueTokenRequest, token string) []byte {
 	expires := ""
-	if request.DaysValid>0 {
+	if request.DaysValid > 0 {
 		expires = time.Now().Add(time.Duration(request.DaysValid*24) * time.Hour).UTC().Format(time.RFC3339)
 	}
 	answer := structs.IssueTokenResponse{
diff --git a/authorizer/src/asapo_authorizer/cli/cli.go b/authorizer/src/asapo_authorizer/cli/cli.go
index 0851d568c782b2d97f80894b3afd280f0c5a1b64..e39bbd65638b2cfc93c02ec9023870f1d4acd5b9 100644
--- a/authorizer/src/asapo_authorizer/cli/cli.go
+++ b/authorizer/src/asapo_authorizer/cli/cli.go
@@ -3,6 +3,7 @@
 package cli
 
 import (
+	"asapo_authorizer/token_store"
 	"errors"
 	"flag"
 	"fmt"
@@ -16,6 +17,8 @@ var flHelp bool
 
 var outBuf io.Writer = os.Stdout
 
+var store token_store.Store
+
 func printHelp(f *flag.FlagSet) bool {
 	if flHelp {
 		f.Usage()
@@ -39,6 +42,10 @@ func DoCommand(name string, args []string) error {
 
 	method := methodVal.Interface().(func() error)
 
+	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 ba06241279ffc28371ed2021fd982ba11cfdd9c1..4d267a204f3f2fe4caed814e5e406fac8b33cfe8 100644
--- a/authorizer/src/asapo_authorizer/cli/create_token.go
+++ b/authorizer/src/asapo_authorizer/cli/create_token.go
@@ -3,11 +3,13 @@ package cli
 import (
 	"asapo_authorizer/authorization"
 	"asapo_authorizer/server"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"errors"
 	"fmt"
 	"os"
 	"strings"
+	"time"
 )
 
 type tokenFlags struct {
@@ -19,21 +21,26 @@ type tokenFlags struct {
 }
 
 func userTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err error) {
-	if (flags.Beamline=="" && flags.Beamtime=="") || (flags.Beamline!="" && flags.Beamtime!="") {
-		return request,errors.New("beamtime or beamline must be set")
+	if (flags.Beamline == "" && flags.Beamtime == "") || (flags.Beamline != "" && flags.Beamtime != "") {
+		return request, errors.New("beamtime or beamline must be set")
 	}
 
-	request.Subject = make(map[string]string,1)
-	if (flags.Beamline!="") {
-		request.Subject["beamline"]=flags.Beamline
+	request.Subject = make(map[string]string, 1)
+	if flags.Beamline != "" {
+		request.Subject["beamline"] = flags.Beamline
 	} else {
-		request.Subject["beamtimeId"]=flags.Beamtime
+		request.Subject["beamtimeId"] = flags.Beamtime
 	}
 
-	request.AccessTypes = strings.Split(flags.AccessType,",")
-	for _,at:=range request.AccessTypes {
-		if at!="read" && at!="write" {
-			return request,errors.New("access type must be read of write")
+	request.AccessTypes = strings.Split(flags.AccessType, ",")
+	for _, at := range request.AccessTypes {
+		if at != "read" && at != "write" && !(at == "writeraw" && request.Subject["beamline"] != "") {
+			if request.Subject["beamline"] != "" {
+				return request, errors.New("access type must be read, write or writeraw")
+			} else {
+				return request, errors.New("access type must be read or write")
+			}
+
 		}
 	}
 
@@ -42,21 +49,20 @@ func userTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err
 	return
 }
 
-
 func adminTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, err error) {
-	if flags.Beamline+flags.Beamtime!="" {
-		return request,errors.New("beamtime and beamline must not be set for admin token")
+	if flags.Beamline+flags.Beamtime != "" {
+		return request, errors.New("beamtime and beamline must not be set for admin token")
 	}
 
-	request.AccessTypes = strings.Split(flags.AccessType,",")
-	for _,at:=range request.AccessTypes {
-		if at!="create" && at!="revoke" && at!="list" {
-			return request,errors.New("access type must be create,revoke of list")
+	request.AccessTypes = strings.Split(flags.AccessType, ",")
+	for _, at := range request.AccessTypes {
+		if at != "create" && at != "revoke" && at != "list" {
+			return request, errors.New("access type must be create,revoke of list")
 		}
 	}
 
-	request.Subject = make(map[string]string,1)
-	request.Subject["user"]="admin"
+	request.Subject = make(map[string]string, 1)
+	request.Subject["user"] = "admin"
 	request.DaysValid = flags.DaysValid
 
 	return
@@ -78,7 +84,14 @@ func (cmd *command) CommandCreate_token() (err error) {
 		return err
 	}
 
-	token, err := server.Auth.PrepareAccessToken(request,userToken)
+	token, claims, err := server.Auth.PrepareAccessToken(request, userToken)
+	if err != nil {
+		return err
+	}
+	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
 	}
@@ -100,12 +113,11 @@ func getTokenRequest(flags tokenFlags) (request structs.IssueTokenRequest, userT
 		return structs.IssueTokenRequest{}, false, errors.New("wrong token type")
 	}
 	if err != nil {
-		return structs.IssueTokenRequest{},false,  err
+		return structs.IssueTokenRequest{}, false, err
 	}
 	return request, userToken, err
 }
 
-
 func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) {
 
 	var flags tokenFlags
@@ -113,10 +125,9 @@ func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) {
 	flagset.StringVar(&flags.Type, "type", "", "token type")
 	flagset.StringVar(&flags.Beamtime, "beamtime", "", "beamtime for user token")
 	flagset.StringVar(&flags.Beamline, "beamline", "", "beamline for user token")
-	flagset.StringVar(&flags.AccessType, "access-types", "", "read/write for user token")
+	flagset.StringVar(&flags.AccessType, "access-types", "", "read/write/writeraw(beamline only) for user token")
 	flagset.IntVar(&flags.DaysValid, "duration-days", 0, "token duration (in days)")
 
-
 	flagset.Parse(cmd.args)
 
 	if printHelp(flagset) {
@@ -124,10 +135,9 @@ func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) {
 	}
 
 	if flags.Type == "" {
-		return flags, errors.New("secret file missed ")
+		return flags, errors.New("access types missing")
 	}
 
-
 	return flags, nil
 
 }
diff --git a/authorizer/src/asapo_authorizer/cli/create_token_test.go b/authorizer/src/asapo_authorizer/cli/create_token_test.go
index ca16f199ed5c20d2a0f0946c76bd00c06d168778..bedd7476bfdbcecfcc1dda25ad6436bc46a7eb44 100644
--- a/authorizer/src/asapo_authorizer/cli/create_token_test.go
+++ b/authorizer/src/asapo_authorizer/cli/create_token_test.go
@@ -3,9 +3,11 @@ package cli
 import (
 	"asapo_authorizer/authorization"
 	"asapo_authorizer/server"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"encoding/json"
+	"github.com/stretchr/testify/mock"
 	"testing"
 
 	"bytes"
@@ -41,13 +43,22 @@ var tokenTests = []struct {
 
 func TestGenerateToken(t *testing.T) {
 	server.Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret"))
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
+
 	for _, test := range tokenTests {
 		outBuf = new(bytes.Buffer)
+
+		if test.ok {
+			mock_store.On("AddToken", mock.Anything).Return(nil)
+		}
+
 		err := test.cmd.CommandCreate_token()
 		if !test.ok {
 			assert.NotNil(t, err, test.msg)
 			continue
 		}
+
 		assert.Nil(t, err, test.msg)
 		var token structs.IssueTokenResponse
 		json.Unmarshal(outBuf.(*bytes.Buffer).Bytes(), &token)
@@ -63,5 +74,9 @@ func TestGenerateToken(t *testing.T) {
 		} else {
 			assert.Empty(t, token.Expires, test.msg)
 		}
+
+		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
new file mode 100644
index 0000000000000000000000000000000000000000..923cc8f8b622b38023b1c256f190bce8faf478c9
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/cli/list_tokens.go
@@ -0,0 +1,23 @@
+package cli
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+func (cmd *command) CommandList_tokens() (err error) {
+	message_string := "List tokens"
+	if cmd.description(message_string) {
+		return nil
+	}
+
+	tokens,err := store.GetTokenList()
+	if err != nil {
+		return err
+	}
+
+	answer,_ := json.Marshal(tokens)
+	fmt.Fprintf(outBuf, "%s\n", string(answer))
+	return 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/go.mod b/authorizer/src/asapo_authorizer/go.mod
index 67af7e095034c70910d408c70222786da84d7b83..81f46fd127f9573baccc9992a45bcedc15ce2812 100644
--- a/authorizer/src/asapo_authorizer/go.mod
+++ b/authorizer/src/asapo_authorizer/go.mod
@@ -9,5 +9,6 @@ require (
 	github.com/go-ldap/ldap v3.0.3+incompatible
 	github.com/rs/xid v1.2.1
 	github.com/stretchr/testify v1.7.0
+	go.mongodb.org/mongo-driver v1.7.2
 	gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
 )
diff --git a/authorizer/src/asapo_authorizer/go.sum b/authorizer/src/asapo_authorizer/go.sum
index 4e1c32df6dd6e14736ec4b0bf11f74f6a56b7ca0..f9db70a3f9f6979312428952e43576663fda2592 100644
--- a/authorizer/src/asapo_authorizer/go.sum
+++ b/authorizer/src/asapo_authorizer/go.sum
@@ -1,3 +1,4 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -5,26 +6,132 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
 github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
+github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
+github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
+github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
+github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
+github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
+github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
+github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
+github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
+github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
+github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
+github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
+github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
+github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
+github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
+github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
+github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
+github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
+github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
+github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
+github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
+github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
 github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
 github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
-github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
+github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
+github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+go.mongodb.org/mongo-driver v1.7.2 h1:pFttQyIiJUHEn50YfZgC9ECjITMT44oiN36uArf/OFg=
+go.mongodb.org/mongo-driver v1.7.2/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
+golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/authorizer/src/asapo_authorizer/server/authorize.go b/authorizer/src/asapo_authorizer/server/authorize.go
index bac5853d8ad5a547c93e7c359fa71319112ed8e5..1afe3d9da5fb11154a68c6bd0896b1601aaa8e7e 100644
--- a/authorizer/src/asapo_authorizer/server/authorize.go
+++ b/authorizer/src/asapo_authorizer/server/authorize.go
@@ -14,9 +14,9 @@ import (
 type SourceCredentials struct {
 	BeamtimeId string
 	Beamline   string
-	DataSource     string
+	DataSource string
 	Token      string
-	Type 	   string
+	Type       string
 }
 
 type authorizationRequest struct {
@@ -26,15 +26,14 @@ type authorizationRequest struct {
 
 func getSourceCredentials(request authorizationRequest) (SourceCredentials, error) {
 
-
 	vals := strings.Split(request.SourceCredentials, "%")
-	nvals:=len(vals)
+	nvals := len(vals)
 	if nvals < 5 {
 		return SourceCredentials{}, errors.New("cannot get source credentials from " + request.SourceCredentials)
 	}
 
-	creds := SourceCredentials{Type:vals[0], BeamtimeId: vals[1], Beamline: vals[2], Token:vals[nvals-1]}
-	creds.DataSource=strings.Join(vals[3:nvals-1],"%")
+	creds := SourceCredentials{Type: vals[0], BeamtimeId: vals[1], Beamline: vals[2], Token: vals[nvals-1]}
+	creds.DataSource = strings.Join(vals[3:nvals-1], "%")
 	if creds.DataSource == "" {
 		creds.DataSource = "detector"
 	}
@@ -54,27 +53,26 @@ func getSourceCredentials(request authorizationRequest) (SourceCredentials, erro
 	return creds, nil
 }
 
-
 func splitHost(hostPort string) string {
 	s := strings.Split(hostPort, ":")
 	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,49 +80,59 @@ 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) {
+	cachedMetas.lock.Lock()
+	meta, ok := cachedMetas.cache[beamtime_id]
+	cachedMetas.lock.Unlock()
+	if ok {
+		return meta, nil
+	}
+
 	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) {
+	for _, match := range matches {
 		btInfo, err := beamtimeMetaFromMatch(match)
 		if err != nil {
 			continue
 		}
 		if btInfo.BeamtimeId == beamtime_id {
+			cachedMetas.lock.Lock()
+			cachedMetas.cache[beamtime_id] = btInfo
+			cachedMetas.lock.Unlock()
 			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){
+func findMetaFileInFolder(beamline string, iscommissioning bool) (string, string, error) {
 	sep := string(filepath.Separator)
-	var pattern,folder string
+	var pattern, folder string
 	if !iscommissioning {
 		pattern = "beamtime-metadata-*.json"
 		folder = "current"
@@ -132,22 +140,22 @@ 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
+		return "", "", err
 	}
 	if len(matches) != 1 {
-		return "","", errors.New("should be one beamtime-metadata file in folder")
+		return "", "", errors.New("should be one beamtime-metadata file in folder")
 	}
-	return matches[0],online_path, nil
+	return matches[0], online_path, nil
 
 }
 
-func findBeamtimeMetaFromBeamline(beamline string,iscommissioning bool) (meta beamtimeMeta, err error) {
-	fName,online_path, err := findMetaFileInFolder(beamline,iscommissioning)
-	if (err != nil) {
-		return beamtimeMeta{}, err
+func findBeamtimeMetaFromBeamline(beamline string, iscommissioning bool) (meta common.BeamtimeMeta, err error) {
+	fName, online_path, err := findMetaFileInFolder(beamline, iscommissioning)
+	if err != nil {
+		return common.BeamtimeMeta{}, err
 	}
 
 	if iscommissioning {
@@ -155,103 +163,119 @@ func findBeamtimeMetaFromBeamline(beamline string,iscommissioning bool) (meta be
 	} else {
 		meta, err = beamtimeMetaFromJson(fName)
 	}
-	if (err != nil) {
-		return beamtimeMeta{}, err
+	if err != nil {
+		return common.BeamtimeMeta{}, err
 	}
 
-	if meta.BeamtimeId == "" || meta.OfflinePath=="" || meta.Beamline == ""{
-		return beamtimeMeta{}, errors.New("cannot set meta fields from beamtime file")
+	if meta.BeamtimeId == "" || meta.OfflinePath == "" || meta.Beamline == "" {
+		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
-			pair.AccessTypes = []string{"read","write"}
+			pair.AccessTypes = []string{"read", "write"}
 			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)
+func authorizeByHost(host_ip, beamline string) error {
+	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
 	}
 
-	if (!utils.StringInSlice(splitHost(host_ip),allowed_ips)) {
-		err_string := "beamine " +beamline+" not allowed for host " + host_ip
+	if !utils.StringInSlice(splitHost(host_ip), allowed_ips) {
+		err_string := "beamine " + beamline + " not allowed for host " + host_ip
 		log.Error(err_string)
 		return errors.New(err_string)
 	}
 	return nil
 }
 
-func needHostAuthorization(creds SourceCredentials) bool {
-	return creds.Type == "raw" || len(creds.Token) == 0
+func canUseHostAuthorization(creds SourceCredentials) bool {
+	return len(creds.Token) == 0
+}
+
+func checkTokenRevoked(tokenId string) (err error) {
+	revoked, err := store.IsTokenRevoked(tokenId)
+	if err != nil {
+		return &common.ServerError{utils.StatusServiceUnavailable, err.Error()}
+	}
+	if revoked {
+		return errors.New("token was revoked")
+	}
+	return nil
 }
 
 func checkToken(token string, subject_expect string) (accessTypes []string, err error) {
 	var extra_claim structs.AccessTokenExtraClaim
-	subject,err := Auth.UserAuth().CheckAndGetContent(token,&extra_claim)
-	if err!=nil {
-		return nil,err
+	claim, err := Auth.UserAuth().CheckAndGetContent(token, &extra_claim)
+	if err != nil {
+		return nil, err
 	}
 
-	if extra_claim.AccessTypes==nil || len(extra_claim.AccessTypes)==0 {
-		return nil,errors.New("missing access types")
+	err = checkTokenRevoked(claim.Id)
+	if err != nil {
+		return nil, err
+	}
+
+	if extra_claim.AccessTypes == nil || len(extra_claim.AccessTypes) == 0 {
+		return nil, errors.New("missing access types")
 	}
 
-	if subject!=subject_expect {
-		return nil,errors.New("wrong token for "+subject_expect)
+	if claim.Subject != subject_expect {
+		return nil, errors.New("wrong token for " + subject_expect)
 	}
-	return extra_claim.AccessTypes,err
+	return extra_claim.AccessTypes, err
 }
 
 func authorizeByToken(creds SourceCredentials) (accessTypes []string, err error) {
-	subject_expect:=""
-	if (creds.BeamtimeId != "auto") {
+	subject_expect := ""
+	if creds.BeamtimeId != "auto" {
 		subject_expect = utils.SubjectFromBeamtime(creds.BeamtimeId)
 	} else {
 		subject_expect = utils.SubjectFromBeamline(creds.Beamline)
 	}
-	return checkToken(creds.Token,subject_expect)
+	return checkToken(creds.Token, subject_expect)
 }
 
 func iscommissioning(beamtime string) bool {
-	return len(beamtime)>0 && beamtime[0]=='c'
+	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
-	if (creds.BeamtimeId != "auto") {
+	var meta common.BeamtimeMeta
+	if creds.BeamtimeId != "auto" {
 		meta, err = findBeamtimeInfoFromId(creds.BeamtimeId)
-		if (err == nil ) {
-			meta_onilne, err_online := findBeamtimeMetaFromBeamline(meta.Beamline,iscommissioning(creds.BeamtimeId))
+		if err == nil {
+			meta_onilne, err_online := findBeamtimeMetaFromBeamline(meta.Beamline, iscommissioning(creds.BeamtimeId))
 			if err_online == nil && meta.BeamtimeId == meta_onilne.BeamtimeId {
 				meta.OnlinePath = meta_onilne.OnlinePath
 			}
 		}
 	} else {
-		meta, err = findBeamtimeMetaFromBeamline(creds.Beamline,false)
+		meta, err = findBeamtimeMetaFromBeamline(creds.Beamline, false)
 	}
 
 	if creds.Type == "processed" {
 		meta.OnlinePath = ""
 	}
 
-	if (err != nil) {
+	if err != nil {
 		log.Error(err.Error())
-		return beamtimeMeta{}, err
+		return common.BeamtimeMeta{}, err
 	}
 
 	meta.DataSource = creds.DataSource
@@ -260,84 +284,116 @@ 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"
+	if creds.Type == "raw" && meta.OnlinePath == "" {
+		err_string := "beamtime " + meta.BeamtimeId + " is not online"
 		log.Error(err_string)
-		return nil,errors.New(err_string)
+		return nil, errors.New(err_string)
 	}
 
 	if creds.Beamline != "auto" && meta.Beamline != creds.Beamline {
 		err_string := "given beamline (" + creds.Beamline + ") does not match the found one (" + meta.Beamline + ")"
 		log.Debug(err_string)
-		return nil,errors.New(err_string)
+		return nil, errors.New(err_string)
 	}
 
-	if needHostAuthorization(creds) {
+	if canUseHostAuthorization(creds) {
 		if err := authorizeByHost(request.OriginHost, meta.Beamline); err != nil {
-			return nil,err
+			return nil, err
+		}
+		if creds.Type == "raw" {
+			accessTypes = []string{"read", "write", "writeraw"}
+		} else {
+			accessTypes = []string{"read", "write"}
 		}
-		accessTypes = []string{"read","write"}
 	} else {
-		accessTypes,err = authorizeByToken(creds)
+		accessTypes, err = authorizeByToken(creds)
 	}
 
-	return accessTypes,err
+	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
-	log.Debug("authorized creds bl/bt: ", creds.Beamline+"/"+creds.BeamtimeId+", beamtime " + meta.BeamtimeId + " for " + request.OriginHost + " in " +
-		meta.Beamline+", type "+meta.Type, "online path "+meta.OnlinePath + ", offline path "+meta.OfflinePath)
+	log.Debug("authorized creds bl/bt: ", creds.Beamline+"/"+creds.BeamtimeId+", beamtime "+meta.BeamtimeId+" for "+request.OriginHost+" in "+
+		meta.Beamline+", type "+meta.Type, "online path "+meta.OnlinePath+", offline path "+meta.OfflinePath)
 	return meta, nil
 }
 
+func writeServerError(w http.ResponseWriter, err error) {
+	serr, ok := err.(*common.ServerError)
+	if ok {
+		utils.WriteServerError(w, err, serr.Code)
+		return
+	}
+	utils.WriteServerError(w, err, http.StatusUnauthorized)
+	return
+}
+
 func routeAuthorize(w http.ResponseWriter, r *http.Request) {
 	var request authorizationRequest
-	err := utils.ExtractRequest(r,&request)
+	err := utils.ExtractRequest(r, &request)
 	if err != nil {
-		utils.WriteServerError(w,err,http.StatusBadRequest)
+		utils.WriteServerError(w, err, http.StatusBadRequest)
 		return
 	}
 
 	creds, err := getSourceCredentials(request)
 	if err != nil {
-		utils.WriteServerError(w,err,http.StatusBadRequest)
+		utils.WriteServerError(w, err, http.StatusBadRequest)
 		return
 	}
 
 	beamtimeInfo, err := authorize(request, creds)
-	if (err != nil) {
-		serr,ok:=err.(*common.ServerError)
-		if ok {
-			utils.WriteServerError(w,err,serr.Code)
-			return
-		}
-		utils.WriteServerError(w,err,http.StatusUnauthorized)
+	if err != nil {
+		writeServerError(w, err)
 		return
 	}
 
 	res, err := utils.MapToJson(&beamtimeInfo)
 	if err != nil {
-		utils.WriteServerError(w,err,http.StatusInternalServerError)
+		utils.WriteServerError(w, err, http.StatusInternalServerError)
 		return
 	}
 
 	w.WriteHeader(http.StatusOK)
 	w.Write([]byte(res))
 }
+
+func checkRole(w http.ResponseWriter, r *http.Request, role string) error {
+	var extraClaim structs.AccessTokenExtraClaim
+	var claims *utils.CustomClaims
+	if err := utils.JobClaimFromContext(r, &claims, &extraClaim); err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte(err.Error()))
+		return err
+	}
+
+	if err := checkTokenRevoked(claims.Id); err != nil {
+		writeServerError(w, err)
+		return err
+	}
+
+	if claims.Subject != "admin" || !utils.StringInSlice(role, extraClaim.AccessTypes) {
+		err_txt := "wrong token claims"
+		w.WriteHeader(http.StatusUnauthorized)
+		w.Write([]byte(err_txt))
+		return errors.New(err_txt)
+	}
+	return nil
+}
diff --git a/authorizer/src/asapo_authorizer/server/authorize_test.go b/authorizer/src/asapo_authorizer/server/authorize_test.go
index f4e97e788fda426fc463b3019d69d1b857f75925..1c6e6400554b5ee6fe8fbc6dd324b8ad1ae7d346 100644
--- a/authorizer/src/asapo_authorizer/server/authorize_test.go
+++ b/authorizer/src/asapo_authorizer/server/authorize_test.go
@@ -4,6 +4,7 @@ import (
 	"asapo_authorizer/authorization"
 	"asapo_authorizer/common"
 	"asapo_authorizer/ldap_client"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"github.com/stretchr/testify/assert"
@@ -16,20 +17,22 @@ import (
 	"testing"
 )
 
+var expectedTokenId = "123"
 
-func prepareUserToken(payload string, accessTypes []string) string{
-	auth := authorization.NewAuth(nil,utils.NewJWTAuth("secret_user"),nil)
+func prepareAsapoToken(payload string, accessTypes []string) string {
+	auth := authorization.NewAuth(nil, utils.NewJWTAuth("secret_user"), nil)
 	var claims utils.CustomClaims
 	var extraClaim structs.AccessTokenExtraClaim
 	claims.Subject = payload
+	claims.Id = expectedTokenId
 	extraClaim.AccessTypes = accessTypes
 	claims.ExtraClaims = &extraClaim
 	token, _ := auth.AdminAuth().GenerateToken(&claims)
 	return token
 }
 
-func prepareAdminToken(payload string) string{
-	auth:= authorization.NewAuth(nil,utils.NewJWTAuth("secret_admin"),nil)
+func prepareAdminToken(payload string) string {
+	auth := authorization.NewAuth(nil, utils.NewJWTAuth("secret_admin"), nil)
 
 	var claims utils.CustomClaims
 	var extraClaim structs.AccessTokenExtraClaim
@@ -42,7 +45,6 @@ func prepareAdminToken(payload string) string{
 
 var mockClient = new(ldap_client.MockedLdapClient)
 
-
 type request struct {
 	path    string
 	cmd     string
@@ -50,11 +52,10 @@ type request struct {
 	message string
 }
 
-func allowBeamlines(beamlines []beamtimeMeta) {
-	settings.AlwaysAllowedBeamtimes=beamlines
+func allowBeamlines(beamlines []common.BeamtimeMeta) {
+	common.Settings.AlwaysAllowedBeamtimes = beamlines
 }
 
-
 func containsMatcher(substr string) func(str string) bool {
 	return func(str string) bool { return strings.Contains(str, substr) }
 }
@@ -64,56 +65,56 @@ func makeRequest(request interface{}) string {
 	return string(buf)
 }
 
-func doPostRequest(path string,buf string,authHeader string) *httptest.ResponseRecorder {
+func doPostRequest(path string, buf string, authHeader string) *httptest.ResponseRecorder {
 	mux := utils.NewRouter(listRoutes)
 	req, _ := http.NewRequest("POST", path, strings.NewReader(buf))
-	if authHeader!="" {
-		req.Header.Add("Authorization",authHeader)
+	if authHeader != "" {
+		req.Header.Add("Authorization", authHeader)
 	}
 	w := httptest.NewRecorder()
 	mux.ServeHTTP(w, req)
 	return w
 }
 
-var credTests = [] struct {
+var credTests = []struct {
 	request string
-	cred SourceCredentials
-	ok bool
+	cred    SourceCredentials
+	ok      bool
 	message string
-} {
-	{"processed%asapo_test%auto%%", SourceCredentials{"asapo_test","auto","detector","","processed"},true,"auto beamline, source and no token"},
-	{"processed%asapo_test%auto%%token", SourceCredentials{"asapo_test","auto","detector","token","processed"},true,"auto beamline, source"},
-	{"processed%asapo_test%auto%source%", SourceCredentials{"asapo_test","auto","source","","processed"},true,"auto beamline, no token"},
-	{"processed%asapo_test%auto%source%token", SourceCredentials{"asapo_test","auto","source","token","processed"},true,"auto beamline,source, token"},
-	{"processed%asapo_test%beamline%source%token", SourceCredentials{"asapo_test","beamline","source","token","processed"},true,"all set"},
-	{"processed%auto%beamline%source%token", SourceCredentials{"auto","beamline","source","token","processed"},true,"auto beamtime"},
-	{"raw%auto%auto%source%token", SourceCredentials{},false,"auto beamtime and beamline"},
-	{"raw%%beamline%source%token", SourceCredentials{"auto","beamline","source","token","raw"},true,"empty beamtime"},
-	{"raw%asapo_test%%source%token", SourceCredentials{"asapo_test","auto","source","token","raw"},true,"empty bealine"},
-	{"raw%%%source%token", SourceCredentials{},false,"both empty"},
-	{"processed%asapo_test%beamline%source%blabla%token", SourceCredentials{"asapo_test","beamline","source%blabla","token","processed"},true,"% in source"},
-	{"processed%asapo_test%beamline%source%blabla%", SourceCredentials{"asapo_test","beamline","source%blabla","","processed"},true,"% in source, no token"},
+}{
+	{"processed%asapo_test%auto%%", SourceCredentials{"asapo_test", "auto", "detector", "", "processed"}, true, "auto beamline, source and no token"},
+	{"processed%asapo_test%auto%%token", SourceCredentials{"asapo_test", "auto", "detector", "token", "processed"}, true, "auto beamline, source"},
+	{"processed%asapo_test%auto%source%", SourceCredentials{"asapo_test", "auto", "source", "", "processed"}, true, "auto beamline, no token"},
+	{"processed%asapo_test%auto%source%token", SourceCredentials{"asapo_test", "auto", "source", "token", "processed"}, true, "auto beamline,source, token"},
+	{"processed%asapo_test%beamline%source%token", SourceCredentials{"asapo_test", "beamline", "source", "token", "processed"}, true, "all set"},
+	{"processed%auto%beamline%source%token", SourceCredentials{"auto", "beamline", "source", "token", "processed"}, true, "auto beamtime"},
+	{"raw%auto%auto%source%token", SourceCredentials{}, false, "auto beamtime and beamline"},
+	{"raw%%beamline%source%token", SourceCredentials{"auto", "beamline", "source", "token", "raw"}, true, "empty beamtime"},
+	{"raw%asapo_test%%source%token", SourceCredentials{"asapo_test", "auto", "source", "token", "raw"}, true, "empty bealine"},
+	{"raw%%%source%token", SourceCredentials{}, false, "both empty"},
+	{"processed%asapo_test%beamline%source%blabla%token", SourceCredentials{"asapo_test", "beamline", "source%blabla", "token", "processed"}, true, "% in source"},
+	{"processed%asapo_test%beamline%source%blabla%", SourceCredentials{"asapo_test", "beamline", "source%blabla", "", "processed"}, true, "% in source, no token"},
 }
 
 func TestSplitCreds(t *testing.T) {
 
 	for _, test := range credTests {
-		request :=  authorizationRequest{test.request,"host"}
-		creds,err := getSourceCredentials(request)
+		request := authorizationRequest{test.request, "host"}
+		creds, err := getSourceCredentials(request)
 		if test.ok {
-			assert.Nil(t,err)
-			assert.Equal(t,test.cred,creds,test.message)
+			assert.Nil(t, err)
+			assert.Equal(t, test.cred, creds, test.message)
 		} else {
-			assert.NotNil(t,err,test.message)
+			assert.NotNil(t, err, test.message)
 		}
 
 	}
 }
 
 func TestAuthorizeDefaultOK(t *testing.T) {
-	allowBeamlines([]beamtimeMeta{{"asapo_test","beamline","","2019","tf","",nil}})
-	request :=  makeRequest(authorizationRequest{"processed%asapo_test%%%","host"})
-	w := doPostRequest("/authorize",request,"")
+	allowBeamlines([]common.BeamtimeMeta{{"asapo_test", "beamline", "", "2019", "tf", "", nil}})
+	request := makeRequest(authorizationRequest{"processed%asapo_test%%%", "host"})
+	w := doPostRequest("/authorize", request, "")
 
 	body, _ := ioutil.ReadAll(w.Body)
 
@@ -125,7 +126,7 @@ func TestAuthorizeDefaultOK(t *testing.T) {
 	assert.Equal(t, http.StatusOK, w.Code, "")
 }
 
-var beamtime_meta_online =`
+var beamtime_meta_online = `
 {
 "beamline": "bl1",
 "beamtimeId": "test_online",
@@ -133,7 +134,7 @@ var beamtime_meta_online =`
 }
 `
 
-var beamtime_meta =`
+var beamtime_meta = `
 {
 "applicant": {
 "email": "test",
@@ -172,7 +173,7 @@ var beamtime_meta =`
 }
 `
 
-var commissioning_meta =`
+var commissioning_meta = `
 {
     "beamline": "P04",
     "corePath": "/asap3/petra3/gpfs/p04/2021/commissioning/c20210823_000_MAA",
@@ -182,74 +183,82 @@ var commissioning_meta =`
 }
 `
 
-
-var authTests = [] struct {
+var authTests = []struct {
 	source_type string
 	beamtime_id string
-	beamline string
-	dataSource string
-	token string
-	originHost string
-	status int
-	message string
-	answer string
+	beamline    string
+	dataSource  string
+	token       string
+	originHost  string
+	status      int
+	message     string
+	answer      string
+	mode        int
 }{
-	{"processed","test","auto","dataSource", prepareUserToken("bt_test",nil),"127.0.0.2",http.StatusUnauthorized,"missing access types",
-		""},
-	{"processed","test","auto","dataSource", prepareUserToken("bt_test",[]string{}),"127.0.0.2",http.StatusUnauthorized,"empty access types",
-		""},
-	{"processed","test","auto","dataSource", prepareUserToken("bt_test",[]string{"write"}),"127.0.0.2",http.StatusOK,"user source with correct token",
-		`{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["write"]}`},
-	{"processed","test_online","auto","dataSource", prepareUserToken("bt_test_online",[]string{"read"}),"127.0.0.1",http.StatusOK,"with online path, processed type",
-		`{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"","source-type":"processed","access-types":["read"]}`},
-	{"processed","test1","auto","dataSource", prepareUserToken("bt_test1",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"correct token, beamtime not found",
-		""},
-	{"processed","test","auto","dataSource", prepareUserToken("wrong",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"user source with wrong token",
-		""},
-	{"processed","test","bl1","dataSource", prepareUserToken("bt_test",[]string{"read"}),"127.0.0.1",http.StatusOK,"correct beamline given",
-		`{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read"]}`},
-		{"processed","test","bl2","dataSource", prepareUserToken("bt_test",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"incorrect beamline given",
-		""},
-	{"processed","auto","p07", "dataSource", prepareUserToken("bl_p07",[]string{"read"}),"127.0.0.1",http.StatusOK,"beamtime found",
-		`{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"","source-type":"processed","access-types":["read"]}`},
-	{"processed","auto","p07", "dataSource", prepareUserToken("bl_p06",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"wrong token",
-		""},
-	{"processed","auto","p08", "dataSource", prepareUserToken("bl_p08",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"beamtime not found",
-		""},
-	{"raw","test_online","auto","dataSource", "","127.0.0.1",http.StatusOK,"raw type",
-		`{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write"]}`},
-	{"raw","test_online","auto","dataSource", "","127.0.0.1",http.StatusOK,"raw type",
-		`{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write"]}`},
- 	{"raw","auto","p07","dataSource", "","127.0.0.1",http.StatusOK,"raw type, auto beamtime",
-		`{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"./p07/current","source-type":"raw","access-types":["read","write"]}`},
-	{"raw","auto","p07","noldap", "","127.0.0.1",http.StatusNotFound,"no conection to ldap",
-		""},
-	{"raw","test_online","auto","dataSource", "","127.0.0.2",http.StatusUnauthorized,"raw type, wrong origin host",
-		""},
-	{"raw","test","auto","dataSource", prepareUserToken("bt_test",[]string{"read"}),"127.0.0.1",http.StatusUnauthorized,"raw when not online",
-		""},
-	{"processed","test","auto","dataSource", "","127.0.0.1:1001",http.StatusOK,"processed without token",
-		`{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read","write"]}`},
-	{"processed","test","auto","dataSource", "","127.0.0.2",http.StatusUnauthorized,"processed without token, wrong host",
-		""},
-	{"raw","c20210823_000_MAA","auto","dataSource", "","127.0.0.1",http.StatusOK,"raw type commissioning",
-		`{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"./p04/commissioning","source-type":"raw","access-types":["read","write"]}`},
-	{"processed","c20210823_000_MAA","auto","dataSource", "","127.0.0.1",http.StatusOK,"processed type commissioning",
-		`{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"","source-type":"processed","access-types":["read","write"]}`},
+	{"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", nil), "127.0.0.2", http.StatusUnauthorized, "missing access types",
+		"", 0},
+	{"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{}), "127.0.0.2", http.StatusUnauthorized, "empty access types",
+		"", 0},
+	{"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{"write"}), "127.0.0.2", http.StatusOK, "user source with correct token",
+		`{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["write"]}`, 0},
+	{"processed", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{"write"}), "127.0.0.2", http.StatusUnauthorized, "token was revoked",
+		"", 2},
+	{"processed", "test_online", "auto", "dataSource", prepareAsapoToken("bt_test_online", []string{"read"}), "127.0.0.1", http.StatusOK, "with online path, processed type",
+		`{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"","source-type":"processed","access-types":["read"]}`, 0},
+	{"processed", "test1", "auto", "dataSource", prepareAsapoToken("bt_test1", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "correct token, beamtime not found",
+		"", 1},
+	{"processed", "test", "auto", "dataSource", prepareAsapoToken("wrong", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "user source with wrong token",
+		"", 0},
+	{"processed", "test", "bl1", "dataSource", prepareAsapoToken("bt_test", []string{"read"}), "127.0.0.1", http.StatusOK, "correct beamline given",
+		`{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read"]}`, 0},
+	{"processed", "test", "bl2", "dataSource", prepareAsapoToken("bt_test", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "incorrect beamline given",
+		"", 1},
+	{"processed", "auto", "p07", "dataSource", prepareAsapoToken("bl_p07", []string{"read"}), "127.0.0.1", http.StatusOK, "beamtime found",
+		`{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"","source-type":"processed","access-types":["read"]}`, 0},
+	{"processed", "auto", "p07", "dataSource", prepareAsapoToken("bl_p06", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "wrong token",
+		"", 0},
+	{"processed", "auto", "p08", "dataSource", prepareAsapoToken("bl_p08", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "beamtime not found",
+		"", 1},
+	{"raw", "test_online", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type",
+		`{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write","writeraw"]}`, 0},
+	{"raw", "test_online", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type",
+		`{"beamtimeId":"test_online","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test_online","beamline-path":"./bl1/current","source-type":"raw","access-types":["read","write","writeraw"]}`, 0},
+	{"raw", "auto", "p07", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type, auto beamtime",
+		`{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"./p07/current","source-type":"raw","access-types":["read","write","writeraw"]}`, 0},
+	{"raw", "auto", "p07", "noldap", "", "127.0.0.1", http.StatusServiceUnavailable, "no conection to ldap",
+		"", 0},
+
+	{"raw", "auto", "p07", "dataSource", prepareAsapoToken("bl_p07", []string{"read", "writeraw"}), "127.0.0.2", http.StatusOK, "raw type with token",
+		`{"beamtimeId":"11111111","beamline":"p07","dataSource":"dataSource","corePath":"asap3/petra3/gpfs/p07/2020/data/11111111","beamline-path":"./p07/current","source-type":"raw","access-types":["read","writeraw"]}`, 0},
+
+	{"raw", "test_online", "auto", "dataSource", "", "127.0.0.2", http.StatusUnauthorized, "raw type, wrong origin host",
+		"", 0},
+	{"raw", "test", "auto", "dataSource", prepareAsapoToken("bt_test", []string{"read"}), "127.0.0.1", http.StatusUnauthorized, "raw when not online",
+		"", 1},
+	{"processed", "test", "auto", "dataSource", "", "127.0.0.1:1001", http.StatusOK, "processed without token",
+		`{"beamtimeId":"test","beamline":"bl1","dataSource":"dataSource","corePath":"./tf/gpfs/bl1/2019/data/test","beamline-path":"","source-type":"processed","access-types":["read","write"]}`, 0},
+	{"processed", "test", "auto", "dataSource", "", "127.0.0.2", http.StatusUnauthorized, "processed without token, wrong host",
+		"", 0},
+	{"raw", "c20210823_000_MAA", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "raw type commissioning",
+		`{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"./p04/commissioning","source-type":"raw","access-types":["read","write","writeraw"]}`, 0},
+	{"processed", "c20210823_000_MAA", "auto", "dataSource", "", "127.0.0.1", http.StatusOK, "processed type commissioning",
+		`{"beamtimeId":"c20210823_000_MAA","beamline":"p04","dataSource":"dataSource","corePath":"./tf/gpfs/p04/2019/commissioning/c20210823_000_MAA","beamline-path":"","source-type":"processed","access-types":["read","write"]}`, 0},
 }
 
 func TestAuthorize(t *testing.T) {
 	ldapClient = mockClient
-	allowBeamlines([]beamtimeMeta{})
-	Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"),utils.NewJWTAuth("secret_admin"),utils.NewJWTAuth("secret"))
+	allowBeamlines([]common.BeamtimeMeta{})
+	Auth = authorization.NewAuth(utils.NewJWTAuth("secret_user"), utils.NewJWTAuth("secret_admin"), utils.NewJWTAuth("secret"))
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
 	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)
@@ -262,109 +271,118 @@ func TestAuthorize(t *testing.T) {
 	ioutil.WriteFile(filepath.Clean("bl1/current/beamtime-metadata-test_online.json"), []byte(beamtime_meta_online), 0644)
 	ioutil.WriteFile(filepath.Clean("p04/commissioning/commissioning-metadata-c20210823_000_MAA.json"), []byte(commissioning_meta), 0644)
 
-	defer 	os.RemoveAll("p07")
-	defer 	os.RemoveAll("p04")
-	defer 	os.RemoveAll("tf")
-	defer 	os.RemoveAll("bl1")
+	defer os.RemoveAll("p07")
+	defer os.RemoveAll("p04")
+	defer os.RemoveAll("tf")
+	defer os.RemoveAll("bl1")
 
 	for _, test := range authTests {
-		if test.source_type == "raw" || test.token == "" {bl := test.beamline
+		if test.token != "" && test.mode != 1 {
+			if test.mode == 2 {
+				mock_store.On("IsTokenRevoked", expectedTokenId).Return(true, nil)
+			} else {
+				mock_store.On("IsTokenRevoked", expectedTokenId).Return(false, nil)
+			}
+		}
+
+		if test.source_type == "raw" || test.token == "" {
+			bl := test.beamline
 			if test.beamline == "auto" {
 				bl = "bl1"
 			}
-			if iscommissioning(test.beamtime_id)  && test.beamline == "auto" {
+			if iscommissioning(test.beamtime_id) && test.beamline == "auto" {
 				bl = "p04"
 			}
-			expected_filter:="a3"+bl+"-hosts"
+			expected_filter := "a3" + bl + "-hosts"
 			if test.dataSource == "noldap" {
-				err := &common.ServerError{utils.StatusServiceUnavailable,""}
-				mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base,expected_filter).Return([]string{}, err)
+				err := &common.ServerError{utils.StatusServiceUnavailable, ""}
+				mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base, expected_filter).Return([]string{}, err)
 			} else {
-				mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base,expected_filter).Return(allowed_ips, nil)
+				mockClient.On("GetAllowedIpsForBeamline", expected_uri, expected_base, expected_filter).Return(allowed_ips, nil)
 			}
 		}
 
-		request :=  makeRequest(authorizationRequest{test.source_type+"%"+test.beamtime_id+"%"+test.beamline+"%"+test.dataSource+"%"+test.token,test.originHost})
-		w := doPostRequest("/authorize",request,"")
+		request := makeRequest(authorizationRequest{test.source_type + "%" + test.beamtime_id + "%" + test.beamline + "%" + test.dataSource + "%" + test.token, test.originHost})
+		w := doPostRequest("/authorize", request, "")
 
 		body, _ := ioutil.ReadAll(w.Body)
-		if test.status==http.StatusOK {
-			body_str:=string(body)
-			body_str = strings.Replace(body_str,string(os.PathSeparator),"/",-1)
-			body_str = strings.Replace(body_str,"//","/",-1)
-			assert.Equal(t, test.answer,body_str,test.message)
+		if test.status == http.StatusOK {
+			body_str := string(body)
+			body_str = strings.Replace(body_str, string(os.PathSeparator), "/", -1)
+			body_str = strings.Replace(body_str, "//", "/", -1)
+			assert.Equal(t, test.answer, body_str, test.message)
 		}
-		assert.Equal(t, test.status,w.Code, test.message)
+		assert.Equal(t, test.status, w.Code, test.message)
 		mockClient.AssertExpectations(t)
-		mockClient.ExpectedCalls=nil
+		mockClient.ExpectedCalls = nil
+		mock_store.AssertExpectations(t)
+		mock_store.ExpectedCalls = nil
+		mock_store.Calls = nil
 	}
 }
 
 func TestNotAuthorized(t *testing.T) {
-	request :=  makeRequest(authorizationRequest{"raw%any_id%%%","host"})
-	w := doPostRequest("/authorize",request,"")
+	request := makeRequest(authorizationRequest{"raw%any_id%%%", "host"})
+	w := doPostRequest("/authorize", request, "")
 	assert.Equal(t, http.StatusUnauthorized, w.Code, "")
 }
 
-
 func TestAuthorizeWrongRequest(t *testing.T) {
-	w := doPostRequest("/authorize","babla","")
+	w := doPostRequest("/authorize", "babla", "")
 	assert.Equal(t, http.StatusBadRequest, w.Code, "")
 }
 
-
 func TestAuthorizeWrongPath(t *testing.T) {
-	w := doPostRequest("/authorized","","")
+	w := doPostRequest("/authorized", "", "")
 	assert.Equal(t, http.StatusNotFound, w.Code, "")
 }
 
 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)
-	_,err := authorize(request,creds)
-	assert.Error(t,err, "")
+	request := authorizationRequest{"asapo_test%%", "host"}
+	creds, _ := getSourceCredentials(request)
+	_, err := authorize(request, creds)
+	assert.Error(t, err, "")
 }
 
 func TestSplitHost(t *testing.T) {
 	host := splitHost("127.0.0.1:112")
-	assert.Equal(t,"127.0.0.1", host, "")
+	assert.Equal(t, "127.0.0.1", host, "")
 }
 
-
 func TestSplitHostNoPort(t *testing.T) {
 	host := splitHost("127.0.0.1")
-	assert.Equal(t,"127.0.0.1", host, "")
+	assert.Equal(t, "127.0.0.1", host, "")
 }
 
-var extractBtinfoTests = [] struct {
-	root string
-	fname string
+var extractBtinfoTests = []struct {
+	root     string
+	fname    string
 	beamline string
-	id string
-	ok bool
+	id       string
+	ok       bool
 }{
-	{".",filepath.Clean("tf/gpfs/bl1.01/2019/data/123"),"bl1.01","123",true},
-	{filepath.Clean("/blabla/tratartra"),filepath.Clean("tf/gpfs/bl1.01/2019/data/123"), "bl1.01","123",true},
-	{".",filepath.Clean("tf/gpfs/common/2019/data/123"), "bl1.01","123",false},
-	{".",filepath.Clean("tf/gpfs/BeamtimeUsers/2019/data/123"), "bl1.01","123",false},
-	{".",filepath.Clean("tf/gpfs/state/2019/data/123"), "bl1.01","123",false},
-	{".",filepath.Clean("tf/gpfs/support/2019/data/123"), "bl1.01","123",false},
-	{".",filepath.Clean("petra3/gpfs/p01/2019/commissioning/c20180508-000-COM20181"), "p01","c20180508-000-COM20181",true},
-
+	{".", filepath.Clean("tf/gpfs/bl1.01/2019/data/123"), "bl1.01", "123", true},
+	{filepath.Clean("/blabla/tratartra"), filepath.Clean("tf/gpfs/bl1.01/2019/data/123"), "bl1.01", "123", true},
+	{".", filepath.Clean("tf/gpfs/common/2019/data/123"), "bl1.01", "123", false},
+	{".", filepath.Clean("tf/gpfs/BeamtimeUsers/2019/data/123"), "bl1.01", "123", false},
+	{".", filepath.Clean("tf/gpfs/state/2019/data/123"), "bl1.01", "123", false},
+	{".", filepath.Clean("tf/gpfs/support/2019/data/123"), "bl1.01", "123", false},
+	{".", filepath.Clean("petra3/gpfs/p01/2019/commissioning/c20180508-000-COM20181"), "p01", "c20180508-000-COM20181", true},
 }
+
 func TestGetBeamtimeInfo(t *testing.T) {
 	for _, test := range extractBtinfoTests {
-		settings.RootBeamtimesFolder=test.root
-		bt,err:= beamtimeMetaFromMatch(test.root+string(filepath.Separator)+test.fname)
+		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)
-			assert.Equal(t,bt.Beamline,test.beamline)
-			assert.Equal(t,bt.BeamtimeId,test.id)
-			assert.Nil(t,err,"should not be error")
+			assert.Equal(t, bt.OfflinePath, test.root+string(filepath.Separator)+test.fname)
+			assert.Equal(t, bt.Beamline, test.beamline)
+			assert.Equal(t, bt.BeamtimeId, test.id)
+			assert.Nil(t, err, "should not be error")
 		} else {
-			assert.NotNil(t,err,"should be error")
+			assert.NotNil(t, err, "should be error")
 		}
 	}
 
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 d5cf1f6c195904dbac3608cd1064d4ad5fc80a0d..ae39cee67b223f2d4d04da60cd749d240af877f7 100644
--- a/authorizer/src/asapo_authorizer/server/folder_token_test.go
+++ b/authorizer/src/asapo_authorizer/server/folder_token_test.go
@@ -2,16 +2,19 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
+	"asapo_authorizer/common"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"asapo_common/version"
+	"fmt"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	"io/ioutil"
 	"net/http"
-	"testing"
 	"os"
 	"path/filepath"
-	"fmt"
+	"testing"
 )
 
 var  fodlerTokenTests = [] struct {
@@ -23,23 +26,26 @@ var  fodlerTokenTests = [] struct {
 	status        int
 	message       string
 }{
-	{"test", false,"tf/gpfs/bl1/2019/data/test", "",prepareUserToken("bt_test",[]string{"read"}),http.StatusOK,"beamtime found"},
-	{"test_online",false, "bl1/current", "",prepareUserToken("bt_test_online",[]string{"read"}),http.StatusOK,"online beamtime found"},
-	{"test", false,"bl1/current", "",prepareUserToken("bt_test",[]string{"read"}),http.StatusUnauthorized,"no online beamtime found"},
-	{"test_online",false, "bl2/current", "",prepareUserToken("bt_test_online",[]string{"read"}),http.StatusUnauthorized,"wrong online folder"},
-	{"test", false,"tf/gpfs/bl1/2019/data/test1", "",prepareUserToken("bt_test",[]string{"read"}),http.StatusUnauthorized,"wrong folder"},
-	{"test", false,"tf/gpfs/bl1/2019/data/test", "",prepareUserToken("bt_test1",[]string{"read"}),http.StatusUnauthorized,"wrong token"},
-	{"11111111", false,"tf/gpfs/bl1/2019/data/test", "",prepareUserToken("bt_11111111",[]string{"read"}),http.StatusBadRequest,"bad request"},
+	{"test", false,"tf/gpfs/bl1/2019/data/test", "", prepareAsapoToken("bt_test",[]string{"read"}),http.StatusOK,"beamtime found"},
+	{"test_online",false, "bl1/current", "", prepareAsapoToken("bt_test_online",[]string{"read"}),http.StatusOK,"online beamtime found"},
+	{"test", false,"bl1/current", "", prepareAsapoToken("bt_test",[]string{"read"}),http.StatusUnauthorized,"no online beamtime found"},
+	{"test_online",false, "bl2/current", "", prepareAsapoToken("bt_test_online",[]string{"read"}),http.StatusUnauthorized,"wrong online folder"},
+	{"test", false,"tf/gpfs/bl1/2019/data/test1", "", prepareAsapoToken("bt_test",[]string{"read"}),http.StatusUnauthorized,"wrong folder"},
+	{"test", false,"tf/gpfs/bl1/2019/data/test", "", prepareAsapoToken("bt_test1",[]string{"read"}),http.StatusUnauthorized,"wrong token"},
+	{"11111111", false,"tf/gpfs/bl1/2019/data/test", "", prepareAsapoToken("bt_11111111",[]string{"read"}),http.StatusBadRequest,"bad request"},
 
-	{"test", true,"tf/gpfs/bl1/2019/data/test", "",prepareUserToken("bt_test",[]string{"read"}),http.StatusOK,"auto without onilne"},
-	{"test_online",true, "tf/gpfs/bl1/2019/data/test_online", "bl1/current",prepareUserToken("bt_test_online",[]string{"read"}),http.StatusOK,"auto with online"},
+	{"test", true,"tf/gpfs/bl1/2019/data/test", "", prepareAsapoToken("bt_test",[]string{"read"}),http.StatusOK,"auto without onilne"},
+	{"test_online",true, "tf/gpfs/bl1/2019/data/test_online", "bl1/current", prepareAsapoToken("bt_test_online",[]string{"read"}),http.StatusOK,"auto with online"},
 
 }
 
 func TestFolderToken(t *testing.T) {
-	allowBeamlines([]beamtimeMeta{})
-	settings.RootBeamtimesFolder ="."
-	settings.CurrentBeamlinesFolder="."
+	allowBeamlines([]common.BeamtimeMeta{})
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
+
+	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 +58,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 {
@@ -64,6 +70,8 @@ func TestFolderToken(t *testing.T) {
 		request :=  makeRequest(folderTokenRequest{path_in_token,test.beamtime_id,test.token})
 		if test.status == http.StatusBadRequest {
 			request =makeRequest(authorizationRequest{})
+		} else {
+			mock_store.On("IsTokenRevoked", mock.Anything).Return(false, nil)
 		}
 		w := doPostRequest("/"+version.GetAuthorizerApiVersion()+"/folder",request,"")
 		if w.Code == http.StatusOK {
@@ -77,7 +85,9 @@ func TestFolderToken(t *testing.T) {
 			body, _ := ioutil.ReadAll(w.Body)
 			fmt.Println(string(body))
 		}
-
+		mock_store.AssertExpectations(t)
+		mock_store.ExpectedCalls = nil
+		mock_store.Calls = nil
 		assert.Equal(t, test.status, w.Code, test.message)
 	}
 }
diff --git a/authorizer/src/asapo_authorizer/server/introspect.go b/authorizer/src/asapo_authorizer/server/introspect.go
index 1cc6bd37add60a6f8604ef0c47bc91b7eecb5345..5dd591a2c37f274e552cba63aab7e60829b5431b 100644
--- a/authorizer/src/asapo_authorizer/server/introspect.go
+++ b/authorizer/src/asapo_authorizer/server/introspect.go
@@ -19,10 +19,16 @@ func extractToken(r *http.Request) (string, error) {
 
 func verifyUserToken(token string) (response structs.IntrospectTokenResponse, err error) {
 	var extra_claim structs.AccessTokenExtraClaim
-	response.Sub,err = Auth.UserAuth().CheckAndGetContent(token,&extra_claim)
+	claim,err := Auth.UserAuth().CheckAndGetContent(token,&extra_claim)
 	if err!=nil {
 		return
 	}
+	err = checkTokenRevoked(claim.Id)
+	if err != nil {
+		return
+	}
+
+	response.Sub = claim.Subject
 	response.AccessTypes = extra_claim.AccessTypes
 	return
 }
diff --git a/authorizer/src/asapo_authorizer/server/introspect_test.go b/authorizer/src/asapo_authorizer/server/introspect_test.go
index a9827bea06e6eba2c8d842def06037e86a9d896d..40a99b52fe531d720cb902feccc577202b472709 100644
--- a/authorizer/src/asapo_authorizer/server/introspect_test.go
+++ b/authorizer/src/asapo_authorizer/server/introspect_test.go
@@ -2,6 +2,7 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"encoding/json"
@@ -27,11 +28,16 @@ func TestIntrospect(t *testing.T) {
 	authJWT := utils.NewJWTAuth("secret")
 	authAdmin := utils.NewJWTAuth("secret_admin")
 	authUser := utils.NewJWTAuth("secret_user")
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
+
 	Auth = authorization.NewAuth(authUser,authAdmin,authJWT)
 	for _, test := range IntrospectTests {
-		token := prepareUserToken(test.tokenSubject,test.roles)
+		token := prepareAsapoToken(test.tokenSubject,test.roles)
 		if test.status==http.StatusUnauthorized {
 			token = "blabla"
+		} else {
+			mock_store.On("IsTokenRevoked", expectedTokenId).Return(false, nil)
 		}
 		request :=  makeRequest(structs.IntrospectTokenRequest{token})
 		w := doPostRequest("/introspect",request,"")
@@ -46,6 +52,9 @@ func TestIntrospect(t *testing.T) {
 			body, _ := ioutil.ReadAll(w.Body)
 			fmt.Println(string(body))
 		}
+		mock_store.AssertExpectations(t)
+		mock_store.ExpectedCalls = nil
+		mock_store.Calls = nil
 	}
 }
 
diff --git a/authorizer/src/asapo_authorizer/server/issue_token.go b/authorizer/src/asapo_authorizer/server/issue_token.go
index 7a332ad935d23c502514e98b440d9239afb4e560..bf2524abf9317081b2dd22f063cc1cf05d1774a9 100644
--- a/authorizer/src/asapo_authorizer/server/issue_token.go
+++ b/authorizer/src/asapo_authorizer/server/issue_token.go
@@ -2,11 +2,13 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
+	"asapo_authorizer/token_store"
 	log "asapo_common/logger"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"errors"
 	"net/http"
+	"time"
 )
 
 func extractUserTokenrequest(r *http.Request) (request structs.IssueTokenRequest, err error) {
@@ -28,7 +30,7 @@ func extractUserTokenrequest(r *http.Request) (request structs.IssueTokenRequest
 	}
 
 	for _, ar := range request.AccessTypes {
-		if ar != "read" && ar != "write" {
+		if ar != "read" && ar != "write" && !(ar== "writeraw" && request.Subject["beamline"]!="") {
 			return request, errors.New("wrong requested access rights: "+ar)
 		}
 	}
@@ -37,19 +39,12 @@ func extractUserTokenrequest(r *http.Request) (request structs.IssueTokenRequest
 }
 
 func routeAuthorisedTokenIssue(w http.ResponseWriter, r *http.Request) {
-	Auth.AdminAuth().ProcessAuth(checkAccessToken, "admin")(w, r)
+	Auth.AdminAuth().ProcessAuth(checkAccessToken, "")(w, r)
 }
+
 func checkAccessToken(w http.ResponseWriter, r *http.Request) {
-	var extraClaim structs.AccessTokenExtraClaim
-	var claims *utils.CustomClaims
-	if err := utils.JobClaimFromContext(r, &claims, &extraClaim); err != nil {
-		w.WriteHeader(http.StatusInternalServerError)
-		w.Write([]byte(err.Error()))
-	}
-	if claims.Subject != "admin" || !utils.StringInSlice("create",extraClaim.AccessTypes) {
-		err_txt := "wrong token claims"
-		w.WriteHeader(http.StatusUnauthorized)
-		w.Write([]byte(err_txt))
+	if checkRole(w, r, "create") != nil {
+		return
 	}
 
 	issueUserToken(w, r)
@@ -62,12 +57,22 @@ func issueUserToken(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	token, err := Auth.PrepareAccessToken(request, true)
+	token, claims, err := Auth.PrepareAccessToken(request, true)
 	if err != nil {
 		utils.WriteServerError(w, err, http.StatusInternalServerError)
 		return
 	}
 
+	claims.StandardClaims.Issuer = "asapo-auth"
+	claims.StandardClaims.IssuedAt = time.Now().Unix()
+	record := token_store.TokenRecord{claims.Id, claims, token, false}
+	err = store.AddToken(record)
+	if err != nil {
+		utils.WriteServerError(w, err, http.StatusInternalServerError)
+		return
+	}
+
+
 	log.Debug("generated user token ")
 
 	answer := authorization.UserTokenResponce(request, token)
diff --git a/authorizer/src/asapo_authorizer/server/issue_token_test.go b/authorizer/src/asapo_authorizer/server/issue_token_test.go
index 5ed8c9b6a865449ecbda9f61f1c31183f3eb8d93..a36552063691c66572e98f276d1fd6550e2d7e00 100644
--- a/authorizer/src/asapo_authorizer/server/issue_token_test.go
+++ b/authorizer/src/asapo_authorizer/server/issue_token_test.go
@@ -2,11 +2,13 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
+	"asapo_authorizer/token_store"
 	"asapo_common/structs"
 	"asapo_common/utils"
 	"encoding/json"
 	"fmt"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	"io/ioutil"
 	"net/http"
 	"testing"
@@ -24,7 +26,9 @@ var  IssueTokenTests = [] struct {
 	message        string
 }{
 	{map[string]string{"beamtimeId":"test"},"bt_test",[]string{"read"},180,prepareAdminToken("admin"),"aaa",http.StatusOK,"read for beamtime"},
-	{map[string]string{"beamtimeId":"test"},"bt_test",[]string{"read"},90,prepareAdminToken("admin"),"aaa",http.StatusOK,"write for beamtime"},
+	{map[string]string{"beamtimeId":"test"},"bt_test",[]string{"write"},90,prepareAdminToken("admin"),"aaa",http.StatusOK,"write for beamtime"},
+	{map[string]string{"beamtimeId":"test"},"bt_test",[]string{"writeraw"},90,prepareAdminToken("admin"),"",http.StatusBadRequest,"wrong role"},
+	{map[string]string{"beamline":"test"},"bl_test",[]string{"writeraw"},90,prepareAdminToken("admin"),"aaa",http.StatusOK,"raw for beamline"},
 	{map[string]string{"beamline":"test"},"bl_test",[]string{"read"},180,prepareAdminToken("admin"),"aaa",http.StatusOK,"read for beamline"},
 	{map[string]string{"blabla":"test"},"",[]string{"read"},180,prepareAdminToken("admin"),"",http.StatusBadRequest,"beamline or beamtime not given"},
 	{map[string]string{"beamtimeId":"test"},"",[]string{"bla"},180,prepareAdminToken("admin"),"",http.StatusBadRequest,"wrong role"},
@@ -38,8 +42,15 @@ func TestIssueToken(t *testing.T) {
 	authAdmin := utils.NewJWTAuth("secret_admin")
 	authUser := utils.NewJWTAuth("secret_user")
 	Auth = authorization.NewAuth(authUser,authAdmin,authJWT)
+	mock_store := new(token_store.MockedStore)
+	store = mock_store
+
 	for _, test := range IssueTokenTests {
 		request :=  makeRequest(structs.IssueTokenRequest{test.requestSubject,test.validDays,test.roles})
+		mock_store.On("IsTokenRevoked", mock.Anything).Return(false,nil)
+		if test.status == http.StatusOK {
+			mock_store.On("AddToken", mock.Anything).Return(nil)
+		}
 		w := doPostRequest("/admin/issue",request,authAdmin.Name()+" "+test.adminToken)
 		if w.Code == http.StatusOK {
 			body, _ := ioutil.ReadAll(w.Body)
@@ -58,6 +69,9 @@ func TestIssueToken(t *testing.T) {
 			body, _ := ioutil.ReadAll(w.Body)
 			fmt.Println(string(body))
 		}
+		mock_store.AssertExpectations(t)
+		mock_store.ExpectedCalls = nil
+		mock_store.Calls = nil
 
 		assert.Equal(t, test.status, w.Code, test.message)
 	}
diff --git a/authorizer/src/asapo_authorizer/server/listroutes.go b/authorizer/src/asapo_authorizer/server/listroutes.go
index 09b695091ffc4ecf686715a5b7a4f10434280cbb..d1f027f0aa048b2a0a3e41c9340694e99a11effb 100644
--- a/authorizer/src/asapo_authorizer/server/listroutes.go
+++ b/authorizer/src/asapo_authorizer/server/listroutes.go
@@ -17,6 +17,12 @@ var listRoutes = utils.Routes{
 		"/introspect",
 		routeIntrospect,
 	},
+	utils.Route{
+		"Authorize",
+		"POST",
+		"/admin/revoke",
+		routeRevoke,
+	},
 	utils.Route{
 		"HealthCheck",
 		"Get",
diff --git a/authorizer/src/asapo_authorizer/server/revoke_token.go b/authorizer/src/asapo_authorizer/server/revoke_token.go
new file mode 100644
index 0000000000000000000000000000000000000000..c1c98c6f2adf4c3a3fe05fd94b5773352861b75c
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/server/revoke_token.go
@@ -0,0 +1,39 @@
+package server
+
+import (
+	log "asapo_common/logger"
+	"asapo_common/utils"
+	"encoding/json"
+	"net/http"
+)
+
+func routeRevoke(w http.ResponseWriter, r *http.Request) {
+	Auth.AdminAuth().ProcessAuth(processRevoke, "")(w, r)
+}
+
+func processRevoke(w http.ResponseWriter, r *http.Request) {
+	if checkRole(w, r, "revoke") != nil {
+		return
+	}
+	revokeToken(w, r)
+}
+
+func revokeToken(w http.ResponseWriter, r *http.Request) {
+	token, err := extractToken(r)
+	if err != nil {
+		utils.WriteServerError(w, err, http.StatusBadRequest)
+		return
+	}
+
+	rec, err := store.RevokeToken(token, "")
+	if err != nil {
+		log.Error("could not revoke token "+ token+": "+ err.Error())
+		utils.WriteServerError(w, err, http.StatusBadRequest)
+		return
+	}
+
+	log.Debug("revoked token " + rec.Token)
+	answer, _ := json.Marshal(&rec)
+	w.WriteHeader(http.StatusOK)
+	w.Write(answer)
+}
diff --git a/authorizer/src/asapo_authorizer/server/server.go b/authorizer/src/asapo_authorizer/server/server.go
index 59d43987d7aafface0687500409b7efdcf0ee2be..8a854967d01912370a7e133612bf2ab6c5a76cce 100644
--- a/authorizer/src/asapo_authorizer/server/server.go
+++ b/authorizer/src/asapo_authorizer/server/server.go
@@ -2,43 +2,16 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
+	"asapo_authorizer/common"
 	"asapo_authorizer/ldap_client"
+	"asapo_authorizer/token_store"
+	"sync"
 )
 
-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
-	}
-}
-
-var settings serverSettings
 var ldapClient ldap_client.LdapClient
 var Auth *authorization.Auth
-
+var store token_store.Store
+var cachedMetas = struct {
+	cache map[string]common.BeamtimeMeta
+	lock  sync.Mutex
+}{cache: make(map[string]common.BeamtimeMeta, 0)}
diff --git a/authorizer/src/asapo_authorizer/server/server_nottested.go b/authorizer/src/asapo_authorizer/server/server_nottested.go
index 2f428370f6955303d582f7bd22a551cb4dace88c..6841cf5f377ddbbbcbd2c91aa3c4710ce6540bbf 100644
--- a/authorizer/src/asapo_authorizer/server/server_nottested.go
+++ b/authorizer/src/asapo_authorizer/server/server_nottested.go
@@ -4,29 +4,42 @@ package server
 
 import (
 	"asapo_authorizer/authorization"
+	"asapo_authorizer/common"
 	"asapo_authorizer/ldap_client"
+	"asapo_authorizer/token_store"
 	log "asapo_common/logger"
 	"asapo_common/utils"
 	"asapo_common/version"
 	"errors"
 	"net/http"
+	_ "net/http/pprof"
 	"strconv"
 )
 
 func Start() {
 	mux := utils.NewRouter(listRoutes)
 	ldapClient = new(ldap_client.OpenLdapClient)
+
 	log.Info("Starting ASAPO Authorizer, version " + version.GetVersion())
-	log.Info("Listening on port: " + strconv.Itoa(settings.Port))
-	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP)))
+
+	store = new(token_store.TokenStore)
+	err := 	store.Init(nil)
+	if err != nil {
+		log.Error(err.Error())
+	}
+	defer store.Close()
+
+	log.Info("Listening on port: " + strconv.Itoa(common.Settings.Port))
+	mux.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
+	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
 	}
@@ -34,29 +47,37 @@ 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 common.Settings.DatabaseServer == "" {
+		return log.FatalLevel, errors.New("Database server not set")
+	}
+
+	if common.Settings.DatabaseServer == "auto" && common.Settings.DiscoveryServer=="" {
+		return log.FatalLevel, errors.New("Discovery server not set")
+	}
+
 	var err error
 	Auth, err = createAuth()
 	if err != nil {
 		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
new file mode 100644
index 0000000000000000000000000000000000000000..f18a0f738e4b4ed9397f8449a679f8650b7f6928
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/server/server_test.go
@@ -0,0 +1,143 @@
+package server
+
+/*
+
+import (
+	"asapo_authorizer/database"
+	"asapo_common/discovery"
+	"asapo_common/logger"
+	"errors"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func setup() *database.MockedDatabase {
+	mock_db := new(database.MockedDatabase)
+	mock_db.On("Connect", mock.AnythingOfType("string")).Return(nil)
+
+	return mock_db
+}
+
+func setup_and_init(t *testing.T) *database.MockedDatabase {
+	mock_db := new(database.MockedDatabase)
+	mock_db.On("Connect", mock.AnythingOfType("string")).Return(nil)
+
+	InitDB(mock_db)
+	assertExpectations(t, mock_db)
+	return mock_db
+}
+
+func assertExpectations(t *testing.T, mock_db *database.MockedDatabase) {
+	mock_db.AssertExpectations(t)
+	mock_db.ExpectedCalls = nil
+	logger.MockLog.AssertExpectations(t)
+	logger.MockLog.ExpectedCalls = nil
+}
+
+var initDBTests = []struct {
+	address string
+	answer  error
+	message string
+}{
+	{"bad address", errors.New(""), "error on get bad address"},
+	{"good address", nil, "no error on good address"},
+}
+
+func TestInitDBWithWrongAddress(t *testing.T) {
+	mock_db := setup()
+
+	mock_db.ExpectedCalls = nil
+
+	settings.DatabaseServer = "0.0.0.0:0000"
+
+	for _, test := range initDBTests {
+		mock_db.On("Connect", "0.0.0.0:0000").Return(test.answer)
+
+		err := InitDB(mock_db)
+
+		assert.Equal(t, test.answer, err, test.message)
+		assertExpectations(t, mock_db)
+	}
+	db = nil
+}
+
+func TestInitDBWithAutoAddress(t *testing.T) {
+	mongo_address := "0.0.0.0:0000"
+	mock_db := setup()
+
+	mock_db.ExpectedCalls = nil
+
+	settings.DatabaseServer = "auto"
+	mock_server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		assert.Equal(t, req.URL.String(), "/asapo-mongodb", "request string")
+		rw.Write([]byte(mongo_address))
+	}))
+	defer mock_server.Close()
+
+	discoveryService = discovery.CreateDiscoveryService(mock_server.Client(), mock_server.URL)
+
+	mock_db.On("Connect", "0.0.0.0:0000").Return(nil)
+
+	err := InitDB(mock_db)
+
+	assert.Equal(t, nil, err, "auto connect ok")
+	assertExpectations(t, mock_db)
+	db = nil
+}
+
+func TestReconnectDB(t *testing.T) {
+	mongo_address := "0.0.0.0:0000"
+	mock_server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		assert.Equal(t, req.URL.String(), "/asapo-mongodb", "request string")
+		rw.Write([]byte(mongo_address))
+	}))
+	discoveryService = discovery.CreateDiscoveryService(mock_server.Client(), mock_server.URL)
+
+	defer mock_server.Close()
+
+	settings.DatabaseServer = "auto"
+	mock_db := setup_and_init(t)
+	mock_db.ExpectedCalls = nil
+
+	mongo_address = "1.0.0.0:0000"
+
+	mock_db.On("Close").Return()
+
+	mock_db.On("Connect", "1.0.0.0:0000").Return(nil)
+
+	err := ReconnectDb()
+	assert.Equal(t, nil, err, "auto connect ok")
+	assertExpectations(t, mock_db)
+
+	db = nil
+}
+
+func TestErrorWhenReconnectNotConnectedDB(t *testing.T) {
+	err := ReconnectDb()
+	assert.NotNil(t, err, "error reconnect")
+	db = nil
+}
+
+
+func TestCleanupDBWithoutInit(t *testing.T) {
+	mock_db := setup()
+
+	mock_db.AssertNotCalled(t, "Close")
+
+	CleanupDB()
+}
+
+func TestCleanupDBInit(t *testing.T) {
+	settings.DatabaseServer = "0.0.0.0"
+	mock_db := setup_and_init(t)
+
+	mock_db.On("Close").Return()
+
+	CleanupDB()
+
+	assertExpectations(t, mock_db)
+}
+*/
\ No newline at end of file
diff --git a/authorizer/src/asapo_authorizer/token_store/database.go b/authorizer/src/asapo_authorizer/token_store/database.go
new file mode 100644
index 0000000000000000000000000000000000000000..2afabbe9ffe0fc058e3da879bb751d466d268042
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/database.go
@@ -0,0 +1,50 @@
+package token_store
+
+import "asapo_common/utils"
+
+
+const KAdminDb = "asapo_admin"
+const KTokens = "tokens"
+const KRevokedTokens = "revoked_tokens"
+
+type Request struct {
+	DbName         string
+	Collection     string
+	Op             string
+}
+
+type TokenRecord struct {
+	Id string `bson:"_id"`
+	*utils.CustomClaims
+	Token string
+	Revoked bool
+}
+
+type IdRecord struct {
+	Id string `bson:"_id"`
+}
+
+type Agent interface {
+	ProcessRequest(request Request, extraParams ...interface{}) ([]byte, error)
+	Ping() error
+	Connect(string) error
+	Close()
+}
+
+type DBError struct {
+	Code    int
+	Message string
+}
+
+func (err *DBError) Error() string {
+	return err.Message
+}
+
+func GetStatusCodeFromError(err error) int {
+	err_db, ok := err.(*DBError)
+	if ok {
+		return err_db.Code
+	} else {
+		return utils.StatusServiceUnavailable
+	}
+}
diff --git a/authorizer/src/asapo_authorizer/token_store/mock_database.go b/authorizer/src/asapo_authorizer/token_store/mock_database.go
new file mode 100644
index 0000000000000000000000000000000000000000..7878da8d43baecbb694a9d880a5c7db972444a2b
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/mock_database.go
@@ -0,0 +1,50 @@
+// +build !release
+
+package token_store
+
+import (
+	"github.com/stretchr/testify/mock"
+)
+
+type MockedDatabase struct {
+	mock.Mock
+}
+
+func (db *MockedDatabase) Connect(address string) error {
+	args := db.Called(address)
+	return args.Error(0)
+}
+
+func (db *MockedDatabase) Close() {
+	db.Called()
+}
+
+func (db *MockedDatabase) Ping() error {
+	args := db.Called()
+	return args.Error(0)
+}
+
+func (db *MockedDatabase) ProcessRequest(request Request, extraParams ...interface{}) (answer []byte, err error) {
+	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..7d0a35923d098701ad1fb329de1c2d85db944b70
--- /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)  IsTokenRevoked(tokenId string) (bool, error) {
+	args := store.Called(tokenId)
+	return args.Get(0).(bool), args.Error(1)
+}
+
+func (store *MockedStore) Close() {
+	store.Called()
+	return
+}
+
+
diff --git a/authorizer/src/asapo_authorizer/token_store/mongodb.go b/authorizer/src/asapo_authorizer/token_store/mongodb.go
new file mode 100644
index 0000000000000000000000000000000000000000..fbd1359125d9ad023f7074a909280b3e5a1b946b
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/mongodb.go
@@ -0,0 +1,210 @@
+//+build !test
+
+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"
+	"strings"
+	"sync"
+	"time"
+)
+
+const no_session_msg = "database client not created"
+const already_connected_msg = "already connected"
+
+var dbSessionLock sync.Mutex
+var dbClientLock sync.RWMutex
+
+type Mongodb struct {
+	client                *mongo.Client
+	timeout               time.Duration
+	lastReadFromInprocess map[string]int64
+}
+
+func (db *Mongodb) Ping() (err error) {
+	if db.client == nil {
+		return &DBError{utils.StatusServiceUnavailable, no_session_msg}
+	}
+	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+	defer cancel()
+	return db.client.Ping(ctx, nil)
+}
+
+func (db *Mongodb) Connect(address string) (err error) {
+	dbClientLock.Lock()
+	defer dbClientLock.Unlock()
+
+	if db.client != nil {
+		return &DBError{utils.StatusServiceUnavailable, already_connected_msg}
+	}
+	db.client, err = mongo.NewClient(options.Client().SetConnectTimeout(20 * time.Second).ApplyURI("mongodb://" + address))
+	if err != nil {
+		return err
+	}
+	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+	defer cancel()
+	err = db.client.Connect(ctx)
+	if err != nil {
+		db.client = nil
+		return err
+	}
+
+	if db.lastReadFromInprocess == nil {
+		db.lastReadFromInprocess = make(map[string]int64, 100)
+	}
+
+	return db.Ping()
+}
+
+func (db *Mongodb) Close() {
+	dbClientLock.Lock()
+	defer dbClientLock.Unlock()
+	if db.client != nil {
+		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+		defer cancel()
+		db.client.Disconnect(ctx)
+		db.client = nil
+	}
+}
+
+func (db *Mongodb) insertRecord(dbname string, collection_name string, s interface{}) error {
+	if db.client == nil {
+		return &DBError{utils.StatusServiceUnavailable, no_session_msg}
+	}
+
+	c := db.client.Database(dbname).Collection(collection_name)
+
+	_, err := c.InsertOne(context.TODO(), s)
+	return err
+}
+
+func duplicateError(err error) bool {
+	command_error, ok := err.(mongo.CommandError)
+	if !ok {
+		write_exception_error, ok1 := err.(mongo.WriteException)
+		if !ok1 {
+			return false
+		}
+		return strings.Contains(write_exception_error.Error(), "duplicate key")
+	}
+	return command_error.Name == "DuplicateKey"
+}
+
+func (db *Mongodb) checkDatabaseOperationPrerequisites(request Request) error {
+	if db.client == nil {
+		return &DBError{utils.StatusServiceUnavailable, no_session_msg}
+	}
+
+	if len(request.DbName) == 0 || len(request.Collection) == 0 {
+		return &DBError{utils.StatusWrongInput, "database ans collection must be set"}
+	}
+
+	return nil
+}
+
+func (db *Mongodb) createRecord(request Request, extra_params ...interface{}) ([]byte, error) {
+	if len(extra_params) != 1 {
+		return nil, errors.New("wrong number of parameters")
+	}
+	record := extra_params[0]
+	c := db.client.Database(request.DbName).Collection(request.Collection)
+	res, err := c.InsertOne(context.TODO(), record, options.InsertOne())
+	if err != nil {
+		return nil, err
+	}
+	newId, ok := res.InsertedID.(primitive.ObjectID)
+	if ok {
+		return []byte(newId.Hex()), nil
+	}
+	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) {
+	dbClientLock.RLock()
+	defer dbClientLock.RUnlock()
+
+	if err := db.checkDatabaseOperationPrerequisites(request); err != nil {
+		return nil, err
+	}
+
+	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..756d7cc2d970292697c0b2fd4ecd0fcebcd909a9
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/token_store.go
@@ -0,0 +1,246 @@
+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)
+	IsTokenRevoked(tokenId string) (bool, 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)
+	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)
+	}
+
+	store.revokedTokenList.lock.Lock()
+	defer store.revokedTokenList.lock.Unlock()
+	store.revokedTokenList.tokens = append(store.revokedTokenList.tokens, tokenRecord.Id)
+	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.revokedTokenList.tokens = nil
+			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.lastError = nil
+			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) IsTokenRevoked(tokenId string) (bool, error) {
+	tokens, err := store.getRevokedTokenIds()
+	if err != nil {
+		return true, err
+	}
+	return utils.StringInSlice(tokenId, tokens), nil
+}
+
+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..d648f0ef3afbcf9d259237f425a3ca7ccc85e449
--- /dev/null
+++ b/authorizer/src/asapo_authorizer/token_store/token_store_test.go
@@ -0,0 +1,147 @@
+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) TestProcessRequestCheckRevokedToken() {
+	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)
+	res,err := suite.store.IsTokenRevoked("123")
+	suite.Equal(err, nil, "ok")
+	suite.Equal(false, res, "ok")
+}
\ No newline at end of file
diff --git a/broker/src/asapo_broker/database/mongodb.go b/broker/src/asapo_broker/database/mongodb.go
index d248f016fd2cd719da9dc8c0b86f4c1803142e8d..37eb03008fd1ab8df9b4f8c187e5542459ce5c11 100644
--- a/broker/src/asapo_broker/database/mongodb.go
+++ b/broker/src/asapo_broker/database/mongodb.go
@@ -75,6 +75,7 @@ const stream_filter_finished = "finished"
 const stream_filter_unfinished = "unfinished"
 
 var dbSessionLock sync.Mutex
+var dbClientLock sync.RWMutex
 
 type SizeRecord struct {
 	Size int `bson:"size" json:"size"`
@@ -101,8 +102,8 @@ func (db *Mongodb) Ping() (err error) {
 }
 
 func (db *Mongodb) Connect(address string) (err error) {
-	dbSessionLock.Lock()
-	defer dbSessionLock.Unlock()
+	dbClientLock.Lock()
+	defer dbClientLock.Unlock()
 
 	if db.client != nil {
 		return &DBError{utils.StatusServiceUnavailable, already_connected_msg}
@@ -127,8 +128,8 @@ func (db *Mongodb) Connect(address string) (err error) {
 }
 
 func (db *Mongodb) Close() {
-	dbSessionLock.Lock()
-	defer dbSessionLock.Unlock()
+	dbClientLock.Lock()
+	defer dbClientLock.Unlock()
 	if db.client != nil {
 		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
 		defer cancel()
@@ -672,7 +673,7 @@ func (db *Mongodb) resetCounter(request Request) ([]byte, error) {
 	c := db.client.Database(request.DbName).Collection(inprocess_collection_name_prefix + request.Stream + "_" + request.GroupId)
 	_, err_del := c.DeleteMany(context.Background(), bson.M{"_id": bson.M{"$gte": id}})
 	if err_del != nil {
-		return nil, &DBError{utils.StatusWrongInput, err.Error()}
+		return nil, &DBError{utils.StatusWrongInput, err_del.Error()}
 	}
 
 	return []byte(""), nil
@@ -1021,6 +1022,9 @@ func (db *Mongodb) getStreams(request Request) ([]byte, error) {
 }
 
 func (db *Mongodb) ProcessRequest(request Request) (answer []byte, err error) {
+	dbClientLock.RLock()
+	defer dbClientLock.RUnlock()
+
 	if err := db.checkDatabaseOperationPrerequisites(request); err != nil {
 		return nil, err
 	}
diff --git a/broker/src/asapo_broker/database/mongodb_streams.go b/broker/src/asapo_broker/database/mongodb_streams.go
index 243df816d1e182c2dd767e5dc0db106a57ee40d8..a182f5080409c00116af1958d2b65dcc49983e75 100644
--- a/broker/src/asapo_broker/database/mongodb_streams.go
+++ b/broker/src/asapo_broker/database/mongodb_streams.go
@@ -190,16 +190,8 @@ func (ss *Streams) updateFromDb(db *Mongodb, db_name string) (StreamsRecord, err
 }
 
 func getFiltersFromString(filterString string) (string, string, error) {
-	firstStream := ""
-	streamStatus := ""
-	s := strings.Split(filterString, "_")
-	switch len(s) {
-	case 1:
-		firstStream = s[0]
-	case 2:
-		firstStream = s[0]
-		streamStatus = s[1]
-	default:
+	firstStream, streamStatus, err := utils.DecodeTwoStrings(filterString)
+	if err!=nil {
 		return "", "", errors.New("wrong format: " + filterString)
 	}
 	if streamStatus == "" {
diff --git a/broker/src/asapo_broker/database/mongodb_test.go b/broker/src/asapo_broker/database/mongodb_test.go
index e4c8f458d43b84ae615dfd8e0b0ef4a1435c53f2..c81813611b03dbed3b7124ca1fa8227ce88ca897 100644
--- a/broker/src/asapo_broker/database/mongodb_test.go
+++ b/broker/src/asapo_broker/database/mongodb_test.go
@@ -930,7 +930,7 @@ func TestMongoDBListStreams(t *testing.T) {
 		}
 		var rec_streams_expect, _ = json.Marshal(test.expectedStreams)
 
-		res, err := db.ProcessRequest(Request{DbName: dbname, Stream: "0", Op: "streams", ExtraParam: test.from})
+		res, err := db.ProcessRequest(Request{DbName: dbname, Stream: "0", Op: "streams", ExtraParam: utils.EncodeTwoStrings(test.from,"")})
 		if test.ok {
 			assert.Nil(t, err, test.test)
 			assert.Equal(t, string(rec_streams_expect), string(res), test.test)
diff --git a/broker/src/asapo_broker/database/streams_test.go b/broker/src/asapo_broker/database/streams_test.go
index fef6ff3afb5d5f5f74bcf85975c5e22748d2388e..4ba11e0b3986ff93ea26289054a11f573d670e5c 100644
--- a/broker/src/asapo_broker/database/streams_test.go
+++ b/broker/src/asapo_broker/database/streams_test.go
@@ -3,6 +3,7 @@
 package database
 
 import (
+	"asapo_common/utils"
 	"fmt"
 	"github.com/stretchr/testify/suite"
 	"testing"
@@ -105,8 +106,8 @@ func (suite *StreamsTestSuite) TestStreamsMultipleRequests() {
 	db.insertRecord(dbname, collection, &rec_dataset1_incomplete)
 	db.insertRecord(dbname, collection, &rec_finished)
 	db.insertRecord(dbname, collection2, &rec_dataset1_incomplete)
-	rec, err := streams.getStreams(&db, Request{DbName: dbname, ExtraParam: "_unfinished"})
-	rec2, err2 := streams.getStreams(&db, Request{DbName: dbname, ExtraParam: "_finished"})
+	rec, err := streams.getStreams(&db, Request{DbName: dbname, ExtraParam: "0/unfinished"})
+	rec2, err2 := streams.getStreams(&db, Request{DbName: dbname, ExtraParam: "0/finished"})
 	suite.Nil(err)
 	suite.Equal(collection2, rec.Streams[0].Name)
 	suite.Equal(1, len(rec.Streams))
@@ -143,17 +144,17 @@ var streamFilterTests=[]struct{
 	message string
 }{
 	{request: Request{DbName:dbname, ExtraParam:""},error: false,streams: []string{collection,collection2},message: "default all streams"},
-	{request: Request{DbName:dbname, ExtraParam:"_"},error: false,streams: []string{collection,collection2},message: "default _ all streams"},
-	{request: Request{DbName:dbname, ExtraParam:collection},error: false,streams: []string{collection,collection2},message: "first parameter only -  all streams"},
-	{request: Request{DbName:dbname, ExtraParam:"_all"},error: false,streams: []string{collection,collection2},message: "second parameter only -  all streams"},
-	{request: Request{DbName:dbname, ExtraParam:"_finished"},error: false,streams: []string{collection2},message: "second parameter only -  finished streams"},
-	{request: Request{DbName:dbname, ExtraParam:"_unfinished"},error: false,streams: []string{collection},message: "second parameter only -  unfinished streams"},
-	{request: Request{DbName:dbname, ExtraParam:collection2+"_all"},error: false,streams: []string{collection2},message: "from stream2"},
-	{request: Request{DbName:dbname, ExtraParam:collection2+"_unfinished"},error: false,streams: []string{},message: "from stream2 and filter"},
-	{request: Request{DbName:dbname, ExtraParam:collection2+"_bla"},error: true,streams: []string{},message: "wrong filter"},
-	{request: Request{DbName:dbname, ExtraParam:collection2+"_all_aaa"},error: true,streams: []string{},message: "wrong filter2"},
-	{request: Request{DbName:dbname, ExtraParam:"blabla"},error: false,streams: []string{},message: "from unknown stream returns nothing"},
-	{request: Request{DbName:dbname, ExtraParam:collection2+"_"},error: false,streams: []string{collection2},message: "from stream2, first parameter only"},
+	{request: Request{DbName:dbname, ExtraParam:"0/"},error: false,streams: []string{collection,collection2},message: "default 0/ all streams"},
+	{request: Request{DbName:dbname, ExtraParam:utils.EncodeTwoStrings(collection,"")},error: false,streams: []string{collection,collection2},message: "first parameter only -  all streams"},
+	{request: Request{DbName:dbname, ExtraParam:"0/all"},error: false,streams: []string{collection,collection2},message: "second parameter only -  all streams"},
+	{request: Request{DbName:dbname, ExtraParam:"0/finished"},error: false,streams: []string{collection2},message: "second parameter only -  finished streams"},
+	{request: Request{DbName:dbname, ExtraParam:"0/unfinished"},error: false,streams: []string{collection},message: "second parameter only -  unfinished streams"},
+	{request: Request{DbName:dbname, ExtraParam:utils.EncodeTwoStrings(collection2,"all")},error: false,streams: []string{collection2},message: "from stream2"},
+	{request: Request{DbName:dbname, ExtraParam:utils.EncodeTwoStrings(collection2,"unfinished")},error: false,streams: []string{},message: "from stream2 and filter"},
+	{request: Request{DbName:dbname, ExtraParam:utils.EncodeTwoStrings(collection2,"bla")},error: true,streams: []string{},message: "wrong filter"},
+	{request: Request{DbName:dbname, ExtraParam:utils.EncodeTwoStrings(collection2,"all_aaa")},error: true,streams: []string{},message: "wrong filter2"},
+	{request: Request{DbName:dbname, ExtraParam:utils.EncodeTwoStrings("blabla","")},error: false,streams: []string{},message: "from unknown stream returns nothing"},
+	{request: Request{DbName:dbname, ExtraParam:utils.EncodeTwoStrings(collection2,"")},error: false,streams: []string{collection2},message: "from stream2, first parameter only"},
 }
 
 func (suite *StreamsTestSuite) TestStreamFilters() {
diff --git a/broker/src/asapo_broker/server/authorizer.go b/broker/src/asapo_broker/server/authorizer.go
index 66a01d31a8ac1866082ef8b4ce4810090ce59126..cac330d1be034930207613d9cd15c69b368a1153 100644
--- a/broker/src/asapo_broker/server/authorizer.go
+++ b/broker/src/asapo_broker/server/authorizer.go
@@ -23,8 +23,8 @@ type HttpClient interface {
 	Do(req *http.Request) (*http.Response, error)
 }
 
-type AuthorizationError struct{
-	err error
+type AuthorizationError struct {
+	err        error
 	statusCode int
 }
 
@@ -33,34 +33,34 @@ func (m AuthorizationError) Error() string {
 }
 
 type AsapoAuthorizer struct {
-	serverUrl string
+	serverUrl  string
 	httpClient HttpClient
 }
 
-type cachedToken struct{
+type cachedToken struct {
 	Token
 	lastUpdate time.Time
 }
 
-var cachedTokens  = struct {
-	tokens map[string]cachedToken
+var cachedTokens = struct {
+	tokens           map[string]cachedToken
 	cachedTokensLock sync.RWMutex
-}{tokens:make(map[string]cachedToken,0)}
+}{tokens: make(map[string]cachedToken, 0)}
 
-func getCachedToken(tokenJWT string)(token Token, ok bool) {
+func getCachedToken(tokenJWT string) (token Token, ok bool) {
 	cachedTokens.cachedTokensLock.RLock()
-	defer cachedTokens.cachedTokensLock.RUnlock()
-	cachedToken,ok:=cachedTokens.tokens[tokenJWT]
-	if !ok{
-		return  token,false
+	cachedToken, ok := cachedTokens.tokens[tokenJWT]
+	cachedTokens.cachedTokensLock.RUnlock()
+	if !ok {
+		return token, false
 	}
-	if time.Now().Sub(cachedToken.lastUpdate) < 10000*time.Second {
+	if time.Now().Sub(cachedToken.lastUpdate) < time.Duration(settings.GetTokenCacheUpdateInterval())*time.Millisecond {
 		return cachedToken.Token, true
 	}
-	return token,false
+	return token, false
 }
 
-func cacheToken(tokenJWT string,token Token) {
+func cacheToken(tokenJWT string, token Token) {
 	cachedTokens.cachedTokensLock.Lock()
 	defer cachedTokens.cachedTokensLock.Unlock()
 
@@ -70,7 +70,7 @@ func cacheToken(tokenJWT string,token Token) {
 	}
 }
 
-func (a * AsapoAuthorizer) doRequest(req *http.Request) (token Token, err error) {
+func (a *AsapoAuthorizer) doRequest(req *http.Request) (token Token, err error) {
 	resp, err := a.httpClient.Do(req)
 	if err != nil {
 		return token, err
@@ -85,8 +85,10 @@ func (a * AsapoAuthorizer) doRequest(req *http.Request) (token Token, err error)
 	switch resp.StatusCode {
 	case http.StatusOK:
 		//do nothing
-	case  http.StatusUnauthorized:
-		return token, &AuthorizationError{errors.New("authorizer rejected to authorize: " + string(body)),http.StatusUnauthorized}
+	case http.StatusUnauthorized:
+		return token, &AuthorizationError{errors.New("authorizer rejected to authorize: " + string(body)), http.StatusUnauthorized}
+	case http.StatusServiceUnavailable:
+		return token, &AuthorizationError{errors.New("authorizer service unavailable: " + string(body)), http.StatusServiceUnavailable}
 	default:
 		return token, errors.New("authorizer returned " + resp.Status + ": " + string(body))
 	}
@@ -96,7 +98,7 @@ func (a * AsapoAuthorizer) doRequest(req *http.Request) (token Token, err error)
 }
 
 func createIntrospectTokenRequest(tokenJWT string) (*http.Request, error) {
-	path := "http://"+settings.AuthorizationServer + "/introspect"
+	path := "http://" + settings.AuthorizationServer + "/introspect"
 	request := struct {
 		Token string
 	}{tokenJWT}
@@ -109,8 +111,8 @@ func createIntrospectTokenRequest(tokenJWT string) (*http.Request, error) {
 	return req, nil
 }
 
-func (a * AsapoAuthorizer) AuthorizeToken(tokenJWT string) (token Token, err error) {
-	token,ok := getCachedToken(tokenJWT)
+func (a *AsapoAuthorizer) AuthorizeToken(tokenJWT string) (token Token, err error) {
+	token, ok := getCachedToken(tokenJWT)
 	if ok {
 		return
 	}
@@ -127,4 +129,3 @@ func (a * AsapoAuthorizer) AuthorizeToken(tokenJWT string) (token Token, err err
 
 	return
 }
-
diff --git a/broker/src/asapo_broker/server/get_commands_test.go b/broker/src/asapo_broker/server/get_commands_test.go
index 980946e49cd6022dc1c781fd8e3098729a6778f3..3f34750b20451d69d8fc62f456070e905f1327b7 100644
--- a/broker/src/asapo_broker/server/get_commands_test.go
+++ b/broker/src/asapo_broker/server/get_commands_test.go
@@ -52,7 +52,7 @@ var testsGetCommand = []struct {
 		expectedGroupID + "/next","&resend_nacks=true&delay_ms=10000&resend_attempts=3","10000_3"},
 	{"size", expectedSource,expectedStream, "", expectedStream  + "/size","",""},
 	{"size",expectedSource, expectedStream, "", expectedStream  + "/size","&incomplete=true","true"},
-	{"streams",expectedSource, "0", "", "0/streams","","_"},
+	{"streams",expectedSource, "0", "", "0/streams","","0/"},
 	{"lastack", expectedSource,expectedStream, expectedGroupID, expectedStream + "/" + expectedGroupID + "/lastack","",""},
 }
 
diff --git a/broker/src/asapo_broker/server/get_streams.go b/broker/src/asapo_broker/server/get_streams.go
index a22274553f58663c2bdbd830c246344b48f0dea9..01e1e8edc57e920fecee4d2b9560f5087363ca3f 100644
--- a/broker/src/asapo_broker/server/get_streams.go
+++ b/broker/src/asapo_broker/server/get_streams.go
@@ -1,6 +1,7 @@
 package server
 
 import (
+	"asapo_common/utils"
 	"net/http"
 )
 
@@ -8,5 +9,7 @@ func routeGetStreams(w http.ResponseWriter, r *http.Request) {
 	keys := r.URL.Query()
 	from := keys.Get("from")
 	filter := keys.Get("filter")
-	processRequest(w, r, "streams", from+"_"+filter, false)
+	utils.EncodeTwoStrings(from,filter)
+	encoded := utils.EncodeTwoStrings(from,filter)
+	processRequest(w, r, "streams", encoded, false)
 }
diff --git a/broker/src/asapo_broker/server/process_request_test.go b/broker/src/asapo_broker/server/process_request_test.go
index 781a7f16b2df345e4c1cb77aca727f7ec7a85608..cf0d41626723cd026791b9c9c9ac9471d78c0ce2 100644
--- a/broker/src/asapo_broker/server/process_request_test.go
+++ b/broker/src/asapo_broker/server/process_request_test.go
@@ -176,7 +176,7 @@ func (suite *ProcessRequestTestSuite) TestProcessRequestWithConnectionError() {
 
 	w := doRequest("/beamtime/" + expectedBeamtimeId + "/" + expectedSource + "/" + expectedStream + "/" + expectedGroupID + "/next" + correctTokenSuffix)
 	time.Sleep(time.Second)
-	suite.Equal(http.StatusNotFound, w.Code, "data not found")
+	suite.Equal(http.StatusServiceUnavailable, w.Code, "data not found")
 }
 
 func (suite *ProcessRequestTestSuite) TestProcessRequestWithInternalDBError() {
@@ -191,7 +191,7 @@ func (suite *ProcessRequestTestSuite) TestProcessRequestWithInternalDBError() {
 	w := doRequest("/beamtime/" + expectedBeamtimeId + "/" + expectedSource + "/" + expectedStream + "/" + expectedGroupID + "/next" + correctTokenSuffix)
 	time.Sleep(time.Second)
 
-	suite.Equal(http.StatusNotFound, w.Code, "internal error")
+	suite.Equal(http.StatusServiceUnavailable, w.Code, "internal error")
 }
 
 func (suite *ProcessRequestTestSuite) TestProcessRequestAddsCounter() {
diff --git a/broker/src/asapo_broker/server/request_common.go b/broker/src/asapo_broker/server/request_common.go
index 17a4b30aedd417fd018b69382284ab5e305322f9..1a0d5e875034ed369f85128929ef3baae1c17c1f 100644
--- a/broker/src/asapo_broker/server/request_common.go
+++ b/broker/src/asapo_broker/server/request_common.go
@@ -16,7 +16,7 @@ func writeAuthAnswer(w http.ResponseWriter, requestName string, db_name string,
 	case AuthorizationError:
 		w.WriteHeader(er.statusCode)
 	default:
-		w.WriteHeader(http.StatusInternalServerError)
+		w.WriteHeader(http.StatusServiceUnavailable)
 	}
 	w.Write([]byte(err.Error()))
 }
diff --git a/broker/src/asapo_broker/server/server.go b/broker/src/asapo_broker/server/server.go
index 1537fa08642c36454b679b54e218d5ad5b6f4007..f736671739406613093df46730000f3782421a61 100644
--- a/broker/src/asapo_broker/server/server.go
+++ b/broker/src/asapo_broker/server/server.go
@@ -2,14 +2,15 @@ package server
 
 import (
 	"asapo_broker/database"
+	"asapo_common/discovery"
 	log "asapo_common/logger"
 	"errors"
-	"io/ioutil"
 	"net/http"
 )
 
 const kDefaultresendInterval = 10
 const kDefaultStreamCacheUpdateIntervalMs = 100
+const kDefaultTokenCacheUpdateIntervalMs = 60000
 
 var db database.Agent
 
@@ -25,6 +26,14 @@ type serverSettings struct {
 	discoveredDbAddress         string
 	CheckResendInterval         *int
 	StreamCacheUpdateIntervalMs *int
+	TokenCacheUpdateIntervalMs  *int
+}
+
+func (s *serverSettings) GetTokenCacheUpdateInterval() int {
+	if s.TokenCacheUpdateIntervalMs == nil {
+		return kDefaultTokenCacheUpdateIntervalMs
+	}
+	return *s.TokenCacheUpdateIntervalMs
 }
 
 func (s *serverSettings) GetResendInterval() int {
@@ -58,17 +67,7 @@ type discoveryAPI struct {
 	baseURL string
 }
 
-var discoveryService discoveryAPI
-
-func (api *discoveryAPI) GetMongoDbAddress() (string, error) {
-	resp, err := api.Client.Get(api.baseURL + "/asapo-mongodb")
-	if err != nil {
-		return "", err
-	}
-	defer resp.Body.Close()
-	body, err := ioutil.ReadAll(resp.Body)
-	return string(body), err
-}
+var discoveryService discovery.DiscoveryAPI
 
 func ReconnectDb() (err error) {
 	if db == nil {
@@ -97,7 +96,7 @@ func InitDB(dbAgent database.Agent) (err error) {
 }
 
 func CreateDiscoveryService() {
-	discoveryService = discoveryAPI{&http.Client{}, "http://" + settings.DiscoveryServer}
+	discoveryService = discovery.CreateDiscoveryService(&http.Client{},"http://" + settings.DiscoveryServer)
 }
 
 func CleanupDB() {
diff --git a/broker/src/asapo_broker/server/server_nottested.go b/broker/src/asapo_broker/server/server_nottested.go
index ae96af4d5a957f7bc5529ad0f78510a648cdabd0..ec512247a9d1ce7052f1ac32ad99ff205a434f12 100644
--- a/broker/src/asapo_broker/server/server_nottested.go
+++ b/broker/src/asapo_broker/server/server_nottested.go
@@ -7,6 +7,7 @@ import (
 	"asapo_common/utils"
 	"errors"
 	"net/http"
+	_ "net/http/pprof"
 	"strconv"
 )
 
@@ -22,6 +23,7 @@ func Start() {
 		StartStatistics()
 	}
 	mux := utils.NewRouter(listRoutes)
+	mux.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
 	log.Info("Listening on port: " + strconv.Itoa(settings.Port))
 	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP)))
 }
diff --git a/broker/src/asapo_broker/server/server_test.go b/broker/src/asapo_broker/server/server_test.go
index 3a7243782c43ca7e5aaae7c7a98f638c547c7ae3..fc593fd028796f6f6dd3b94ddd476b38bc2c3583 100644
--- a/broker/src/asapo_broker/server/server_test.go
+++ b/broker/src/asapo_broker/server/server_test.go
@@ -2,6 +2,7 @@ package server
 
 import (
 	"asapo_broker/database"
+	"asapo_common/discovery"
 	"asapo_common/logger"
 	"errors"
 	"github.com/stretchr/testify/assert"
@@ -77,7 +78,7 @@ func TestInitDBWithAutoAddress(t *testing.T) {
 	}))
 	defer mock_server.Close()
 
-	discoveryService = discoveryAPI{mock_server.Client(), mock_server.URL}
+	discoveryService = discovery.CreateDiscoveryService(mock_server.Client(), mock_server.URL)
 
 	mock_db.On("Connect", "0.0.0.0:0000").Return(nil)
 	mock_db.On("SetSettings", mock.Anything).Return()
@@ -95,7 +96,7 @@ func TestReconnectDB(t *testing.T) {
 		assert.Equal(t, req.URL.String(), "/asapo-mongodb", "request string")
 		rw.Write([]byte(mongo_address))
 	}))
-	discoveryService = discoveryAPI{mock_server.Client(), mock_server.URL}
+	discoveryService = discovery.CreateDiscoveryService(mock_server.Client(), mock_server.URL)
 
 	defer mock_server.Close()
 
diff --git a/common/cpp/include/asapo/common/io_error.h b/common/cpp/include/asapo/common/io_error.h
index 7375ec548a3d0c00665c30cc27489e434c77cb33..e11399e00bcbe73554358d49a26f4da301457910 100644
--- a/common/cpp/include/asapo/common/io_error.h
+++ b/common/cpp/include/asapo/common/io_error.h
@@ -27,8 +27,8 @@ enum class IOErrorType {
     kSocketOperationUnknownAtLevel,
     kSocketOperationValueOutOfBound,
     kAddressNotValid,
-    kBrokenPipe
-
+    kBrokenPipe,
+    kNotConnected
 };
 
 using IOError = ServiceError<IOErrorType, ErrorType::kIOError>;
@@ -67,6 +67,10 @@ auto const kAddressAlreadyInUse = IOErrorTemplate {
 auto const kConnectionRefused = IOErrorTemplate {
     "Connection refused", IOErrorType::kConnectionRefused
 };
+auto const kNotConnected = IOErrorTemplate {
+    "Not connected", IOErrorType::kNotConnected
+};
+
 auto const kConnectionResetByPeer = IOErrorTemplate {
     "kConnectionResetByPeer", IOErrorType::kConnectionResetByPeer
 };
diff --git a/common/cpp/src/logger/spd_logger.cpp b/common/cpp/src/logger/spd_logger.cpp
index 9228b8f6104db2154d7c17718f2c903209c23b9e..4cc5c49b098abdbb2ef4321642055c64ca913943 100644
--- a/common/cpp/src/logger/spd_logger.cpp
+++ b/common/cpp/src/logger/spd_logger.cpp
@@ -2,6 +2,10 @@
 
 #include "fluentd_sink.h"
 
+#include <sstream>
+#include <iomanip>
+
+
 namespace asapo {
 
 void SpdLogger::SetLogLevel(LogLevel level) {
@@ -25,9 +29,47 @@ void SpdLogger::SetLogLevel(LogLevel level) {
         }
     }
 }
+
+std::string escape_json(const std::string& s) {
+    std::ostringstream o;
+    for (auto c = s.cbegin(); c != s.cend(); c++) {
+        switch (*c) {
+        case '"':
+            o << "\\\"";
+            break;
+        case '\\':
+            o << "\\\\";
+            break;
+        case '\b':
+            o << "\\b";
+            break;
+        case '\f':
+            o << "\\f";
+            break;
+        case '\n':
+            o << "\\n";
+            break;
+        case '\r':
+            o << "\\r";
+            break;
+        case '\t':
+            o << "\\t";
+            break;
+        default:
+            if ('\x00' <= *c && *c <= '\x1f') {
+                o << "\\u"
+                  << std::hex << std::setw(4) << std::setfill('0') << (int)*c;
+            } else {
+                o << *c;
+            }
+        }
+    }
+    return o.str();
+}
+
 std::string EncloseMsg(std::string msg) {
     if (msg.find("\"") != 0) {
-        return std::string(R"("message":")") + msg + "\"";
+        return std::string(R"("message":")") + escape_json(msg) + "\"";
     } else {
         return msg;
     }
diff --git a/common/cpp/src/system_io/system_io_linux_mac.cpp b/common/cpp/src/system_io/system_io_linux_mac.cpp
index b933a506eeb3e4bc4131dc81ec182c24f1f39d41..9d36dca7b3830fdd7af0221e203cca47d2ab669c 100644
--- a/common/cpp/src/system_io/system_io_linux_mac.cpp
+++ b/common/cpp/src/system_io/system_io_linux_mac.cpp
@@ -50,6 +50,8 @@ Error GetLastErrorFromErrno() {
         return IOErrorTemplates::kFileAlreadyExists.Generate();
     case ENOSPC:
         return IOErrorTemplates::kNoSpaceLeft.Generate();
+    case ENOTCONN:
+        return IOErrorTemplates::kNotConnected.Generate();
     case ECONNREFUSED:
         return IOErrorTemplates::kConnectionRefused.Generate();
     case EADDRINUSE:
diff --git a/common/cpp/unittests/logger/test_logger.cpp b/common/cpp/unittests/logger/test_logger.cpp
index 6dc03c84104187ee38fb8bfc2dd710d39062ed99..ed67359e1479f6d65fda1b7dc1d7a8a1cb4e5382 100644
--- a/common/cpp/unittests/logger/test_logger.cpp
+++ b/common/cpp/unittests/logger/test_logger.cpp
@@ -71,11 +71,11 @@ class LoggerTests : public Test {
     spdlog::details::log_msg msg;
     spdlog::details::log_msg msg_json;
 
-    std::string test_string{"Hello"};
+    std::string test_string{"Hello\""};
     std::string test_string_json{R"("Hello":"test","int":1,"double":123.234)"};
 
     void SetUp() override {
-        msg.raw << R"("message":"Hello")";
+        msg.raw << R"("message":"Hello\"")";
         msg_json.raw << R"("Hello":"test","int":1,"double":123.234)";
         log.reset(new spdlog::logger("mylogger", mock_sink));
         logger.log__ = std::move(log);
diff --git a/common/go/src/asapo_common/discovery/discovery.go b/common/go/src/asapo_common/discovery/discovery.go
new file mode 100644
index 0000000000000000000000000000000000000000..552c07cf91d29b1b1747632bb96b5418bb59c577
--- /dev/null
+++ b/common/go/src/asapo_common/discovery/discovery.go
@@ -0,0 +1,29 @@
+package discovery
+
+import (
+	"io/ioutil"
+	"net/http"
+	"errors"
+)
+
+type DiscoveryAPI struct {
+	client  *http.Client
+	baseURL string
+}
+
+func (api *DiscoveryAPI) GetMongoDbAddress() (string, error) {
+	resp, err := api.client.Get(api.baseURL + "/asapo-mongodb")
+	if err != nil {
+		return "", err
+	}
+	if resp.StatusCode!=http.StatusOK {
+		return "", errors.New("cannot get mongodb server, status: "+resp.Status)
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	return string(body), err
+}
+
+func CreateDiscoveryService(client *http.Client,uri string) DiscoveryAPI{
+	return DiscoveryAPI{client, uri}
+}
\ No newline at end of file
diff --git a/common/go/src/asapo_common/utils/authorization.go b/common/go/src/asapo_common/utils/authorization.go
index c913611730477d2611c0e6410a77bbf25e72f05b..d707819b9d11758a87f5f3538b204e8d76ed5ee3 100644
--- a/common/go/src/asapo_common/utils/authorization.go
+++ b/common/go/src/asapo_common/utils/authorization.go
@@ -17,7 +17,7 @@ type Auth interface {
 	GenerateToken(...interface{}) (string, error)
 	ProcessAuth(http.HandlerFunc, string) http.HandlerFunc
 	Name() string
-	CheckAndGetContent(token string, extraClaims interface{}, payload ...interface{}) (string,error)
+	CheckAndGetContent(token string, extraClaims interface{}, payload ...interface{}) (*jwt.StandardClaims,error)
 }
 
 func SubjectFromBeamtime(bt string)string {
@@ -147,23 +147,21 @@ func ProcessJWTAuth(fn http.HandlerFunc, key string) http.HandlerFunc {
 	}
 }
 
-func (a *JWTAuth) CheckAndGetContent(token string, extraClaims interface{}, payload ...interface{}) (subject string,err error) {
+func (a *JWTAuth) CheckAndGetContent(token string, extraClaims interface{}, payload ...interface{}) (claims *jwt.StandardClaims, err error) {
 	// payload ignored
 	c, ok := CheckJWTToken(token,a.Key)
 	if !ok {
-		return "",errors.New("wrong JWT token")
+		return nil,errors.New("wrong JWT token")
 	}
 	claim,ok  := c.(*CustomClaims)
 	if !ok {
-		return "",errors.New("cannot get CustomClaims")
+		return nil,errors.New("cannot get CustomClaims")
 	}
 
-	subject = claim.Subject
-
 	if extraClaims!=nil {
 		err = MapToStruct(claim.ExtraClaims.(map[string]interface{}), extraClaims)
 	}
-	return subject,err
+	return &claim.StandardClaims,err
 }
 
 
@@ -264,20 +262,22 @@ func ProcessHMACAuth(fn http.HandlerFunc, payload, key string) http.HandlerFunc
 	}
 }
 
-func (a *HMACAuth) CheckAndGetContent(token string, _ interface{}, payload ...interface{}) (string,error) {
+func (a *HMACAuth) CheckAndGetContent(token string, _ interface{}, payload ...interface{}) (*jwt.StandardClaims,error) {
 	if len(payload) != 1 {
-		return "",errors.New("wrong payload")
+		return nil,errors.New("wrong payload")
 	}
 	value, ok := payload[0].(string)
 	if !ok {
-		return "",errors.New("wrong payload")
+		return nil,errors.New("wrong payload")
 	}
 
 	ok = CheckHMACToken(token,value,a.Key)
 	if !ok {
-		return "",errors.New("wrong HMAC token")
+		return nil,errors.New("wrong HMAC token")
 	}
-	return value,nil
+	claim := jwt.StandardClaims{}
+	claim.Subject = value
+	return &claim,nil
 
 }
 
diff --git a/common/go/src/asapo_common/utils/helpers.go b/common/go/src/asapo_common/utils/helpers.go
index 714cebf61ad85db215259c5147d06af861c51ad5..ed19b875d14bdcba203b19efaaf0d65798520933 100644
--- a/common/go/src/asapo_common/utils/helpers.go
+++ b/common/go/src/asapo_common/utils/helpers.go
@@ -2,9 +2,10 @@ package utils
 
 import (
 	json "encoding/json"
+	"errors"
 	"io/ioutil"
+	"strconv"
 	"strings"
-	"errors"
 )
 
 func StringInSlice(a string, list []string) bool {
@@ -102,3 +103,20 @@ func MapToStruct(m map[string]interface{}, val interface{}) error {
 	}
 	return nil
 }
+
+func EncodeTwoStrings(first,second string) string {
+	return strconv.Itoa(len(first))+"/"+first + second
+}
+
+func DecodeTwoStrings(encoded string) (string,string,error) {
+	temp := strings.SplitN(encoded,"/",2);
+	if len(temp)!=2 {
+		return "","",errors.New("wrong input: "+encoded)
+	}
+	length,err := strconv.Atoi(temp[0]);
+	if err!=nil {
+		return "","",errors.New("wrong input: "+encoded)
+	}
+	return temp[1][:length],temp[1][length:],err
+
+}
\ No newline at end of file
diff --git a/common/go/src/asapo_common/utils/helpers_test.go b/common/go/src/asapo_common/utils/helpers_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c817f837ec34759c66e4b1d9f4cd933a2f84068
--- /dev/null
+++ b/common/go/src/asapo_common/utils/helpers_test.go
@@ -0,0 +1,33 @@
+package utils
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+
+
+var encodeTests = []struct {
+	input       string
+	str1        string
+	str2       string
+	message    string
+}{
+	{"5/hellohello1","hello", "hello1","ok"},
+	{"0/","", "","ok"},
+	{"5/hello","hello", "","ok"},
+	{"10/hello_testall","hello_test", "all","ok"},
+
+}
+
+func TestEncodeDecode(t *testing.T) {
+	for _, test := range encodeTests {
+		encoded:=EncodeTwoStrings(test.str1,test.str2)
+		assert.Equal(t, test.input,encoded,test.message)
+		s1,s2,err:=DecodeTwoStrings(test.input)
+		assert.Equal(t, test.str1,s1,test.message)
+		assert.Equal(t, test.str2,s2,test.message)
+		assert.NoError(t, err,test.message)
+	}
+}
+
diff --git a/common/go/src/asapo_common/utils/status_codes.go b/common/go/src/asapo_common/utils/status_codes.go
index 7002a963e250b20b9af88c1125b54c2993215aca..fdd205418a2fb6abd8e22078bdb104e35fa5ad71 100644
--- a/common/go/src/asapo_common/utils/status_codes.go
+++ b/common/go/src/asapo_common/utils/status_codes.go
@@ -9,7 +9,7 @@ const (
 const (
 	//error codes
 	StatusTransactionInterrupted = http.StatusInternalServerError
-	StatusServiceUnavailable     = http.StatusNotFound
+	StatusServiceUnavailable     = http.StatusServiceUnavailable
 	StatusWrongInput             = http.StatusBadRequest
 	StatusNoData                 = http.StatusConflict
 	StatusPartialData            = http.StatusPartialContent
diff --git a/consumer/api/cpp/include/asapo/consumer/consumer_error.h b/consumer/api/cpp/include/asapo/consumer/consumer_error.h
index 55618fddc9ea98dd8f68cbf0bfa38821d02f72d8..efa0631dab069a96595de024b4873d2d36e68235 100644
--- a/consumer/api/cpp/include/asapo/consumer/consumer_error.h
+++ b/consumer/api/cpp/include/asapo/consumer/consumer_error.h
@@ -41,8 +41,6 @@ auto const kPartialData = ConsumerErrorTemplate {
     "partial data", ConsumerErrorType::kPartialData
 };
 
-
-
 auto const kLocalIOError = ConsumerErrorTemplate {
     "local i/o error", ConsumerErrorType::kLocalIOError
 };
@@ -76,8 +74,6 @@ auto const kUnavailableService = ConsumerErrorTemplate {
     "service unavailable", ConsumerErrorType::kUnavailableService
 };
 
-
-
 }
 }
 
diff --git a/consumer/api/cpp/src/consumer_impl.cpp b/consumer/api/cpp/src/consumer_impl.cpp
index 973e6a98a5313364d75c2455662476fd25e2401f..659ad07a261974ce1cf0dba75cddc107cdb463a7 100644
--- a/consumer/api/cpp/src/consumer_impl.cpp
+++ b/consumer/api/cpp/src/consumer_impl.cpp
@@ -91,6 +91,8 @@ Error ConsumerErrorFromHttpCode(const RequestOutput* response, const HttpCode& c
         return ConsumerErrorTemplates::kWrongInput.Generate(response->to_string());
     case HttpCode::InternalServerError:
         return ConsumerErrorTemplates::kInterruptedTransaction.Generate(response->to_string());
+    case HttpCode::ServiceUnavailable:
+        return ConsumerErrorTemplates::kUnavailableService.Generate(response->to_string());
     case HttpCode::NotFound:
         return ConsumerErrorTemplates::kUnavailableService.Generate(response->to_string());
     case HttpCode::Conflict:
diff --git a/consumer/api/cpp/unittests/test_consumer_impl.cpp b/consumer/api/cpp/unittests/test_consumer_impl.cpp
index ae11c5592d26e5e7746cf4ad9159f8d439fddb36..0b783b42bf055819ae9f07860b4bdb3d34414c66 100644
--- a/consumer/api/cpp/unittests/test_consumer_impl.cpp
+++ b/consumer/api/cpp/unittests/test_consumer_impl.cpp
@@ -146,7 +146,7 @@ class ConsumerImplTests : public Test {
 
     void MockGetError() {
         EXPECT_CALL(mock_http_client, Get_t(HasSubstr(expected_broker_api), _, _)).WillOnce(DoAll(
-                    SetArgPointee<1>(HttpCode::NotFound),
+                    SetArgPointee<1>(HttpCode::ServiceUnavailable),
                     SetArgPointee<2>(asapo::IOErrorTemplates::kUnknownIOError.Generate().release()),
                     Return("")
                 ));
@@ -442,7 +442,7 @@ TEST_F(ConsumerImplTests, GetMessageReturnsNoDataAfterTimeoutEvenIfOtherErrorOcc
                                         "/stream/0/"
                                         + std::to_string(expected_dataset_id) + "?token="
                                         + expected_token, _, _)).Times(AtLeast(1)).WillRepeatedly(DoAll(
-                                                    SetArgPointee<1>(HttpCode::NotFound),
+                                                    SetArgPointee<1>(HttpCode::ServiceUnavailable),
                                                     SetArgPointee<2>(nullptr),
                                                     Return("")));
 
@@ -1400,7 +1400,7 @@ TEST_F(ConsumerImplTests, NegativeAcknowledgeUsesCorrectUri) {
 
 TEST_F(ConsumerImplTests, CanInterruptOperation) {
     EXPECT_CALL(mock_http_client, Get_t(_, _, _)).Times(AtLeast(1)).WillRepeatedly(DoAll(
-                SetArgPointee<1>(HttpCode::NotFound),
+                SetArgPointee<1>(HttpCode::ServiceUnavailable),
                 SetArgPointee<2>(nullptr),
                 Return("")));
 
diff --git a/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json b/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json
index b6c3d5534036a419d0ba4ae7462bb569c00c7030..db3745ac6dfe47362541d0413508dd13714ef655 100644
--- a/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json
+++ b/deploy/asapo_helm_chart/asapo/configs/asapo-authorizer.json
@@ -14,5 +14,6 @@
   "Uri" : "ldap://localhost:389",
   "BaseDn" : "ou=rgy,o=desy,c=de",
   "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)"
-  }
+  },
+  "UpdateRevokedTokensIntervalSec": 60
 }
diff --git a/deploy/asapo_services/asap3.tfvars b/deploy/asapo_services/asap3.tfvars
index 4ff7c3cb46700d4f61dc52eea6f5e3524fc267ab..1070bcc4026d94dd2223a0bbaae0951dacc22fa1 100644
--- a/deploy/asapo_services/asap3.tfvars
+++ b/deploy/asapo_services/asap3.tfvars
@@ -1,4 +1,5 @@
 elk_logs = true
+perf_monitor = true
 
 asapo_imagename_suffix = ""
 asapo_image_tag = ""
@@ -26,8 +27,8 @@ influxdb_total_memory_size = 2000
 fluentd_total_memory_size = 1000
 elasticsearch_total_memory_size = 3000
 kibana_total_memory_size = 1000
-mongo_total_memory_size = 20000
-authorizer_total_memory_size = 512
+mongo_total_memory_size = 40000
+authorizer_total_memory_size = 20000
 discovery_total_memory_size = 512
 
 n_receivers = 2
diff --git a/deploy/asapo_services/scripts/asapo-logging.nmd.tpl b/deploy/asapo_services/scripts/asapo-logging.nmd.tpl
index 3252f86773cdc6f57a0b244ec47ef24a3ed1869d..a033d9bb8764a63a5363e520663301b2252b631d 100644
--- a/deploy/asapo_services/scripts/asapo-logging.nmd.tpl
+++ b/deploy/asapo_services/scripts/asapo-logging.nmd.tpl
@@ -170,12 +170,12 @@ job "asapo-logging" {
            name = "alive"
            type     = "http"
            path     = "/logsview"
-           interval = "10s"
+           interval = "60s"
            timeout  = "1s"
        }
        check_restart {
          limit = 2
-         grace = "90s"
+         grace = "600s"
          ignore_warnings = false
        }
      }
diff --git a/deploy/asapo_services/scripts/asapo-mongo.nmd.tpl b/deploy/asapo_services/scripts/asapo-mongo.nmd.tpl
index 9c06fa7da69beeec0fee5a4f6a0b51725ef36403..7fefa0d18462177b604ed607ece288d3255e23e2 100644
--- a/deploy/asapo_services/scripts/asapo-mongo.nmd.tpl
+++ b/deploy/asapo_services/scripts/asapo-mongo.nmd.tpl
@@ -56,7 +56,7 @@ job "asapo-mongo" {
         }
         check_restart {
           limit = 2
-          grace = "90s"
+          grace = "1800s"
           ignore_warnings = false
         }
       }
diff --git a/deploy/asapo_services/scripts/authorizer.json.tpl b/deploy/asapo_services/scripts/authorizer.json.tpl
index 3aecc0df652245b0f4c51ed5008301f17cee10fd..1d77de613c0a03883370bb4812b92b83f22d89b9 100644
--- a/deploy/asapo_services/scripts/authorizer.json.tpl
+++ b/deploy/asapo_services/scripts/authorizer.json.tpl
@@ -14,5 +14,8 @@
         "Uri" : "{{ env "NOMAD_META_ldap_uri" }}",
         "BaseDn" : "ou=rgy,o=desy,c=de",
         "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)"
-    }
+    },
+  "DatabaseServer":"auto",
+  "DiscoveryServer": "localhost:8400/asapo-discovery",
+  "UpdateRevokedTokensIntervalSec": 60
 }
diff --git a/deploy/asapo_services/scripts/fluentd.conf.tpl b/deploy/asapo_services/scripts/fluentd.conf.tpl
index c97a9048e4be3f3114fd59c225be2ca433910cef..4224257406b115139d958e77d807edf43423aa91 100644
--- a/deploy/asapo_services/scripts/fluentd.conf.tpl
+++ b/deploy/asapo_services/scripts/fluentd.conf.tpl
@@ -40,12 +40,19 @@
   host localhost
   port 8400
   path /elasticsearch/
-  flush_interval 5s
   logstash_format true
   time_key_format %Y-%m-%dT%H:%M:%S.%N
   time_key time
   time_key_exclude_timestamp true
-  buffer_type memory
+  <buffer>
+      @type memory
+      total_limit_size 100MB
+      flush_mode interval
+      flush_interval 1s
+      flush_thread_count 3
+      chunk_limit_size 500KB
+      overflow_action drop_oldest_chunk
+  </buffer>
   </store>
 {{ end }}
   <store>
@@ -56,4 +63,3 @@
   path /shared/asapo-logs
   </store>
 </match>
-
diff --git a/deploy/asapo_services/scripts/nginx.conf.tpl b/deploy/asapo_services/scripts/nginx.conf.tpl
index e0c52d1ed7dfab0791f81cb226e5fa2d81a43d59..03bc7377435d913959c003949822b9e7f230d293 100644
--- a/deploy/asapo_services/scripts/nginx.conf.tpl
+++ b/deploy/asapo_services/scripts/nginx.conf.tpl
@@ -19,6 +19,7 @@ http {
 
 
     client_body_temp_path  "/tmp/client_body" 1 2;
+    client_max_body_size   10M;
     proxy_temp_path        "/tmp/proxy" 1 2;
     fastcgi_temp_path      "/tmp/fastcgi" 1 2;
     scgi_temp_path         "/tmp/scgi" 1 2;
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 \
     ..
diff --git a/discovery/src/asapo_discovery/protocols/hard_coded_producer.go b/discovery/src/asapo_discovery/protocols/hard_coded_producer.go
index 7207d0547d80767119b324b53e1ad2dd33d82d16..9ef311c1261095631feb7996fc68f06b38a66181 100644
--- a/discovery/src/asapo_discovery/protocols/hard_coded_producer.go
+++ b/discovery/src/asapo_discovery/protocols/hard_coded_producer.go
@@ -2,11 +2,16 @@ package protocols
 
 func GetSupportedProducerProtocols() []Protocol {
 	return []Protocol{
+		Protocol{"v0.4",
+			map[string]string{
+				"Discovery": "v0.1",
+				"Receiver": "v0.4",
+			}, &protocolValidatorCurrent{}},
 		Protocol{"v0.3",
 			map[string]string{
 				"Discovery": "v0.1",
 				"Receiver": "v0.3",
-			}, &protocolValidatorCurrent{}},
+			}, &protocolValidatorDeprecated{getTimefromDate("2022-09-01")}},
 		Protocol{"v0.2",
 			map[string]string{
 				"Discovery": "v0.1",
diff --git a/discovery/src/asapo_discovery/protocols/protocol_test.go b/discovery/src/asapo_discovery/protocols/protocol_test.go
index 344699581b2a09b20a7db3f72170dd4ca79d953b..fbf1bc205d66e7a0241b83f75dfcda8134046171 100644
--- a/discovery/src/asapo_discovery/protocols/protocol_test.go
+++ b/discovery/src/asapo_discovery/protocols/protocol_test.go
@@ -22,8 +22,10 @@ var protocolTests = []protocolTest{
 	{"consumer", "v1000.2", false, "unknown", "unknown protocol"},
 
 
+
 // producer
-	{"producer", "v0.3", true, "current", "v0.3"},
+	{"producer", "v0.4", true, "current", "v0.4"},
+	{"producer", "v0.3", true, "deprecates", "v0.3"},
 	{"producer", "v0.2", true, "deprecates", "v0.2"},
 	{"producer", "v0.1", true, "deprecates", "v0.1"},
 	{"producer", "v1000.2", false, "unknown", "unknown protocol"},
diff --git a/discovery/src/asapo_discovery/server/server_nottested.go b/discovery/src/asapo_discovery/server/server_nottested.go
index 091db8e202b96c25b357139a147c236b1a3a3493..2c96a7c9cafd0e7e31b8380896d7a7724c05f027 100644
--- a/discovery/src/asapo_discovery/server/server_nottested.go
+++ b/discovery/src/asapo_discovery/server/server_nottested.go
@@ -8,10 +8,12 @@ import (
     "asapo_common/version"
 	"net/http"
 	"strconv"
+	_ "net/http/pprof"
 )
 
 func Start() {
 	mux := utils.NewRouter(listRoutes)
+	mux.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
 	log.Info("Starting ASAPO Discovery, version " + version.GetVersion())
 	log.Info("Listening on port: " + strconv.Itoa(settings.Port))
 	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP)))
diff --git a/docs/site/examples/start_asapo_socket.sh b/docs/site/examples/start_asapo_socket.sh
old mode 100755
new mode 100644
diff --git a/file_transfer/src/asapo_file_transfer/server/server_nottested.go b/file_transfer/src/asapo_file_transfer/server/server_nottested.go
index a55f7df422932df339e39f61d2836fb91e2d06df..c6472e77d64d90ebeb2d2e6e88d5a49399b48d22 100644
--- a/file_transfer/src/asapo_file_transfer/server/server_nottested.go
+++ b/file_transfer/src/asapo_file_transfer/server/server_nottested.go
@@ -9,12 +9,15 @@ import (
 	"errors"
 	"net/http"
 	"strconv"
+	_ "net/http/pprof"
+
 )
 
 func Start() {
 	mux := utils.NewRouter(listRoutes)
 	log.Info("Starting ASAPO Authorizer, version " + version.GetVersion())
 	log.Info("Listening on port: " + strconv.Itoa(settings.Port))
+	mux.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
 	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), utils.ProcessJWTAuth(mux.ServeHTTP, settings.key)))
 }
 
diff --git a/producer/api/cpp/unittests/test_producer_impl.cpp b/producer/api/cpp/unittests/test_producer_impl.cpp
index 3ba5836b150644a76bf5c3f77ed02be868eb56e9..e62c21735ead0d311de4b2cf6a221e0ad244679e 100644
--- a/producer/api/cpp/unittests/test_producer_impl.cpp
+++ b/producer/api/cpp/unittests/test_producer_impl.cpp
@@ -602,10 +602,10 @@ TEST_F(ProducerImplTests, ReturnDataIfCanotAddToQueue) {
 TEST_F(ProducerImplTests, GetVersionInfoWithServer) {
 
     std::string result =
-        R"({"softwareVersion":"21.06.0, build 7a9294ad","clientSupported":"no", "clientProtocol":{"versionInfo":"v0.3"}})";
+        R"({"softwareVersion":"21.06.0, build 7a9294ad","clientSupported":"no", "clientProtocol":{"versionInfo":"v0.4"}})";
 
     EXPECT_CALL(*mock_http_client, Get_t(HasSubstr(expected_server_uri +
-                                                   "/asapo-discovery/v0.1/version?client=producer&protocol=v0.3"), _, _)).WillOnce(DoAll(
+                                                   "/asapo-discovery/v0.1/version?client=producer&protocol=v0.4"), _, _)).WillOnce(DoAll(
                                                            SetArgPointee<1>(asapo::HttpCode::OK),
                                                            SetArgPointee<2>(nullptr),
                                                            Return(result)));
@@ -614,7 +614,7 @@ TEST_F(ProducerImplTests, GetVersionInfoWithServer) {
     auto err = producer.GetVersionInfo(&client_info, &server_info, nullptr);
     ASSERT_THAT(err, Eq(nullptr));
     ASSERT_THAT(server_info, HasSubstr("21.06.0"));
-    ASSERT_THAT(server_info, HasSubstr("v0.3"));
+    ASSERT_THAT(server_info, HasSubstr("v0.4"));
 }
 
 MATCHER_P4(M_CheckDeleteStreamRequest, op_code, source_credentials, stream, flag,
diff --git a/producer/api/cpp/unittests/test_producer_request.cpp b/producer/api/cpp/unittests/test_producer_request.cpp
index cf0b8aba9a20becec1928e260146699087d7cff4..7a48b685d332754b6c4bee9c99aefabd14b5a794 100644
--- a/producer/api/cpp/unittests/test_producer_request.cpp
+++ b/producer/api/cpp/unittests/test_producer_request.cpp
@@ -40,7 +40,7 @@ TEST(ProducerRequest, Constructor) {
     uint64_t expected_file_size = 1337;
     uint64_t expected_meta_size = 137;
     std::string expected_meta = "meta";
-    std::string expected_api_version = "v0.3";
+    std::string expected_api_version = "v0.4";
     asapo::Opcode expected_op_code = asapo::kOpcodeTransferData;
 
     asapo::GenericRequestHeader header{expected_op_code, expected_file_id, expected_file_size,
diff --git a/producer/api/cpp/unittests/test_receiver_discovery_service.cpp b/producer/api/cpp/unittests/test_receiver_discovery_service.cpp
index d94988a48a1ee545ef9a473ff3684d35ea6d544f..0c501e6329373afbb3a796486355bfe891860756 100644
--- a/producer/api/cpp/unittests/test_receiver_discovery_service.cpp
+++ b/producer/api/cpp/unittests/test_receiver_discovery_service.cpp
@@ -48,7 +48,7 @@ class ReceiversStatusTests : public Test {
     NiceMock<asapo::MockLogger> mock_logger;
     NiceMock<MockHttpClient>* mock_http_client;
 
-    std::string expected_endpoint{"endpoint/asapo-discovery/v0.1/asapo-receiver?protocol=v0.3"};
+    std::string expected_endpoint{"endpoint/asapo-discovery/v0.1/asapo-receiver?protocol=v0.4"};
     ReceiverDiscoveryService status{"endpoint", 20};
 
     void SetUp() override {
diff --git a/receiver/src/request_handler/request_handler_authorize.cpp b/receiver/src/request_handler/request_handler_authorize.cpp
index d171bebca59cf3ec98ff178b2f33c8bf192a13cd..0230988c1920527393d390d486fff1423d668ecf 100644
--- a/receiver/src/request_handler/request_handler_authorize.cpp
+++ b/receiver/src/request_handler/request_handler_authorize.cpp
@@ -29,8 +29,9 @@ Error RequestHandlerAuthorize::ErrorFromAuthorizationServerResponse(const Error&
     }
 }
 
-Error CheckAccessType(const std::vector<std::string>& access_types) {
-    if(std::find(access_types.begin(), access_types.end(), "write") != access_types.end()) {
+Error CheckAccessType(SourceType source_type, const std::vector<std::string>& access_types) {
+    if(std::find(access_types.begin(), access_types.end(),
+                 source_type == SourceType::kProcessed ? "write" : "writeraw") != access_types.end()) {
         return nullptr;
     } else {
         return asapo::ReceiverErrorTemplates::kAuthorizationFailure.Generate("wrong access types");
@@ -42,7 +43,6 @@ Error RequestHandlerAuthorize::Authorize(Request* request, const char* source_cr
     HttpCode code;
     Error err;
     std::string request_string = GetRequestString(request, source_credentials);
-
     auto response = http_client__->Post(GetReceiverConfig()->authorization_server + "/authorize", "", request_string, &code,
                                         &err);
     if (err || code != HttpCode::OK) {
@@ -69,7 +69,7 @@ Error RequestHandlerAuthorize::Authorize(Request* request, const char* source_cr
         return ErrorFromAuthorizationServerResponse(err, "", code);
     }
 
-    err = CheckAccessType(access_types);
+    err = CheckAccessType(source_type_, access_types);
     if (err) {
         log__->Error("failure authorizing at " + GetReceiverConfig()->authorization_server + " request: " + request_string +
                      " - " +
diff --git a/receiver/unittests/request_handler/test_request_handler_authorizer.cpp b/receiver/unittests/request_handler/test_request_handler_authorizer.cpp
index b4488cd47942e5c72b89604b6537752e6d78fa0b..30069b6e2531890a4a8ac168f0ab092ea964e620 100644
--- a/receiver/unittests/request_handler/test_request_handler_authorizer.cpp
+++ b/receiver/unittests/request_handler/test_request_handler_authorizer.cpp
@@ -95,7 +95,7 @@ class AuthorizerHandlerTests : public Test {
     void TearDown() override {
         handler.http_client__.release();
     }
-    void MockAuthRequest(bool error, HttpCode code = HttpCode::OK) {
+    void MockAuthRequest(bool error, HttpCode code = HttpCode::OK, bool opError = false) {
         if (error) {
             EXPECT_CALL(mock_http_client, Post_t(expected_authorization_server + "/authorize", _, expect_request_string, _, _)).
             WillOnce(
@@ -128,7 +128,7 @@ class AuthorizerHandlerTests : public Test {
                                                      HasSubstr(expected_data_source),
                                                      HasSubstr(expected_producer_uri),
                                                      HasSubstr(expected_authorization_server))));
-            } else if (expected_access_type_str == "[\"write\"]") {
+            } else if (!opError) {
                 EXPECT_CALL(mock_logger, Debug(AllOf(HasSubstr("authorized"),
                                                      HasSubstr(expected_beamtime_id),
                                                      HasSubstr(expected_beamline),
@@ -142,7 +142,7 @@ class AuthorizerHandlerTests : public Test {
 
 
     }
-    Error MockFirstAuthorization(bool error, HttpCode code = HttpCode::OK) {
+    Error MockFirstAuthorization(bool error, HttpCode code = HttpCode::OK, bool opError = false) {
         EXPECT_CALL(*mock_request, GetOpCode())
         .WillOnce(Return(asapo::kOpcodeAuthorize))
         ;
@@ -155,7 +155,7 @@ class AuthorizerHandlerTests : public Test {
         ;
 
 
-        MockAuthRequest(error, code);
+        MockAuthRequest(error, code, opError);
         return handler.ProcessRequest(mock_request.get());
     }
     Error MockRequestAuthorization(bool error, HttpCode code = HttpCode::OK, bool set_request = true) {
@@ -218,11 +218,32 @@ TEST_F(AuthorizerHandlerTests, AuthorizeOk) {
 
 TEST_F(AuthorizerHandlerTests, AuthorizeFailsOnWrongAccessType) {
     expected_access_type_str = "[\"read\"]";
-    auto err = MockFirstAuthorization(false);
+    auto err = MockFirstAuthorization(false, HttpCode::OK, true);
+
+    ASSERT_THAT(err, Eq(asapo::ReceiverErrorTemplates::kAuthorizationFailure));
+}
 
+TEST_F(AuthorizerHandlerTests, AuthorizeFailsOnWrongAccessTypeForRaw) {
+    expected_access_type_str = "[\"write\"]";
+    expected_source_type_str = "raw";
+    auto err = MockFirstAuthorization(false, HttpCode::OK, true);
     ASSERT_THAT(err, Eq(asapo::ReceiverErrorTemplates::kAuthorizationFailure));
 }
 
+TEST_F(AuthorizerHandlerTests, AuthorizeFailsOnWrongAccessTypeForProcessed) {
+    expected_access_type_str = "[\"writeraw\"]";
+    expected_source_type_str = "processed";
+    auto err = MockFirstAuthorization(false, HttpCode::OK, true);
+    ASSERT_THAT(err, Eq(asapo::ReceiverErrorTemplates::kAuthorizationFailure));
+}
+
+TEST_F(AuthorizerHandlerTests, AuthorizeOkForRaw) {
+    expected_access_type_str = "[\"writeraw\"]";
+    expected_source_type_str = "raw";
+    auto err = MockFirstAuthorization(false, HttpCode::OK, false);
+    ASSERT_THAT(err, Eq(nullptr));
+}
+
 TEST_F(AuthorizerHandlerTests, ErrorOnSecondAuthorize) {
     MockFirstAuthorization(false);
     EXPECT_CALL(*mock_request, GetOpCode())
diff --git a/tests/automatic/authorizer/check_authorize/check_linux.sh b/tests/automatic/authorizer/check_authorize/check_linux.sh
index ea92dadbc998cf9e6a37b71534d35d24287cf7c1..340c715bce4db2135a33d2015d171d6599d921c6 100644
--- a/tests/automatic/authorizer/check_authorize/check_linux.sh
+++ b/tests/automatic/authorizer/check_authorize/check_linux.sh
@@ -6,6 +6,7 @@ trap Cleanup EXIT
 
 Cleanup() {
 	echo cleanup
+  echo "db.dropDatabase()" | mongo asapo_admin
 }
 
 mkdir -p /tmp/asapo/asap3/petra3/gpfs/p00/2019/comissioning/c20180508-000-COM20181
@@ -20,8 +21,10 @@ cp beamtime-metadata-11111112.json /tmp/asapo/beamline/p08/current/
 AdminToken=$ASAPO_CREATE_TOKEN
 echo admin $AdminToken
 
-curl -v --silent -H "Authorization: Bearer $AdminToken" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessType":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr -  | tee /dev/stderr | grep "bt_12345678"
-curl -v --silent -H "Authorization: Bearer blabla" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessType":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr -  | tee /dev/stderr | grep "token does not match"
+RevokeToken=$ASAPO_REVOKE_TOKEN
+
+curl -v --silent -H "Authorization: Bearer $AdminToken" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessTypes":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr -  | tee /dev/stderr | grep "bt_12345678"
+curl -v --silent -H "Authorization: Bearer blabla" --data '{"Subject": {"beamtimeId":"12345678"},"DaysValid":123,"AccessTypes":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue --stderr -  | tee /dev/stderr | grep "token does not match"
 
 curl -v --silent --data '{"SourceCredentials":"processed%c20180508-000-COM20181%%detector%","OriginHost":"127.0.0.1:5555"}' 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep c20180508-000-COM20181
 curl -v --silent --data '{"SourceCredentials":"processed%c20180508-000-COM20181%%detector%","OriginHost":"127.0.0.1:5555"}' 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep p00
@@ -44,7 +47,8 @@ curl -v --silent --data "{\"SourceCredentials\":\"raw%11000016%%detector%${token
 token=$BLP07_TOKEN
 
 curl -v --silent --data "{\"SourceCredentials\":\"processed%auto%p07%detector%$token\",\"OriginHost\":\"bla\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep 11111111
-curl -v --silent --data "{\"SourceCredentials\":\"raw%auto%p07%detector%$token\",\"OriginHost\":\"127.0.0.1:8400/asapo-authorizer\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep 11111111
+curl -v --silent --data "{\"SourceCredentials\":\"raw%auto%p07%detector%\",\"OriginHost\":\"127.0.0.1:8400/asapo-authorizer\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep writeraw
+! curl -v --silent --data "{\"SourceCredentials\":\"raw%auto%p07%detector%$token\",\"OriginHost\":\"127.0.0.1:8400/asapo-authorizer\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  |  grep writeraw
 curl -v --silent --data "{\"SourceCredentials\":\"raw%auto%p07%detector%$token\",\"OriginHost\":\"127.0.0.1:8400/asapo-authorizer\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep p07
 curl -v --silent --data "{\"SourceCredentials\":\"raw%auto%p07%detector%$token\",\"OriginHost\":\"127.0.0.1:8400/asapo-authorizer\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep /asap3/petra3/gpfs/p07/2020/data/11111111
 
@@ -58,4 +62,18 @@ curl -v --silent --data "{\"SourceCredentials\":\"processed%auto%p07%detector%$t
 token=$BLP07_W_TOKEN
 curl -v --silent --data "{\"SourceCredentials\":\"processed%auto%p07%detector%$token\",\"OriginHost\":\"bla\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep write
 
+
+#revocation
+token=`curl --silent -H "Authorization: Bearer $AdminToken" --data '{"Subject": {"beamtimeId":"11000015"},"DaysValid":123,"AccessTypes":["read"]}' 127.0.0.1:8400/asapo-authorizer/admin/issue | jq -r .Token`
+echo $token
+
+curl -v --silent --data "{\"SourceCredentials\":\"processed%11000015%auto%detector%$token\",\"OriginHost\":\"bla\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep p00
+
+#revoke token
+curl -v --silent -H "Authorization: Bearer $RevokeToken" --data '{"Token": "'"$token"'"}' 127.0.0.1:8400/asapo-authorizer/admin/revoke | grep '"Revoked":true'
+
+sleep 1
+
+curl -v --silent --data "{\"SourceCredentials\":\"processed%11000015%auto%detector%$token\",\"OriginHost\":\"bla\"}" 127.0.0.1:8400/asapo-authorizer/authorize --stderr -  | tee /dev/stderr  | grep 401
+
 rm -rf /tmp/asapo/asap3 /tmp/asapo/beamline
\ No newline at end of file
diff --git a/tests/automatic/common_scripts/start_services.bat b/tests/automatic/common_scripts/start_services.bat
index 87f16087a311774bc134cc6092dd9a69ebabca8f..459fa99a075ae4bc19e2a6cf155524330576be90 100644
--- a/tests/automatic/common_scripts/start_services.bat
+++ b/tests/automatic/common_scripts/start_services.bat
@@ -1,10 +1,10 @@
 c:\opt\consul\nomad run nginx.nmd
 c:\opt\consul\nomad run discovery.nmd
-c:\opt\consul\nomad run authorizer.nmd
 
 ping 192.0.2.1 -n 1 -w 3000 > nul
 
 set i=0
+set started=0
 :repeat
 set /a i=%i%+1
 echo %i%
@@ -13,10 +13,12 @@ if %i% EQU 20 (
 )
 ping 192.0.2.1 -n 1 -w 1000 >nul
 curl --silent --fail 127.0.0.1:8400/asapo-discovery/asapo-mongodb --stderr - | findstr 127.0.0.1  || goto :repeat
-if %i% EQU 1 (
+if %started% EQU 0 (
+    c:\opt\consul\nomad run authorizer.nmd
     c:\opt\consul\nomad run receiver_tcp.nmd
     c:\opt\consul\nomad run broker.nmd
     c:\opt\consul\nomad run file_transfer.nmd
+    set started=1
     ping 192.0.2.1 -n 1 -w 3000 > nul
 )
 curl --silent --fail 127.0.0.1:8400/asapo-discovery/v0.1/asapo-receiver?protocol=v0.1 --stderr - | findstr 127.0.0.1  || goto :repeat
diff --git a/tests/automatic/common_scripts/start_services.sh b/tests/automatic/common_scripts/start_services.sh
index 5c3f70a7138f9297d572559728bfbaf8c21ded42..944f3ed0bfdf42d0d4bab7ea7c736770202dbbfe 100755
--- a/tests/automatic/common_scripts/start_services.sh
+++ b/tests/automatic/common_scripts/start_services.sh
@@ -1,4 +1,3 @@
-nomad run authorizer.nmd
 nomad run discovery.nmd
 nomad run nginx.nmd
 
@@ -11,7 +10,7 @@ do
   break
 done
 
-
+nomad run authorizer.nmd
 nomad run file_transfer.nmd
 nomad run receiver_tcp.nmd
 nomad run broker.nmd
diff --git a/tests/automatic/high_avail/broker_mongo_restart/check_linux.sh b/tests/automatic/high_avail/broker_mongo_restart/check_linux.sh
index 2e40ee88eaeb8155fd9de4e11c5bd7557ab84f41..fc8a1a4869bfab4e436535145bbd3d6249c59d0b 100755
--- a/tests/automatic/high_avail/broker_mongo_restart/check_linux.sh
+++ b/tests/automatic/high_avail/broker_mongo_restart/check_linux.sh
@@ -82,7 +82,7 @@ producerid=`echo $!`
 wait
 
 echo "Start consumer in $network_type mode"
-$consumer_bin ${proxy_address} ${receiver_folder} ${beamtime_id} 2 $token 10000 0 &> output.txt &
+$consumer_bin ${proxy_address} ${receiver_folder} ${beamtime_id} 2 $token 50000 0 &> output.txt &
 workerid=`echo $!`
 
 sleep 2
diff --git a/tests/automatic/high_avail/services_restart/check_linux.sh b/tests/automatic/high_avail/services_restart/check_linux.sh
index 7043c1ac971b7c8dac5b21cd1b8b3bc02a384a8c..e3260de4c2d00bda08cb724e69af0e19ea69157e 100644
--- a/tests/automatic/high_avail/services_restart/check_linux.sh
+++ b/tests/automatic/high_avail/services_restart/check_linux.sh
@@ -25,6 +25,7 @@ Cleanup() {
     echo cleanup
     rm -rf ${receiver_folder}
     echo "db.dropDatabase()" | mongo ${beamtime_id}_detector
+    set +e
     influx -execute "drop database ${monitor_database_name}"
 }
 
diff --git a/tests/automatic/settings/authorizer_settings.json.tpl.lin b/tests/automatic/settings/authorizer_settings.json.tpl.lin
index 0980b029ab5f75ff7c2fc7fc4c6a735eb763ad40..dd30b9d3c0a6f7361f55b2a700cd39b37e5b3b67 100644
--- a/tests/automatic/settings/authorizer_settings.json.tpl.lin
+++ b/tests/automatic/settings/authorizer_settings.json.tpl.lin
@@ -15,5 +15,8 @@
      "Uri" : "ldap://localhost:389",
      "BaseDn" : "ou=rgy,o=desy,c=de",
      "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)"
-  }
+  },
+  "DatabaseServer":"auto",
+  "DiscoveryServer": "localhost:8400/asapo-discovery",
+  "UpdateRevokedTokensIntervalSec": 60
 }
diff --git a/tests/automatic/settings/authorizer_settings.json.tpl.win b/tests/automatic/settings/authorizer_settings.json.tpl.win
index a34aeee04de2331356421a45113a7717f9ffb798..dff507941808259b140f48ad9ec816b643c57bb5 100644
--- a/tests/automatic/settings/authorizer_settings.json.tpl.win
+++ b/tests/automatic/settings/authorizer_settings.json.tpl.win
@@ -15,5 +15,8 @@
      "Uri" : "ldap://localhost:389",
      "BaseDn" : "ou=rgy,o=desy,c=de",
      "FilterTemplate" : "(cn=a3__BEAMLINE__-hosts)"
-  }
+  },
+  "DatabaseServer":"auto",
+  "DiscoveryServer": "localhost:8400/asapo-discovery",
+  "UpdateRevokedTokensIntervalSec": 60
 }