← back to Tiktok Oauth Catcher

catcher.py.bak

64 lines

#!/usr/bin/env python3
"""One-shot TikTok OAuth token catcher.
Listens on :9276, catches /tiktok/callback?code=..., exchanges the code for a
user access_token + refresh_token, writes them to /tmp/tiktok-tokens.json, shows
a success page, then exits so the parent can route the tokens via the secrets skill.
Reads client_key/secret DIRECTLY from the master .env (avoids the source bug).
"""
import http.server, urllib.parse, urllib.request, json, os, sys, re

ENV = os.path.expanduser("~/Projects/secrets-manager/.env")
REDIRECT = "http://localhost:9276/tiktok/callback"
OUT = "/tmp/tiktok-tokens.json"

def envval(key):
    for l in open(ENV):
        if l.startswith(key + "="):
            return l.partition("=")[2].strip().strip('"').strip("'")
    return ""

CK, CS = envval("TIKTOK_CLIENT_KEY"), envval("TIKTOK_CLIENT_SECRET")

class H(http.server.BaseHTTPRequestHandler):
    def log_message(self, *a): pass
    def do_GET(self):
        u = urllib.parse.urlparse(self.path)
        if not u.path.startswith("/tiktok/callback"):
            self.send_response(404); self.end_headers(); return
        q = urllib.parse.parse_qs(u.query)
        code = (q.get("code") or [""])[0]
        err  = (q.get("error") or [""])[0]
        if err or not code:
            self._html(f"<h2>OAuth error</h2><pre>{err or 'no code returned'}</pre>")
            self.server._result = {"error": err or "no_code"}; return
        data = urllib.parse.urlencode({
            "client_key": CK, "client_secret": CS, "code": code,
            "grant_type": "authorization_code", "redirect_uri": REDIRECT,
        }).encode()
        req = urllib.request.Request("https://open.tiktokapis.com/v2/oauth/token/",
              data=data, headers={"Content-Type": "application/x-www-form-urlencoded"})
        try:
            resp = json.load(urllib.request.urlopen(req, timeout=20))
        except Exception as e:
            resp = {"error": "token_exchange_failed", "detail": str(e)}
        json.dump(resp, open(OUT, "w"), indent=2)
        self.server._result = resp
        if resp.get("access_token"):
            self._html("<h2>✅ TikTok connected</h2><p>Token captured. You can close this tab — Claude will finish wiring it.</p>")
        else:
            self._html(f"<h2>Token exchange failed</h2><pre>{json.dumps(resp)[:400]}</pre>")
    def _html(self, body):
        self.send_response(200); self.send_header("Content-Type","text/html"); self.end_headers()
        self.wfile.write(f"<html><body style='font-family:system-ui;max-width:640px;margin:80px auto'>{body}</body></html>".encode())

if not CK or not CS:
    print("ERROR: TIKTOK_CLIENT_KEY/SECRET not found in .env"); sys.exit(2)
srv = http.server.HTTPServer(("127.0.0.1", 9276), H)
srv._result = None
print(f"listening on {REDIRECT} — waiting for one callback…", flush=True)
while srv._result is None:
    srv.handle_request()
r = srv._result
print("RESULT:", "OK access_token captured" if r.get("access_token") else json.dumps(r)[:300], flush=True)
sys.exit(0 if r.get("access_token") else 1)