diff --git a/.gitignore b/.gitignore
index 1dcbc1b245a61925f2a7879ea2e89510e6faaf92..32dbba481b82ad0ce03caea4c4fc4e99270a13aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,4 +125,4 @@ broker/pkg
 discovery/pkg
 common/go/pkg
 authorizer/pkg
-
+asapo_tools/pkg
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cd1e06fd386d49ccc06236e87a8bf592ee36d640..31dc962d885da0880e1b54fdfaabf7655074bfbc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -70,6 +70,8 @@ add_subdirectory(discovery)
 
 add_subdirectory(authorizer)
 
+add_subdirectory(asapo_tools)
+
 
 if(BUILD_INTEGRATION_TESTS)
     add_subdirectory(tests)
diff --git a/asapo_tools/CMakeLists.txt b/asapo_tools/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2a517df18b3ffb1417721dd797003b0fabb53fbf
--- /dev/null
+++ b/asapo_tools/CMakeLists.txt
@@ -0,0 +1,36 @@
+set (TARGET_NAME asapo)
+
+if (NOT "$ENV{GOPATH}" STREQUAL "")
+	set(GOPATH $ENV{GOPATH})
+endif()
+
+if (NOT GOPATH)
+    message (FATAL_ERROR "GOPATH not set")
+endif()
+
+message(STATUS "global gopath ${GOPATH}")
+
+IF(WIN32)
+    set (gopath "${GOPATH}\;${CMAKE_CURRENT_SOURCE_DIR}\;${CMAKE_SOURCE_DIR}/common/go")
+    set (exe_name "${TARGET_NAME}.exe")
+ELSE()
+    set (gopath ${GOPATH}:${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/common/go)
+    set (exe_name "${TARGET_NAME}")
+ENDIF()
+
+include(testing_go)
+
+add_custom_target(asapo ALL
+    COMMAND  ${CMAKE_COMMAND} -E env GOPATH=${gopath}
+    go build ${GO_OPTS} -o ${exe_name} asapo_tools/main
+    VERBATIM)
+define_property(TARGET PROPERTY EXENAME
+        BRIEF_DOCS <executable name>
+        FULL_DOCS <full-doc>)
+
+set_target_properties(${TARGET_NAME} PROPERTIES EXENAME ${CMAKE_CURRENT_BINARY_DIR}/${exe_name})
+
+
+install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${exe_name} DESTINATION bin)
+
+gotest(${TARGET_NAME} "./...")
diff --git a/asapo_tools/src/asapo_tools/cli/cli.go b/asapo_tools/src/asapo_tools/cli/cli.go
new file mode 100644
index 0000000000000000000000000000000000000000..031d25285cc96bae4d6dca8067cb2c4e8fd87728
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/cli/cli.go
@@ -0,0 +1,59 @@
+// Package contains asapo commands that can be executed from command line.
+// Every CommandXxxx function that is a member of a cmd struct processes asapo xxxx command
+package cli
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+	"strings"
+)
+
+var flHelp bool
+
+var outBuf io.Writer = os.Stdout
+
+func printHelp(f *flag.FlagSet) bool {
+	if flHelp {
+		f.Usage()
+		return true
+	} else {
+		return false
+	}
+}
+
+// DoCommand takes command name as a parameter and executes corresponding to this name cmd method
+func DoCommand(name string, args []string) error {
+	commandName := "Command" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
+	cmd := new(command)
+
+	methodVal := reflect.ValueOf(cmd).MethodByName(commandName)
+	if !methodVal.IsValid() {
+		return errors.New("wrong asapo command: " + name + "\nType 'asapo --help'")
+	}
+	cmd.name = name
+	cmd.args = args
+
+	method := methodVal.Interface().(func() error)
+
+	return method()
+}
+
+// PrintAllCommands prints all available commands (found wihtin methods of cmd)
+func PrintAllCommands() {
+	fmt.Fprintln(outBuf, "\nCommands:")
+	cmd := new(command)
+	CmdType := reflect.TypeOf(cmd)
+	for i := 0; i < CmdType.NumMethod(); i++ {
+		methodVal := CmdType.Method(i)
+		if strings.HasPrefix(methodVal.Name, "Command") {
+			method := methodVal.Func.Interface().(func(*command) error)
+			cmd.name = strings.ToLower(methodVal.Name)[7:]
+			cmd.args = []string{"description"}
+			method(cmd)
+		}
+	}
+}
diff --git a/asapo_tools/src/asapo_tools/cli/command.go b/asapo_tools/src/asapo_tools/cli/command.go
new file mode 100644
index 0000000000000000000000000000000000000000..b61171a1a94dc23e97eebd89f41d7d8bd635198b
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/cli/command.go
@@ -0,0 +1,40 @@
+package cli
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+)
+
+// A command consists of a command name and arguments, passed to this command (all after asapo name ...)
+type command struct {
+	name string
+	args []string
+}
+
+// description prints description line and returns true if first command argument is "description".
+func (cmd *command) description(d string) bool {
+	if len(cmd.args) == 1 && cmd.args[0] == "description" {
+		fmt.Fprintf(outBuf, "   %-10s %s\n", cmd.name, d)
+		return true
+	}
+	return false
+}
+
+func (cmd *command) errBadOptions(err string) error {
+	return errors.New("asapo " + cmd.name + ": " + err + "\nType 'asapo " + cmd.name + " --help'")
+}
+
+// createDefaultFlagset creates new flagset and adds default help behaviour.
+func (cmd *command) createDefaultFlagset(description, args string) *flag.FlagSet {
+
+	flags := flag.NewFlagSet(cmd.name, flag.ExitOnError)
+	flags.BoolVar(&flHelp, "help", false, "Print usage")
+	flags.Usage = func() {
+		fmt.Fprintf(outBuf, "Usage:\t\nasapo %s "+args, cmd.name)
+		fmt.Fprintf(outBuf, "\n\n%s\n", description)
+		flags.PrintDefaults()
+	}
+
+	return flags
+}
diff --git a/asapo_tools/src/asapo_tools/cli/command_test.go b/asapo_tools/src/asapo_tools/cli/command_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c4e89f2239f69728ba190d4db6d7aa59011550b0
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/cli/command_test.go
@@ -0,0 +1,34 @@
+package cli
+
+import (
+	"bytes"
+	"testing"
+	"github.com/stretchr/testify/assert"
+)
+
+var CommandTests = []struct {
+	cmd    command
+	answer string
+}{
+	{command{"token", []string{"-secret", "secret_file", "beamtime"}}, "secret"},
+	{command{"dummy", []string{"description"}}, "wrong"},
+}
+
+func TestCommand(t *testing.T) {
+	outBuf = new(bytes.Buffer)
+
+	for _, test := range CommandTests {
+		outBuf.(*bytes.Buffer).Reset()
+		err := DoCommand(test.cmd.name, test.cmd.args)
+		assert.Contains(t, err.Error(), test.answer, "")
+		assert.NotNil(t, err, "Should be error")
+
+	}
+
+}
+
+func TestPrintAllCommands(t *testing.T) {
+	outBuf = new(bytes.Buffer)
+	PrintAllCommands()
+	assert.Contains(t, outBuf.(*bytes.Buffer).String(), "token", "all commands must have token")
+}
diff --git a/asapo_tools/src/asapo_tools/cli/token.go b/asapo_tools/src/asapo_tools/cli/token.go
new file mode 100644
index 0000000000000000000000000000000000000000..3f0ecfe7ccbd5b53d5a296e125058e5bd2c3e3fb
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/cli/token.go
@@ -0,0 +1,83 @@
+package cli
+
+import (
+	"errors"
+	"os"
+	"fmt"
+	"asapo_common/utils"
+)
+
+type tokenFlags struct {
+	BeamtimeID string
+	SecretFile string
+}
+
+func generateToken(id string,secret string) string {
+	jwt := utils.NewJWTAuth(secret)
+	load := utils.JobClaim{id}
+
+	var c utils.CustomClaims
+	c.ExtraClaims = &load
+
+	token,err := jwt.GenerateToken(&c)
+
+	if (err!=nil) {
+		fmt.Println(err.Error())
+	}
+	return token
+}
+
+
+// GenerateToken generates token for workers
+func (cmd *command) CommandToken() error {
+
+	message_string := "Generate token"
+
+	if cmd.description(message_string) {
+		return nil
+	}
+
+	flags, err := cmd.parseTokenFlags(message_string)
+	if err != nil {
+		return err
+	}
+
+	strings, err := utils.ReadStringsFromFile(flags.SecretFile)
+	if err !=nil  {
+		return err
+	}
+
+
+
+	fmt.Fprintf(outBuf, "%s\n", generateToken(flags.BeamtimeID,strings[0]))
+
+	return nil
+}
+
+
+func (cmd *command) parseTokenFlags(message_string string) (tokenFlags, error) {
+
+	var flags tokenFlags
+	flagset := cmd.createDefaultFlagset(message_string, "<beamtime id>")
+	flagset.StringVar(&flags.SecretFile, "secret", "", "path to file with secret")
+
+	flagset.Parse(cmd.args)
+
+	if printHelp(flagset) {
+		os.Exit(0)
+	}
+
+	flags.BeamtimeID = flagset.Arg(0)
+
+	if flags.BeamtimeID == "" {
+		return flags, errors.New("beamtime id missed ")
+	}
+
+	if flags.SecretFile == "" {
+		return flags, errors.New("secret file missed ")
+	}
+
+
+	return flags, nil
+
+}
diff --git a/asapo_tools/src/asapo_tools/cli/token_test.go b/asapo_tools/src/asapo_tools/cli/token_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4149f9ae48b1b19b7110eff8c1ada26e955fac36
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/cli/token_test.go
@@ -0,0 +1,39 @@
+package cli
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"bytes"
+	"io/ioutil"
+	"os"
+)
+
+var tokenTests = []struct {
+	cmd      command
+	answer string
+	msg  string
+}{
+	{command{args: []string{"beamtime_id"}},  "secret", "no secret parameter"},
+	{command{args: []string{"-secret","secret.tmp"}},  "beamtime id", "no file"},
+	{command{args: []string{"-secret","not_existing_file","beamtime_id"}},  "not_existing_file", "no file"},
+	{command{args: []string{"-secret","secret.tmp","beamtime_id"}},  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
+		"eyJFeHRyYUNsYWltcyI6eyJCZWFtdGltZUlkIjoiYmVhbXRpbWVfaWQifX0.OEYaJfOL6-0yY7B7zW73l372ZnR9GF2IUYMLuDQtmSo", "no file"},
+}
+
+func TestParseTokenFlags(t *testing.T) {
+
+	ioutil.WriteFile("secret.tmp", []byte("secret"), 0644)
+	outBuf = new(bytes.Buffer)
+	for _, test := range tokenTests {
+		err := test.cmd.CommandToken()
+		if err == nil {
+			assert.Contains(t, outBuf.(*bytes.Buffer).String(), test.answer, test.msg)
+		} else {
+			assert.Contains(t, err.Error(), test.answer, test.msg)
+		}
+
+	}
+	os.Remove("secret.tmp")
+
+}
diff --git a/asapo_tools/src/asapo_tools/main/asapo.go b/asapo_tools/src/asapo_tools/main/asapo.go
new file mode 100644
index 0000000000000000000000000000000000000000..e326889315ffd4f4c324a83bc646b79aa09acb92
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/main/asapo.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"asapo_tools/version"
+	"asapo_tools/cli"
+)
+
+var (
+	flHelp = flag.Bool("help", false, "Print usage")
+)
+
+func main() {
+
+	if ret := version.ShowVersion(os.Stdout, "asapo"); ret {
+		return
+	}
+
+	flag.Parse()
+
+	if *flHelp || flag.NArg() == 0 {
+		flag.Usage()
+		cli.PrintAllCommands()
+		return
+	}
+
+		if err := cli.DoCommand(flag.Arg(0), flag.Args()[1:]); err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+}
diff --git a/asapo_tools/src/asapo_tools/version/version.go b/asapo_tools/src/asapo_tools/version/version.go
new file mode 100644
index 0000000000000000000000000000000000000000..e2427a17c6038e422fb6c7b102562dd254043f33
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/version/version.go
@@ -0,0 +1,23 @@
+package version
+
+import (
+	"flag"
+	"fmt"
+	"io"
+	"os"
+)
+
+var version, buildTime, gitCommit, shortVersion string
+
+func ShowVersion(w io.Writer, name string) bool {
+	flags := flag.NewFlagSet("version", flag.ExitOnError)
+	flag.Bool("version", false, "Print version information") // to have it in main help
+	flVersion := flags.Bool("version", false, "Print version information")
+	flags.Bool("help", false, "Print usage") // define help flag but ignore it
+	flags.Parse(os.Args[1:])
+	if *flVersion {
+		fmt.Fprintf(w, "%s version %s, build time %s\n", name, version, buildTime)
+		return true
+	}
+	return false
+}
diff --git a/asapo_tools/src/asapo_tools/version/version_lib.go.in b/asapo_tools/src/asapo_tools/version/version_lib.go.in
new file mode 100644
index 0000000000000000000000000000000000000000..3fe1989dbbc62d3515284e43bd2c3e415e874471
--- /dev/null
+++ b/asapo_tools/src/asapo_tools/version/version_lib.go.in
@@ -0,0 +1,10 @@
+package version
+
+// Default build-time variable for library-import.
+// This file is overridden on build with build-time informations.
+func init(){
+	gitCommit = "@VERSION_SHA1@"
+	version   = "@VERSION@"
+	shortVersion   = "@VERSION_SHORT@"
+	buildTime = "@TIMESTAMP@"
+}
diff --git a/common/go/src/asapo_common/utils/authorization.go b/common/go/src/asapo_common/utils/authorization.go
new file mode 100644
index 0000000000000000000000000000000000000000..ff8f69d210c170bcad7bbf8add02421e110a7843
--- /dev/null
+++ b/common/go/src/asapo_common/utils/authorization.go
@@ -0,0 +1,172 @@
+package utils
+
+import (
+	"errors"
+	"net/http"
+	"net/url"
+	"strings"
+	"context"
+	"github.com/dgrijalva/jwt-go"
+)
+
+type AuthorizationRequest struct {
+	Token   string
+	Command string
+	URL     string
+}
+
+type AuthorizationResponce struct {
+	Status       int
+	StatusText   string
+	UserName     string
+	Token        string
+	ValidityTime int
+}
+
+type Auth interface {
+	GenerateToken(...interface{}) (string, error)
+	Name() string
+}
+
+
+func (a *JWTAuth) Name() string {
+	return "Bearer"
+}
+
+
+func stripURL(u *url.URL) string {
+	s := u.Path + u.RawQuery
+	s = strings.Replace(s, "/", "", -1)
+	s = strings.Replace(s, "?", "", -1)
+	return s
+
+}
+
+func SplitAuthToken(s string) (authType, token string, err error) {
+	keys := strings.Split(s, " ")
+
+	if len(keys) != 2 {
+		err = errors.New("authorization error - wrong token")
+		return
+	}
+
+	authType = keys[0]
+	token = keys[1]
+	return
+}
+
+func ExtractAuthInfo(r *http.Request) (authType, token string, err error) {
+
+	t := r.Header.Get("Authorization")
+
+	if t != "" {
+		return SplitAuthToken(t)
+	}
+
+	cookie, err := r.Cookie("Authorization")
+	if err == nil {
+		return SplitAuthToken(cookie.Value)
+	}
+
+	err = errors.New("no authorization info")
+	return
+
+}
+
+type CustomClaims struct {
+	jwt.StandardClaims
+	ExtraClaims interface{}
+}
+
+type JobClaim struct {
+	BeamtimeId string
+}
+
+type JWTAuth struct {
+	Key string
+}
+
+func NewJWTAuth(key string) *JWTAuth {
+	a := JWTAuth{key}
+	return &a
+}
+
+func (t JWTAuth) GenerateToken(val ...interface{}) (string, error) {
+	if len(val) != 1 {
+		return "", errors.New("No claims")
+	}
+	claims, ok := val[0].(*CustomClaims)
+	if !ok {
+		return "", errors.New("Wrong claims")
+	}
+
+//	if claims.Duration > 0 {
+//		claims.ExpiresAt = time.Now().Add(claims.Duration).Unix()
+//	}
+
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	tokenString, err := token.SignedString([]byte(t.Key))
+
+	if err != nil {
+		return "", err
+	}
+
+	return tokenString, nil
+}
+
+func ProcessJWTAuth(fn http.HandlerFunc, key string) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+
+		authType, token, err := ExtractAuthInfo(r)
+
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusUnauthorized)
+			return
+		}
+
+		ctx := r.Context()
+
+		if authType == "Bearer" {
+			if claims, ok := CheckJWTToken(token, key); !ok {
+				http.Error(w, "Internal authorization error - tocken does not match", http.StatusUnauthorized)
+				return
+			} else {
+				ctx = context.WithValue(ctx, "JobClaim", claims)
+			}
+		} else {
+			http.Error(w, "Internal authorization error - wrong auth type", http.StatusUnauthorized)
+			return
+		}
+		fn(w, r.WithContext(ctx))
+	}
+}
+
+func CheckJWTToken(token, key string) (jwt.Claims, bool) {
+
+	if token == "" {
+		return nil, false
+	}
+
+	t, err := jwt.ParseWithClaims(token, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
+		return []byte(key), nil
+	})
+
+	if err == nil && t.Valid {
+		return t.Claims, true
+	}
+
+	return nil, false
+}
+
+func JobClaimFromContext(r *http.Request, val interface{}) error {
+	c := r.Context().Value("JobClaim")
+
+	if c == nil {
+		return errors.New("Empty context")
+	}
+
+	claim := c.(*CustomClaims)
+
+	return MapToStruct(claim.ExtraClaims.(map[string]interface{}), val)
+}
+
diff --git a/common/go/src/asapo_common/utils/helpers.go b/common/go/src/asapo_common/utils/helpers.go
index 9f02ac4263ae8f69f0839bb3c204f2374bce4415..72b7afd835eacde88be72bbd9c370dbf47869c92 100644
--- a/common/go/src/asapo_common/utils/helpers.go
+++ b/common/go/src/asapo_common/utils/helpers.go
@@ -47,4 +47,16 @@ func ReadStringsFromFile(fname string) ([]string, error) {
 	lines := strings.Split(string(content), "\n")
 
 	return lines,nil
-}
\ No newline at end of file
+}
+
+func MapToStruct(m map[string]interface{}, val interface{}) error {
+	tmp, err := json.Marshal(m)
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(tmp, val)
+	if err != nil {
+		return err
+	}
+	return nil
+}