Authorization code with PKCE for Flutter

Boonsong Srithong
odds.team
Published in
3 min readMay 1, 2024

OAuth 2.0 แนะนำให้ใช้ Authorization code ด้วย Proof Key for Code Exchange (PKCE) มันคืออะไร ทำไมต้องใช้ และมันทำงานอย่างไร

What is PKCE?

ก่อนจะลงเรืออยากพานั่งไทม์แมชชีนเพื่อย้อนเวลากลับไป ทำความเข้าใจ Proof Key for Code Exchange (PKCE RFC 7636) กันก่อน เจ้าหนู PKCE คือ extension ขอ OAuth 2.0 Industry-standard protocol ที่ใช้สำหรับการทำ Authorization framework เกิดและดำรงอยู่เพื่อ improve security เรื่อง authorization code interception attacks

แนวคิดของ PKCE คือทำให้มั่นใจวว่า application ที่เริ่มทำ authentication flow กับ application ที่ได้ข้อมูล ID token, Access token คือ application เดียวกัน ไม่ได้มี malicious app ที่ไหนมาขโมย authentication code แล้วสวมรอยนำ access token ไปใช้งาน
ตัวอย่างการรักเล็กขโมยน้อย OAuth 2.0 Auth Code Injection Attack in Action

Why use PKCE?

PKCE ออกแบบเพื่ออุดช่องโหว่ authorization code ปัญหาที่เจอคือ malicious app สามารถขโมย authorization code และนำไป request access tokens ผลกระทบคือ ไอ้ผู้ร้ายไอ้คนนิสัยไม่ดีสามารถนำ access token ไปใช้ authentication เพื่อเข้าถึง resource ต่างๆ ใน application ของเรา ปัญหานี้เกิดในฝั่งของ desktop, mobile application และ single page applications ที่ไม่สามารถ(ไม่ควร)เก็บค่าเก็บ secret key ฝังลงใน application เพราะสามารถ decompiling app หรืออ่านค่าจาก browser ดังนั่นเจ้าหนู PKCE จึงเข้ามาช่วยอุดรูรั่ว วิธีคิดของ PKCE คือการเพิ่ม code verifier และ code challenge ใช้เป็นตัวยืนยันระหว่าง client และ authorization server

How it works?

PKCE flow:

  1. ผู้ใช้งานเข้าใช้งาน application และกดปุ่ม login ตรงส่วนนี้คือการเริ่ม authentication process
  2. application ทำ cryptographically-random จะได้ค่า characters ระหว่าง 43–128 characters เพื่อใช้เป็นค่า code_verifier หลังจากนั่นจะสร้างค่า code_challenge โดยสามารถสร้างได้ 2 วิธี
    Code Challenge Method:
    - plain
    : code challenge จะมีค่าเหมือนกันกับ code verifier
    - S256: code challenge จะมีค่าเท่ากับการนำ code verifier มา hash ด้วย SHA256 และ base64 URL Encoding
    Example: EBase64UrlEncode(SHA256Hash(code_verifier))
  3. application redirect ผู้ใช้งานไปที่ authorization server พร้อมแนบค่า code_challeng และ code_challenge_method เป็น parameter ไปใน endpoint
    Example: https://boonsong.dev/authorization ?client_id=(client-id} &response_type=code &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM &code_challenge_method=S256
  4. authorization server redirect ผู้ใช้งานไปยัง Login page
  5. ผู้ใช้งานกรอกข้อมูล username/password หรือเลือก login options เช่น login with google, login with facebook หรือ login with vk
  6. เมื่อ login สำเร็จเจ้า authorization server จะเก็บค่า code challenge และทำการ redirect ผู้ใช้กลับไปยัง application พร้อมค่า authorization code
    Authorization code:
    เรื่องสั่นๆ ว่า auth code คือ key ที่ผู้ใช้งานให้สิทธิ์เพื่อใช้ขอ access token ในการเข้าถึง resource โดยค่า auth code จะต้อง short-lived และ single use
    Example: https://boonsong.dev/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
  7. application นำค่า auth code และ code verifier (ที่สร้างขึ้นตอน step 2) ส่งไปยัง authorization server เพื่อขอ access token และ refresh token
  8. authorization server verifies ค่า code verifier และ code challenge ในขั้นตอนนี้เป็นการตรวจสอบเพื่อให้แน่ใจว่าไม่มีใครขโมย auth code ถึงแม้มี malicious app ดักจับ auth code ก็ไม่มี code verifier ในการยื่นยันตัวตนอยู่ดี
  9. ถ้า authorization code และ code verifier ถูกต้อง authorization server จะ response ข้อมูล access_token และค่าอื่นๆ เช่น refresh_token, token_type ตามที่มีการ configs ไว้
    Example:
    {
    “access_token”: “KPLXrl_wJSHqU708R9kp3bNRGi0LgKUdh0kh-CQhx9g”, “refresh_token”: “YLRJXfratV4yq0_65seCT0bF6YxxgU5jKBUvhOZPrb4”, “token_type”: “Bearer”, “expires_in”: 86400
    }

How to implement it

เราจะมาลอง Implement PKCE โดยมี client เป็น mobile app พัฒนาด้วย Flutter และใช้ authorization server เป็น Keycloak ที่พัฒนา based on standard protocols ของ OAuth 2.0 (ถ้าคุณใช้ keycloak เราคือเพื่อนกัน https://www.facebook.com/groups/keycloak.thailand/)

สำหรับ Flutter เราสามารถใช้ flutter_appauth เป็นไลบรารีช่วยจัดการเรื่อง authenticate และ authorize end-users ของ OAuth 2.0 ตัวไลบรารีช่วยจัดการเรื่อง code verifier และ code challenge ให้หรือเราจะสร้างเองและส่งให้ flutter_appauth ก็ได้เหมือนกัน https://appauth.io/

ตัวอย่าง Code AuthorizationRequest

      
const String clientId = 'pkce-client';
const String redirectUrl = 'com.example.mobileapp:/oauth2redirect';
const String discoveryUrl =
'http://localhost:8080/realms/heyboonsong/.well-known/openid-configuration';
const List<String> scopes = <String>[
'email',
];

final AuthorizationResponse? response = await appAuth.authorize(
AuthorizationRequest(clientId, redirectUrl,
discoveryUrl: discoveryUrl, scopes: scopes),
);

ตัวอย่าง Code TokenRequest

    const String clientId = 'pkce-client';
const String redirectUrl = 'com.example.mobileapp:/oauth2redirect';
const String discoveryUrl =
'http://localhost:8080/realms/heyboonsong/.well-known/openid-configuration';
return appAuth.token(TokenRequest(clientId, redirectUrl,
discoveryUrl: discoveryUrl,
codeVerifier: codeVerifier,
authorizationCode: authorizationCode));

สำหรับการตั้งค่า Code Challenge Method ใน Keycloak ให้ Login ที่ Admin Console -> Clients -> เลือก Clients(ใน Code ตัวอย่างชื่อ pkce-client) -> เลือก Tab Advanced เลื่อนลงมาจะเจอคำว่า Proof Key for Code Exchange Code Challenge Method

Sample application

ตัวอย่างโค้ดอยู่ที่ https://github.com/heyboonsong/flutter-with-keycloak-with-pkce

Pre-requisites:

  • Docker
  • Flutter 3.19.3
  • Dart 3.3.1

Refs:
https://blog.postman.com/what-is-pkce/

https://medium.com/@rangika123.kanchana/keycloak-integration-for-flutter-web-using-openid-client-with-authorization-code-flow-489afeac6e9f

--

--

Boonsong Srithong
odds.team

A product developer and obsessed with coding. Nice to meet you all. I'm currently working at ODDS Team