diff --git a/bridge/core/bridge.go b/bridge/core/bridge.go
index a3133b9ce35f168f1c7f21777a5bb32780fead4a..3a36dfaa8a0dcc10ca7d05d13e0af6bec76c0e6a 100644
--- a/bridge/core/bridge.go
+++ b/bridge/core/bridge.go
@@ -13,6 +13,7 @@ import (
 	"github.com/pkg/errors"
 
 	"github.com/MichaelMure/git-bug/cache"
+	"github.com/MichaelMure/git-bug/entity"
 	"github.com/MichaelMure/git-bug/repository"
 )
 
@@ -20,8 +21,10 @@ var ErrImportNotSupported = errors.New("import is not supported")
 var ErrExportNotSupported = errors.New("export is not supported")
 
 const (
-	ConfigKeyTarget = "target"
-	MetaKeyOrigin   = "origin"
+	ConfigKeyTarget  = "target"
+	ConfigKeyToken   = "token"
+	ConfigKeyTokenId = "token-id"
+	MetaKeyOrigin    = "origin"
 
 	bridgeConfigKeyPrefix = "git-bug.bridge"
 )
@@ -35,6 +38,7 @@ type BridgeParams struct {
 	Project    string
 	URL        string
 	Token      string
+	TokenId    string
 	TokenStdin bool
 }
 
@@ -276,6 +280,13 @@ func (b *Bridge) ensureInit() error {
 		return nil
 	}
 
+	token, err := LoadToken(b.repo, entity.Id(b.conf[ConfigKeyTokenId]))
+	if err != nil {
+		return err
+	}
+
+	b.conf[ConfigKeyToken] = token.Value
+
 	importer := b.getImporter()
 	if importer != nil {
 		err := importer.Init(b.conf)
diff --git a/bridge/core/token.go b/bridge/core/token.go
index 449ebbb56cf0ed182f47ce70a280790358be3f8c..28c64f5c4f9ea5edc7fee5fb8f7d01463e58923a 100644
--- a/bridge/core/token.go
+++ b/bridge/core/token.go
@@ -122,8 +122,7 @@ func LoadTokenPrefix(repo repository.RepoCommon, prefix string) (*Token, error)
 	return LoadToken(repo, matching[0])
 }
 
-// ListTokens return a map representing the stored tokens in the repo config and global config
-// along with their type (global: true, local:false)
+// ListTokens list all existing token ids
 func ListTokens(repo repository.RepoCommon) ([]entity.Id, error) {
 	configs, err := repo.GlobalConfig().ReadAll(tokenConfigKeyPrefix + ".")
 	if err != nil {
@@ -157,6 +156,99 @@ func ListTokens(repo repository.RepoCommon) ([]entity.Id, error) {
 	return result, nil
 }
 
+// ListTokensWithTarget list all token ids associated with the target
+func ListTokensWithTarget(repo repository.RepoCommon, target string) ([]entity.Id, error) {
+	var ids []entity.Id
+	tokensIds, err := ListTokens(repo)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, tokenId := range tokensIds {
+		token, err := LoadToken(repo, tokenId)
+		if err != nil {
+			return nil, err
+		}
+
+		if token.Target == target {
+			ids = append(ids, tokenId)
+		}
+	}
+	return ids, nil
+}
+
+// LoadTokens load all existing tokens
+func LoadTokens(repo repository.RepoCommon) ([]*Token, error) {
+	tokensIds, err := ListTokens(repo)
+	if err != nil {
+		return nil, err
+	}
+
+	var tokens []*Token
+	for _, id := range tokensIds {
+		token, err := LoadToken(repo, id)
+		if err != nil {
+			return nil, err
+		}
+		tokens = append(tokens, token)
+	}
+	return tokens, nil
+}
+
+// LoadTokensWithTarget load all existing tokens for a given target
+func LoadTokensWithTarget(repo repository.RepoCommon, target string) ([]*Token, error) {
+	tokensIds, err := ListTokens(repo)
+	if err != nil {
+		return nil, err
+	}
+
+	var tokens []*Token
+	for _, id := range tokensIds {
+		token, err := LoadToken(repo, id)
+		if err != nil {
+			return nil, err
+		}
+		if token.Target == target {
+			tokens = append(tokens, token)
+		}
+	}
+	return tokens, nil
+}
+
+// TokenIdExist return wether token id exist or not
+func TokenIdExist(repo repository.RepoCommon, id entity.Id) bool {
+	_, err := LoadToken(repo, id)
+	return err == nil
+}
+
+// TokenExist return wether there is a token with a certain value or not
+func TokenExist(repo repository.RepoCommon, value string) bool {
+	tokens, err := LoadTokens(repo)
+	if err != nil {
+		return false
+	}
+	for _, token := range tokens {
+		if token.Value == value {
+			return true
+		}
+	}
+	return false
+}
+
+// TokenExistWithTarget same as TokenExist but restrict search for a given target
+func TokenExistWithTarget(repo repository.RepoCommon, value string, target string) bool {
+	tokens, err := LoadTokensWithTarget(repo, target)
+	if err != nil {
+		return false
+	}
+	for _, token := range tokens {
+		if token.Value == value {
+			return true
+		}
+	}
+	return false
+}
+
 // StoreToken stores a token in the repo config
 func StoreToken(repo repository.RepoCommon, token *Token) error {
 	storeValueKey := fmt.Sprintf("git-bug.token.%s.%s", token.ID().String(), tokenValueKey)
@@ -180,3 +272,25 @@ func RemoveToken(repo repository.RepoCommon, id entity.Id) error {
 	keyPrefix := fmt.Sprintf("git-bug.token.%s", id)
 	return repo.GlobalConfig().RemoveAll(keyPrefix)
 }
+
+// LoadOrCreateToken will try to load a token matching the same value or create it
+func LoadOrCreateToken(repo repository.RepoCommon, target, tokenValue string) (*Token, error) {
+	tokens, err := LoadTokensWithTarget(repo, target)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, token := range tokens {
+		if token.Value == tokenValue {
+			return token, nil
+		}
+	}
+
+	token := NewToken(tokenValue, target)
+	err = StoreToken(repo, token)
+	if err != nil {
+		return nil, err
+	}
+
+	return token, nil
+}
diff --git a/bridge/github/config.go b/bridge/github/config.go
index e76a14f4e19afe98dc473bd73193310068b17d97..0fbbd5aa7bc5e8df5a4b348973757dd83fe113a6 100644
--- a/bridge/github/config.go
+++ b/bridge/github/config.go
@@ -21,6 +21,7 @@ import (
 	"golang.org/x/crypto/ssh/terminal"
 
 	"github.com/MichaelMure/git-bug/bridge/core"
+	"github.com/MichaelMure/git-bug/entity"
 	"github.com/MichaelMure/git-bug/repository"
 	"github.com/MichaelMure/git-bug/util/interrupt"
 )
@@ -43,10 +44,12 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 	conf := make(core.Configuration)
 	var err error
 	var token string
+	var tokenId entity.Id
+	var tokenObj *core.Token
 	var owner string
 	var project string
 
-	if (params.Token != "" || params.TokenStdin) &&
+	if (params.Token != "" || params.TokenId != "" || params.TokenStdin) &&
 		(params.URL == "" && (params.Project == "" || params.Owner == "")) {
 		return nil, fmt.Errorf("you must provide a project URL or Owner/Name to configure this bridge with a token")
 	}
@@ -87,11 +90,11 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 		return nil, fmt.Errorf("invalid parameter owner: %v", owner)
 	}
 
-	// try to get token from params if provided, else use terminal prompt to either
-	// enter a token or login and generate a new one
+	// try to get token from params if provided, else use terminal prompt
+	// to either enter a token or login and generate a new one, or choose
+	// an existing token
 	if params.Token != "" {
 		token = params.Token
-
 	} else if params.TokenStdin {
 		reader := bufio.NewReader(os.Stdin)
 		token, err = reader.ReadString('\n')
@@ -99,15 +102,33 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 			return nil, fmt.Errorf("reading from stdin: %v", err)
 		}
 		token = strings.TrimSuffix(token, "\n")
+	} else if params.TokenId != "" {
+		tokenId = entity.Id(params.TokenId)
 	} else {
-		token, err = promptTokenOptions(owner, project)
+		tokenObj, err = promptTokenOptions(repo, owner, project)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// at this point, we check if the token already exist or we create a new one
+	if token != "" {
+		tokenObj, err = core.LoadOrCreateToken(repo, target, token)
 		if err != nil {
 			return nil, err
 		}
+	} else if tokenId != "" {
+		tokenObj, err = core.LoadToken(repo, entity.Id(tokenId))
+		if err != nil {
+			return nil, err
+		}
+		if tokenObj.Target != target {
+			return nil, fmt.Errorf("token target is incompatible %s", tokenObj.Target)
+		}
 	}
 
 	// verify access to the repository with token
-	ok, err = validateProject(owner, project, token)
+	ok, err = validateProject(owner, project, tokenObj.Value)
 	if err != nil {
 		return nil, err
 	}
@@ -116,7 +137,7 @@ func (g *Github) Configure(repo repository.RepoCommon, params core.BridgeParams)
 	}
 
 	conf[core.ConfigKeyTarget] = target
-	conf[keyToken] = token
+	conf[core.ConfigKeyTokenId] = tokenObj.ID().String()
 	conf[keyOwner] = owner
 	conf[keyProject] = project
 
@@ -135,8 +156,8 @@ func (*Github) ValidateConfig(conf core.Configuration) error {
 		return fmt.Errorf("unexpected target name: %v", v)
 	}
 
-	if _, ok := conf[keyToken]; !ok {
-		return fmt.Errorf("missing %s key", keyToken)
+	if _, ok := conf[core.ConfigKeyTokenId]; !ok {
+		return fmt.Errorf("missing %s key", core.ConfigKeyTokenId)
 	}
 
 	if _, ok := conf[keyOwner]; !ok {
@@ -220,32 +241,58 @@ func randomFingerprint() string {
 	return string(b)
 }
 
-func promptTokenOptions(owner, project string) (string, error) {
+func promptTokenOptions(repo repository.RepoCommon, owner, project string) (*core.Token, error) {
 	for {
+		tokens, err := core.LoadTokensWithTarget(repo, target)
+		if err != nil {
+			return nil, err
+		}
+
 		fmt.Println()
 		fmt.Println("[1]: user provided token")
 		fmt.Println("[2]: interactive token creation")
+
+		if len(tokens) > 0 {
+			fmt.Println("known tokens for Github:")
+			for i, token := range tokens {
+				if token.Target == target {
+					fmt.Printf("[%d]: %s\n", i+3, token.ID())
+				}
+			}
+		}
 		fmt.Print("Select option: ")
 
 		line, err := bufio.NewReader(os.Stdin).ReadString('\n')
 		fmt.Println()
 		if err != nil {
-			return "", err
+			return nil, err
 		}
 
 		line = strings.TrimRight(line, "\n")
 
 		index, err := strconv.Atoi(line)
-		if err != nil || (index != 1 && index != 2) {
+		if err != nil || index < 1 || index > len(tokens)+2 {
 			fmt.Println("invalid input")
 			continue
 		}
 
-		if index == 1 {
-			return promptToken()
+		var token string
+		switch index {
+		case 1:
+			token, err = promptToken()
+			if err != nil {
+				return nil, err
+			}
+		case 2:
+			token, err = loginAndRequestToken(owner, project)
+			if err != nil {
+				return nil, err
+			}
+		default:
+			return tokens[index-3], nil
 		}
 
-		return loginAndRequestToken(owner, project)
+		return core.LoadOrCreateToken(repo, target, token)
 	}
 }
 
diff --git a/bridge/github/export.go b/bridge/github/export.go
index 2fb92636cbcecc0a5cd7d07d7f47c38766f22518..8d515802d39aa2bde18648510ef85cba28a58184 100644
--- a/bridge/github/export.go
+++ b/bridge/github/export.go
@@ -87,14 +87,14 @@ func (ge *githubExporter) ExportAll(ctx context.Context, repo *cache.RepoCache,
 		return nil, err
 	}
 
-	ge.identityToken[user.Id()] = ge.conf[keyToken]
+	ge.identityToken[user.Id()] = ge.conf[core.ConfigKeyToken]
 
 	// get repository node id
 	ge.repositoryID, err = getRepositoryNodeID(
 		ctx,
 		ge.conf[keyOwner],
 		ge.conf[keyProject],
-		ge.conf[keyToken],
+		ge.conf[core.ConfigKeyToken],
 	)
 
 	if err != nil {
@@ -512,7 +512,7 @@ func (ge *githubExporter) createGithubLabel(ctx context.Context, label, color st
 	req = req.WithContext(ctx)
 
 	// need the token for private repositories
-	req.Header.Set("Authorization", fmt.Sprintf("token %s", ge.conf[keyToken]))
+	req.Header.Set("Authorization", fmt.Sprintf("token %s", ge.conf[core.ConfigKeyToken]))
 
 	resp, err := client.Do(req)
 	if err != nil {
diff --git a/bridge/github/import.go b/bridge/github/import.go
index 864440574fc59881fc00c634473752d7e6da564e..c0fb3d6ca2ee641bedcff48ccef01d666e520013 100644
--- a/bridge/github/import.go
+++ b/bridge/github/import.go
@@ -39,7 +39,7 @@ func (gi *githubImporter) Init(conf core.Configuration) error {
 // ImportAll iterate over all the configured repository issues and ensure the creation of the
 // missing issues / timeline items / edits / label events ...
 func (gi *githubImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) {
-	gi.iterator = NewIterator(ctx, 10, gi.conf[keyOwner], gi.conf[keyProject], gi.conf[keyToken], since)
+	gi.iterator = NewIterator(ctx, 10, gi.conf[keyOwner], gi.conf[keyProject], gi.conf[core.ConfigKeyToken], since)
 	out := make(chan core.ImportResult)
 	gi.out = out
 
@@ -553,7 +553,7 @@ func (gi *githubImporter) getGhost(repo *cache.RepoCache) (*cache.IdentityCache,
 		"login": githubv4.String("ghost"),
 	}
 
-	gc := buildClient(gi.conf[keyToken])
+	gc := buildClient(gi.conf[core.ConfigKeyToken])
 
 	ctx, cancel := context.WithTimeout(gi.iterator.ctx, defaultTimeout)
 	defer cancel()
diff --git a/bridge/gitlab/config.go b/bridge/gitlab/config.go
index f2e667a85ea84f39ed131e799fe3cc7eb8092e89..88ba7db85f409ca5165ea8513fe343a10bec87d8 100644
--- a/bridge/gitlab/config.go
+++ b/bridge/gitlab/config.go
@@ -13,6 +13,7 @@ import (
 	"github.com/xanzy/go-gitlab"
 
 	"github.com/MichaelMure/git-bug/bridge/core"
+	"github.com/MichaelMure/git-bug/entity"
 	"github.com/MichaelMure/git-bug/repository"
 )
 
@@ -32,6 +33,8 @@ func (g *Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams)
 	var err error
 	var url string
 	var token string
+	var tokenId entity.Id
+	var tokenObj *core.Token
 
 	if (params.Token != "" || params.TokenStdin) && params.URL == "" {
 		return nil, fmt.Errorf("you must provide a project URL to configure this bridge with a token")
@@ -65,21 +68,38 @@ func (g *Gitlab) Configure(repo repository.RepoCommon, params core.BridgeParams)
 			return nil, fmt.Errorf("reading from stdin: %v", err)
 		}
 		token = strings.TrimSuffix(token, "\n")
+	} else if params.TokenId != "" {
+		tokenId = entity.Id(params.TokenId)
 	} else {
-		token, err = promptToken()
+		tokenObj, err = promptTokenOptions(repo)
 		if err != nil {
 			return nil, errors.Wrap(err, "token prompt")
 		}
 	}
 
+	if token != "" {
+		tokenObj, err = core.LoadOrCreateToken(repo, target, token)
+		if err != nil {
+			return nil, err
+		}
+	} else if tokenId != "" {
+		tokenObj, err = core.LoadToken(repo, entity.Id(tokenId))
+		if err != nil {
+			return nil, err
+		}
+		if tokenObj.Target != target {
+			return nil, fmt.Errorf("token target is incompatible %s", tokenObj.Target)
+		}
+	}
+
 	// validate project url and get its ID
-	id, err := validateProjectURL(url, token)
+	id, err := validateProjectURL(url, tokenObj.Value)
 	if err != nil {
 		return nil, errors.Wrap(err, "project validation")
 	}
 
 	conf[keyProjectID] = strconv.Itoa(id)
-	conf[keyToken] = token
+	conf[core.ConfigKeyTokenId] = tokenObj.ID().String()
 	conf[core.ConfigKeyTarget] = target
 
 	err = g.ValidateConfig(conf)
@@ -108,6 +128,54 @@ func (g *Gitlab) ValidateConfig(conf core.Configuration) error {
 	return nil
 }
 
+func promptTokenOptions(repo repository.RepoCommon) (*core.Token, error) {
+	for {
+		tokens, err := core.LoadTokensWithTarget(repo, target)
+		if err != nil {
+			return nil, err
+		}
+
+		fmt.Println()
+		fmt.Println("[1]: user provided token")
+
+		if len(tokens) > 0 {
+			fmt.Println("known tokens for Gitlab:")
+			for i, token := range tokens {
+				if token.Target == target {
+					fmt.Printf("[%d]: %s\n", i+2, token.ID())
+				}
+			}
+		}
+		fmt.Print("Select option: ")
+
+		line, err := bufio.NewReader(os.Stdin).ReadString('\n')
+		fmt.Println()
+		if err != nil {
+			return nil, err
+		}
+
+		line = strings.TrimRight(line, "\n")
+		index, err := strconv.Atoi(line)
+		if err != nil || index < 1 || index > len(tokens)+1 {
+			fmt.Println("invalid input")
+			continue
+		}
+
+		var token string
+		switch index {
+		case 1:
+			token, err = promptToken()
+			if err != nil {
+				return nil, err
+			}
+		default:
+			return tokens[index-2], nil
+		}
+
+		return core.LoadOrCreateToken(repo, target, token)
+	}
+}
+
 func promptToken() (string, error) {
 	fmt.Println("You can generate a new token by visiting https://gitlab.com/profile/personal_access_tokens.")
 	fmt.Println("Choose 'Create personal access token' and set the necessary access scope for your repository.")
diff --git a/bridge/gitlab/export.go b/bridge/gitlab/export.go
index 7c00e39de58b1fdc28b0d186985728509b6dff24..092434a5e73e7808f81c39151b765f9fc5135b23 100644
--- a/bridge/gitlab/export.go
+++ b/bridge/gitlab/export.go
@@ -79,7 +79,7 @@ func (ge *gitlabExporter) ExportAll(ctx context.Context, repo *cache.RepoCache,
 		return nil, err
 	}
 
-	ge.identityToken[user.Id().String()] = ge.conf[keyToken]
+	ge.identityToken[user.Id().String()] = ge.conf[core.ConfigKeyToken]
 
 	// get repository node id
 	ge.repositoryID = ge.conf[keyProjectID]
diff --git a/bridge/gitlab/import.go b/bridge/gitlab/import.go
index 92e9952ec52fe5f80655744514b709521688887b..4fcf8568ffbdb73a1d7f7dc61a1e58a35def2a54 100644
--- a/bridge/gitlab/import.go
+++ b/bridge/gitlab/import.go
@@ -34,7 +34,7 @@ func (gi *gitlabImporter) Init(conf core.Configuration) error {
 // ImportAll iterate over all the configured repository issues (notes) and ensure the creation
 // of the missing issues / comments / label events / title changes ...
 func (gi *gitlabImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) {
-	gi.iterator = NewIterator(ctx, 10, gi.conf[keyProjectID], gi.conf[keyToken], since)
+	gi.iterator = NewIterator(ctx, 10, gi.conf[keyProjectID], gi.conf[core.ConfigKeyToken], since)
 	out := make(chan core.ImportResult)
 	gi.out = out
 
diff --git a/commands/bridge_configure.go b/commands/bridge_configure.go
index 3562af17c8a471df5f3e87f851706d8203af2903..6f3141355df2b768ba7e61eafaef513ef83c1323 100644
--- a/commands/bridge_configure.go
+++ b/commands/bridge_configure.go
@@ -34,7 +34,8 @@ func runBridgeConfigure(cmd *cobra.Command, args []string) error {
 	defer backend.Close()
 	interrupt.RegisterCleaner(backend.Close)
 
-	if (bridgeParams.TokenStdin || bridgeParams.Token != "") && (bridgeConfigureName == "" || bridgeConfigureTarget == "") {
+	if (bridgeParams.TokenStdin || bridgeParams.Token != "" || bridgeParams.TokenId != "") &&
+		(bridgeConfigureName == "" || bridgeConfigureTarget == "") {
 		return fmt.Errorf("you must provide a bridge name and target to configure a bridge with a token")
 	}
 
@@ -195,6 +196,7 @@ func init() {
 	bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.URL, "url", "u", "", "The URL of the target repository")
 	bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Owner, "owner", "o", "", "The owner of the target repository")
 	bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Token, "token", "T", "", "The authentication token for the API")
+	bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.TokenId, "token-id", "i", "", "The authentication token identifier for the API")
 	bridgeConfigureCmd.Flags().BoolVar(&bridgeParams.TokenStdin, "token-stdin", false, "Will read the token from stdin and ignore --token")
 	bridgeConfigureCmd.Flags().StringVarP(&bridgeParams.Project, "project", "p", "", "The name of the target repository")
 	bridgeConfigureCmd.Flags().SortFlags = false