02
Phase 1: Basic • Foundation

Day 2: The Core OIDC Protocol & Authorization Code with PKCE

Phase 1 Index ↗ RFC 7636 / RFC 6749 Constructive Alignment: 100%
Module 02 • First Principles เริ่มต้นจากศูนย์: ปรับความเข้าใจโปรโตคอลสากล

1. OAuth 2.0, OIDC และ PKCE คืออะไร? ทำไมโลกเว็บและโมบายสมัยใหม่ขาดไม่ได้?

ใน Day 1 เราติดตั้ง Keycloak เป็นเซิร์ฟเวอร์กลางสำเร็จแล้ว แต่คำถามถัดมาคือ: "เมื่อผู้ใช้กดปุ่ม Login บนหน้าเว็บหรือแอปมือถือ ข้อมูลถูกส่งไปหา Keycloak อย่างไร และ Keycloak ตอบอะไรกลับมาให้แอปใช้งาน?"

ในอดีต (ยุคก่อนปี 2010) หากแอปพลิเคชันภายนอกต้องการเข้าถึงข้อมูลของคุณ (เช่น แอปพิมพ์รูปต้องการดึงรูปภาพจาก Google Drive) ทางเดียวคือคุณต้อง "กรอก Username และ Password ของ Google ให้แอปพิมพ์รูปไปตรงๆ"! ซึ่งอันตรายอย่างยิ่ง เพราะแอปนั้นจะได้รหัสผ่านตัวจริงของคุณไปทำอะไรก็ได้

🔑 OAuth 2.0 (RFC 6749) Authorization Only

"มอบอำนาจให้ทำอะไรได้บ้าง (Delegated Access)"

  • เป็นกรอบการทำงาน (Framework) สำหรับแจก Access Token เพื่อใช้เรียก API
  • ไม่ได้บอกว่าผู้ใช้คือใคร ชื่ออะไร หรืออีเมลอะไร (ไม่สน Identity)
  • เปรียบเสมือน "บัตรผ่านประตูที่ระบุแค่ว่า เข้าห้องไหนได้บ้าง"
🪪 OpenID Connect / OIDC Authentication Layer

"ยืนยันว่าคุณคือใคร (Identity Assertion)"

  • เป็นชั้นมาตรฐานระบุตัวตนที่สร้างครอบอยู่บน OAuth 2.0 อีกชั้นหนึ่ง
  • เพิ่ม ID Token (JWT) ที่มีข้อมูลของผู้ใช้ (ชื่อ, อีเมล, sub/ID) ให้หน้าแอปแสดงผล
  • จัดเตรียม Endpoint /userinfo ให้ดึงข้อมูลโปรไฟล์เพิ่มเติม
สูตรจำวิศวกรรมที่ถูกต้อง 100%: OAuth 2.0 = มอบอำนาจเข้าถึงทรัพยากร (Authorization) | OIDC = ยืนยันตัวตนว่าใครล็อกอิน (Authentication)

🛡️ แล้ว PKCE คืออะไร? ทำไมแอปเว็บ SPA และแอปมือถือถึงต้องใช้?

แอปพลิเคชันแบ่งเป็น 2 ประเภทหลักตามความสามารถในการรักษาความลับ:

1. Confidential Client (เช่น Backend NodeJS/Java/Go)

ทำงานอยู่บนเซิร์ฟเวอร์ปิดหลังบ้าน สามารถเก็บ Client Secret (รหัสผ่านประจำแอป) ไว้เป็นความลับได้ 100% บุคคลภายนอกไม่สามารถมองเห็นได้

2. Public Client (เช่น React, Vue, Flutter, iOS, Android)

โค้ดทั้งหมดทำงานอยู่บนเบราว์เซอร์หรือเครื่องของลูกค้า ห้ามใส่ Client Secret เด็ดขาด เพราะผู้ใช้สามารถกด Inspect ดู Source Code หรือ Decompile ไฟล์ APK/IPA เพื่อขโมย Secret ได้ทันที!

เมื่อ Public Client ไม่มี Client Secret ทาง IETF จึงคิดค้นมาตรฐาน PKCE (Proof Key for Code Exchange - RFC 7636) ขึ้นมา โดยใช้หลักการ "สร้างรหัสลับเฉพาะกิจแบบสุ่มขึ้นมาใช้ชั่วคราว (One-time Dynamic Secret) ในทุกๆ ครั้งที่มีการขอล็อกอิน" ทำให้แม้จะมีคนแอบดักจับ Authorization Code ไปได้ ก็ไม่สามารถนำไปแลกเป็น Token ได้หากไม่มีรหัสลับที่แท้จริง

📚 ศัพท์เทคนิคสำคัญประจำโปรโตคอล OIDC & PKCE (Formal Technical Glossary)

ตารางนิยามศัพท์ที่เป็นรากฐานของข้อสอบและงานวิศวกรรม IAM ทั้งหมด:

ศัพท์เทคนิค นิยามทางวิศวกรรม หน้าที่หลัก & ประโยชน์ ข้อแตกต่างสำคัญ
Front-Channel vs Back-Channel Front-channel: การส่งข้อมูลผ่าน Browser URL / Address Bar
Back-channel: การส่ง Direct HTTPS POST ระหว่าง Server ถึง Server
Front-channel มองเห็นได้ผ่าน History และดักฟังได้ง่าย ส่วน Back-channel เข้ารหัส TLS ปลอดภัย ไม่ผ่านเบราว์เซอร์ Token ตัวจริงต้องส่งผ่าน Back-Channel เท่านั้น
code_verifier รหัสลับความปลอดภัยสูง (High-entropy Cryptographic String) สุ่มขึ้นมาความยาว 43–128 ตัวอักษร เก็บไว้ใน RAM ของ Client Application และส่งไปให้ Keycloak เฉพาะตอนยิง Back-channel POST /token เท่านั้น ห้ามส่งใน URL Query เด็ดขาด
code_challenge (S256) ผลลัพธ์จากการนำ code_verifier มาคำนวณผ่าน Hash ทางเดียว: Base64URL(SHA-256(verifier)) ส่งไปในขั้นตอนแรกที่ /auth หากแฮกเกอร์ดักจับได้ ก็ไม่สามารถย้อนหารหัส Verifier ต้นฉบับได้ ต้องใช้ S256 (ห้ามใช้ plain)
state Parameter Cryptographic Random String ประจำ Session ที่ Client สุ่มสร้างขึ้นก่อน Redirect ไปหน้า Login ป้องกันการโจมตีประเภท Cross-Site Request Forgery (CSRF) ในขั้นตอน Login Callback โดย Client ต้องเทียบว่าค่าที่ส่งกลับมาตรงกับตอนส่งไปหรือไม่ ผูกติดกับ Session Cookie
nonce Parameter String สุ่มที่ Client แนบไปใน Authentication Request เพื่อให้ Keycloak นำไปฝังลงใน Claim nonce ของ ID Token ป้องกัน ID Token Replay Attacks โดย Client จะตรวจว่า nonce ใน ID Token ตรงกับที่ตนเองจำไว้ในหน่วยความจำหรือไม่ ใช้ตรวจความสดใหม่ของ ID Token
id_token vs access_token id_token: JWT ยืนยันตัวตนผู้ใช้แก่ Client App
access_token: สิทธิ์การเข้าถึงส่งไปให้ Resource API
ID Token ใช้สำหรับแสดงชื่อผู้ใช้บน UI ส่วน Access Token ใช้แนบใน Authorization: Bearer <token> เพื่อเรียกข้อมูลจาก Resource Server ID = Client อ่าน
Access = Resource API อ่าน
Mental Model #02 ภาพเปรียบเทียบเชิงกายภาพในชีวิตประจำวัน

The Airport Luggage Claim with Cryptographic Seal

เปรียบเทียบกลไก PKCE เสมือน "การส่งกระเป๋าสัมภาระที่สนามบิน": ผู้เดินทางสุ่มรหัสลับขึ้นมาในใจ (code_verifier) และส่งเพียงตราประทับลายนิ้วมือที่แฮชแล้ว (code_challenge) ให้เจ้าหน้าที่เคาน์เตอร์ เมื่อเจ้าหน้าที่ส่งตั๋วชั่วคราว (authorization_code) วิ่งผ่านสายพานเปิด แม้จะมีมิจฉาชีพแอบขโมยตั๋วใบนั้นไปได้ แต่มิจฉาชีพก็ไม่สามารถนำตั๋วไปเปิดรับกระเป๋าตัวจริง (Tokens) ได้ เพราะไม่มีรหัสลับต้นฉบับที่อยู่ในใจของผู้เดินทางตัวจริง!

User / Browser Client App (SPA / Mobile) Keycloak IdP Server Realm: "app-dev" Resource API 1. Generate code_verifier challenge = S256(verifier) Front-channel: GET /auth?response_type=code&code_challenge=...&state=xyz 2. Keycloak renders Login Form ➔ User Authenticates (Password / 2FA) Front-channel: 302 Redirect: callback?code=AUTH_CODE_789&state=xyz App extracts Code from URL Back-channel: POST /token (grant_type=authorization_code, code=AUTH_CODE_789, code_verifier=RAW_SECRET) Keycloak S256 Cryptographic Verification: Base64URL(SHA256(verifier)) == stored_challenge Back-channel: 200 OK: { access_token, id_token, refresh_token } 6. GET /api/v1/orders (Authorization: Bearer <access_token>) 7. Verified Signature via Keycloak JWKS: Return Protected Data
Case Study #02 Security Incident Post-Mortem

The $1.2M Custom URI Scheme Hijack: Insecure Public Client & Authorization Code Theft

Direct Financial Loss
$1,240,000

เงินฝากและสินทรัพย์ดิจิทัลถูกถ่ายโอนออกจากบัญชีผู้ใช้ 480 ราย ก่อนที่ธนาคารจะระงับการทำงานของระบบ

Vulnerability Mechanism
No PKCE & Wildcard Redirect URI

ใช้ Public Client โดยไม่ออกแบบ Proof Key (PKCE) และยอมรับ Wildcard Redirect URI ส่งผลให้มัลแวร์ดักจับ Authorization Code ได้สำเร็จ

Architectural Takeaway
Mandatory RFC 7636 (S256) & Exact URI

บังคับใช้ PKCE โหมด S256 เสมอ, แบน Implicit Flow อย่างเด็ดขาด และห้ามใช้ Wildcard ใน Redirect URIs

สถานการณ์จริง: แอปพลิเคชัน Neo-Bank บนสมาร์ตโฟนแห่งหนึ่ง ได้ทำการต่อ Authentication เข้ากับ Keycloak โดยใช้ Authorization Code Flow สำหรับการส่ง Authorization Code กลับมายังแอป ทีมนักพัฒนาได้จดทะเบียน Custom URI Scheme ชื่อ bankapp://oauth-callback และตั้งค่า Redirect URI บนเซิร์ฟเวอร์แบบผ่อนปรนเป็น bankapp://*

เนื่องจากเป็น Mobile Application (Public Client) ทีมงานจึงไม่ได้ใส่ Client Secret และที่ร้ายแรงที่สุดคือ ไม่ได้เปิดใช้งาน PKCE (Proof Key for Code Exchange)

ผู้โจมตีได้สร้างมัลแวร์แฝงมาในคราบแอปอัปเดตสภาพอากาศ แล้วจดทะเบียน Intent Filter บนระบบปฏิบัติการของเหยื่อเพื่อดักจับ Custom Scheme bankapp://oauth-callback เมื่อเหยื่อเข้าสู่หน้าจอ Login ผ่าน Browser และกดยืนยันตัวตนสำเร็จ IdP จึง Redirect Code กลับมาที่เครื่อง ผลปรากฏว่ามัลแวร์สามารถสกัดกั้น (Intercept) นำ code=AUTH_CODE_XYZ ไปได้ทันที และเนื่องจากไม่มีกลไก PKCE ตรวจสอบ มัลแวร์จึงยิง HTTP POST ตรงเข้าหา /token endpoint เพื่อแลกเป็น Access Token และ Refresh Token ของเหยื่อ สวมรอยโอนเงินออกไปได้สำเร็จนับล้านดอลลาร์

Defense Argument สำหรับคุยกับทีม: "ในโลกของ Modern IAM แอป Public Client เช่น Single Page Apps (React/Vue) หรือ Mobile Apps ไม่สามารถรักษาความลับของ Client Secret ได้ การส่ง Authorization Code ผ่านเบราว์เซอร์จึงเหมือนการส่งรหัสผ่านช่องทางที่ถูกดักฟังได้เสมอ มาตรฐาน OAuth 2.1 และ RFC 7636 จึงบังคับใช้ PKCE ด้วยอัลกอริทึม S256 เสมอ เพื่อสร้าง Dynamic Cryptographic Secret สำหรับยืนยันตัวตนในทุกๆ Login Session โดยห้ามใช้ Implicit Flow และห้ามใช้ Wildcard Redirect URI เป็นอันขาด!"
Hands-on Breakdown แกะโครงสร้างคำสั่ง & ลงมือสร้างจริง 3 เลเวล

Anatomy of OIDC Authorization & Token Endpoints

ทุกการทำ OIDC Handshake ประกอบด้วยสองฝั่ง: Front-Channel (ผ่าน Browser Redirect) และ Back-Channel (Direct HTTPS POST Request) ซึ่งต้องส่งค่าพารามิเตอร์ให้ถูกต้องตามมาตรฐาน RFC:

Parameter Name Endpoint & Method หน้าที่ทางวิศวกรรมความปลอดภัย Security Guardrail
response_type=code /auth (GET) สั่งให้ Keycloak คืน Authorization Code ชั่วคราวแทนการคืน Token ตรงๆ ห้ามใส่ response_type=token (Implicit Flow) เด็ดขาด
code_challenge /auth (GET) สตริง Base64URL-encoded ของผลลัพธ์ SHA-256(code_verifier) ต้องสุ่มและคำนวณใหม่ทุกครั้งที่มีการ Login
code_challenge_method /auth (GET) อัลกอริทึมที่ใช้แปลง verifier เป็น challenge ต้องเซ็ตเป็น S256 เท่านั้น (ห้ามใช้ plain)
state /auth & callback Cryptographic Random String ประจำ Session เพื่อป้องกัน CSRF Client ต้องตรวจสอบว่าค่าที่ส่งกลับมาตรงกับที่ตนจำไว้
code_verifier /token (POST) รหัสลับต้นฉบับ (43–128 ตัวอักษร) ที่ส่งไปให้ Keycloak คำนวณแฮชเทียบ ต้องส่งใน HTTP POST Body (x-www-form-urlencoded) เท่านั้น
LEVEL 1: Inspecting OIDC Discovery Metadata Standard OpenID Endpoint

ดึงข้อมูล Metadata ของ Realm เพื่อสำรวจ Public Endpoints และ Supported Algorithms จาก Keycloak ที่เรารันไว้ใน Day 1

# Query OpenID Connect Discovery Metadata สำหรับ Realm 'app-dev'
curl -s http://localhost:8080/realms/app-dev/.well-known/openid-configuration | jq '{
  issuer: .issuer,
  authorization_endpoint: .authorization_endpoint,
  token_endpoint: .token_endpoint,
  jwks_uri: .jwks_uri,
  code_challenge_methods_supported: .code_challenge_methods_supported
}'
LEVEL 2: Pure CLI PKCE Generation & Token Exchange ⚡ Manual Handshake with Bash/OpenSSL

สร้าง Cryptographic Pairs สดๆ บน Terminal โดยไม่ต้องพึ่งไลบรารีภายนอก และนำไปแลก Token

# 1. สร้าง Code Verifier แบบสุ่ม (Base64URL 43-128 ตัวอักษร)
CODE_VERIFIER=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
echo "Code Verifier: $CODE_VERIFIER"

# 2. คำนวณ Code Challenge ด้วย SHA256 + Base64URL
CODE_CHALLENGE=$(printf "%s" "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=')
echo "Code Challenge: $CODE_CHALLENGE"

# 3. นำ URL ด้านล่างไปเปิดบน Web Browser เพื่อ Login รับ Authorization Code
echo "เปิด URL นี้ในเบราว์เซอร์: http://localhost:8080/realms/app-dev/protocol/openid-connect/auth?client_id=frontend-spa&response_type=code&scope=openid%20profile%20email&redirect_uri=http://localhost:3000/callback&code_challenge=$CODE_CHALLENGE&code_challenge_method=S256&state=random_state_123"

# 4. เมื่อ Browser redirect ไปที่ http://localhost:3000/callback?code=AUTH_CODE_HERE ให้นำ code มายิงแลก Token (ต้องใช้ HTTP POST):
curl -X POST http://localhost:8080/realms/app-dev/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "client_id=frontend-spa" \
  -d "redirect_uri=http://localhost:3000/callback" \
  -d "code=ใส่_AUTH_CODE_ที่ได้จาก_URL_ตรงนี้" \
  -d "code_verifier=$CODE_VERIFIER"
LEVEL 3: Client Security Configuration (Anti-Pattern vs Production-Ready) Client Hardening
❌ Flawed & Insecure Client Setup
  • เปิดใช้งาน Implicit Flow บน Single Page App (SPA)
  • ใส่ Wildcard ใน Valid Redirect URIs เช่น https://myapp.com/* หรือ *
  • ไม่เปิด PKCE หรือเลือกใช้ challenge method เป็น plain
  • พยายามฝัง Client Secret ไว้ในโค้ด JavaScript หรือ Mobile App
  • ไม่ตรวจสอบพารามิเตอร์ state ในหน้า Callback
# เสี่ยง: ผู้โจมตีสามารถทำ Open Redirect และดักขโมย Authorization Code หรือ Token ผ่าน XSS ได้ทันที
✅ Production-Grade Hardened Client
  • เปิดเฉพาะ Standard Flow (Authorization Code) และปิด Implicit Flow อย่างเด็ดขาด
  • ระบุ Exact Redirect URI เช่น https://app.corp.com/callback (ห้ามใช้ Wildcard)
  • บังคับใช้ PKCE Code Challenge Method = S256 ใน Advanced Client Settings
  • ตั้งค่า SPA/Mobile เป็น Public Client (ไม่มี Secret) และใช้ Refresh Token Rotation
  • ตรวจสอบความถูกต้องของ state และ nonce ก่อนบันทึก Session
# ปลอดภัยสูงสุด: ป้องกัน Code Injection, Token Theft และ Replay Attacks ตามมาตรฐาน OAuth 2.1
Simulator #02 Live Cryptographic Hash & Handshake Stepper

Live PKCE Generator & Protocol Verification Sandbox

ทดลองสร้าง code_verifier และดูการคำนวณ code_challenge แบบ Real-time ผ่าน Web Cryptography API พร้อมก้าวผ่านแต่ละสเต็ปของโปรโตคอล OIDC

Step 1: Code Verifier (Secret String)
ความยาวต้องอยู่ระหว่าง 43 ถึง 128 ตัวอักษร (RFC 7636)
Step 2: Computed Code Challenge (S256) Base64URL(SHA256)
Generating...
Generating URL...
Interactive Step-by-Step Flow Verifier Step 1 of 4
1. Client ส่งคำขอ Authorization Request ไปยัง Keycloak พร้อมกับ code_challenge แต่ ยังไม่ส่ง code_verifier ออกจากเครื่องเด็ดขาด
Active Recall Comprehensive Exam 12 Production Scenarios (100% Aligned with Content)

Day 2 OIDC Core & PKCE Cryptographic Mastery Exam

ครอบคลุม OIDC vs OAuth 2.0, PKCE S256 Mechanics, Nonce/State validation, Public Clients และทบทวนสาระสำคัญจาก Day 1

Live Score
0/12 answered
0 / 12
🎯

Day 2 Practical Deliverable (ภารกิจประจำวัน)

ก่อนก้าวไปสู่ Day 3 (Client Scopes, Token Custom Claims & Protocol Mappers) คุณต้องทำภารกิจต่อไปนี้ให้สำเร็จบนสภาพแวดล้อมจริง: