ai
OpenID Connect กับ Developer Experience DX —
OpenID Connect

OpenID Connect OIDC Identity Layer บน OAuth 2.0 Authentication ยืนยันตัวตน ID Token JWT SSO Single Sign-On Google GitHub Microsoft Login
เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง Local LLM 2026: รัน AI บนเครื่องของคุณเองด้วย Ollama คู่มือ Self-Hosted AI แบ…
Developer Experience DX ประสบการณ์นักพัฒนา API SDK Documentation Tools ใช้งานง่าย Docs ชัดเจน Onboarding เร็ว Time-to-First-API-Call
เนื้อหาเกี่ยวข้อง — อ่านต่อ: OpenTelemetry Collector Troubleshooting แก้ปัญหา — คู่มือฉบับสมบูรณ์ 2026
OIDC Authentication Flow
# oidc_auth.py — OpenID Connect Authentication
# pip install authlib httpx pyjwt cryptography
from dataclasses import dataclass, field
from typing import Dict, Optional, List
import hashlib
import base64
import secrets
import json
import time
@dataclass
class OIDCConfig:
issuer: str
client_id: str
client_secret: str
redirect_uri: str
scopes: List[str] = field(default_factory=lambda: ["openid", "profile", "email"])
authorization_endpoint: str = ""
token_endpoint: str = ""
userinfo_endpoint: str = ""
jwks_uri: str = ""
def __post_init__(self):
if not self.authorization_endpoint:
self.authorization_endpoint = f"{self.issuer}/authorize"
if not self.token_endpoint:
self.token_endpoint = f"{self.issuer}/oauth/token"
if not self.userinfo_endpoint:
self.userinfo_endpoint = f"{self.issuer}/userinfo"
if not self.jwks_uri:
self.jwks_uri = f"{self.issuer}/.well-known/jwks.json"
class OIDCClient:
"""OIDC Client with PKCE Support"""
def __init__(self, config: OIDCConfig):
self.config = config
def generate_pkce(self) -> Dict[str, str]:
"""Generate PKCE Code Verifier and Challenge"""
verifier = secrets.token_urlsafe(43)
challenge = base64.urlsafe_b64encode(
hashlib.sha256(verifier.encode()).digest()
).rstrip(b"=").decode()
return {"verifier": verifier, "challenge": challenge}
def get_authorization_url(self, state: str = "") -> Dict[str, str]:
"""สร้าง Authorization URL"""
if not state:
state = secrets.token_urlsafe(32)
pkce = self.generate_pkce()
params = {
"response_type": "code",
"client_id": self.config.client_id,
"redirect_uri": self.config.redirect_uri,
"scope": " ".join(self.config.scopes),
"state": state,
"code_challenge": pkce["challenge"],
"code_challenge_method": "S256",
}
query = "&".join(f"{k}={v}" for k, v in params.items())
url = f"{self.config.authorization_endpoint}?{query}"
return {
"url": url,
"state": state,
"code_verifier": pkce["verifier"],
}
def exchange_code(self, code: str, code_verifier: str) -> dict:
"""แลก Authorization Code เป็น Tokens"""
# payload = {
# "grant_type": "authorization_code",
# "code": code,
# "redirect_uri": self.config.redirect_uri,
# "client_id": self.config.client_id,
# "client_secret": self.config.client_secret,
# "code_verifier": code_verifier,
# }
# response = httpx.post(self.config.token_endpoint, data=payload)
# return response.json()
return {
"access_token": "eyJhbGciOiJSUzI1NiJ9...",
"id_token": "eyJhbGciOiJSUzI1NiJ9...",
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2g...",
"token_type": "Bearer",
"expires_in": 3600,
}
def refresh_token(self, refresh_token: str) -> dict:
"""Refresh Access Token"""
print(f" Refreshing token...")
return {"access_token": "new_token...", "expires_in": 3600}
# ตัวอย่าง
config = OIDCConfig(
issuer="https://auth.example.com",
client_id="my-app-client-id",
client_secret="my-app-client-secret",
redirect_uri="http://localhost:3000/callback",
)
client = OIDCClient(config)
# OIDC Flows
flows = {
"Authorization Code + PKCE": {
"use": "Web App, SPA, Mobile App",
"security": "สูงสุด",
"steps": "1.Auth URL -> 2.User Login -> 3.Callback Code -> 4.Exchange Token",
},
"Client Credentials": {
"use": "Machine-to-Machine, API-to-API",
"security": "สูง",
"steps": "1.Send Client ID+Secret -> 2.Get Access Token",
},
"Device Authorization": {
"use": "Smart TV, CLI, IoT",
"security": "สูง",
"steps": "1.Get Device Code -> 2.User Login on Browser -> 3.Poll for Token",
},
}
print("OIDC Authentication Flows:")
for flow, info in flows.items():
print(f"\n [{flow}]")
for key, value in info.items():
print(f" {key}: {value}")
auth = client.get_authorization_url()
print(f"\n Auth URL: {auth['url'][:80]}...")
print(f" State: {auth['state'][:20]}...")
Developer Experience SDK

# dx_sdk.py — Developer Experience SDK Design
from dataclasses import dataclass
from typing import Dict, Optional, Callable
import time
@dataclass
class SDKConfig:
api_key: str
base_url: str = "https://api.example.com"
timeout: int = 30
retry_count: int = 3
auto_refresh: bool = True
class DeveloperSDK:
"""SDK ที่ออกแบบเพื่อ Developer Experience ที่ดี"""
def __init__(self, config: SDKConfig):
self.config = config
self._access_token: Optional[str] = None
self._token_expiry: float = 0
def _ensure_auth(self):
"""Auto-refresh token (ไม่ต้อง Handle เอง)"""
if time.time() >= self._token_expiry:
self._refresh_token()
def _refresh_token(self):
"""Refresh token อัตโนมัติ"""
self._access_token = "refreshed_token"
self._token_expiry = time.time() + 3600
print(" [SDK] Token refreshed automatically")
def _request(self, method: str, path: str, **kwargs) -> dict:
"""HTTP Request พร้อม Auto-retry และ Error Handling"""
self._ensure_auth()
for attempt in range(self.config.retry_count):
try:
# response = httpx.request(
# method, f"{self.config.base_url}{path}",
# headers={"Authorization": f"Bearer {self._access_token}"},
# timeout=self.config.timeout,
# **kwargs
# )
# response.raise_for_status()
# return response.json()
return {"status": "ok", "data": {}}
except Exception as e:
if attempt == self.config.retry_count - 1:
raise
time.sleep(2 ** attempt)
# DX-friendly API Methods
def get_user(self, user_id: str) -> dict:
"""ดึงข้อมูลผู้ใช้"""
return self._request("GET", f"/users/{user_id}")
def list_users(self, page: int = 1, limit: int = 20) -> dict:
"""ดึงรายชื่อผู้ใช้"""
return self._request("GET", "/users", params={"page": page, "limit": limit})
def create_user(self, email: str, name: str, **kwargs) -> dict:
"""สร้างผู้ใช้ใหม่"""
return self._request("POST", "/users", json={"email": email, "name": name, **kwargs})
# DX Best Practices
dx_practices = {
"Quick Start": {
"goal": "First API Call ภายใน 5 นาที",
"how": "3 บรรทัด: Install -> Config -> Call",
"example": "sdk = SDK(api_key='xxx'); sdk.get_user('123')",
},
"Auto Auth": {
"goal": "ไม่ต้อง Handle Token เอง",
"how": "SDK จัดการ Token Refresh อัตโนมัติ",
"example": "ใส่ API Key ครั้งเดียว ใช้ได้ตลอด",
},
"Clear Errors": {
"goal": "Error Messages เข้าใจง่าย",
"how": "บอกสาเหตุ + วิธีแก้ + Link Docs",
"example": "AuthError: API key expired. Refresh at dashboard.example.com/keys",
},
"Type Safety": {
"goal": "IDE Autocomplete ทำงานได้",
"how": "TypeScript Types, Python Type Hints",
"example": "Response มี Type ชัดเจน ไม่ต้องเดา",
},
"Pagination": {
"goal": "ใช้ Pagination ง่าย",
"how": "Iterator Pattern หรือ cursor-based",
"example": "for user in sdk.list_users(): ...",
},
}
print("Developer Experience Best Practices:")
for practice, info in dx_practices.items():
print(f"\n [{practice}]")
print(f" Goal: {info['goal']}")
print(f" How: {info['how']}")
print(f" Example: {info['example']}")
Developer Portal
# dev_portal.py — Developer Portal Components
portal_components = {
"Documentation": {
"tools": "Mintlify, Docusaurus, ReadMe.io",
"must_have": "Quick Start, API Reference, Guides, FAQ",
"dx_tip": "ทุกหน้ามี Code Example Copy-paste ได้",
},
"API Playground": {
"tools": "Swagger UI, Stoplight, Custom React",
"must_have": "Interactive API Testing, Auth Built-in",
"dx_tip": "ทดสอบ API ได้ทันทีไม่ต้องเขียนโค้ด",
},
"Dashboard": {
"tools": "Custom Next.js + Shadcn UI",
"must_have": "API Keys, Usage Stats, Billing, Logs",
"dx_tip": "สร้าง API Key ได้ทันที ดู Usage Real-time",
},
"SDKs": {
"tools": "OpenAPI Generator, Stainless, Fern",
"must_have": "Python, JavaScript, Go, Java, Ruby",
"dx_tip": "Auto-generate จาก OpenAPI Spec ทุกภาษา",
},
"Changelog": {
"tools": "GitHub Releases, Headway, LaunchNotes",
"must_have": "Breaking Changes, New Features, Fixes",
"dx_tip": "แจ้ง Breaking Changes ล่วงหน้า Migration Guide",
},
"Status Page": {
"tools": "Statuspage, Instatus, Upptime",
"must_have": "Uptime, Incidents, Maintenance",
"dx_tip": "แจ้ง Incident ทันที Transparent",
},
}
print("Developer Portal Components:")
for component, info in portal_components.items():
print(f"\n [{component}]")
for key, value in info.items():
print(f" {key}: {value}")
# DX Metrics
metrics = {
"TTFHW": "Time to First Hello World — เวลาที่ Dev ใช้ทำ First API Call",
"TTFC": "Time to First Commit — เวลาจาก Signup ถึง Commit Code จริง",
"API Error Rate": "อัตรา Error ที่ Dev พบ ควรต่ำกว่า 1%",
"Doc Bounce Rate": "อัตราที่ Dev ออกจาก Docs ทันที ควรต่ำ",
"Support Ticket Rate": "จำนวน Support Tickets ต่อ Developer ควรลดลง",
"NPS (Net Promoter Score)": "Dev แนะนำ API ให้คนอื่นไหม ควรมากกว่า 40",
}
print(f"\n\nDX Metrics:")
for metric, desc in metrics.items():
print(f" {metric}")
print(f" {desc}")
Best Practices
- PKCE: ใช้ Authorization Code + PKCE เสมอ ไม่ว่า Web หรือ Mobile
- Auto Refresh: SDK ต้อง Handle Token Refresh อัตโนมัติ
- 5 Minutes: First API Call ต้องทำได้ภายใน 5 นาที
- Error Messages: บอกสาเหตุ วิธีแก้ Link ไป Docs
- Code Examples: ทุกหน้า Docs มี Code Example Copy-paste
- OpenAPI Spec: ใช้ OpenAPI Spec เป็น Source of Truth Auto-generate SDK
OpenID Connect คืออะไร
Identity Layer บน OAuth 2.0 Authentication ยืนยันตัวตน ID Token JWT SSO Single Sign-On Google GitHub Microsoft ปลอดภัยกว่า Session-based
แนะนำเพิ่มเติม — บทวิเคราะห์จาก XM Signal
เนื้อหาเกี่ยวข้อง — ดูเพิ่มเติมเรื่อง Prometheus Alertmanager Log Management ELK





