mirror of
https://github.com/vale981/outlook-oauth-hack
synced 2025-03-04 08:31:38 -05:00
implement profiles
This commit is contained in:
parent
c9bdbb305c
commit
f85a1900b7
6 changed files with 119 additions and 132 deletions
28
config.py
28
config.py
|
@ -1,16 +1,24 @@
|
|||
from pathlib import Path
|
||||
import tomllib
|
||||
from types import SimpleNamespace
|
||||
import sys
|
||||
|
||||
with open(Path.home() / ".o365-oauth-config.toml", "rb") as f:
|
||||
config_data = tomllib.load(f)
|
||||
|
||||
cache_path = Path.home() / ".chache/o365-oauth"
|
||||
cache_path.mkdir(parents=True, exist_ok=True)
|
||||
def get_config(profile):
|
||||
with open(Path.home() / ".o365-auth-config.toml", "rb") as f:
|
||||
toplevel_data = tomllib.load(f)
|
||||
|
||||
ClientId = config_data["ClientId"]
|
||||
ClientSecret = config_data["ClientSecret"]
|
||||
Scopes = config_data["Scopes"]
|
||||
RefreshTokenFileName = cache_path / "imap_smtp_refresh_token"
|
||||
AccessTokenFileName = cache_path / "imap_smtp_access_token"
|
||||
if profile not in toplevel_data:
|
||||
sys.exit("Invalid profile specified.")
|
||||
|
||||
Authority = config_data["Authority"] or None
|
||||
config_data = toplevel_data[profile]
|
||||
cache_path = Path.home() / ".cache/o365-oauth" / profile
|
||||
cache_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return SimpleNamespace(
|
||||
ClientId = config_data["ClientId"],
|
||||
ClientSecret = config_data["ClientSecret"],
|
||||
Scopes = config_data["Scopes"],
|
||||
RefreshTokenFileName = cache_path / "imap_smtp_refresh_token",
|
||||
AccessTokenFileName = cache_path / "imap_smtp_access_token",
|
||||
Authority = config_data["Authority"] or None)
|
||||
|
|
87
flake.lock
generated
87
flake.lock
generated
|
@ -13,61 +13,11 @@
|
|||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"locked": {
|
||||
"lastModified": 1642700792,
|
||||
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mach-nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pypi-deps-db": "pypi-deps-db"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705470643,
|
||||
"narHash": "sha256-CqgkRcvOonJ2fL6542/ykZR3yL6qVLWy0pdyY5YKRlE=",
|
||||
"owner": "DavHau",
|
||||
"repo": "mach-nix",
|
||||
"rev": "28f563aeb8c9e679a3f2b531c728573fce7a4594",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "mach-nix",
|
||||
"id": "flake-utils",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1643805626,
|
||||
"narHash": "sha256-AXLDVMG+UaAGsGSpOtQHPIKB+IZ0KSd9WS77aanGzgc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "554d2d8aa25b6e583575459c297ec23750adb6cb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1712192574,
|
||||
"narHash": "sha256-LbbVOliJKTF4Zl2b9salumvdMXuQBr2kuKP5+ZwbYq4=",
|
||||
|
@ -83,27 +33,11 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pypi-deps-db": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1685526402,
|
||||
"narHash": "sha256-V0SXx0dWlUBL3E/wHWTszrkK2dOnuYYnBc7n6e0+NQU=",
|
||||
"owner": "DavHau",
|
||||
"repo": "pypi-deps-db",
|
||||
"rev": "ba35683c35218acb5258b69a9916994979dc73a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "DavHau",
|
||||
"repo": "pypi-deps-db",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"mach-nix": "mach-nix",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
|
@ -120,6 +54,21 @@
|
|||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
52
flake.nix
52
flake.nix
|
@ -3,29 +3,35 @@
|
|||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
systems.url = "github:nix-systems/default";
|
||||
};
|
||||
outputs = { nixpkgs, flake-utils, mach-nix, ... }:
|
||||
outputs = { self, nixpkgs, flake-utils, systems, ... }:
|
||||
let
|
||||
eachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||
in
|
||||
{
|
||||
homeManagerModules.default = import ./nix/hm-module.nix self;
|
||||
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
with pkgs.python3Packages; {
|
||||
packages.default = pkgs.stdenv.mkDerivation {
|
||||
name = "myscript";
|
||||
propagatedBuildInputs = [
|
||||
(pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
|
||||
msal
|
||||
]))
|
||||
];
|
||||
dontUnpack = true;
|
||||
installPhase = ''
|
||||
install -Dm755 ${./get_token.py} $out/bin/o365-get-token
|
||||
install -Dm755 ${./refresh_token.py} $out/bin/o365-refresh-token
|
||||
install -Dm755 ${./config.py} $out/bin/config.py
|
||||
'';
|
||||
};
|
||||
homeManagerModules.default = import ./nix/hm-module.nix self;
|
||||
});
|
||||
|
||||
packages = eachSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
with pkgs.python3Packages; {
|
||||
default = pkgs.stdenv.mkDerivation {
|
||||
name = "o365-auth";
|
||||
propagatedBuildInputs = [
|
||||
(pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
|
||||
msal
|
||||
]))
|
||||
];
|
||||
dontUnpack = true;
|
||||
installPhase = ''
|
||||
install -Dm755 ${./get_token.py} $out/bin/o365-get-token
|
||||
install -Dm755 ${./refresh_token.py} $out/bin/o365-refresh-token
|
||||
install -Dm755 ${./config.py} $out/bin/config.py
|
||||
'';
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
22
get_token.py
22
get_token.py
|
@ -8,14 +8,22 @@ import threading
|
|||
import urllib.parse
|
||||
import webbrowser
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
profile = sys.argv[1]
|
||||
else:
|
||||
sys.exit("Please provide a profile name as the first argument.")
|
||||
|
||||
|
||||
profile_config = config.get_config(profile)
|
||||
|
||||
|
||||
redirect_uri = "http://localhost:8745/"
|
||||
|
||||
# We use the cache to extract the refresh token
|
||||
cache = SerializableTokenCache()
|
||||
app = ConfidentialClientApplication(config.ClientId, client_credential=config.ClientSecret, token_cache=cache, authority=config.Authority)
|
||||
app = ConfidentialClientApplication(profile_config.ClientId, client_credential=profile_config.ClientSecret, token_cache=cache, authority=profile_config.Authority)
|
||||
|
||||
url = app.get_authorization_request_url(config.Scopes, redirect_uri=redirect_uri)
|
||||
url = app.get_authorization_request_url(profile_config.Scopes, redirect_uri=redirect_uri)
|
||||
|
||||
# webbrowser.open may fail silently
|
||||
print("Navigate to the following url in a web browser, if doesn't open automatically:")
|
||||
|
@ -62,7 +70,7 @@ if code == '':
|
|||
i = resp.find('code') + 5
|
||||
code = resp[i : resp.find('&', i)] if i > 4 else resp
|
||||
|
||||
token = app.acquire_token_by_authorization_code(code, config.Scopes, redirect_uri=redirect_uri)
|
||||
token = app.acquire_token_by_authorization_code(code, profile_config.Scopes, redirect_uri=redirect_uri)
|
||||
|
||||
print()
|
||||
|
||||
|
@ -70,10 +78,10 @@ if 'error' in token:
|
|||
print(token)
|
||||
sys.exit("Failed to get access token")
|
||||
|
||||
with open(config.RefreshTokenFileName, 'w') as f:
|
||||
print(f'Refresh token acquired, writing to file {config.RefreshTokenFileName}')
|
||||
with open(profile_config.RefreshTokenFileName, 'w') as f:
|
||||
print(f'Refresh token acquired, writing to file {profile_config.RefreshTokenFileName}')
|
||||
f.write(token['refresh_token'])
|
||||
|
||||
with open(config.AccessTokenFileName, 'w') as f:
|
||||
print(f'Access token acquired, writing to file {config.AccessTokenFileName}')
|
||||
with open(profile_config.AccessTokenFileName, 'w') as f:
|
||||
print(f'Access token acquired, writing to file {profile_config.AccessTokenFileName}')
|
||||
f.write(token['access_token'])
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
self: {
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
self: { config
|
||||
, lib
|
||||
, pkgs
|
||||
, ...
|
||||
}:
|
||||
with lib;
|
||||
let
|
||||
inherit (pkgs.stdenv.hostPlatform) system;
|
||||
|
||||
package = self.packages.${system}.default;
|
||||
cfg = config.programs.o365-auth;
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.programs.o365-auth = {
|
||||
enable = mkEnableOption "o365 token refresh script";
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = package;
|
||||
};
|
||||
config = mkOption {
|
||||
type = types.str;
|
||||
default = ''
|
||||
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']
|
||||
RefreshTokenFileName = "imap_smtp_refresh_token"
|
||||
AccessTokenFileName = "imap_smtp_access_token"
|
||||
|
||||
Authority = false
|
||||
'';
|
||||
[mcgill]
|
||||
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
|
||||
'';
|
||||
};
|
||||
};
|
||||
config = mkIf cfg.enable {
|
||||
home.packages = [ package ];
|
||||
home.file.".o365-auth-config".source = cfg.config;
|
||||
home.packages = [ cfg.package ];
|
||||
home.file.".o365-auth-config".text = cfg.config;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,27 +3,41 @@ from msal import ConfidentialClientApplication, SerializableTokenCache
|
|||
import config
|
||||
import sys
|
||||
|
||||
print_access_token = True
|
||||
|
||||
print_access_token = True
|
||||
# get first command line argument
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
profile = sys.argv[1]
|
||||
else:
|
||||
sys.exit("Please provide a profile name as the first argument.")
|
||||
|
||||
|
||||
profile_config = config.get_config(profile)
|
||||
# We use the cache to extract the refresh token
|
||||
cache = SerializableTokenCache()
|
||||
app = ConfidentialClientApplication(config.ClientId, client_credential=config.ClientSecret, token_cache=cache, authority=config.Authority)
|
||||
app = ConfidentialClientApplication(profile_config.ClientId, client_credential=profile_config.ClientSecret, token_cache=cache, authority=profile_config.Authority)
|
||||
|
||||
|
||||
old_refresh_token = open(config.RefreshTokenFileName,'r').read()
|
||||
# check if file exists and error out if it doesn't
|
||||
try:
|
||||
old_refresh_token = open(profile_config.RefreshTokenFileName,'r').read()
|
||||
except FileNotFoundError:
|
||||
sys.exit("Please get the initial token by running `o365-get-token` first.")
|
||||
|
||||
token = app.acquire_token_by_refresh_token(old_refresh_token,config.Scopes)
|
||||
|
||||
token = app.acquire_token_by_refresh_token(old_refresh_token,profile_config.Scopes)
|
||||
|
||||
if 'error' in token:
|
||||
print(token)
|
||||
sys.exit("Failed to get access token")
|
||||
|
||||
# you're supposed to save the old refresh token each time
|
||||
with open(config.RefreshTokenFileName, 'w') as f:
|
||||
with open(profile_config.RefreshTokenFileName, 'w') as f:
|
||||
#f.write(cache.find('RefreshToken')[0]['secret'])
|
||||
f.write(token['refresh_token'])
|
||||
|
||||
with open(config.AccessTokenFileName, 'w') as f:
|
||||
with open(profile_config.AccessTokenFileName, 'w') as f:
|
||||
f.write(token['access_token'])
|
||||
if print_access_token:
|
||||
print(token['access_token'])
|
||||
|
|
Loading…
Add table
Reference in a new issue