diff --git a/config.py b/config.py index 7b1d4ac..c7100cb 100644 --- a/config.py +++ b/config.py @@ -2,12 +2,18 @@ from pathlib import Path import tomllib from types import SimpleNamespace import sys - +import json +import gnupg +gpg = gnupg.GPG() def get_config(profile): with open(Path.home() / ".o365-auth-config.toml", "rb") as f: toplevel_data = tomllib.load(f) + password = None + if "security" in toplevel_data and "password" in toplevel_data["security"]: + password = Path(toplevel_data["security"]["PasswordPath"]).read_text().strip() + default_data = toplevel_data["default"] config_data = default_data | toplevel_data.get(profile, {}) @@ -20,8 +26,33 @@ def get_config(profile): Scopes = config_data["Scopes"], CacheFile = cache_path / "cache.json", Authority = config_data["Authority"] or None, + Password = password ) +def get_cache(config): + if not config.CacheFile.exists(): + return None + + data = config.CacheFile.read_text() + if config.Password: + data = gpg.decrypt(data, passphrase=config.Password).data.decode("utf-8") + + return json.loads(data) + +def write_cache(config, token): + with open(config.CacheFile, "w") as f: + payload = {'refresh_token': token['refresh_token'], + 'expires_in': token['expires_in'], + 'access_token': token['access_token']} + + + json_string = json.dumps(payload) + if config.Password: + encrypted_data = gpg.encrypt(json_string, symmetric="AES256", passphrase=config.Password, armor=True, recipients=None) + json_string = str(encrypted_data) + + f.write(json_string) + if __name__ == "__main__": if len(sys.argv) < 3: sys.exit(f"Usage: {sys.argv[0]} ") diff --git a/config.toml b/config.toml index f59fc46..c640066 100644 --- a/config.toml +++ b/config.toml @@ -3,5 +3,4 @@ ClientId = "08162f7c-0fd2-4200-a84a-f25a4db0b584" ClientSecret = "TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82" Scopes = ['https://outlook.office.com/IMAP.AccessAsUser.All','https://outlook.office.com/SMTP.Send'] Authority = false - -[mcgill] +PasswordPath = false diff --git a/flake.nix b/flake.nix index 1040de7..b7490c1 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ name = "o365-auth"; propagatedBuildInputs = [ (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [ - msal + msal python-gnupg ])) ]; dontUnpack = true; diff --git a/get_token.py b/get_token.py index ff6cbfb..f1d51ed 100755 --- a/get_token.py +++ b/get_token.py @@ -78,8 +78,4 @@ if 'error' in token: print(token) sys.exit("Failed to get access token") - -with open(profile_config.CacheFile, 'w') as f: - json.dump({'refresh_token': token['refresh_token'], - 'expires_in': token['expires_in'], - 'access_token': token['access_token']}, f) +config.write_cache(profile_config, token) diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 1565a38..ed23ea2 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -17,6 +17,9 @@ in type = types.package; default = package; }; + passwordPath = mkOption { + type = types.str; + }; config = mkOption { type = types.str; default = '' @@ -25,12 +28,14 @@ in ClientSecret = "TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82" Scopes = ['https://outlook.office.com/IMAP.AccessAsUser.All','https://outlook.office.com/SMTP.Send'] Authority = false - Timeout = 3600 ''; }; }; config = mkIf cfg.enable { home.packages = [ cfg.package ]; - home.file.".o365-auth-config.toml".text = cfg.config; + home.file.".o365-auth-config.toml".text = cfg.config + '' +[security] +PasswordPath = "${cfg.passwordPath}" +''; }; } diff --git a/refresh_token.py b/refresh_token.py index 2db519e..9501df5 100644 --- a/refresh_token.py +++ b/refresh_token.py @@ -4,7 +4,7 @@ import config import sys import os import time -import json + print_access_token = True if len(sys.argv) > 1: @@ -19,11 +19,10 @@ cache = SerializableTokenCache() app = ConfidentialClientApplication(profile_config.ClientId, client_credential=profile_config.ClientSecret, token_cache=cache, authority=profile_config.Authority) -if not profile_config.CacheFile.exists(): +token_cache = config.get_cache(profile_config) +if not token_cache: sys.exit("Please get the initial token by running `o365-get-token` first.") -token_cache = json.loads(profile_config.CacheFile.read_text()) - st = os.stat(profile_config.CacheFile) if (time.time()-st.st_mtime) < token_cache["expires_in"]: print(token_cache["access_token"]) @@ -37,10 +36,6 @@ if 'error' in token: sys.exit("Failed to get access token") -# you're supposed to save the old refresh token each time -with open(profile_config.CacheFile, 'w') as f: - json.dump({'refresh_token': token['refresh_token'], - 'expires_in': token['expires_in'], - 'access_token': token['access_token']}, f) - if print_access_token: - print(token['access_token']) +config.write_cache(profile_config, token) +if print_access_token: + print(token['access_token'])