diff --git a/pkg/config/unix.go b/pkg/config/unix.go
index 7425f5ba35a5fe2124b6457eb570abf873955d97..f96f6a65086f9a72235fc08282ee89b23aa0baf9 100644
--- a/pkg/config/unix.go
+++ b/pkg/config/unix.go
@@ -18,7 +18,8 @@ package config
 
 import (
 	"os"
+	"path"
 )
 
 // Path for the config files
-var Path = os.Getenv("HOME") + "/.config/leap"
+var Path = path.Join(os.Getenv("HOME"), ".config", "leap")
diff --git a/pkg/vpn/bonafide/auth_sip.go b/pkg/vpn/bonafide/auth_sip.go
index 1f200bbf36baecb251d710f516d9946d0333f30a..e00252f3cb4069423faa8e7aad198a03fc0048ac 100644
--- a/pkg/vpn/bonafide/auth_sip.go
+++ b/pkg/vpn/bonafide/auth_sip.go
@@ -19,7 +19,13 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"log"
+	"os"
+	"path"
 	"strings"
+	"time"
+
+	"0xacab.org/leap/bitmask-vpn/pkg/config"
 )
 
 type sipAuthentication struct {
@@ -32,10 +38,11 @@ func (a *sipAuthentication) needsCredentials() bool {
 }
 
 func (a *sipAuthentication) getToken(user, password string) ([]byte, error) {
-	/* TODO
-	[ ] get token from disk?
-	[ ] check if expired? set a goroutine to refresh it periodically?
-	*/
+	/* TODO refresh session token periodically */
+	if hasRecentToken() {
+		log.Println("Got cached token")
+		return readToken()
+	}
 	credJSON, err := formatCredentials(user, password)
 	if err != nil {
 		return nil, fmt.Errorf("Cannot encode credentials: %s", err)
@@ -48,7 +55,49 @@ func (a *sipAuthentication) getToken(user, password string) ([]byte, error) {
 	if resp.StatusCode != 200 {
 		return nil, fmt.Errorf("Cannot get token: Error %d", resp.StatusCode)
 	}
-	return ioutil.ReadAll(resp.Body)
+	token, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	writeToken(token)
+	return token, nil
+}
+
+func getTokenPath() string {
+	return path.Join(config.Path, config.ApplicationName+".token")
+}
+
+func writeToken(token []byte) {
+	tp := getTokenPath()
+	err := ioutil.WriteFile(tp, token, 0600)
+	if err != nil {
+		log.Println("BUG: cannot write token to", tp)
+	}
+}
+
+func readToken() ([]byte, error) {
+	f, err := os.Open(getTokenPath())
+	if err != nil {
+		log.Println("Error: cannot open token file")
+		return nil, err
+	}
+	token, err := ioutil.ReadAll(f)
+	if err != nil {
+		log.Println("Error: cannot read token")
+		return nil, err
+	}
+	return token, nil
+}
+
+func hasRecentToken() bool {
+	statinfo, err := os.Stat(getTokenPath())
+	if err != nil {
+		return false
+	}
+	lastWrote := statinfo.ModTime().Unix()
+	/* in vpnweb we set the duration of the token to 24 hours */
+	old := time.Now().Add(-time.Hour * 20).Unix()
+	return lastWrote >= old
 }
 
 func formatCredentials(user, pass string) (string, error) {
diff --git a/pkg/vpn/bonafide/bonafide.go b/pkg/vpn/bonafide/bonafide.go
index dd8c597cbea5ae6cc4c69cc0e26a0c9ce3d46476..b81fd841f33d482fec2e7761ffcaca3a0f706a9a 100644
--- a/pkg/vpn/bonafide/bonafide.go
+++ b/pkg/vpn/bonafide/bonafide.go
@@ -114,8 +114,18 @@ func New() *Bonafide {
 	return b
 }
 
+/* NeedsCredentials signals if we have to ask user for credentials. If false, it can be that we have a cached token */
 func (b *Bonafide) NeedsCredentials() bool {
-	return b.auth.needsCredentials()
+	if !b.auth.needsCredentials() {
+		return false
+	}
+	/* try cached */
+	/* TODO cleanup this call - maybe expose getCachedToken instead of relying on empty creds? */
+	_, err := b.auth.getToken("", "")
+	if err != nil {
+		return true
+	}
+	return false
 }
 
 func (b *Bonafide) DoLogin(username, password string) (bool, error) {
@@ -136,9 +146,13 @@ func (b *Bonafide) GetPemCertificate() ([]byte, error) {
 	if b.auth == nil {
 		log.Fatal("ERROR: bonafide did not initialize auth")
 	}
-	if b.auth.needsCredentials() && b.token == nil {
-		log.Println("Needs token, but token is empty")
-		return nil, errors.New("Needs to login, but it was not logged in. Please, restart the application and report it if it continues happening")
+	if b.auth.needsCredentials() {
+		/* try cached token */
+		token, err := b.auth.getToken("", "")
+		if err != nil {
+			return nil, errors.New("BUG: This service needs login, but we were not logged in.")
+		}
+		b.token = token
 	}
 
 	req, err := http.NewRequest("POST", b.getURL("certv3"), strings.NewReader(""))
diff --git a/pkg/vpn/openvpn.go b/pkg/vpn/openvpn.go
index 11bba69a4d5b3f8a0b7a2bf517c07813faac2346..6055e6a5becd4a821c52b912d644a96de4c84060 100644
--- a/pkg/vpn/openvpn.go
+++ b/pkg/vpn/openvpn.go
@@ -51,14 +51,7 @@ func (b *Bitmask) StartVPN(provider string) error {
 }
 
 func (b *Bitmask) CanStartVPN() bool {
-	if !b.bonafide.NeedsCredentials() {
-		return true
-	}
-	_, err := b.getCert()
-	if err != nil {
-		return false
-	}
-	return true
+	return !b.bonafide.NeedsCredentials()
 }
 
 func (b *Bitmask) startTransport() (proxy string, err error) {