implement phase 5 และ fix bugs
This commit is contained in:
parent
f4b1c3e14b
commit
1bb850c6b8
40 changed files with 1507 additions and 429 deletions
114
scripts/dev-token.py
Executable file
114
scripts/dev-token.py
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/env python3
|
||||
"""ขอ access token แบบ PKCE (authorization code flow) จาก Keycloak dev — สำหรับทดสอบ backend ผ่าน curl/Swagger.
|
||||
|
||||
ทำไมต้องมีสคริปต์นี้: ทุก client ใน realm ปิด direct grants บังคับ PKCE ตาม spec
|
||||
ส่วน token ของ `admin-cli` ไม่มี claim `aud` จึงเรียก backend ไม่ผ่าน (401)
|
||||
|
||||
วิธีใช้:
|
||||
python3 scripts/dev-token.py # ปริ้น access token
|
||||
python3 scripts/dev-token.py --clip # คัดลอกเข้า clipboard (macOS)
|
||||
|
||||
แก้ user/password ผ่าน env: DEV_TOKEN_USERNAME / DEV_TOKEN_PASSWORD (default testuser/Test1234!)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import hashlib
|
||||
import http.cookiejar
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import ssl
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
REALM = "https://localhost:8443/realms/word-versioning"
|
||||
CLIENT_ID = "word-versioning-web"
|
||||
REDIRECT_URI = "http://localhost:5173/callback" # ไม่ต้องมี server จริง — แค่ต้องตรงกับที่ลงทะเบียนไว้
|
||||
CA_FILE = os.path.expanduser("~/.office-addin-dev-certs/ca.crt")
|
||||
|
||||
USERNAME = os.environ.get("DEV_TOKEN_USERNAME", "testuser")
|
||||
PASSWORD = os.environ.get("DEV_TOKEN_PASSWORD", "Test1234!")
|
||||
|
||||
|
||||
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||
"""หยุดที่ 302 เพื่อเก็บ authorization code จาก Location"""
|
||||
|
||||
def redirect_request(self, req, fp, code, msg, headers, newurl): # noqa: ARG002
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--clip", action="store_true", help="คัดลอก token เข้า clipboard (pbcopy)")
|
||||
args = parser.parse_args()
|
||||
|
||||
verifier = base64.urlsafe_b64encode(os.urandom(48)).rstrip(b"=").decode()
|
||||
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b"=").decode()
|
||||
|
||||
ctx = ssl.create_default_context(cafile=CA_FILE)
|
||||
opener = urllib.request.build_opener(
|
||||
urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar()),
|
||||
NoRedirect,
|
||||
urllib.request.HTTPSHandler(context=ctx),
|
||||
)
|
||||
|
||||
auth_url = f"{REALM}/protocol/openid-connect/auth?" + urllib.parse.urlencode(
|
||||
{
|
||||
"client_id": CLIENT_ID,
|
||||
"redirect_uri": REDIRECT_URI,
|
||||
"response_type": "code",
|
||||
"scope": "openid",
|
||||
"state": "dev-token",
|
||||
"code_challenge": challenge,
|
||||
"code_challenge_method": "S256",
|
||||
}
|
||||
)
|
||||
login_page = opener.open(auth_url).read().decode()
|
||||
action = re.search(r'action="([^"]+)"', login_page)
|
||||
if not action:
|
||||
print("ไม่พบ login form — ตรวจ Keycloak ที่ https://localhost:8443", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
credentials = urllib.parse.urlencode(
|
||||
{"username": USERNAME, "password": PASSWORD, "credentialId": ""}
|
||||
).encode()
|
||||
try:
|
||||
opener.open(urllib.request.Request(action.group(1).replace("&", "&"), data=credentials))
|
||||
print("login ไม่ redirect — ตรวจ username/password", file=sys.stderr)
|
||||
return 1
|
||||
except urllib.error.HTTPError as error:
|
||||
location = error.headers.get("Location", "")
|
||||
code = urllib.parse.parse_qs(urllib.parse.urlparse(location).query).get("code", [None])[0]
|
||||
if not code:
|
||||
print("ไม่ได้รับ authorization code (redirect ผิดหรือ login พลาด)", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
token_body = urllib.parse.urlencode(
|
||||
{
|
||||
"grant_type": "authorization_code",
|
||||
"client_id": CLIENT_ID,
|
||||
"code": code,
|
||||
"redirect_uri": REDIRECT_URI,
|
||||
"code_verifier": verifier,
|
||||
}
|
||||
).encode()
|
||||
token_response = json.loads(
|
||||
opener.open(urllib.request.Request(f"{REALM}/protocol/openid-connect/token", data=token_body)).read()
|
||||
)
|
||||
token = token_response["access_token"]
|
||||
|
||||
if args.clip:
|
||||
import subprocess
|
||||
|
||||
subprocess.run("pbcopy", input=token.encode(), check=True)
|
||||
print(f"คัดลอก access token ({len(token)} chars) เข้า clipboard แล้ว")
|
||||
else:
|
||||
print(token)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue