This is a minimal python script to generate rclone config for onedrive personal account. I will submit a PR later
import base64, json, secrets, threading, webbrowser
from datetime import datetime, timedelta, timezone
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlencode, urlparse, parse_qs
import requests
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
rcloneClientID = "b15665d9-eda6-4092-8539-0eec376afd59"
rcloneEncryptedClientSecret = "_JUdzh3LnKNqSPcf4Wu5fgMFIQOI8glZu_akYgR8yf6egowNBg-R"
redirect = "http://localhost:53682/"
scopes = "Files.Read Files.ReadWrite Files.Read.All Files.ReadWrite.All Sites.Read.All offline_access"
# rclone's fixed obscure key
key = bytes([
0x9c,0x93,0x5b,0x48,0x73,0x0a,0x55,0x4d,
0x6b,0xfd,0x7c,0x63,0xc8,0x86,0xa9,0x2b,
0xd3,0x90,0x19,0x8e,0xb8,0x12,0x8a,0xfb,
0xf4,0xde,0x16,0x2b,0x8b,0x95,0xf6,0x38
])
raw = base64.urlsafe_b64decode(rcloneEncryptedClientSecret + "==")
client_secret = Cipher(algorithms.AES(key), modes.CTR(raw[:16])).decryptor().update(raw[16:])
state = secrets.token_urlsafe(16)
result = {}
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
q = parse_qs(urlparse(self.path).query)
result.update(q)
self.send_response(200)
self.end_headers()
self.wfile.write(b"Done. You can close this window.")
threading.Thread(target=self.server.shutdown).start()
def log_message(self, *args):
pass
auth = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?" + urlencode({
"client_id": rcloneClientID,
"response_type": "code",
"redirect_uri": redirect,
"scope": scopes,
"state": state,
})
server = HTTPServer(("localhost", 53682), Handler)
webbrowser.open(auth)
server.serve_forever()
if result.get("state", [None])[0] != state:
raise RuntimeError("OAuth state mismatch")
token = requests.post(
"https://login.microsoftonline.com/common/oauth2/v2.0/token",
data={
"client_id": rcloneClientID,
"client_secret": client_secret.decode(),
"code": result["code"][0],
"redirect_uri": redirect,
"grant_type": "authorization_code",
},
).json()
if "access_token" not in token:
raise RuntimeError(token)
headers = {"Authorization": "Bearer " + token["access_token"]}
r = requests.get(
"https://graph.microsoft.com/v1.0/me/drive",
headers=headers,
)
print(r.status_code, r.text)
r.raise_for_status()
drive = r.json()
expiry = (
datetime.now(timezone.utc)
+ timedelta(seconds=token["expires_in"])
).isoformat()
rclone_token = json.dumps({
"access_token": token["access_token"],
"expiry": expiry,
"refresh_token": token["refresh_token"],
"token_type": "Bearer",
}, separators=(",", ":"))
with open("rclone.conf", "w") as f:
f.write(f"""[onedrive]
type = onedrive
drive_id = {drive["id"]}
drive_type = personal
client_id = {rcloneClientID}
client_secret = {client_secret.decode()}
token = {rclone_token}
""")
print("Created rclone.conf")
print("drive_id =", drive["id"])