diff --git a/Cargo.toml b/Cargo.toml
index 3f9b5e8579e4a715a1896e60dadab75026b2c17f..f32095c5a152f8332be6ff4f528a7d5eab1be2ac 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ serde = "^1.0.125"
 log = "^0.4.11"
 syslog = "^5.0.0"
 xdg = "^2.2.0"
+serde_json = "^1.0.64"
 
 [profile.release]
 opt-level = 'z'
diff --git a/src/cache.rs b/src/cache.rs
index fa9cb0e124a62bc9fcb063e1e4feff196babeca5..23f286191386be82003138b8b5b301cef66353d2 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -26,14 +26,18 @@ use std::ffi::CString;
 use oauth2::basic::BasicTokenResponse;
 
 use std::env;
-use std::fs::remove_file;
+use std::fs;
 use std::io;
 use std::path::PathBuf;
 use xdg::{BaseDirectories,BaseDirectoriesError};
 
+use serde::{Deserialize, Serialize};
+use serde_json;
+
 const TOKEN_DEFAULT_EXPIRES: u64 = 24 * 60 * 60;
 const USER_TOKEN_FILENAME: &str = "user_token.json";
 
+#[derive(Serialize, Deserialize)]
 struct UserToken {
     access_token: String,
     expires_at: u64,
@@ -128,11 +132,47 @@ impl Cache {
     }
 
     pub fn load_user_token(&self, owner: String) -> Option<&UserToken> {
-        return self.user_tokens.get(&owner);
+        match self.user_tokens.get(&owner) {
+            Some(t) => {
+                if t.is_expired() {
+                    // Try to load token from file
+                    self.drop_privileges(owner).ok();
+                    let new_token = match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
+                        Ok(path) => match load_json(path) {
+                            Ok(read_token) => {
+                                self.user_tokens.insert(owner, read_token);
+                                Some(&read_token)
+                            },
+                            Err(e) => {
+                                self.user_tokens.remove(&owner);
+                                None
+                            }
+                        },
+                        Err(e) => {
+                            self.user_tokens.remove(&owner);
+                            None
+                        }
+                    };
+                    self.restore_privileges();
+                    new_token
+                } else {
+                    Some(t)
+                }
+            },
+            None => None
+        }
     }
 
     pub fn save_user_token(&self, owner: String, token: UserToken) {
         self.user_tokens.insert(owner, token);
+
+        // Try to write user's token cache file
+        self.drop_privileges(owner).ok();
+        match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
+            Ok(path) => save_json(path, token),
+            Err(e) => Err(e)
+        };
+        self.restore_privileges();
     }
 
     pub fn delete_user_token(&self, owner: String) {
@@ -141,7 +181,7 @@ impl Cache {
         // Try to remove user's token cache file
         self.drop_privileges(owner).ok();
         match self.place_user_cache_file(owner, USER_TOKEN_FILENAME) {
-            Ok(path) => remove_file(path),
+            Ok(path) => fs::remove_file(path),
             Err(e) => Err(e)
         };
         self.restore_privileges();
@@ -156,6 +196,23 @@ impl Cache {
     }
 }
 
+fn load_json<'de, O: Deserialize<'de>>(path: PathBuf) -> Result<O, io::Error> {
+    let json = fs::read_to_string(path)?;
+    match serde_json::from_str(&json) {
+        Ok(o) => Ok(o),
+        Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e))
+    }
+}
+
+fn save_json<O: Serialize>(path: PathBuf, obj: O) -> Result<(), io::Error> {
+    let json = match serde_json::to_string(&obj) {
+        Ok(j) => j,
+        Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e))
+    };
+
+    fs::write(path, json)
+}
+
 lazy_static! {
     pub static ref CACHE: Cache = Cache::new();
 }